原生html无法复用组件,因其是声明式标记语言,无作用域、变量、条件渲染或文件引入机制;常见错误包括误用iframe、document.write或未激活template;web components或构建时预处理是可行解法。

纯 HTML 本身不支持 import、include 或组件复用,硬写 10 个页面的 header 和 footer 就是给自己埋雷——改一处,漏九处,连测试都容易漏。
为什么原生 HTML 无法直接复用组件
HTML 是声明式标记语言,不是编程语言。它没有作用域、变量、条件渲染或文件引入机制。像 <include src="header.html"></include> 这种写法在浏览器里根本不会解析,也不会生效。开发者常误以为加个自定义标签(如 <my-header></my-header>)就能复用,但没 JS 驱动、没构建流程介入,它只是个空壳,DOM 里啥都不会出现。
常见错误现象包括:
- 把
header.html直接扔进主页面用<iframe src="header.html"></iframe>加载——导致样式隔离、SEO 失效、滚动错位、JS 上下文断裂 - 用
document.write()拼接 HTML 字符串——破坏 DOM 构建流,触发重排,且 XSS 风险高 - 依赖浏览器原生
<template></template>标签却忘了用 JS 克隆并 append —— 页面始终空白
Web Components 是唯一真正“原生”的解法
如果你必须坚持纯前端、零构建工具、不依赖后端模板,Custom Elements + Shadow DOM 是目前唯一被所有现代浏览器稳定支持的方案。它不靠打包、不靠服务端,运行时即可注册并复用。
实操关键点:
- 用
customElements.define()注册组件,名称必须含短横线(如"nav-bar"),否则浏览器拒绝识别 -
<template></template>内部不能有<script></script>或<style></style>,它们需通过 JS 动态注入或用adoptedStyleSheets关联 - Shadow DOM 默认
mode="closed"会屏蔽外部 CSS,若要继承全局样式,得显式设为mode="open" - 组件内响应用户操作(如点击菜单)必须用
this.shadowRoot.addEventListener(),而非document.addEventListener()
示例片段(可直接粘贴到 HTML 文件底部运行):
<template id="tmpl-header"><header style="background:#333;color:white;padding:1rem;"><h1><slot name="title">My App</slot></h1>
</header></template><script>
class NavBar extends HTMLElement {
constructor() {
super();
const tmpl = document.getElementById('tmpl-header');
this.attachShadow({ mode: 'open' }).appendChild(tmpl.content.cloneNode(true));
}
}
customElements.define('nav-bar', NavBar);
</script><nav-bar><span slot="title">Dashboard</span></nav-bar>
构建阶段预处理是最务实的选择
对毕业设计、内部管理系统这类无需 SSR、但追求开发体验的项目,硬啃 Web Components 成本偏高。更现实的做法是:用构建工具在编译时把模块拼好,输出标准 HTML。
推荐组合:Webpack + html-webpack-plugin + posthtml-include(轻量、无 JS 运行时依赖)。
使用前提与注意事项:
- 每个组件存为独立文件,如
src/includes/header.html,内容仅包含片段(不要带或) - 主 HTML 中用
<include src="./includes/header.html"></include>(注意路径是相对于当前 HTML 文件) -
posthtml-include不解析 JS,也不支持循环/变量;想动态生成列表?得换html-loader+webpack的require语法,或切到模板引擎 - 最终产物是静态 HTML,部署即用,无运行时性能损耗,也兼容老旧 CDN 缓存策略
别为了“组件化”而牺牲可维护性
很多团队过早引入 Web Components 或强行套 Vue SFC,结果组件粒度太细、props 接口混乱、CSS 作用域打架,反而比复制粘贴更难 debug。真正值得提取的组件只有三类:
- 结构固定、样式强约束的 UI 块(如带图标+文字+状态色的
status-badge) - 跨页面一致的导航/表单布局(如含搜索框+筛选项+导出按钮的
list-toolbar) - 需统一行为控制的交互单元(如带加载态、防重复提交的
submit-button)
其它场景,比如两个页面的表格列数不同、排序逻辑各异,硬抽成一个 data-table 组件,最后往往变成一堆 v-if 和 props 配置项,阅读成本远超直接写两份 HTML。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











