@lit-labs/nextjs 实战指南:在 Next.js 中深度服务端渲染 Lit Web Components
2026/9/13 18:01:51 网站建设 项目流程

@lit-labs/nextjs 实战指南:在 Next.js 中深度服务端渲染 Lit Web Components

【免费下载链接】litLit is a simple library for building fast, lightweight web components.项目地址: https://gitcode.com/GitHub_Trending/li/lit

本篇技术指南围绕 Lit 官方仓库中的@lit-labs/nextjs包(其变更记录见 packages/labs/nextjs/CHANGELOG.md)展开,讲解如何借助该 Next.js 插件实现 Lit 组件的深度服务端渲染(Deep Server Rendering)——即不仅渲染自定义元素的标签与属性,还渲染其 Shadow DOM 内容。读完本文,你将掌握插件的安装接入、全部配置项(addDeclarativeShadowDomPolyfillwebpackModuleRulesTestwebpackModuleRulesExclude)的语义与默认值、App Router 下的'use client'边界约束,以及插件背后"补丁 React.createElement + 注入 Declarative Shadow DOM"的底层实现原理。

一、背景:为什么 Lit 组件在 Next.js 中默认只能"浅渲染"

Lit 组件可以直接被引入 Next.js 项目并在 JSX 中使用,但默认情况下服务端只会进行浅渲染(shallow render):渲染出自定义元素标签和通过 JSX 设置的属性,而组件内部的 Shadow DOM 结构不会被渲染(参见 packages/labs/nextjs/README.md)。

这意味着首屏 HTML 中只有<my-element></my-element>这样的空壳,用户需要等待客户端 JavaScript 下载、执行并手动挂载 Shadow DOM 后才能看到完整内容。这不仅拖慢首屏渲染,还会造成明显的闪烁(FOUC)。

@lit-labs/nextjs解决的正是这个问题:它把@lit-labs/ssr-react的能力整合进 Next.js 的构建流程,实现 Lit 组件的深度服务端渲染。该包于 0.1.0 版本首次发布,定位明确:"a plugin for Next.js that enables deep server rendering of Lit components"。

⚠️Lit Labs 状态提示:该包属于 Lit Labs 实验性系列,发布目的是收集设计反馈,后续可能引入破坏性变更或停止维护(README 中有明确警告)。生产环境使用前请评估风险。

二、快速接入:插件安装与 next.config.js 配置

插件通过包装next.config.js使用,最小配置如下(与 examples/nextjs-v15/next.config.js 等示例一致):

// next.config.js const withLitSSR = require('@lit-labs/nextjs')(); /** @type {import('next').NextConfig} */ const nextConfig = { // 你自己的配置 reactStrictMode: true, swcMinify: true, }; module.exports = withLitSSR(nextConfig);

要点说明:

  • withLitSSR是一个高阶包装函数withLitSSR(pluginOptions)返回一个接收NextConfig并返回增强后NextConfig的函数,因此既能接收插件自己的选项,也能原样接收你的 Next.js 配置对象;
  • 插件会向 webpack 配置注入一条module.rules规则,并在返回前调用你已有的nextConfig.webpack函数(若存在),保证与你自定义的 webpack 配置共存(见 packages/labs/nextjs/src/index.ts 源码中的合并逻辑);
  • 包的peerDependencies声明next: "13 || 14 || 15 || 16"(见 packages/labs/nextjs/package.json),对应 CHANGELOG 中各版本对 Next.js 的支持演进:0.2.0 起支持 Next.js 14 并停止支持 Next.js 12;0.2.1 起增加对 Next.js 15 的支持;当前版本支持到 Next.js 16。

仓库中的examples/目录提供了可直接对照的完整示例,包括 Pages Router(examples/nextjs-v13v14v15v16)与 App Router(examples/nextjs-v14-appv15-appv16-app)两种路由体系下的用法。

三、插件选项全解:三个配置项及默认值

插件支持传入一个可选的 options 对象,例如:

const withLitSSR = require('@lit-labs/nextjs')({ addDeclarativeShadowDomPolyfill: true, webpackModuleRulesTest: /\/my-app-pages\/.*\.tsx?$/, });

下表汇总了全部选项(README 与 CHANGELOG 中均有记载,源码默认值见 packages/labs/nextjs/src/index.ts):

