为 Lit SSR 编写组件
Lit 在服务端环境中渲染 Web 组件的方式对组件代码施加了一些限制,以实现高效的服务端渲染。在编写组件时,请注意以下考虑事项,以确保它们与 Lit SSR 兼容。
注意:本页列出的限制可能会随着我们对 Lit SSR 的改进而变更。如果你希望支持某种用例,请 提交 issue 或发起 讨论 线程。
仅限浏览器的代码
Permalink to "仅限浏览器的代码"大多数浏览器 DOM API 在 Node 环境中不可用。Lit SSR 使用了一个 DOM shim,仅包含渲染 Lit 模板和组件所需的最低限度 API。要查看所有可用 API 的完整列表,请参阅 DOM 模拟 页面。
在编写组件时,应从仅在客户端调用的生命周期方法中执行命令式 DOM 操作,而不是在服务端执行。例如,如果你需要测量更新后的 DOM,请使用 updated()。此回调仅在浏览器中运行,因此访问 DOM 是安全的。
有关哪些特定方法在服务端调用、哪些仅在浏览器中调用的列表,请参阅下面的生命周期部分。
一些定义 Lit 组件的模块可能还会使用浏览器 API 产生副作用——例如检测某些浏览器特性——这会导致模块在非浏览器环境中导入时出错。在这种情况下,你可以将副作用代码移到仅限浏览器的生命周期回调中,或添加条件判断使其仅在浏览器中运行。
对于简单的情况,对某些 DOM 访问添加条件判断或可选链可能足以保护不被可用的 DOM API 影响。例如:
const hasConstructableStylesheets = typeof globalThis.CSSStyleSheet?.prototype.replaceSync === 'function'; lit 包还提供了一个 isServer 环境检查器,可用于编写针对不同环境的条件代码块:
import {isServer} from 'lit';
if (isServer) { // 仅在 Node 等服务端环境中运行} else { // 在浏览器中运行} 对于更复杂的用例,可以考虑在 Node 中使用条件导出,专门为 "node" 环境匹配,这样你可以根据模块是在 Node 还是浏览器中导入来使用不同的代码。用户将根据是从 Node 还是浏览器导入来获得相应版本的包。导出条件也受主流打包工具支持,如 rollup 和 webpack,因此用户可以为你的包引入适当的代码。
不要将 Lit 打包到发布的组件中。
因为 Lit 包使用条件导出为 Node 和浏览器环境提供不同的模块,我们强烈建议不要将 lit 打包到你要发布到 NPM 的包中。如果你这样做,你的打包结果将只包含你打包时所用环境对应的 lit 模块,而不会根据环境自动切换。
只有部分生命周期回调会在服务端渲染期间运行。这些回调生成组件的初始样式和标记。其他生命周期方法在客户端的水合期间以及组件水合后的运行时被调用。
下表列出了标准自定义元素和 Lit 的生命周期方法,以及它们是否在 SSR 期间调用。在元素注册和水合后,所有生命周期在浏览器中均可用。
在服务端调用的方法不应包含对未被 shim 的浏览器/DOM API 的引用。不在服务端调用的方法可以包含这些引用而不会导致错误。
在 Lit SSR 作为 Lit Labs 的一部分期间,方法是否在服务端调用可能会发生变化。
标准自定义元素和 LitElement
Permalink to "标准自定义元素和 LitElement"| 方法 | 是否在服务端调用 | 备注 |
|---|---|---|
constructor() | 是 ⚠️ | |
connectedCallback() | 否 | |
disconnectedCallback() | 否 | |
attributeChangedCallback() | 否 | |
adoptedCallback() | 否 | |
hasChanged() | 是 ⚠️ | 属性被设置时调用 |
shouldUpdate() | 否 | |
willUpdate() | 是 ⚠️ | 在 render() 之前调用 |
update() | 否 | |
render() | 是 ⚠️ | |
firstUpdate() | 否 | |
updated() | 否 |
ReactiveController
Permalink to "ReactiveController"| 方法 | 是否在服务端调用 | 备注 |
|---|---|---|
constructor() | 是 ⚠️ | |
hostConnected() | 否 | |
hostDisconnected() | 否 | |
hostUpdate() | 否 | |
hostUpdated() | 否 |
指令(Directive)
Permalink to "指令(Directive)"| 方法 | 是否在服务端调用 | 备注 |
|---|---|---|
constructor() | 是 ⚠️ | |
update() | 否 | |
render() | 是 ⚠️ | |
disconnected() | 否 | 仅限异步指令 |
reconnected() | 否 | 仅限异步指令 |
目前还没有一种机制可以在继续渲染之前等待异步结果(例如来自异步指令或控制器的结果),不过我们正在考虑在未来允许这样做。当前的解决方案是在服务端渲染顶层模板之前完成所有异步工作,并通过某个属性或特性将数据提供给模板。
例如:
asyncAppend()或asyncReplace()等异步指令在服务端不会产生任何可渲染的结果。until()指令将仅返回最高优先级的非 Promise 占位值。
@lit-labs/testing 包包含实用函数,利用 Web Test Runner 插件创建使用 @lit-labs/ssr 进行服务端渲染的测试 fixtures。它可以帮助测试你的组件是否支持服务端渲染。更多信息请参阅 readme。