用 customelement 封装 最干净,需继承 htmlelement、用 shadow dom 隔离样式结构、在 connectedcallback 启动定时器、disconnectedcallback 清理、支持 format/time-zone 属性及 attributechangedcallback 监听。

直接用 CustomElement 封装最干净,避免全局变量污染、样式泄漏和重复初始化问题;不推荐用 setInterval + 普通函数反复调用的方式——它无法隔离状态,多实例时会互相干扰。
用 CustomElement 实现可复用的 <live-clock></live-clock>
核心是继承 HTMLElement,在 connectedCallback 中启动定时器,在 disconnectedCallback 中清理。每个实例独享自己的 intervalId 和格式化配置。
- 必须调用
this.attachShadow({ mode: 'open' }),否则样式和结构无法封装 - 时间更新逻辑写在
updateTime()方法里,不要直接塞进connectedCallback - 支持传入
format属性(如format="HH:mm:ss")或time-zone(如time-zone="Asia/Shanghai"),通过getAttribute读取 - 使用
Intl.DateTimeFormat格式化,比手拼字符串更可靠,且自动处理时区、12/24 小时制等边界
textContent 是唯一安全的 DOM 更新方式
时间显示纯文本,不需要解析 HTML,用 innerHTML 是多余风险。但注意:textContent 会抹掉所有空白符,如果模板里写了换行或缩进,渲染结果会塌成一行。
- 正确写法:
this.shadowRoot.textContent = formattedTime - 错误写法:
this.shadowRoot.innerHTML = formattedTime(可能被注入、触发重排、慢 20%+) - 如果需要保留空格(比如对齐双数字),改用
white-space: preCSS 控制,而不是靠innerHTML混入
定时器必须和元素生命周期严格绑定
常见错误是只在 connectedCallback 启动 setInterval,却不在 disconnectedCallback 清除——用户切走标签页、组件被 v-if 或 ngIf 销毁时,定时器还在后台跑,吃内存还错乱。
- 每次
connectedCallback都要检查是否已有intervalId,有则先clearInterval -
disconnectedCallback必须无条件clearInterval(this._timer),并置this._timer = null - 页面隐藏时(
document.visibilityState === 'hidden')可额外暂停,但不是必须;真正关键的是卸载时清理
多实例同时存在时的格式与样式隔离
如果你在同一个页面放了两个 <live-clock format="HH:mm"></live-clock> 和一个 <live-clock time-zone="UTC"></live-clock>,它们必须互不干扰——这是封装的价值所在,也是普通函数做不到的。
- 每个实例的
shadowRoot是独立的,CSS 不会穿透,也不用担心 class 名冲突 - 格式选项(如
hour12、minute)应作为Intl.DateTimeFormat的 options 参数传入,不能硬编码 - 避免在类外定义共享的
formatCache或formatter实例——不同实例可能需要不同配置,缓存必须按 key 分离
真正容易被忽略的是:CustomElement 的属性变更监听(attributeChangedCallback)必须显式声明 observedAttributes,否则 time-zone 改了不会触发重绘;还有,connectedCallback 可能被多次调用(比如元素被移出又插回 DOM),所以内部逻辑必须可重入。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











