<template>标签本身不渲染,是浏览器原生“跳过式”容器,其内容需通过document.importNode()克隆并手动填充数据后挂载,否则页面必然空白;它无响应式、无样式隔离、无生命周期,仅适用于静态低频场景。

<template> 标签不是组件,它只是个“待激活的壳”——直接放 HTML 里不会渲染、不执行脚本、不加载图片,页面空白是常态,不是 bug。
为什么<template>写完就空白
浏览器解析到 <template> 时会跳过其内容:DOM 中无节点、<script> 不执行、<img src> 不发起请求、表单控件(如 <input checked>)状态不保留。常见误操作包括:
- 把
<template id="card">...</template>当 Vue 的<template>用,期待自动替换 - 用
innerHTML = tmpl.innerHTML注入,导致复选框/下拉选中态丢失 - 模板里写
{{title}}却没 JS 替换逻辑,最终原样输出
怎样安全克隆并填充<template>内容
必须用 document.importNode(),不能用 cloneNode() —— 前者保留表单状态、正确处理 <select> 选项、兼容 Shadow DOM;后者只做浅层复制,交互态全丢。
- 获取模板:
const tmpl = document.getElementById('card-template') - 克隆内容:
const fragment = document.importNode(tmpl.content, true) - 查找绑定点:
fragment.querySelectorAll('[data-bind]'),按data-bind="title"匹配节点 - 填充数据:文本用
node.textContent = data.title,HTML 内容用node.innerHTML = data.content(注意 XSS) - 挂载:
container.appendChild(fragment),别用innerHTML覆盖已有结构
如何让<template>支持参数和复用
它没有属性、生命周期或响应式机制,“传参”本质是调用 JS 工厂函数时传入对象,再手动映射到 DOM 节点。
- 模板内用
data-bind="author-name",别用class="author"—— 后者易冲突、难定位 - 工厂函数返回
DocumentFragment,不是字符串,避免重复解析 - 若需更新,缓存首次生成的节点,后续只改
textContent或dataset值,不重新克隆 - 禁止硬编码 ID(如
id="card-1"),多次实例化会导致 ID 重复,破坏 CSS 查询和 JS 逻辑
<template> 和 Web Components 的边界在哪
它不封装样式、不隔离作用域、不提供事件代理或属性监听 —— 所有这些都得手写。适合静态卡片、弹窗、表单项等低频、轻量、无复杂交互的场景。
立即学习“前端免费学习笔记(深入)”;
- 想用全局 CSS?可以,但得自己确保类名带前缀(如
c-card__header),否则样式泄漏 - 需要主题切换?靠 CSS 变量 + JS 切换
:root值,<template>本身不参与 - 要响应用户点击?得在克隆后手动绑定
fragment.querySelector('button').addEventListener(...) - 真要封装性、样式隔离、属性绑定?该上
customElements.define()了,<template>是起点,不是终点



















