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">,配合 [data-theme="dark"] { background: #111; }。这种方案零 JS、无克隆开销,但只适用于纯样式或布尔状态类分支。
- 适用场景:
data-state="loading"控制 loading 图标显隐,data-size="large"切换按钮尺寸 - 不适用场景:需要插入不同结构(
<table>vs<div grid>)、不同子组件数量、或依赖异步数据的分支 - 注意:所有
dataset值都是字符串,el.dataset.active === 'true'才是安全判断,不是el.dataset.active === true
自定义元素中管理分支容易忽略重渲染边界
当你把模板分支封装进 class MyView extends HTMLElement,很多人以为改 this.setAttribute('type', 'list') 就能触发更新——但 attributeChangedCallback 不会自动重渲染,更不会帮你选对 template。你必须在回调里显式调用 this.render(),且 render() 要清空旧 DOM 再克隆新模板。
立即学习“前端免费学习笔记(深入)”;
- 关键点:不要在
connectedCallback里做异步加载后才首次render(),否则用户可能看到空白几秒 - 分支逻辑别耦合到 HTML 结构里,比如用
<slot name="card">让使用者传内容——这属于内容分发,不是视图分支 - 如果分支选项超过 3 种,建议把
template节点缓存在 Map 中:const TEMPLATES = new Map([['card', cardTpl], ['list', listTpl]]),避免重复getElementById
真正难的不是怎么写分支,而是判断哪些该用 JS 渲染分支、哪些该交给 CSS 或服务端。一个 template 克隆一次要 0.1ms,但 20 次就是 2ms —— 这种开销在滚动列表里会立刻暴露。



















