web components 在 ssr 中仅输出静态 html 结构,不执行 js 生命周期,需确保初始 html 含合理默认内容、保留属性值,并支持客户端平滑激活;推荐用 lit 实现 ssr 友好渲染,或通过 template、data- 属性等原生方案降级兜底。

Web Components 本身是客户端技术,浏览器原生支持,但服务端渲染(SSR)时无法直接执行 JavaScript 生命周期(如 connectedCallback),也不会解析自定义标签行为。所以“正确处理”不是让 SSR 执行组件逻辑,而是确保 HTML 结构可被服务端输出、语义合理、且能平滑过渡到客户端激活。
服务端只输出静态结构,不执行组件逻辑
SSR 环境(如 Node.js + Express、Next.js、Nuxt 等)中,<countdown-timer seconds="30"></countdown-timer> 这类标签会被当作普通未知元素原样输出——它不会渲染倒计时数字,也不会触发 JS 初始化。这是正常且预期的行为。
关键原则:服务端只负责生成有意义的初始 HTML,包括:
- 合理的默认内容(例如用
<slot></slot>fallback 或innerHTML预设文本) - 必要的属性值(如
seconds="30")保留,供客户端 JS 读取 - 避免依赖 JS 渲染的关键信息(如时间、用户数据)应由服务端写入 DOM
推荐搭配 Lit 或 Polymer 实现 SSR 友好组件
原生 Web Components API 不提供 SSR 支持,但 Lit(基于 Web Components 的轻量库)提供了 Lit SSR 工具链,能将组件编译为纯 HTML 字符串。
例如 Lit 组件:
class CountdownTimer extends LitElement {
static properties = { seconds: { type: Number } };
render() {
return html`<div class="timer">${this.formatTime(this.seconds)}</div>`;
}
}
通过 renderComponent() 可在服务端同步生成带初始值的 HTML:
<countdown-timer seconds="30"><div class="timer">00:30</div> </countdown-timer>
客户端 hydrate 时,会复用该 DOM 并接管交互逻辑,避免重复渲染或闪烁。
纯原生方案:用 template + 静态降级兜底
若坚持不用 Lit 等工具,可通过以下方式增强 SSR 兼容性:
- 在自定义元素内部使用
<template></template>定义结构,并在服务端预渲染其内容(如用正则提取或构建时注入) - 给自定义标签添加
is="x-countdown"属性(需注册时声明extends),部分 SSR 框架能识别并保留 - 在
constructor或connectedCallback中检查document.readyState,跳过非浏览器环境的初始化 - 对关键展示内容(如倒计时起始值),同时写入
data-属性和文本节点,服务端可提取data-seconds渲染对应数字
注意 hydration 时机与状态同步
客户端 JS 加载后,需确保组件能读取服务端已输出的 DOM 状态,而不是覆盖它:
- 避免在
connectedCallback中无条件重写shadowRoot.innerHTML,先检查是否已有内容 - 用
this.getAttribute('seconds')读取服务端写入的属性,而非假设初始值为 0 或默认值 - 如果组件有内部状态(如剩余秒数),服务端不应输出动态值;应只输出初始值,由客户端启动定时器











