医学院英文网站建设方案,局域网内的网站建设,google搜索关键词,网站开发 群文章目录 前言服务端的响应性组件生命周期钩子访问平台特有 API跨请求状态污染激活不匹配自定义指令teleports后言 前言 hello world欢迎来到前端的新世界 #x1f61c;当前文章系列专栏#xff1a;vue.js #x1f431;#x1f453;博主在前端领域还有很多… 文章目录 前言服务端的响应性组件生命周期钩子访问平台特有 API跨请求状态污染激活不匹配自定义指令teleports后言 前言 hello world欢迎来到前端的新世界 当前文章系列专栏vue.js 博主在前端领域还有很多知识和技术需要掌握正在不断努力填补技术短板。(如果出现错误感谢大家指出) 感谢大家支持您的观看就是作者创作的动力 服务端的响应性
在 SSR 期间每一个请求 URL 都会映射到我们应用中的一个期望状态。因为没有用户交互和 DOM 更新所以响应性在服务端是不必要的。为了更好的性能默认情况下响应性在 SSR 期间是禁用的。
组件生命周期钩子
因为没有任何动态更新所以像 onMounted或者 onUpdated 这样的生命周期钩子不会在 SSR 期间被调用而只会在客户端运行。 你应该避免在 setup() 或者 script setup 的根作用域中使用会产生副作用且需要被清理的代码。这类副作用的常见例子是使用 setInterval 设置定时器。我们可能会在客户端特有的代码中设置定时器然后在 onBeforeUnmount 或 onUnmounted 中清除。然而由于 unmount 钩子不会在 SSR 期间被调用所以定时器会永远存在。为了避免这种情况请将含有副作用的代码放到 onMounted 中。
访问平台特有 API
通用代码不能访问平台特有的 API如果你的代码直接使用了浏览器特有的全局变量比如 window 或 document他们会在 Node.js 运行时报错反过来也一样。
对于在服务器和客户端之间共享但使用了不同的平台 API 的任务建议将平台特定的实现封装在一个通用的 API 中或者使用能为你做这件事的库。例如你可以使用 node-fetch 在服务端和客户端使用相同的 fetch API。
对于浏览器特有的 API通常的方法是在仅客户端特有的生命周期钩子中惰性地访问它们例如 onMounted。
请注意如果一个第三方库编写时没有考虑到通用性那么要将它集成到一个 SSR 应用中可能会很棘手。你或许可以通过模拟一些全局变量来让它工作但这只是一种 hack 手段并且可能会影响到其他库的环境检测代码。
跨请求状态污染
在状态管理一章中我们介绍了一种使用响应式 API 的简单状态管理模式。而在 SSR 环境中这种模式需要一些额外的调整。
上述模式在一个JavaScript模块的根作用域中声明共享的状态。这是一种单例模式——即在应用的整个生命周期中只有一个响应式对象的实例。这在纯客户端的 Vue 应用中是可以的因为对于浏览器的每一个页面访问应用模块都会重新初始化。
然而在 SSR 环境下应用模块通常只在服务器启动时初始化一次。同一个应用模块会在多个服务器请求之间被复用而我们的单例状态对象也一样。如果我们用单个用户特定的数据对共享的单例状态进行修改那么这个状态可能会意外地泄露给另一个用户的请求。我们把这种情况称为跨请求状态污染。
从技术上讲我们可以在每个请求上重新初始化所有 JavaScript 模块就像我们在浏览器中所做的那样。但是初始化 JavaScript 模块的成本可能很高因此这会显著影响服务器性能。
推荐的解决方案是在每个请求中为整个应用创建一个全新的实例包括 router 和全局 store。然后我们使用应用层级的 provide 方法来提供共享状态并将其注入到需要它的组件中而不是直接在组件中将其导入
// app.js 在服务端和客户端间共享
import { createSSRApp } from vue
import { createStore } from ./store.js// 每次请求时调用
export function createApp() {const app createSSRApp(/* ... */)// 对每个请求都创建新的 store 实例const store createStore(/* ... */)// 提供应用级别的 storeapp.provide(store, store)// 也为激活过程暴露出 storereturn { app, store }
}激活不匹配
如果预渲染的 HTML 的 DOM 结构不符合客户端应用的期望就会出现激活不匹配。最常见的激活不匹配是以下几种原因导致的
组件模板中存在不符合规范的 HTML 结构渲染后的 HTML 被浏览器原生的 HTML 解析行为纠正导致不匹配。举例来说一个常见的错误是 不能被放在 中
pdivhi/div/p如果我们在服务器渲染的 HTML 中出现这样的代码当遇到
时浏览器会结束第一个 并解析为以下 DOM 结构 p/p
divhi/div
p/p渲染所用的数据中包含随机生成的值。由于同一个应用会在服务端和客户端执行两次每次执行生成的随机数都不能保证相同。避免随机数不匹配有两种选择 利用 v-if onMounted 让需要用到随机数的模板只在客户端渲染。你所用的上层框架可能也会提供简化这个用例的内置 API比如 VitePress 的 组件。 使用一个能够接受随机种子的随机数生成库并确保服务端和客户端使用同样的随机数种子 (比如把种子包含在序列化的状态中然后在客户端取回)。 服务端和客户端的时区不一致。有时候我们可能会想要把一个时间转换为用户的当地时间但在服务端的时区跟用户的时区可能并不一致我们也并不能可靠的在服务端预先知道用户的时区。这种情况下当地时间的转换也应该作为纯客户端逻辑去执行。
当 Vue 遇到激活不匹配时它将尝试自动恢复并调整预渲染的 DOM 以匹配客户端的状态。这将导致一些渲染性能的损失因为需要丢弃不匹配的节点并渲染新的节点但大多数情况下应用应该会如预期一样继续工作。尽管如此最好还是在开发过程中发现并避免激活不匹配。
自定义指令
因为大多数的自定义指令都包含了对 DOM 的直接操作所以它们会在 SSR 时被忽略。但如果你想要自己控制一个自定义指令在 SSR 时应该如何被渲染 (即应该在渲染的元素上添加哪些 attribute)你可以使用 getSSRProps 指令钩子
const myDirective {mounted(el, binding) {// 客户端实现// 直接更新 DOMel.id binding.value},getSSRProps(binding) {// 服务端实现// 返回需要渲染的 prop// getSSRProps 只接收一个 binding 参数return {id: binding.value}}
}teleports
在 SSR 的过程中 Teleport 需要特殊处理。如果渲染的应用包含 Teleport那么其传送的内容将不会包含在主应用渲染出的字符串中。在大多数情况下更推荐的方案是在客户端挂载时条件式地渲染 Teleport。
如果你需要激活 Teleport 内容它们会暴露在服务端渲染上下文对象的 teleports 属性下
const ctx {}
const html await renderToString(app, ctx)console.log(ctx.teleports) // { #teleported: teleported content }跟主应用的 HTML 一样你需要自己将 Teleport 对应的 HTML 嵌入到最终页面上的正确位置处。
后言 创作不易要是本文章对广大读者有那么一点点帮助 不妨三连支持一下您的鼓励就是博主创作的动力