编写 universal
代码(也称为 isomorphic
)意味着编写可在服务器和客户端上运行的代码。由于用例和平台 API 的差异,我们的代码在不同环境中运行时的行为将不完全相同。在这里,我们将介绍您需要注意的关键事项。
服务器上的数据响应性
在仅客户端的应用程序中,每个用户都将在其浏览器中使用应用程序的新实例。对于服务器端渲染,我们也希望如此:每个请求都应该拥有一个新的、隔离的应用程序实例,以避免跨请求状态污染。
由于实际的渲染过程需要确定性,我们还将在服务器上“预取”数据 - 这意味着当我们开始渲染时,应用程序状态将已解析。这意味着在服务器上不需要数据响应性,因此默认情况下已禁用。禁用数据响应性还可以避免将数据转换为响应式对象带来的性能成本。
组件生命周期钩子
由于没有动态更新,在所有 Vue 生命周期钩子中,只有 beforeCreate
和 created
会在 SSR 期间被调用。这意味着其他生命周期钩子(例如 beforeMount
或 mounted
)中的任何代码都只会执行在客户端。
需要注意的另一点是,应避免在 beforeCreate
和 created
中编写产生全局副作用的代码,例如使用 setInterval
设置计时器。在仅客户端代码中,我们可能会设置一个计时器,然后在 beforeUnmount
或 destroyed
中将其销毁。但是,由于销毁钩子在 SSR 期间不会被调用,因此计时器将永远存在。为了避免这种情况,请将副作用代码移至 beforeMount
或 mounted
中。
避免有状态的单例
在编写仅客户端代码时,我们习惯于我们的代码每次都会在新的上下文中执行。但是,Node.js 服务器是一个长期运行的进程。当我们的代码被导入到进程中时,它将被执行一次,然后保留在内存中。这意味着如果您创建了一个单例对象,它将在每个传入请求之间共享。
因此,Quasar CLI 为每个请求创建一个新的根 Vue 实例,以及新的 Router 和 Vuex Store 实例。这类似于每个用户将在其自己的浏览器中使用应用程序的新实例的方式。如果我们在多个请求之间使用共享实例,则很容易导致跨请求状态污染。
您将公开一个工厂函数,而不是直接创建 Router 和 Vuex Store 实例,该函数可以重复执行以创建每个请求的新应用程序实例
export default function (/* { store, ssrContext } */) {
const Router = new VueRouter({...})
return Router
}
export default function (/* { ssrContext } */) {
const Store = new Vuex.Store({...})
return Store
}
如果您正在使用 Vuex 模块,请不要忘记将状态导出为函数,否则将创建单例
export default () => ({
...
})
访问平台特定 API
通用代码不能假定可以访问平台特定的 API,因此,如果您的代码直接使用仅限浏览器的全局变量(如 window
或 document
),则在 Node.js 中执行时会抛出错误,反之亦然。
对于服务器和客户端之间共享但使用不同平台 API 的任务,建议将平台特定的实现包装在通用 API 中,或使用为您执行此操作的库。例如,Axios 是一个 HTTP 客户端,它为服务器和客户端都公开了相同的 API。
对于仅限浏览器的 API,常见的方法是在仅客户端的生命周期钩子中延迟访问它们。
启动文件
请注意,如果第三方库并非旨在用于通用用途,则将其集成到服务器渲染的应用程序中可能会很棘手。您*可能*可以通过模拟一些全局变量来使其工作,但这将是一种非常规的方法,并且可能会干扰其他库的环境检测代码。
当您将第三方库添加到项目中(通过 启动文件)时,请考虑它是否可以在服务器和客户端上运行。如果它只需要在服务器或客户端上运行,则在 /quasar.config
文件中指定。
return {
// ...
boot: [
'some-boot-file', // runs on both server & client
{ path: 'some-other', server: false } // this boot file gets embedded only on client-side
{ path: 'third', client: false } // this boot file gets embedded only on server-side
]
}
数据预取和状态
在 SSR 期间,我们实际上是在渲染应用程序的“快照”,因此,如果应用程序依赖于某些异步数据,则需要在开始渲染过程之前预取并解析这些数据。
Quasar CLI 预取功能 已被创建来解决此问题。花点时间阅读一下。
此页面的一部分摘自官方的 Vue.js SSR 指南。