template标签不支持视图分支逻辑,必须通过js手动判断并克隆对应id的template实现分支渲染,不可在template内写条件指令或注释;纯样式分支可用data-*+css替代,自定义元素中需显式调用render()重绘。

template 标签不支持视图分支逻辑
template 本身只是离线 DOM 片段容器,不解析、不执行、不响应任何条件或变量。你写 <template><div v-if="show">xxx</div></template> 或 {{ title }},浏览器就当纯文本处理——不会报错,也不会渲染出内容。所谓“视图分支”,比如根据 config.type === 'card' 渲染卡片、=== 'list' 渲染列表,必须靠 JS 手动判断后选择克隆哪个 template,而不是让模板自己做分支。
用多个 template + JS 判断实现分支渲染
真正在用的方案是:把不同视图结构拆成多个带 id 的 template,再由 JS 按配置决定克隆哪一个。常见错误是试图在一个 template 里塞 <!-- if: type === 'card' --> 注释,这毫无作用。
- 定义分支模板:
<template id="view-card"><div class="card">...</div></template>和<template id="view-list"><ul class="list">...</ul></template> - JS 层统一入口函数:
renderView(config),内部用switch(config.type)或if/else拿对应template - 每次调用都走完整流程:克隆 → 填值(
textContent/src等)→ 绑事件(不能复用旧节点的监听器)→ 插入目标容器 - 避免在
template中写id属性;克隆后若需唯一标识,用 JS 动态设el.id = `item-${Date.now()}`类似方式
data-* 配合 CSS 可替代部分简单分支
如果分支只是视觉切换(如展开/收起、亮色/暗色主题),不用 JS 渲染整块 DOM,直接用 data- 属性 + CSS 就够了。比如:<div class="panel" data-theme="dark">,配合 <code>[data-theme="dark"] { background: #111; }。这种方案零 JS、无克隆开销,但只适用于纯样式或布尔状态类分支。
- 适用场景:
data-state="loading"控制 loading 图标显隐,data-size="large"切换按钮尺寸 - 不适用场景:需要插入不同结构(
<table> vs <code><div grid>)、不同子组件数量、或依赖异步数据的分支<li>注意:所有 <code>dataset值都是字符串,el.dataset.active === 'true'才是安全判断,不是el.dataset.active === true - 关键点:不要在
connectedCallback里做异步加载后才首次render(),否则用户可能看到空白几秒 - 分支逻辑别耦合到 HTML 结构里,比如用
<slot name="card"></slot>让使用者传内容——这属于内容分发,不是视图分支 - 如果分支选项超过 3 种,建议把
template节点缓存在 Map 中:const TEMPLATES = new Map([['card', cardTpl], ['list', listTpl]]),避免重复getElementById
自定义元素中管理分支容易忽略重渲染边界
当你把模板分支封装进 class MyView extends HTMLElement,很多人以为改 this.setAttribute('type', 'list') 就能触发更新——但 attributeChangedCallback 不会自动重渲染,更不会帮你选对 template。你必须在回调里显式调用 this.render(),且 render() 要清空旧 DOM 再克隆新模板。
真正难的不是怎么写分支,而是判断哪些该用 JS 渲染分支、哪些该交给 CSS 或服务端。一个 template 克隆一次要 0.1ms,但 20 次就是 2ms —— 这种开销在滚动列表里会立刻暴露。











