原生html无数据驱动能力,需手动实现响应逻辑;customelements.define后属性变更不触发ui更新,因attributechangedcallback仅响应html属性修改且需声明observedattributes,js属性赋值无效,须用object.defineproperty或proxy;render应操作shadowroot,避免innerhtml导致状态丢失;data-*属性仅为被动容器,需js主动读取并更新,命名须小写短横线,布尔值和数值需规范处理;onchange内联事件this指向错误;innerhtml暴力替换破坏焦点、滚动和事件;无依赖追踪则ui卡滞;高效更新需最小化diff或精准dom操作。

原生 HTML 本身不支持数据驱动,所谓“可重用的数据驱动型组件”必须靠手动补足响应逻辑——否则你写的只是静态模板,不是可响应的数据视图。
customElements.define 后为什么属性改了 UI 不更新?
因为 attributeChangedCallback 默认不触发,也不监听 JS 属性变化。它只在 HTML 属性(如 count="5")被显式修改时调用,且仅接收字符串值。
- 必须在类中声明
static get observedAttributes(),拼错或漏写会导致回调完全不执行 - 首次渲染时已存在的属性,
attributeChangedCallback不会触发,得在connectedCallback里手动同步一次 -
this.count = 123这种 JS 属性赋值,不会触发任何回调;要响应它,得用Object.defineProperty或Proxy封装 setter -
render()方法应只操作this.shadowRoot,避免直接写this.innerHTML(会丢事件、毁状态)
data-* 属性能当数据源用吗?
可以,但它是被动容器,不是响应机制。浏览器不会因 data-step="3" 自动刷新 UI,必须由 JS 主动读取并触发更新。
- 命名必须小写+短横线,如
data-user-id,否则dataset.userId拿不到值(浏览器自动转为小写无横线) - 布尔值别只写
data-active,要写成data-active="true",否则dataset.active返回空字符串,易误判为 false - 数值需手动转:
const step = Number(el.dataset.step),否则字符串比较会出错("2" > "10"是 true) - 若组件内部有
<input>,别在模板里写onchange="handle(this)"—— this 指向错误,且无法访问组件私有状态
render() 函数里直接 innerHTML = `...` 安全吗?
短期可用,但本质是暴力 DOM 替换,会丢焦点、毁滚动位置、清空事件监听器,且无法局部更新。
- 每次重写
innerHTML都销毁重建全部子节点,<input>失去焦点,<textarea></textarea>滚动回到顶部 - 没有依赖追踪:数据变了但没调
render(),UI 就卡住;你也不知道哪次变更该触发哪块 UI - 调试困难:没法在某个字段更新时打断点,也没生命周期钩子可观察
- 真要高效更新,得自己实现最小化 diff(比如只替换文本节点),或用
textContent/setAttribute精准操作
真正可复用的数据驱动组件,关键不在“怎么写模板”,而在“谁决定什么时候重绘、重绘哪一块”。这个决策链一旦松散,复用性就只剩结构复制,毫无响应能力。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











