编写通用
代码(也称为同构
)意味着编写同时在服务器和客户端上运行的代码。 由于用例和平台API的不同,在不同环境中运行时,我们的代码行为将不完全相同。 在这里,我们将介绍您需要注意的关键事项。
服务器上的数据反应性
在仅客户端的应用程序中,每个用户都将在其浏览器中使用该应用程序的新实例。 对于服务器端渲染,我们希望做到这一点:每个请求都应该有一个新鲜的,隔离的应用程序实例,这样就不会出现跨请求状态污染。
因为实际的渲染过程需要确定性,所以我们还将在服务器上“预取”数据-这意味着当我们开始渲染时,我们的应用程序状态将已经解决。 这意味着服务器上不需要数据反应性,因此默认情况下将其禁用。 禁用数据反应性还避免了将数据转换为反应对象的性能成本。
组件生命周期钩子
由于没有动态更新,因此在所有Vue生命周期钩子中,在SSR期间仅会调用beforeCreate
和created
。 这意味着其他生命周期钩子内的任何代码(例如beforeMount
或mounted
)将仅在客户端上执行。
要注意的另一件事是,您应该避免在beforeCreate
和created
中产生全局副作用的代码,例如,使用setInterval
设置计时器。 在仅客户端代码中,我们可以设置一个计时器,然后在beforeUnmount
或destroyed
中将其拆除。 但是,由于销毁钩子不会在SSR期间被调用,因此计时器将永远存在。 为了避免这种情况,请将副作用代码移到beforeMount
或mounted
中。
避免有状态的单例
在编写仅用于客户端的代码时,我们习惯于每次都会在新的上下文中对我们的代码进行评估。 但是,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.config.js
return {
// ...
boot: [
'some-boot-file', // 在服务器和客户端上运行
{ path: 'some-other', server: false } // 此启动文件仅嵌入在客户端
{ path: 'third', client: false } // 该启动文件仅嵌入在服务器端
]
}
数据预取和状态
在SSR期间,我们实质上是在渲染应用程序的“快照”,因此,如果该应用程序依赖于一些异步数据,则在开始渲染过程之前,需要预先提取并解析此数据。
Quasar CLI的预取功能为解决此问题而创建。 花一些时间来阅读它。
此页面的部分内容摘自官方的Vue.js SSR指南.