编写通用代码

编写通用代码(也称为同构)意味着编写同时在服务器和客户端上运行的代码。 由于用例和平台API的不同,在不同环境中运行时,我们的代码行为将不完全相同。 在这里,我们将介绍您需要注意的关键事项。

服务器上的数据反应性

在仅客户端的应用程序中,每个用户都将在其浏览器中使用该应用程序的新实例。 对于服务器端渲染,我们希望做到这一点:每个请求都应该有一个新鲜的,隔离的应用程序实例,这样就不会出现跨请求状态污染。

因为实际的渲染过程需要确定性,所以我们还将在服务器上“预取”数据-这意味着当我们开始渲染时,我们的应用程序状态将已经解决。 这意味着服务器上不需要数据反应性,因此默认情况下将其禁用。 禁用数据反应性还避免了将数据转换为反应对象的性能成本。

组件生命周期钩子

由于没有动态更新,因此在所有Vue生命周期钩子中,在SSR期间仅会调用beforeCreatecreated。 这意味着其他生命周期钩子内的任何代码(例如beforeMountmounted)将仅在客户端上执行。

要注意的另一件事是,您应该避免在beforeCreatecreated中产生全局副作用的代码,例如,使用setInterval设置计时器。 在仅客户端代码中,我们可以设置一个计时器,然后在beforeDestroydestroyed中将其拆除。 但是,由于销毁钩子不会在SSR期间被调用,因此计时器将永远存在。 为了避免这种情况,请将副作用代码移到beforeMountmounted中。

避免有状态的单例

在编写仅用于客户端的代码时,我们习惯于每次都会在新的上下文中对我们的代码进行评估。 但是,Node.js服务器是一个长期运行的过程。 当需要我们的代码进入流程时,它将被评估一次,然后保留在内存中。 这意味着,如果您创建一个单例对象,则它将在每个传入请求之间共享。

因此,Quasar CLI为每个请求使用新的Router和Vuex Store实例创建新的根Vue实例。 这类似于每个用户在自己的浏览器中使用应用程序的新实例的方式。 如果我们将在多个请求之间使用共享实例,则很容易导致交叉请求状态污染。

与直接创建Router和Vuex Store实例不同,您将公开一个工厂函数,该函数可以重复执行从而为每个请求创建新的应用程序实例:

// src/router/index.js
export default function (/* { store, ssrContext } */) {
  const Router = new VueRouter({...})
  return Router
}
// src/store/index.js
export default function (/* { ssrContext } */) {
  const Store = new Vuex.Store({...})
  return Store
}

如果您正在使用Vuex模块,请不要忘记将状态导出为函数,否则将创建单例:

// src/store/myModule/state.js
export default () => ({
  ...
})

访问特定于平台的API

通用代码不能假定能够访问特定于平台的API,因此,如果您的代码直接使用仅限浏览器的全局变量(例如“ window”或“ document”),则它们在Node.js中执行时会抛出错误,反之亦然。

对于服务器和客户端之间共享但使用不同平台API的任务,建议将特定于平台的实现包装在通用API中,或使用为您完成此任务的库。 例如,Axios是一个HTTP客户端,它为服务器和客户端公开相同的API。

对于仅浏览器的API,常见的方法是在仅客户端的生命周期钩子中延迟访问它们。

启动文件

请注意,如果在编写第三方库时未考虑通用性,那么将其集成到服务器渲染的应用程序中可能会很棘手。 您可以通过模拟某些全局变量来使其工作,但是这样做可能会很麻烦,并且可能会干扰其他库的环境检测代码。

当您将第三方库添加到项目中时(通过引导文件),请考虑它是否可以在服务器和客户端上运行。 如果只需要在服务器上运行,或者仅在客户端上运行,则在quasar.conf.js中指定:

// quasar.conf.js
return {
  // ...
  boot: [
    'some-boot-file', // 在服务器和客户端上运行
    { path: 'some-other', server: false } // 此启动文件仅嵌入在客户端
    { path: 'third', client: false } // 该启动文件仅嵌入在服务器端
  ]
}

数据预取和状态

在SSR期间,我们实质上是在渲染应用程序的“快照”,因此,如果该应用程序依赖于一些异步数据,则在开始渲染过程之前,需要预先提取并解析此数据。

Quasar CLI的预取功能为解决此问题而创建。 花一些时间来阅读它。

此页面的部分内容摘自官方的Vue.js SSR指南.