必须用 template.content.clonenode(true) 获取模板内容,因 template.innerhtml 返回空字符串;克隆后需更新唯一属性,且 slot 仅在 attachshadow 后对直系子节点生效。

template.content.cloneNode(true) 必须用,不能直接 innerHTML
很多人写 template.innerHTML 想拿到结构,结果返回空字符串——因为 template 的内容不在主 DOM 树里,innerHTML 读不到。真正要的是 template.content,它返回一个 DocumentFragment,再用 .cloneNode(true) 才能安全复用。
常见错误现象:
• 直接 document.getElementById('tpl').innerHTML → 空
• 克隆后没改 ID 或 class → 多个实例冲突(比如重复的 id="submit-btn")
• 多次调用 .appendChild(template.content) → 第二次起啥也不显示(DocumentFragment 移动语义)
- 每次插入前必须
cloneNode(true) - 克隆后手动更新唯一属性:如
clone.querySelector('[id]').id = 'btn-' + Date.now() - 若模板含
<style></style>,它只在 Shadow DOM 中生效;在 light DOM 中需额外注入或提取样式规则
slot 不会自动生效,必须配合 attachShadow
slot 标签本身不触发任何分发逻辑,它只在自定义元素的 Shadow DOM 内对**直系子节点**起作用。把 <slot name="header"></slot> 写在 <template></template> 里?完全无效——浏览器当普通标签渲染,不解析、不分发。
使用场景:
• 想让用户用 <my-card><h2 slot="header">标题</h2></my-card> 注入内容
• 想让组件结构和填充内容解耦,避免拼接字符串
- 必须先调用
this.attachShadow({ mode: 'open' }),否则slot被当作文本显示 -
slot匹配严格区分大小写:slot="Header"≠name="header" - 默认插槽(无
name)只能有一个;多个会被合并,顺序按 DOM 流,不是按声明顺序
data- 属性驱动样式切换,但值永远是字符串
用 data-theme="dark" 控制主题比写一堆 if (theme === 'dark') 更干净,但别忘了:所有 dataset 值都是字符串,data-count="42" → this.dataset.count === "42",不是数字。
性能影响:
• 频繁读取 dataset 不慢,但反复 parseInt() 或 JSON.parse() 会拖慢渲染
• CSS 里用 [data-theme="dark"] { ... } 是高效选择器;但 [data-loaded] 这类布尔属性建议用 data-loaded="" 而非 data-loaded="true",更简洁
- 需要数值时显式转换:
const count = parseInt(this.dataset.count, 10) || 0 - 布尔值建议统一用有/无属性判断:
this.hasAttribute('data-disabled')比this.dataset.disabled === 'true'更可靠 - 避免在
connectedCallback里做 heavy parsing,可缓存或惰性计算
is="xxx" 复用内置标签,但 extends 必须匹配
想让 <button is="my-button"></button> 生效,前提是注册时用了 { extends: 'button' },且类继承自 HTMLButtonElement。用 HTMLElement 继承却写 is="button"?浏览器直接忽略,退化成普通 <button></button>。
容易踩的坑:
• is 只对部分内置标签有效(button、input、article、div 等),span 和 p 不支持扩展
• 自定义元素未注册完成就渲染含 is 的 HTML → 控制台报错,且属性不触发 attributeChangedCallback
• SSR 或静态 HTML 输出时,is 属性不会被服务端解析,得靠客户端补注册
- 注册顺序很重要:必须在 HTML 解析前或
DOMContentLoaded时完成customElements.define() - 检查是否生效:
console.log(document.querySelector('button[is="my-button"]').constructor.name)应为"MyButton",不是"HTMLButtonElement" - 不支持
is的标签(如img)想复用行为,只能包装一层或用customElements.define('my-img', ...)
实际应用中最容易被忽略的点是:template 和 slot 之间没有自动桥接关系,它们像两块拼图,缺了 JavaScript 显式挂载这根线,就永远拼不起来。写完 <template><slot></slot></template> 不等于组件就活了——得亲手把 template 插进 shadowRoot,还得确保用户写的 slot 内容真正在自定义元素的直系子节点位置。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











