Lit SSR 服务端用法
服务端渲染从使用 @lit-labs/ssr 包中提供的服务端专用 render() 函数渲染一个 Lit 模板开始。
render 函数的签名为:
render(value: unknown, renderInfo?: Partial<RenderInfo>): RenderResult 通常 value 是由 Lit 模板表达式生成的 TemplateResult,例如:
html`<h1>Hello</h1>` 模板可以包含自定义元素,这些元素会连同它们的模板一起被依次渲染。
import {render} from '@lit-labs/ssr';import {html} from 'lit';
const result = render(html` <h1>Hello SSR!</h1> <my-element></my-element>`); 要渲染单个元素,你可以渲染一个仅包含该元素的模板:
const result = render(html`<my-element></my-element>`); 处理 RenderResults
Permalink to "处理 RenderResults"render() 返回一个 RenderResult:一个可迭代的值集合,可以进行流式传输或拼接为字符串。
RenderResult 可以包含字符串、嵌套的渲染结果,或字符串/渲染结果的 Promise。并非所有渲染结果都包含 Promise——当自定义元素执行异步任务(如获取数据)时才会出现——但由于 RenderResult 可能包含 Promise,将其处理为字符串或 HTTP 响应可能是一个异步操作。
即使 RenderResult 可能包含 Promise,它仍然是一个同步可迭代对象,而不是异步可迭代对象。这是因为同步可迭代对象比异步可迭代对象更快,而且许多服务端渲染不需要异步渲染,因此不应承担异步可迭代对象的开销。
在同步可迭代对象中允许 Promise 创建了一种混合的同步/异步迭代协议。在消费 RenderResult 时,你必须检查每个值是否是 Promise 或可迭代对象,并根据需要等待或递归。
@lit-labs/ssr 包含三个工具来帮你完成这些操作:
RenderResultReadablecollectResult()collectResultSync()
RenderResultReadable
Permalink to "RenderResultReadable" RenderResultReadable 是一个 Node Readable 流实现,提供来自 RenderResult 的值。它可以被管道传输到 Writable 流,或传递给 Koa 等 Web 服务器框架。
在与流式 HTTP 服务器或其他支持流的 API 集成时,这是处理 SSR 结果的首选方式。
import {render} from '@lit-labs/ssr';import {RenderResultReadable} from '@lit-labs/ssr/lib/render-result-readable.js';import {html} from 'lit';
// 使用 Koa 进行流式传输app.use(async (ctx) => { const result = render(html`<my-element></my-element>`); ctx.type = 'text/html'; ctx.body = new RenderResultReadable(result);}); collectResult()
Permalink to "collectResult()" collectResult(result: RenderResult): Promise<string>
collectResult() 是一个异步函数,接受一个 RenderResult 并将其拼接为字符串。它会等待 Promise 并递归处理嵌套的可迭代对象。
示例
import {render} from '@lit-labs/ssr';import {collectResult} from '@lit-labs/ssr/lib/render-result.js';import {html} from 'lit';
const result = render(html`<my-element></my-element>`);const contents = await collectResult(result); collectResultSync()
Permalink to "collectResultSync()" collectResultSync(result: RenderResult): Promise<string>
collectResultSync() 是一个同步函数,接受一个 RenderResult 并将其拼接为字符串。它会递归处理嵌套的可迭代对象,但遇到 Promise 时会抛出错误。
因为此函数不支持异步渲染,建议仅在无法使用异步函数时使用它。
import {render} from '@lit-labs/ssr';import {collectResultSync} from '@lit-labs/ssr/lib/render-result.js';import {html} from 'lit';
const result = render(html`<my-element></my-element>`);// 如果 `result` 包含 Promise 则抛出错误!const contents = collectResultSync(result); render() 的第二个参数是一个 RenderInfo 对象,用于向组件和子模板传递选项和当前渲染状态。
调用者可以设置的主要选项有:
deferHydration:控制是否为顶层自定义元素添加defer-hydration属性,以表明这些元素不应自动水合。默认值为false,因此顶层元素会自动水合。elementRenderers:用于渲染自定义元素的ElementRenderer类数组。默认包含LitElementRenderer用于渲染 Lit 元素。可以设置为包含自定义的ElementRenderer实例(文档即将推出),或设置为空数组以完全禁用自定义元素渲染。
在 VM 模块或全局作用域中运行 SSR
Permalink to "在 VM 模块或全局作用域中运行 SSR"为了在 Node 中渲染自定义元素,它们必须首先通过全局 customElements API 定义和注册,而这是浏览器特有的功能。因此,当 Lit 在 Node 中运行时,它会自动使用一组在服务端渲染 Lit 所需的最小 DOM API,并定义 customElements 全局变量。(有关模拟 API 的列表,请参阅 DOM 模拟。)
Lit SSR 提供了两种在服务端渲染自定义元素的方式:在全局作用域中渲染或通过 VM 模块渲染。VM 模块利用 Node 的 vm.Module API,可以在 V8 虚拟机上下文中运行代码。两种方法的主要区别在于全局状态(如自定义元素注册表)的共享方式。
在全局作用域中渲染时,所有渲染请求将共享一个全局的 customElements 注册表,以及你的组件代码可能设置的任何其他全局状态。
使用 VM 模块渲染允许每个渲染请求拥有自己的上下文,其全局对象独立于主 Node 进程。customElements 注册表仅在该上下文中安装,其他全局状态也将隔离在该上下文中。VM 模块是 Node 的实验性功能。
| 全局作用域 | VM 模块 |
|---|---|
优点:
| 优点:
|
全局作用域
Permalink to "全局作用域"使用全局作用域时,你只需使用模板调用 render() 即可获得 RenderResult 并将其传递给你的服务器:
import {render} from '@lit-labs/ssr';import {RenderResultReadable} from '@lit-labs/ssr/lib/render-result-readable.js';import {myTemplate} from './my-template.js';
// ...
// 例如在 Koa 中间件中app.use(async (ctx) => { const ssrResult = render(myTemplate(data)); ctx.type = 'text/html'; ctx.body = new RenderResultReadable(ssrResult);}); VM 模块
Permalink to "VM 模块"Lit 还提供了一种将应用代码加载到独立的 VM 上下文中并从中渲染的方式,该上下文拥有自己的全局对象。
// render-template.jsimport {render} from '@lit-labs/ssr';import {myTemplate} from './my-template.js';
export const renderTemplate = (someData) => { return render(myTemplate(someData));}; // server.jsimport {ModuleLoader} from '@lit-labs/ssr/lib/module-loader.js';import {RenderResultReadable} from '@lit-labs/ssr/lib/render-result-readable.js';
// ...
// 例如在 Koa 中间件中app.use(async (ctx) => { const moduleLoader = new ModuleLoader(); const importResult = await moduleLoader.importModule( './render-template.js', // 要在 VM 上下文中加载的模块 import.meta.url // 模块的引用 URL ); const {renderTemplate} = importResult.module.namespace as typeof import('./render-template.js') const ssrResult = await renderTemplate({some: "data"}); ctx.type = 'text/html'; ctx.body = new RenderResultReadable(ssrResult);}); // server.jsimport {ModuleLoader} from '@lit-labs/ssr/lib/module-loader.js';
// ...
// 例如在 Koa 中间件中app.use(async (ctx) => { const moduleLoader = new ModuleLoader(); const importResult = await moduleLoader.importModule( './render-template.js', // 要在 VM 上下文中加载的模块 import.meta.url // 模块的引用 URL ); const {renderTemplate} = importResult.module.namespace; const ssrResult = await renderTemplate({some: "data"}); ctx.type = 'text/html'; ctx.body = Readable.from(ssrResult);}); 注意:使用此功能需要 Node 14+ 并向 Node 传递 --experimental-vm-modules 标志,因为它使用实验性的 VM 模块来创建兼容模块的 VM 上下文。