Vue SSR 指南(三)

Vue SSR 指南(三)

根据官网按步骤实现Vue SSR搭建过程

新建一个项目,使用vue-cli3脚手架搭建,vue create ssr-demo根据自己需求选择,项目结构如下:
在这里插入图片描述

路由和代码分割
1.router.js

类似于 createApp,我们也需要给每个请求一个新的 router 实例,所以文件导出一个 createRouter 函数

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

export function createRouter() {
  return new Router({
    mode: 'history',
    routes: [
      { path: '/', component: () => import('../views/Home.vue') },
      { path: '/about', component: () => import('../views/About.vue') },
    ],
  })
}

2.main.js
import Vue from 'vue'
import App from './App.vue'
import { createRouter } from './router'

// 导出一个工厂函数,用于创建新的
// 应用程序、router 和 store 实例
export function createApp() {
  const router = createRouter()
  const app = new Vue({
    // 根实例简单的渲染应用程序组件。
    router,
    render: (h) => h(App),
  })
  return { app, router }
}

3.entry-client.js
import { createApp } from './main'

// 客户端特定引导逻辑……

const { app, router } = createApp()

// 这里假定 App.vue 模板中根元素具有 `id="app"`
router.onReady(() => {
  app.$mount('#app')
})

4.entry-server.js
import { createApp } from './main'

export default (context) => {
  // 因为有可能会是异步路由钩子函数或组件,所以我们将返回一个 Promise,
  // 以便服务器能够等待所有的内容在渲染前,
  // 就已经准备就绪。
  return new Promise((resolve, reject) => {
    const { app, router } = createApp()

    // 设置服务器端 router 的位置
    router.push(context.url)

    // 等到 router 将可能的异步组件和钩子函数解析完
    router.onReady(() => {
      const matchedComponents = router.getMatchedComponents()
      // 匹配不到的路由,执行 reject 函数,并返回 404
      if (!matchedComponents.length) {
        return reject({ code: 404 })
      }

      // Promise 应该 resolve 应用程序实例,以便它可以渲染
      resolve(app)
    }, reject)
  })
}

5.重新构建

npm run build:win 或者 npm run build:mac ,生成新的dist

6.index.template.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <!-- 使用三花括号(triple-mustache)进行 HTML 不转义插值(non-HTML-escaped interpolation) -->
    {{{ meta }}}
    <!-- 使用双花括号(double-mustache)进行 HTML 转义插值(HTML-escaped interpolation) -->
    <title>{{ title }}</title>
  </head>

  <body>
    <!--vue-ssr-outlet-->
  </body>
</html>

7.server.js
const serverBundle = require('./dist/vue-ssr-server-bundle.json')
const express = require('express')
const app = express()
const path = require('path')
const renderer = require('vue-server-renderer').createBundleRenderer(serverBundle, {
  template: require('fs').readFileSync('./index.template.html', 'utf-8'),
})

// app.use('/js', express.static(path.resolve(__dirname, './dist/js')))
// app.use('/img', express.static(path.resolve(__dirname, './dist/img')))
// app.use('/css', express.static(path.resolve(__dirname, './dist/css')))

app.get('*', (req, res) => {
  const context = {
    title: 'ssr',
    meta: `
    <meta charset="utf-8">
    `,
    url: req.url,
  }
  renderer.renderToString(context, (err, html) => {
    if (err) {
      res.status(500).end('Internal Server Error')
      return
    }
    res.end(html)
  })
})

app.listen(8080, () => {
  console.log('已监听 localhost:8080')
})

​ url 会传给 entry-server.js 此时点击home|about,页面均是从服务端发送过来的

在这里插入图片描述

8.图片未加载,引入静态文件

(一)、app.use(express.static(path.resolve(__dirname, ‘./dist’))) 全局引入不可取

修改server.js

const serverBundle = require('./dist/vue-ssr-server-bundle.json')
const express = require('express')
const app = express()
const path = require('path')
const renderer = require('vue-server-renderer').createBundleRenderer(serverBundle, {
  template: require('fs').readFileSync('./index.template.html', 'utf-8'),
})

app.use(express.static(path.resolve(__dirname, './dist')))

app.get('*', (req, res) => {
  const context = {
    title: 'ssr===== 全局引入demo',
    meta: `
    <meta charset="utf-8">
    `,
    url: req.url,
  }
  renderer.renderToString(context, (err, html) => {
    if (err) {
      res.status(500).end('Internal Server Error')
      return
    }
    res.end(html)
  })
})

app.listen(8080, () => {
  console.log('已监听 localhost:8080')
})

运行node server.js结果标题并没有改变 ,你在服务端设置模版了(index.template.html),并设置了title为’ssr===== 全局引入demo’,但是他的显示不是这样,他所显示的是public文件夹下的index.html(若是删掉他,运行npm run build:win,它会通过webpack配置自动生成一个index.html ,title为Vue App)
点击about标签,你会发现并没有发送请求.点击浏览器的刷新按钮,他才发送请求.

在这里插入图片描述

在这里插入图片描述

需要注意的是 静态文件的引入 app.use(express.static(path.resolve(__dirname, ‘./dist’))) 所以连dist中的index.html也一并引入了,此index.html使用了main.js ,所以点击about走的是路由而不是服务端渲染.
测试
在entry-client.js 加入测试

router.onReady(() => {
 console.log('entry-client.js');
  app.$mount('#app')
})

重新打包,并运行,浏览器控制台会显示 entry-client.js

在这里插入图片描述

(二)、要么将dist/index.html 删除 或者将app.use(express.static(path.resolve(__dirname, ‘./dist’)))替换成

app.use('/js', express.static(path.resolve(__dirname, './dist/js')))
app.use('/img', express.static(path.resolve(__dirname, './dist/img')))
app.use('/css', express.static(path.resolve(__dirname, './dist/css')))

也就是说不引入 dist/index.html
在这里插入图片描述

这回浏览器的标题变成了ssr===== demo,点击about,也是从服务请求的.

9.源码

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值