2

Express SSR

yarn add express
#or
npm i express --save

在根目录新建一个 index.js 文件, 内容如下:

index.js

const app = require('express')()

app.get('/', (req, res) => {
  res.send(`
  <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
    </head>
    <body>
      <h1>你好世界!</h1>
    </body>
    </html>
  `)
})

app.listen(3000);
console.info('application is running at: http://localhost:3000');

这时候执行 node .\index.js 是可以访问到你好世界的, 这其实就是 SSR。

Vue-SSR 意思可以解读为将 Vue 对象放在服务端创建。

Express 渲染 Vue

安装 vue 与 vue-server-renderer。

yarn add vue vue-server-renderer
#or
npm i vue vue-server-renderer --save

在 express 中渲染一个 Vue 的实例需要 3 步:

  1. 创建一个 Vue 实例
  2. 创建一个 Renderer
  3. 用 Renderer 渲染 Vue 实例

所以, 打开 index.js 文件,在顶部添加引入

const Vue = require('vue')
const renderer = require('vue-server-renderer').createRenderer()

引入后再添加一个新的路由地址

app.get('/vue', (req, res) => {
  // 创建 Vue 实例
  const app = new Vue({
    data: {
      content: 'Hello Vue'
    },
    template: `
    <h1>{{content}}</h1>
    `
  })

  // 使用 renderer 把 Vue 实例渲染为 HTML
  renderer.renderToString(app, (err, html) => {
    if (err) {
      res.status(500).end('Internal Server Error')
      return
    }
    res.end(`
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
    </head>
    <body>
      ${html}
    </body>
    </html>
    `)
  })
})

准备好以后重新启动

node index.js

访问:http://localhost:3000/vue,即可查看效果。

使用 HTML 模板

观察上面的内容不难发现, Vue 实例被渲染后生成的 HTML 并不是一个完整的文件,他是需要配合 end 函数中的一大堆字符串 HTML 一起工作。

这么一大串 HTML 的字符串写起来可是不太友好。

所以 renderer 对象在创建时可以被指定一个 HTML 模板,这个模板相当于把 end 函数中的那一大串 HTML 字符串提出去了,并且通过约定,将 Vue 实例生成的 HTML 放在约定好的位置。

具体做法如下:

  1. 在根目录新建 index.template.html 文件。
  2. 写入基础 HTML。
  3. 在想要渲染 Vue 实例的位置贴上 <!--vue-ssr-outlet--> 这个注释。

例如:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>

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

</html>
注意:<!--vue-ssr-outlet--> 不可以有空格

然后修改 index.js 文件的 createRenderer 函数:

const renderer = require('vue-server-renderer').createRenderer({
  template: require('fs').readFileSync('./index.template.html', 'utf-8')
})

修改 /vue 路由:

app.get('/vue', (req, res) => {
  const app = new Vue({
    data: {
      content: 'Hello Vue'
    },
    template: `
    <h1>{{content}}</h1>
    `
  })
  renderer.renderToString(app, (err, html) => {
    if (err) {
      res.status(500).end('Internal Server Error')
      return
    }
    res.end(html)
  })
})

重新启动

node index.js

访问:http://localhost:3000/vue, 看起来一切正常。

关于 Renderer

Renderer 接口的定义如下:

interface Renderer {
  renderToString(vm: Vue, callback: RenderCallback): void;
  renderToString(vm: Vue, context: object, callback: RenderCallback): void;
  renderToString(vm: Vue): Promise<string>;
  renderToString(vm: Vue, context: object): Promise<string>;

  renderToStream(vm: Vue, context?: object): Readable;
}

renderToString 具有 4 个重载,另外还有一个 renderToStream 函数, 这些都可以尝试一下。


youbei
318 声望70 粉丝