<template>不能直接当组件用,因其仅为惰性容器,内容不解析、不执行脚本、不加载资源,也不响应属性或事件;必须配合customElements.define()注册、attachShadow()挂载,并在connectedCallback中调用content.cloneNode(true)显式克隆插入Shadow DOM才能生效。

直接用 <template> 标签管理 HTML 模板是可行的,但必须配合 customElements.define() 和 attachShadow() 才能真正复用;单独把 HTML 写进 <template> 不会自动渲染,也不构成组件。
为什么 <template> 不能直接当组件用
<template> 只是浏览器的“惰性容器”,内容不会解析、不执行脚本、不加载资源,也不会响应属性或事件。常见错误是把模板写好就以为能用了,结果 <my-button></my-button> 渲染出来是空的——因为没注册类、没挂载 Shadow DOM、也没把模板内容 clone 进去。
- 模板内容必须显式调用
content.cloneNode(true)才能插入 Shadow DOM -
<template>内部写<script>或<style>会被忽略,得用 JS 动态创建并 append - 多个组件共用一个
<template id="xxx">是安全的,但每个实例都要独立 clone,否则会移动节点(DOM 移动后原位置为空)
customElements.define() 必须在 DOM ready 后调用
如果把组件定义代码放在 <script> 标签里且没加 defer 或没放 </body> 前,很容易遇到 document.getElementById('tmpl-header') 返回 null ——因为模板还没被 parser 解析到 DOM 中。
- 推荐做法:把
<template>放在<body>顶部,<script>放在</body>前;或用DOMContentLoaded包裹定义逻辑 - 不要在
constructor里直接读this.innerHTML,此时元素刚创建,子节点尚未建立连接 - 想基于初始 HTML 属性初始化状态,改用
connectedCallback,它保证元素已挂载且属性可用
Shadow DOM mode="closed" 会让模板调试变困难
设成 mode="closed" 后,外部无法通过 el.shadowRoot 访问内部结构,Chrome DevTools 里也看不到 Shadow 树——这会让样式调试、事件排查和自动化测试几乎失效。
立即学习“前端免费学习笔记(深入)”;
- 开发阶段一律用
mode="open",上线前再评估是否切 closed - 即使 mode="open",CSS 依然隔离:外部样式默认不穿透,
:host和::slotted是唯一可控入口 - 想让全局 CSS 生效?别依赖外部样式表,改用 CSS 自定义属性传参,比如
style="--bg: #007bff",组件内用var(--bg)
真正卡住人的不是语法,而是模板、注册、挂载、属性响应这四步缺一不可;漏掉任意一环,标签就只是个静态字符串。最常被跳过的其实是 connectedCallback 里的事件绑定——按钮点了没反应,八成是因为监听器没挂上去,而不是模板写错了。



