属性类型默认值说明
addDeclarativeShadowDomPolyfillbooleantruetrue时,客户端 bundle 会包含一段脚本,应用 Declarative Shadow DOM polyfill(template-shadowrootponyfill)到 document
webpackModuleRulesTestRegExp/\/pages\/.*\.(?:j\|t)sx?$\|\/app\/.*\.(?:j\|t)sx?$/匹配需要注入 Lit SSR 支持的模块,理想情况下应匹配你路由的入口文件
webpackModuleRulesExcludeArray<RegExp>[/next\/dist\//, /node_modules/]从上述选中模块中排除匹配的文件的 RegExp 数组

三个选项的演进在 CHANGELOG 中有清晰脉络:

  • addDeclarativeShadowDomPolyfill0.1.4版本引入。该版本提示:如果你之前手动添加过 polyfill,可以删除自己的实现,或将此选项显式设为false
  • webpackModuleRulesTestwebpackModuleRulesExclude0.2.4版本引入。此前这两个规则在插件内部硬编码,0.2.4 将其开放为可配置项,并保持原内部值作为默认值。

3.1 webpackModuleRulesTest:控制注入范围

默认正则匹配/pages//app/目录下的 JS/TSX 文件,即路由页面文件。源码注释解释了为什么选择所有页面入口而非仅注入一处:理论上更优雅的做法是只在pages/_document.tsxpages/_app.tsxapp/layout.tsx注入一次,但这些文件并不保证存在,因此插件对全部匹配文件做注入(见 packages/labs/nextjs/src/index.ts 中的TODO(augustjk)注释)。

如果你的路由目录不是默认的pages/app(例如使用src/pages或自定义目录),可以通过该选项调整匹配范围。

3.2 webpackModuleRulesExclude:排除无需处理的文件

默认排除/next\/dist\//(Next.js 自身产物)与/node_modules/。排除 Next 自带文件的原因在源码注释中说明:它们是 CommonJS 模块,与imports-loader配合不佳。0.2.3 版本还专门做了一次更新,将 node_modules 的过滤从部分匹配完善为整体排除。

3.3 addDeclarativeShadowDomPolyfill:兼容老浏览器的 DSD 补丁

开启后,客户端 bundle 会额外引入@lit-labs/nextjs/lib/apply-dsd-polyfill.js。其实现非常简洁(见 packages/labs/nextjs/src/lib/apply-dsd-polyfill.ts):

import {hydrateShadowRoots} from '@webcomponents/template-shadowroot'; if (!HTMLTemplateElement.prototype.hasOwnProperty('shadowRootMode')) { hydrateShadowRoots(document.body); }

逻辑是:仅当浏览器原生不支持template元素的shadowRootMode属性(即不支持 Declarative Shadow DOM)时,才在document.body上执行hydrateShadowRoots。该依赖来自包的dependencies中的@webcomponents/template-shadowroot@^0.2.1(见 packages/labs/nextjs/package.json)。现代浏览器原生支持 DSD,因此这段脚本在较新环境中不会产生额外开销。

四、App Router 与 React Server Components 的关键约束

这是使用该插件最需要注意的边界,CHANGELOG 0.2.0 版本中有专门说明,README 也再次强调:

默认情况下,App Router 中的组件是React Server Components(RSCs)。在 Server Components 内对 Lit 组件进行深度 SSR不生效,原因有二:

  1. RSC payload 中包含序列化后的服务端组件树,其中的<template>元素会导致 React 水合(hydration)不匹配,触发错误;
  2. 在 Server Component 文件中导入的自定义元素定义,不会被打包进客户端 bundle。

因此,任何你希望在客户端使用的 Lit 组件,都必须放在'use client';指令边界之后(即放入 Client Component 文件中)。这些组件在首次页面加载时仍然会像 Pages Router 时代一样被服务端渲染,客户端再执行水合。仓库示例 examples/nextjs-v15-app/README.md 对此有同样的说明,示例中将裸自定义元素与@lit/react包装组件都放在带'use client';指令的文件中。

五、底层原理:插件如何在 webpack 中实现深度 SSR

5.1 注入 side-effect import

插件的 webpack 配置核心是在config.module.rules最前面(unshift)插入一条规则(见 packages/labs/nextjs/src/index.ts):

  • test使用webpackModuleRulesTest
  • exclude使用webpackModuleRulesExclude
  • loader使用imports-loader(依赖版本^4.0.1);
  • 注入两条 side-effect import:
    • side-effects @lit-labs/ssr-react/enable-lit-ssr.js服务端补丁React.createElement与 JSX runtime 函数;客户端引入@lit-labs/ssr-client/lit-element-hydrate-support.js安装水合支持;
    • !isServer && addDeclarativeShadowDomPolyfill时,追加side-effects @lit-labs/nextjs/lib/apply-dsd-polyfill.js,仅注入客户端 bundle。

5.2 服务端:补丁 createElement 渲染 Shadow DOM

@lit-labs/ssr-react的服务端入口enable-lit-ssr.ts(见 packages/labs/ssr-react/src/node/enable-lit-ssr.ts)做了三件事:

  1. wrapCreateElement包装React.createElement(带防重复补丁检查:React.createElement.name !== 'litPatchedCreateElement');
  2. NODE_ENV区分生产/开发环境,分别补丁react/jsx-runtimejsx/jsxsreact/jsx-dev-runtimejsxDEV
  3. 设置globalThis.litSsrReactEnabled = true作为标记。

补丁后的createElement(见 packages/labs/ssr-react/src/lib/node/wrap-create-element.ts)在遇到自定义元素时:调用renderCustomElement渲染其 Shadow DOM,将结果序列化为 HTML 放入一个<template>元素的dangerouslySetInnerHTML,并把该<template>作为自定义元素的子节点返回。这正是Declarative Shadow DOM(DSD)的标准形态——服务端输出的 HTML 中直接包含<template shadowrootmode="open">...</template>,从而让首屏即可见组件内容。

从源码结构可以推断:CHANGELOG 0.2.4 中"Prevent duplicative patching of React.createElement"(防止重复补丁)这一修复,对应enable-lit-ssr.ts中的名称检查守卫;而 0.1.1 中"Use hydration modules from@lit-labs/ssr-client"对应客户端lit-element-hydrate-support.js的水合路径。

5.3 客户端:水合支持与 DSD polyfill

客户端入口enable-lit-ssr.ts(见 packages/labs/ssr-react/src/enable-lit-ssr.ts)仅做一件事:引入@lit-labs/ssr-client/lit-element-hydrate-support.js。该模块(见 packages/labs/ssr-client/src/lit-element-hydrate-support.ts)为LitElement安装水合支持,使客户端在接管组件时能复用服务端渲染的 Shadow DOM,而不是重复渲染。配合上一节所述的 DSD polyfill 注入,老浏览器也能把服务端输出的声明式 Shadow DOM 正确"激活"。

5.4 Turbopack 支持与 directive 保序 loader

从源码(packages/labs/nextjs/src/index.ts 及 packages/labs/nextjs/src/lib/preserve-directive-imports-loader.ts)可以看到插件对Turbopack(Next.js 16 起默认)的适配:

  • 插件会探测用户项目实际安装的 Next.js 主版本(从process.cwd()解析next/package.json,避免 monorepo 中版本提升带来的误判),并检查命令行是否带--webpack显式回退;
  • 当 Next.js ≥ 16 且未显式使用 webpack 时,插件输出turbopack.rules配置,通过自定义 loader 注入 side-effect import;Next.js 15 及以下则仅走 webpack 路径;
  • 关键差异:webpack 会在 loader 运行前的预处理阶段提取'use client'/'use server'指令,所以imports-loader把 import 插到文件顶部是安全的;而 Turbopack 中 loader 先于指令检测运行,若在'use client'之前插入 import,文件会丢失客户端组件身份。自定义的preserve-directive-imports-loader因此将注入的 import 插入到 RSC 指令之后,保证边界不失效;
  • 该 loader 还支持clientOnly选项:只向'use client'模块注入水合支持,确保它在任何 Lit 元素定义之前运行(防止 Shadow DOM 被二次渲染),同时对非客户端组件文件直接放行。

六、版本演进速览(来自 CHANGELOG)

版本关键变更
0.1.0包首次发布;包含用于 Next.js 的插件,启用 Lit 组件深度服务端渲染;依赖@lit-labs/ssr-react@0.1.0
0.1.1修复 README 标题;改从@lit-labs/ssr-client引入水合模块
0.1.2依赖版本稳定化(不再引用自家 pre-release 版本);TypeScript 升级至 v5.0 / ~5.2.0
0.1.4新增addDeclarativeShadowDomPolyfill选项(默认 true)
0.2.0支持 Next.js 14 与 App Router;不再支持 Next.js 12;依赖@lit-labs/ssr-react@0.3.0;详细说明 RSC 限制
0.2.1修复 nextjs 配置包装器类型;支持 Next.js 15
0.2.2README 增加 Lit Labs 声明
0.2.3更新 webpack exclude 以过滤 node_modules
0.2.4新增webpackModuleRulesTest/webpackModuleRulesExclude选项;防止React.createElement被重复补丁

package.jsonpeerDependenciesnext: 13 || 14 || 15 || 16)看,插件覆盖 Next.js 13~16 四个大版本;README 中还说明插件曾在 Next.js 13 与 14 上做过测试,后续版本支持则随 CHANGELOG 逐版推进。

七、总结

@lit-labs/nextjs通过"webpack/Turbopack 规则注入 side-effect import + 补丁React.createElement输出 Declarative Shadow DOM + 客户端水合与 DSD polyfill"这一套组合拳,把 Lit 的深度服务端渲染无缝带入了 Next.js 生态。使用时牢记三点:启用插件包装 next.config.js、将 Lit 组件放在'use client'边界之后、按需调整三个配置项。相关实现细节可继续深入阅读 packages/labs/nextjs/src/index.ts、packages/labs/ssr-react 与 packages/labs/ssr-client,并通过 examples/nextjs-v16-app 等示例快速上手。

【免费下载链接】litLit is a simple library for building fast, lightweight web components.项目地址: https://gitcode.com/GitHub_Trending/li/lit

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询