<template>标签内容不渲染,因其被转为离线DocumentFragment挂载于content属性,须用content.cloneNode(true)克隆后插入DOM才可见;直接写入HTML中不创建节点、不执行脚本、不加载资源,且必须位于<body>或<head>直接子节点位置。

纯 HTML <template> 标签不是“开箱即用”的组件,它必须配合 JS 手动克隆、填充、挂载,否则就是一段被浏览器忽略的静态占位符。
为什么<template>直接写在 HTML 里却什么也不显示
浏览器解析时会跳过 <template> 内容:不创建 DOM 节点、不执行内联 <script>、不加载 <img> 或 <link>。常见错误是把它当 Vue 的 <template> 或 Web Component 的 <slot> 用,结果页面空白,控制台也无报错。
- 检查是否误用了
innerHTML = tmpl.innerHTML—— 这会导致<input checked>等表单状态丢失 - 确认没在模板里写
{{title}}这类插值语法 —— 原生 HTML 不识别,必须 JS 手动替换 - 避免把
<template>放在<body>外或嵌套在非标准父元素中(如直接放在<table>里),部分浏览器会自动移除
如何安全克隆并填充<template>内容
核心是用 document.importNode(),不是 cloneNode()。前者保留表单控件状态、正确处理 <select> 选项、兼容 Shadow DOM;后者只做浅层复制,脚本和样式不生效。
- 获取模板:
const tmpl = document.getElementById('card-template') - 克隆内容:
const fragment = document.importNode(tmpl.content, true) - 填充数据:遍历
fragment.querySelectorAll('[data-bind]'),对文本节点用node.textContent = data[key],对富文本用node.innerHTML = data[key](注意 XSS) - 挂载:
container.appendChild(fragment),别用innerHTML覆盖已有结构,否则会销毁事件监听器
怎样让<template>支持“传参”和复用
所谓“传参”,本质是调用 JS 工厂函数时传入对象,再映射到 DOM。它没有生命周期、无响应式、不监听属性变化。
立即学习“前端免费学习笔记(深入)”;
- 模板内用
data-bind="title"而非class="title",语义清晰且不易与样式类冲突 - 工厂函数返回
DocumentFragment或真实 DOM 节点,避免每次调用都重新解析字符串 - 如需更新,缓存首次生成的节点,后续只改
textContent或dataset,不重复克隆 - 禁止硬编码
id(如id="card-1"),多次实例化会导致 ID 重复,破坏 CSS 选择器和getElementById
<template>和 Web Components 的关键分界点在哪
它不提供封装性、无样式隔离、无属性绑定、无生命周期钩子。适合静态卡片、弹窗、表单项等低频、轻量、无需复杂交互的模块。
- 需要样式作用域?得靠 CSS
:host或手动加唯一 class 前缀,不能靠<template>自身解决 - 要监听属性变更?必须自己实现
MutationObserver或暴露update(data)方法 - 想复用逻辑?只能靠 JS 函数封装,无法像
customElements.define()那样注册全局标签 - 真正复杂的组件(带状态、动画、跨组件通信)——该上 Web Components 或框架了,别硬撑
容易被忽略的是:模板内容一旦克隆进主 DOM,就彻底脱离 <template> 容器,后续对原始 <template> 的修改不会影响已挂载的实例。所有“动态”都发生在 JS 层,不是模板本身的能力。



















