基本用法

本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2021-02-26

全部摘抄于:Vue SSR 指南

基本用法

使用一个页面模板

可以直接在创建 renderer 时提供一个页面模板。多数时候,我们会将页面模板放在特有的文件中,例如 index.template.html:

<!DOCTYPE html>
<html lang="en">
  <head><title>Hello</title></head>
  <body>
    <!--vue-ssr-outlet-->
  </body>
</html>

注意 <!--vue-ssr-outlet--> 注释,这里将是应用程序 HTML 标记注入的地方。

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

renderer.renderToString(app, (err, html) => {
  console.log(html) // html 将是注入应用程序内容的完整页面
})

模板插值

<!-- index.template.html -->
<html>
  <head>
    <!-- 使用双花括号进行 HTML 转义插值 -->
    <title>{{ title }}</title>

    <!-- 使用三花括号进行 HTML 不转义插值 -->
    {{{ meta }}}
  </head>
  <body>
    <!--vue-ssr-outlet-->
  </body>
</html>
const context = {
  title: 'hello',
  meta: `
    <meta ...>
    <meta ...>
  `
}

renderer.renderToString(app, context, (err, html) => {
  // 页面 title 将会是 "Hello"
  // meta 标签也会注入
})

编写通用代码

当运行在不同环境中时,我们的代码将不会完全相同。所以这里我们将会阐述你需要理解的关键事项。

1、对于服务器端渲染,我们应该使得每个请求应该都是全新的、独立的应用程序实例,以便不会有交叉请求造成的状态污染。

Node.js服务器是一个长期运行的进程。当我们的代码进入该进程时,它将进行一次取值并留存在内存中。这意味着如果创建一个单例对象,它将在每个传入的请求之间共享。所以我们为每个请求创建一个新的根 Vue 实例以避免交叉请求状态污染,因此,我们不应该直接创建一个应用程序实例,而是应该暴露一个可以重复执行的工厂函数,为每个请求创建新的应用程序实例。

由于没有动态更新(在服务端处理完之后发送给前端的就是首页完整的HTML文档了,不需要$mount挂载),所有的生命周期钩子函数中,只有 beforeCreatecreated 会在服务器端渲染过程中被调用。这就是说任何其他生命周期钩子函数中的代码只会在客户端执行。

2、此外还需要注意的是,你应该避免在 beforeCreatecreated 生命周期时产生全局副作用的代码。

例如在其中使用 setInterval 设置 timer。在纯客户端的代码中,我们一般在 beforeDestroydestroyed 生命周期时将其销毁。但是,由于在 SSR 期间并不会调用销毁钩子函数,所以 timer 将永远保留下来。为了避免这种情况,请将副作用代码移动到 beforeMountmounted 生命周期中。

3、通用代码不可接受特定平台的 API,因此如果你的代码中,直接使用了像 window 或 document,这种仅浏览器可用的全局变量,则会在 Node.js 中执行时抛出错误,反之也是如此。

建议将平台特定实现包含在通用 API 中,或者使用可以向服务器和客户端暴露相同API的一些第三方库,例如 axios;对于仅浏览器可用的 API,通常方式是,在纯客户端的生命周期钩子函数中惰性访问它们(交由页面加载完成后,应用被激活的时候来处理)。

4、大多数自定义指令直接操作 DOM,因此会在服务器端渲染 (SSR) 过程中导致错误。有两种方法可以解决这个问题:

  • 推荐使用组件作为抽象机制,并运行在虚拟 DOM 层级(例如,使用渲染函数)。

  • 如果你有一个自定义指令,但是不是很容易替换为组件,则可以在创建服务器 renderer 时,使用 createRenderer({ directives }) 选项所提供"服务器端版本"。

v-show 的服务器端版本示例:

export default function show (node: VNodeWithData, dir: VNodeDirective) {
  if (!dir.value) {
    const style: any = node.data.style || (node.data.style = {})
    if (Array.isArray(style)) {
      style.push({ display: 'none' })
    } else {
      style.display = 'none'
    }
  }
}