<template>本身不构成高内聚组件,因其仅为静态内容容器,无生命周期、无状态管理、不响应变更;真正实现高内聚需结合Custom Element,通过customElements.define()绑定模板与逻辑,并利用Shadow DOM、observedAttributes和<slot>机制确保职责单一与隔离。

为什么本身不构成高内聚组件
<template>只是静态内容容器,浏览器跳过解析、不执行脚本、不加载资源,也不响应属性变更或事件。它没有生命周期、无状态管理能力,更不会自动绑定数据或更新 DOM。把一个 <template id="card"> 直接扔进页面,什么都不会发生——这不是组件,只是待加工的原料。
常见错误是把它当 Vue 或 Lit 的模板用,写完就期待“自动渲染”,结果控制台安静如鸡,页面空空如也。
- 直接
innerHTML = template.innerHTML会丢失<input checked>等表单状态 - 用
cloneNode(true)复制含<script>的模板,脚本不会执行 - 模板里写
{{title}},原生 HTML 完全不认识,必须手动替换
让具备高内聚:必须搭配 Custom Element
真正实现高内聚,得靠 customElements.define() 把 <template> 和行为逻辑绑定在一起。组件的“职责边界”由类定义决定,比如 class CardComponent extends HTMLElement 只处理卡片结构、标题渲染、折叠逻辑,不碰 API 请求或路由跳转。
关键操作点:
立即学习“前端免费学习笔记(深入)”;
- 在
constructor()中调用super(),但**不要访问this.shadowRoot**(此时还不存在) - 在
connectedCallback()中调用this.attachShadow({ mode: 'open' }),再用document.importNode(tmpl.content, true)克隆模板内容并注入 - 通过
static observedAttributes = ['title', 'loading']声明监听项,配合attributeChangedCallback()实现响应式更新 - 所有样式必须进 Shadow DOM,外部 CSS 无法穿透,内部样式也不会泄漏出去
插槽(<slot>)是维持低耦合的核心机制
<slot> 不是占位符,而是明确的内容契约协议。父级传什么、怎么传,子组件只定义“洞在哪”,不决定“填什么”。这正是高内聚(自己管好洞)、低耦合(不关心内容来源)的落地体现。
实操注意事项:
- 默认插槽只能有一个;具名插槽如
<slot name="actions">可多个,但同名<slot>只接收第一个匹配节点 - 写
<slot>默认文案</slot>提供 fallback,避免空内容导致布局塌陷 - Shadow DOM 中,CSS 选择器无法跨
<slot>边界定位,所以父级样式不会影响插槽内容——这是隔离的关键保障 - 动态替换插槽内容会触发
slotchange事件,可用于更新内部计数器或状态栏,但注意它不冒泡
容易被忽略的边界问题:命名空间、数据流与复用成本
纯 <template> + JS 工厂函数(如 createCard({ title: 'Hi' }))看似轻量,但它没有属性监听、无生命周期、不支持 CSS 变量主题切换,每次更新都得手动查 DOM 节点重赋值。一旦组件需要响应式、可配置、可主题化,就必须升级为 Custom Element。
另外,即便用了 Shadow DOM,若模板中硬编码 id="card-1" 或用泛化类名如 class="header",依然会导致样式冲突或 JS 查询错乱——高内聚不是靠技术堆砌,而是靠设计约束:前缀命名、CSS 自定义属性、语义化标签、严格 slot 分发。



















