资讯动态

在 Next.js 14 中深度服务端渲染 Lit Web Components:@lit-labs/nextjs 官方示例全解

发布时间:2026/9/13 15:51:09 来源:尧图企业网站定制
在 Next.js 14 中深度服务端渲染 Lit Web Componentslit-labs/nextjs 官方示例全解【免费下载链接】litLit is a simple library for building fast, lightweight web components.项目地址: https://gitcode.com/GitHub_Trending/li/litLit 是构建快速、轻量 Web Components 的标准库。在 React 生态的 Next.js 框架中如何让 Lit 编写的自定义元素获得服务端渲染SSR能力、同时保留客户端的水合hydration交互是跨框架集成的经典难题。本文以仓库 examples/nextjs-v14 官方示例为骨架结合lit-labs/nextjs插件与lit/react桥接层的源码实现完整讲解在 Next.js 14 项目中深度渲染 Lit 组件、在 JSX 中直接使用自定义元素、以及用 React 组件方式包装 Lit 元素的三种实战方案读者读完即可在自己的 Next.js 应用中复刻这套配置。示例概览一个极简但完整的集成骨架examples/nextjs-v14是一个“barebones”极简演示一个用 Lit 编写的 Web Component 在 Next.js 项目中正常工作同时演示了lit/react的用法并借助lit-labs/nextjs插件实现对 Lit 组件的深度服务端渲染。该示例的目录结构与职责如下路径均相对仓库根目录examples/nextjs-v14/pages/_app.tsxNext.js 应用入口仅做常规的全局样式引入与页面渲染examples/nextjs-v14/pages/index.tsx首页同时以原生自定义元素标签与 React 组件两种方式渲染同一个 Lit 组件examples/nextjs-v14/src/simple-greeter.ts核心 Lit 组件simple-greeterexamples/nextjs-v14/src/count-display.ts被simple-greeter组合的子组件count-displayexamples/nextjs-v14/src/simple-greeter-react.ts通过lit/react的createComponent生成的 React 包装组件examples/nextjs-v14/next.config.js接入lit-labs/nextjs插件的关键配置examples/nextjs-v14/package.json依赖与脚本清单。从 package.json 可以看到本示例锁定的技术栈版本next^14.1.0、lit^3.0.0、lit-labs/nextjs^0.2.0、lit/react^1.0.0、react^18.2.0、typescript~5.3.3。示例使用标准的 Next.js 脚本dev开发、build构建、start生产启动、lint代码检查。编写 Lit 组件simple-greeter 与子组件组合示例的核心组件定义在 examples/nextjs-v14/src/simple-greeter.ts它是一个标准的 Lit 3 类组件通过customElement(simple-greeter)装饰器注册自定义元素使用property()声明name默认值Somebody与count{type: Number}两个响应式属性使用static styles css\...声明组件作用域样式含prefers-color-scheme: dark 深色模式适配在render()中组合子组件count-display .count${this.count}并绑定click事件。其中值得注意的组合模式simple-greeter通过属性绑定.count即 property binding向同为 Lit 组件的count-display传值事件处理器() this.count驱动响应式更新。这说明 Lit 组件在 Next.js 项目中的内部行为与纯 Web 环境完全一致——属性、事件、样式隔离都由 Lit 自身运行时负责不依赖 React。tsconfig.json中experimentalDecorators: true与useDefineForClassFields: false两个编译选项是 Lit 装饰器语法customElement、property在 TypeScript 下正常工作的前提迁移此类示例到自己的项目时需原样保留。在 JSX 中直接使用自定义元素类型声明的两种方式pages/index.tsx直接把simple-greeter nameFriend/simple-greeter写进了 JSX。要让 TypeScript 编译器认识这个非内置标签simple-greeter.ts文件末尾提供了两份全局类型声明HTMLElementTagNameMap扩展声明simple-greeter: SimpleGreeter使document.querySelector(simple-greeter)等 DOM API 返回正确的类类型全局JSX.IntrinsicElements扩展声明该标签的属性类型为React.DetailedHTMLPropsReact.HTMLAttributesSimpleGreeter, SimpleGreeter | PartialSimpleGreeter的联合允许 JSX 中直接书写属性与事件。这份声明同时照顾了两类用法作为 DOM 元素查询的类型收窄以及作为 JSX 标签的属性校验。需要注意的是属性名在 JSX 中按 HTML 属性语义传递如name而属性值会被浏览器以字符串形式传给自定义元素若要传对象、布尔值等复杂类型应配合lit/react包装组件使用。用 lit/react 桥接createComponent 生成 React 组件React 与 Web Components 的事件系统和属性传递存在天然差异自定义事件需addEventListener手动监听、复杂属性需 property 赋值而非 attribute 赋值。示例在 examples/nextjs-v14/src/simple-greeter-react.ts 展示了官方桥接方案import React from react; import {createComponent} from lit/react; import {SimpleGreeter} from ./simple-greeter; export default createComponent({ react: React, tagName: simple-greeter, elementClass: SimpleGreeter, });createComponent来自lit/react对应仓库 packages/react其实现位于 packages/react/src/create-component.ts。它接收react、tagName、elementClass三个核心选项返回一个类型安全的 React 组件属性按 React 惯例传递count这类数字属性会被设置为元素 property 而非 attribute避免类型丢失组件卸载时自动断开 Lit 元素上的监听器防止内存泄漏事件可通过events选项映射为 React 事件 props本示例未使用仅传name字符串属性。于是pages/index.tsx中可以写出SimpleGreeter nameReact /与原生simple-greeter nameFriend并存于同一页面两条渲染路径共享同一个SimpleGreeter类。接入 lit-labs/nextjs 插件next.config.js 配置深度 SSR 的关键在 examples/nextjs-v14/next.config.jsconst withLitSSR require(lit-labs/nextjs)({ addDeclarativeShadowDomPolyfill: true, }); /** type {import(next).NextConfig} */ const nextConfig { reactStrictMode: true, swcMinify: true, }; module.exports withLitSSR(nextConfig);插件以高阶函数形式包装 Next 配置返回增强后的nextConfig。addDeclarativeShadowDomPolyfill: true是示例显式开启的唯一选项。从插件源码 packages/labs/nextjs/src/index.ts 可确认它支持的完整选项表选项默认值作用addDeclarativeShadowDomPolyfilltrue是否在客户端注入 Declarative Shadow DOMDSDpolyfill用于不支持原生 DSD 的浏览器webpackModuleRulesTest/\/pages\/.*\.(?:j\|t)sx?$\|\/app\/.*\.(?:j\|t)sx?$/正则匹配需要被插件处理的文件默认覆盖 pages 与 app 目录下的 JS/TSX 文件webpackModuleRulesExclude[/next\/dist\//, /node_modules/]正则数组排除不应被处理的文件默认排除 Next 自身产物与第三方依赖插件声明的 peerDependencies 为next: 13 || 14 || 15 || 16见 packages/labs/nextjs/package.json说明该插件设计上支持 Next.js 13 至 16本示例针对其中的 v14 提供最小可运行配置。插件原理一webpack 侧的 side-effect 注入在 Next.js 14默认 webpack 构建下插件通过config.module.rules.unshift(...)在 webpack 规则最前面插入一条规则源码见 packages/labs/nextjs/src/index.tstest使用webpackModuleRulesTest命中所有 page/app 目录下的入口文件exclude使用webpackModuleRulesExclude跳过next/dist与node_modules这两类文件是 CommonJS 产物与imports-loader不兼容loader使用imports-loader把两个 side-effect 导入注入到文件头部。注入的导入分为两类按构建目标区分服务端isServer true注入side-effects lit-labs/ssr-react/enable-lit-ssr.js。该模块会 monkey-patchReact.createElement与运行时 JSX 函数使 React 在渲染到 Lit 自定义元素时走 Lit 的 SSR 渲染管线从而把组件模板含 shadow DOM 内容序列化为服务端 HTML即“深度 SSR”——页面源码里能直接看到simple-greeter内部的 shadow 树而不是空标签。客户端!isServer除上述导入外若addDeclarativeShadowDomPolyfill为真再注入side-effects lit-labs/nextjs/lib/apply-dsd-polyfill.js用于水合阶段。插件还保留了用户自定义webpack函数的兼容性若nextConfig.webpack原本是函数插件会先注入自己的规则再调用用户函数并把结果返回避免覆盖用户已有配置。插件原理二Declarative Shadow DOM 与客户端水合“深度 SSR”产出的 HTML 依赖 Declarative Shadow DOM 这一标准服务端把 shadow root 序列化为template shadowrootmode形式浏览器解析 HTML 时自动将其附加为元素的 shadow root。现代浏览器原生支持 DSD但旧浏览器需要 polyfill。插件配套的 polyfill 实现在 packages/labs/nextjs/src/lib/apply-dsd-polyfill.ts逻辑非常精简import {hydrateShadowRoots} from webcomponents/template-shadowroot; if (!HTMLTemplateElement.prototype.hasOwnProperty(shadowRootMode)) { hydrateShadowRoots(document.body); }它检测HTMLTemplateElement.prototype上是否存在shadowRootMode原生 DSD 能力标记仅在缺失时调用webcomponents/template-shadowroot的hydrateShadowRoots(document.body)进行一次性水合——把页面中残留的template shadowrootmode就地转换为真正的 shadow root。这保证了深度 SSR 的 HTML 在旧浏览器上也能正确“复活”。由于该脚本只应跑在客户端插件在 webpack 侧仅对非服务端构建注入它在 Turbopack 规则中则显式使用browser条件限定见下文。插件原理三Turbopack 支持与 RSC 指令保护插件源码 packages/labs/nextjs/src/index.ts 中还有一段针对 Turbopack 的适配逻辑可作为升级 Next.js 版本的参考插件会解析用户项目根目录process.cwd()下实际安装的next版本并读取主版本号避免 monorepo 中 hoisting 导致版本误判仅当主版本 ≥ 16 且未显式传--webpack时输出turbopack.rules配置Next.js 16 起默认使用 Turbopack且其rules才支持{condition, loaders}高级形式Next.js 15 及以下即使--turbopack其 schema 也不支持该插件的按文件条件规则Turbopack 场景下改用随包发布的imports-loader替代品——packages/labs/nextjs/src/lib/preserve-directive-imports-loader.ts。这个自定义 loader 解决了一个关键兼容性问题webpack 下 Next.js 会在 loader 运行前先做 RSC 指令use client/use server预提取因此imports-loader直接把 import 放在文件顶部是安全的而 Turbopack 下 loader 先于指令检测运行若 import 被插到use client之前文件就会失去客户端组件身份。该 loader 用正则匹配文件开头的指令前导兼容 BOM、shebang、注释与空白严格遵循 directive prologue 规范把注入的 import 语句插到指令之后、其余源码之前从而保住 RSC 边界clientOnly: true时还仅在use client模块中注入水合支持确保它先于任何被引入的 Lit 元素执行避免 shadow root 被重复渲染。对本示例而言Next.js 14 走的是 webpack 路径上述 Turbopack 逻辑不会激活但理解它有助于在升级到 Next.js 16 时预判插件行为。运行与验证在 examples/nextjs-v14 目录下安装依赖后即可运行npm run dev启动开发服务器访问首页可看到原生与 React 包装两种方式渲染的simple-greeternpm run build npm run start生产构建并启动。用浏览器“查看网页源代码”或 curl 抓取 HTML可在服务端响应中直接看到simple-greeter的完整 shadow DOM 内容含template shadowrootmode即为“深度 SSR”的可验证证据npm run lint运行 Next.js 内置 ESLint 检查。若移除withLitSSR(...)包装页面中的 Lit 组件将退化为仅客户端渲染的空壳标签——这正反两面对比最能体现该插件对 SSR 的意义。延伸同构示例家族本示例并非孤例仓库 examples 下还维护着同构的 nextjs-v13、nextjs-v15、nextjs-v16pages 路由以及 nextjs-v14-app、nextjs-v15-app、nextjs-v16-appapp 路由等多个版本示例核心组件源码simple-greeter.ts、simple-greeter-react.ts、count-display.ts基本保持一致差异集中在next.config.js的插件配置与路由结构上。需要在新版本 Next.js 中接入 Lit 时可直接对照对应版本示例作为起点。插件自身的更多实现细节还可继续阅读 packages/labs/nextjs/src/index.ts 及其 lib 目录下的两个 loader/polyfill 文件理解其在不同构建器下的完整注入策略。【免费下载链接】litLit is a simple library for building fast, lightweight web components.项目地址: https://gitcode.com/GitHub_Trending/li/lit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价