Custom Elements 生命周期与 Lit 的映射关系详解
很多人在使用 Lit 时,会有一种“不太踏实”的感觉:
生命周期好像不多?
没有 beforeMount / mounted / beforeUpdate?
这真的够用吗?
原因在于:
Lit 并没有“发明生命周期”,
它只是忠实地站在 Custom Elements 之上。
本文将从 浏览器原生生命周期 出发,解释:
- Custom Elements 提供了什么
- Lit 在这些生命周期上做了哪些封装
- Lit 的生命周期为什么这么“少”
- 与 Vue / React 生命周期的本质差异
一、先给结论(Very Important)
Lit 的生命周期不是“简化版 Vue”,
而是“Custom Elements 生命周期 + 更新阶段钩子”。
理解这一点,后面一切都会顺。
二、Custom Elements 的原生生命周期
Web Components(Custom Elements)规范只定义了 4 个生命周期回调:
class MyElement extends HTMLElement {
constructor() {}
connectedCallback() {}
disconnectedCallback() {}
attributeChangedCallback() {}
}
就这么多,没有多余概念。
2.1 constructor
- 元素被创建
- 尚未插入 DOM
- 不应该访问 DOM / attributes
constructor() {
super()
}
2.2 connectedCallback
- 元素被插入文档
- 可以访问 DOM
- 可能被多次调用(移动节点)
connectedCallback() {
console.log('attached')
}
2.3 disconnectedCallback
- 元素从文档移除
- 用于清理副作用
disconnectedCallback() {
console.log('detached')
}
2.4 attributeChangedCallback
- 监听 attribute 变化
- 必须显式声明
observedAttributes
三、Lit 是如何“站在”这些生命周期之上的
Lit 并没有绕过这些生命周期,而是:
在 Custom Elements 生命周期中插入“更新流程”
3.1 LitElement 的基本结构
class LitElement extends ReactiveElement {
render() {}
}
真正的魔法发生在 ReactiveElement 中。
四、Lit 的完整生命周期执行顺序(重点)
当一个 Lit 组件被使用时,实际执行顺序是:
constructor
↓
connectedCallback
↓
第一次 requestUpdate
↓
shouldUpdate
↓
willUpdate
↓
render
↓
updated
这是真正需要理解的生命周期链。
五、Lit 生命周期逐个拆解
5.1 constructor
constructor() {
super()
}
特点:
- 不能访问 DOM
- 不要在这里做副作用
- 不要 set 响应式属性(会触发更新)
5.2 connectedCallback
connectedCallback() {
super.connectedCallback()
}
用途:
- 启动副作用(事件监听、observer)
- 定时器
- 与外部系统建立连接
注意:
connectedCallback ≠ mounted
它可能被调用多次。
5.3 requestUpdate(隐式生命周期)
Lit 中没有显式的 beforeMount,
但 requestUpdate 实际上就是更新的起点。
this.requestUpdate()
5.4 shouldUpdate
shouldUpdate(changedProps) {
return true
}
这是 Lit 给你的唯一更新拦截点。
- 返回 false → 跳过整个更新流程
- 非常适合做性能控制
5.5 willUpdate
willUpdate(changedProps) {}
用途:
- 基于属性变化,派生内部状态
- 类似 Vue 的
watch + computed
不要在这里操作 DOM
5.6 render(核心)
render() {
return html`...`
}
特点:
- 每次更新都会执行
- 必须是纯函数
- 不应该产生副作用
5.7 updated
updated(changedProps) {}
用途:
- DOM 已经更新完成
- 安全操作 DOM
- 类似 Vue 的
updated / mounted
5.8 disconnectedCallback
disconnectedCallback() {
super.disconnectedCallback()
}
用途:
- 清理副作用
- 取消事件 / observer / timer
六、Lit 生命周期为什么“这么少”?
这是一个设计结果,不是能力不足。
6.1 Lit 把生命周期拆成两类
- 元素生命周期(来自浏览器)
- 更新生命周期(围绕 render)
而不是像 Vue 那样混合在一起。
6.2 对比 Vue 生命周期
| Vue | Lit |
|---|---|
| beforeCreate | constructor |
| mounted | firstUpdated / updated |
| beforeUpdate | shouldUpdate |
| updated | updated |
| unmounted | disconnectedCallback |
Lit 的生命周期不是少,而是更贴近底层模型。
七、一个非常容易踩的坑(Important)
错误认知
“connectedCallback 只会执行一次”
正确认知
只要元素被移除再插入,就会再次执行
因此:
- 不要在这里假设“只初始化一次”
- 真正的一次性逻辑可以放在
firstUpdated
八、firstUpdated:Lit 的“妥协点”
firstUpdated() {
// 只执行一次
}
这是 Lit 提供的唯一一个非原生生命周期,目的只有一个:
满足开发者对“mounted once”的直觉需求
九、为什么 Lit 不提供更多生命周期?
因为:
- 生命周期越多,心智负担越重
- 浏览器模型已经足够清晰
- 多余的钩子会掩盖真实执行顺序
Lit 的选择是:
让开发者面对真实模型,而不是被抽象包裹
十、在工程实践中的建议(IIImportant)
10.1 推荐的使用方式
| 场景 | 生命周期 |
|---|---|
| 初始化状态 | constructor |
| 启动副作用 | connectedCallback |
| 派生状态 | willUpdate |
| DOM 操作 | updated / firstUpdated |
| 清理资源 | disconnectedCallback |
十一、一个核心结论(Remember)
Lit 的生命周期不是“不够用”,
而是“不替你做决定”。
它把控制权交还给:
- JavaScript
- 浏览器
- 架构设计者

浙公网安备 33010602011771号