template标签本身不渲染也不执行脚本,因为浏览器规范将其定义为“离线仓库”,跳过解析、不构建DOM、不触发副作用;必须通过document.importNode(tmpl.content, true)克隆后手动填充、绑定并插入。

为什么template标签本身不渲染也不执行脚本
因为浏览器规范明确将<template>定义为“离线仓库”——它被跳过解析、不构建在DOM树中、不触发任何副作用(图片加载、脚本执行、样式计算)。你看到的只是源码,不是运行时节点。常见误操作是把<template>当成Vue或React的模板语法容器,往里写{{title}}或v-if,结果页面空白、控制台无报错、调试无从下手。
真正起作用的是后续JS:必须用document.importNode(tmpl.content, true)克隆出可挂载的DocumentFragment,再手动填充、绑定、插入。这个过程没有自动机制,全靠你组织逻辑。
怎么安全克隆并注入配置数据到template内容
用cloneNode(true)容易丢表单状态(比如<input checked>的选中态),尤其在Shadow DOM里失效;document.importNode()才是标准解法。
- 获取模板:
const tmpl = document.getElementById('card-template') - 克隆内容:
const fragment = document.importNode(tmpl.content, true) - 查找绑定点:优先用
data-bind属性(如data-bind="title"),比class更稳定、不易被CSS覆盖 - 填充文本:
node.textContent = config.title(防XSS);填充HTML:node.innerHTML = config.content(需白名单过滤) - 图片资源校验:
src只允许https:、data:、/开头,避免javascript:注入
如何让template组件支持多次复用且不冲突
核心是避免ID污染和事件残留。一个<template>被克隆多次,若里面含id="submit-btn",就会产生多个同名ID,document.getElementById()行为不可控,CSS选择器也失效。
立即学习“前端免费学习笔记(深入)”;
- 禁止在模板内写
id属性;改用data-role="submit-button"这类语义化标记 - 每次克隆后,交互元素(按钮、输入框)必须重新绑定事件,不能依赖事件委托——尤其在Shadow DOM中,委托会漏掉内部事件
- 如果组件需响应式更新,缓存首次生成的fragment,后续只更新
textContent或dataset值,不重复克隆 - 工厂函数返回
DocumentFragment而非字符串,避免重复解析开销
customElements.define()是不是必须的
不是必须,但它是唯一能绕过构建工具、纯前端实现组件注册与复用的原生方案。如果你的项目不允许打包、不走Vite/webpack,又想让<nav-bar>这种标签真实生效,就必须用它。
注意几个硬性限制:
- 自定义标签名必须含短横线(如
my-header),否则浏览器直接忽略 -
<template>里不能放<script>或<style>,它们得用JS动态注入或adoptedStyleSheets关联 - Shadow DOM默认
mode="closed",外部CSS进不去;要继承全局样式,得显式设mode="open" - 属性传参只支持字符串,复杂对象得
JSON.stringify()后塞进attribute,再在attributeChangedCallback里解析——性能损耗明显
最易被忽略的一点:服务端渲染出的<my-card>在客户端JS加载前是空白节点,必须配合defer或async确保脚本在DOM就绪后执行,否则首屏白屏。



















