template 标签不渲染,必须用 javascript 克隆 content 才能生效;它只是离线容器,不参与文档流、不执行脚本、不激活 slot,slot 仅在 shadow dom 中有效,且只匹配直系子节点并严格字符串匹配。

template 标签不渲染,必须用 JavaScript 克隆 content 才能生效
写了 <template><p>Hello</p></template> 却没看到任何内容?不是写错了,是 template 本身就不参与渲染。它只是个“离线容器”,浏览器解析时跳过、不创建节点、不加载图片或脚本。
常见错误:给 <template id="card"></template> 加 class 或 CSS 选择器想直接样式控制——无效,它不在文档流里。
- 必须用
document.querySelector('#card').content.cloneNode(true)或document.importNode(tmpl.content, true)获取可插入的副本 -
tmpl.content是只读的DocumentFragment,不能直接appendChild(tmpl),也不能调用querySelector - 如果模板里有
<script></script>,默认不会执行;需手动创建并运行(注意 CSP)
slot 只在 Shadow DOM 中起作用,template 内部写 slot 没用
<template><slot name="header"></slot></template> 这种写法完全无效。template 不会激活 slot 机制,slot 也不是模板语法的一部分——它只在自定义元素的 shadow root 里才开始分发内容。
典型失败场景:<my-card><h2 slot="header">Title</h2></my-card> 渲染为空白,原因往往不是 slot 写错了,而是没挂 Shadow DOM。
- 必须在自定义元素构造函数中调用
this.attachShadow({ mode: 'open' }) - 再把 template 的
content.cloneNode(true)插入到shadowRoot,而不是直接插入 body - 不要在
connectedCallback里反复attachShadow或清空重建shadowRoot,会破坏节点复用
插槽匹配只认直系子节点,隔一层就丢弃
slot 分发不是递归查找,它只检查自定义元素的**直接子节点**是否带 slot 属性。一旦中间套了 wrapper,内容就静默消失。
例如:<my-card><h2 slot="header">OK</h2></my-card> ✅;但 <my-card><div><h2 slot="header">NO</h2></div></my-card> ❌——整个 <h2></h2> 被丢弃,<div> 成为唯一子节点,而它没带 <code>slot 属性。
- 具名插槽严格字符串匹配:
<slot name="footer"></slot>只接收<p slot="footer"></p>,不接收<p slot="Footer"></p>或<p slot="footer "></p> - 一个组件中只能有一个默认插槽(无
name),多个同名具名插槽时只有第一个能分发内容 - 未加
slot属性的子节点全部进默认插槽;带slot="xxx"但没声明对应<slot name="xxx"></slot>的节点,直接丢弃
::slotted() 样式能力极弱,别指望它做复杂样式
想让插槽里的按钮继承主题色?::slotted(.btn) 可以,但 ::slotted(.btn:hover)、::slotted(div .text) 都不行。它只支持单层简单选择器,且无法穿透多层嵌套。
比如插槽里传入的是 <my-icon><svg></svg></my-icon>,::slotted(my-icon) 能命中,::slotted(my-icon svg) 就失效。
-
::slotted(*)优先级低,容易被 light DOM 原有样式覆盖 - 更可靠的方式是透传 CSS 自定义属性:
host { --color-primary: #007bff; },再在插槽内容里用color: var(--color-primary) - 动态变更内容后,需监听
slotchange事件,而非依赖样式自动更新











