HTML模板本身不参与代码拆分,所有拆分均发生在JS层或构建阶段;<include src="header.html">仅是字符串,需构建工具替换或JS fetch才能生效;动态import()是唯一原生按需加载手段,须在函数体内调用并显式catch错误,避免白屏或竞态问题。

HTML 模板本身不参与代码拆分,所有“拆分”动作都发生在 JS 层或构建阶段;你写的 <include src="header.html"> 浏览器根本不会解析——它只是字符串,必须靠构建工具替换或 JS 运行时 fetch 才能生效。
动态 import() 是唯一真正按需加载的运行时手段
浏览器原生支持 import(),现代构建工具(Vite/Rollup/Webpack)能自动识别并打成独立 chunk。但关键不在语法,而在触发时机和错误处理:
- 别在模块顶层写
import('./dialog.js')——这会被当作静态导入,失去按需意义;必须包裹在事件回调、IntersectionObserver回调或函数体内 -
catch()必须显式处理,否则加载失败时白屏或控制台静默报错:import('./card.js').catch(() => { el.innerHTML = '<div class="error">加载失败</div>'; }) - 避免在
connectedCallback中无条件调用import():Custom Element 可能被反复创建/销毁,导致重复请求或竞态
fetch() + insertAdjacentHTML() 适合纯结构片段
当组件只有 HTML + CSS、不含逻辑(如 <header>、<footer>),用 fetch() 加载字符串比 JS 模块更轻量,也绕过 CSP 对 eval 类执行的限制:
- 永远用
insertAdjacentHTML('beforeend', html)替代innerHTML,防止意外执行内联<script>标签 - URL 路径以入口 HTML 所在目录为基准,不是当前 JS 文件位置;可用
new URL('./footer.html', import.meta.url)构造绝对路径 - 重复加载同一片段时加
cache: 'force-cache'或手动加时间戳参数,否则开发环境下可能拿到 stale response
构建时内联是零运行时风险的选择
适用于内容稳定、无需运行时更新的组件(如全局 header、copyright):
立即学习“前端免费学习笔记(深入)”;
- 用
rollup-plugin-html或posthtml-include在打包阶段展开<include src="header.html">,输出仍是单个 HTML 文件 - 路径解析明确:src 始终相对于入口 HTML 文件,不会因 JS 模块位置变化而失效
- 不支持传参,所以带变量的组件(如
<card title="xxx">)必须改用模板引擎或升级为 Custom Element - 若
header.html里有相对路径的 CSS/JS,构建工具默认不会重写,需额外配置resolvePaths或统一用根路径
真正容易被忽略的是资源生命周期——哪怕用了最“模块化”的方式,Custom Element 实例被移除时,如果没在 disconnectedCallback 里清理 IntersectionObserver、定时器或事件监听器,内存泄漏会比 jQuery 时代更难排查。



















