Vue SSR 知识小记(1)

205 阅读1分钟
  • 我们需要每次创建一个全新的Vue APP

node在使用renderer返回服务端渲染过的html的时候,将会传入一个app,这个app我们之前是在node程序中提前创建好的,这将导致很多问题

const app = new Vue({ ... })
app.get('*', (req, res) => {
    renderer.renderToString(app, (err, html) => {
    ...
    })
}

有没有发现,无论是谁来请求这个文件,传入的其实是app,这个app被所有人给共享了。这是非常不好的,如果王麻子请求页面的时候对app做了修改,那么之后的人去请求页面,都将收到一定的影响。我们需要避免状态单例。

因此,我们不应该直接创建一个应用程序实例,而是应该暴露一个可以重复执行的工厂函数,为每个请求创建新的应用程序实例:

// app.js
const Vue = require('vue')

module.exports = function createApp (context) {
  return new Vue({
    data: {
      url: context.url
    },
    template: `<div>访问的 URL 是: {{ url }}</div>`
  })
}

// server.js
const createApp = require('./app')

app.get('*', (req, res) => {
  const app = createApp({ url: req.url })

  renderer.renderToString(app, (err, html) => {
    // 处理错误……
    res.end(html)
  })
})

同样的规则也适用于 router、store 和 event bus 实例。

了解以上的知识点后我们来看看如何同webpack同时构建出两个版本供给使用。

我们知道预渲染是需要一个Vue实例,而客户端其实也是需要一个Vue实例。我们往往需要做一些差异化的处理。

// 我们在普通编写单页应用的时候会在main.js中直接挂载
// 我们知道这是基于webpack 的写法

new Vue({
    router,
    store,
    render: h => h(App),
}).$mount('#app')

// 我们需要的是两个app实例,一个是给服务端渲染用的,一个是给客户端用的,所以我们便将main.js改造一下,让其变成

export function createApp () {
  const app = new Vue({
    // 根实例简单的渲染应用程序组件。
    render: h => h(App)
  })
  return { app }
}

// 会发现其是便成了创建实例的方法函数
// 我们在需要在客户端使用的时候,单纯的创建实例,挂载就好了

import { createApp } from './app'
const { app } = createApp()
app.$mount('#app')

// 在服务器渲染的时候,需要处理一下app实例
import { createApp } from './app'

export default context => {
  const { app } = createApp()
  return app
}