标签本身不触发任何加载行为,只是离线容器;真懒加载需配合 intersectionobserver,在元素进入视口后动态补全资源(如赋值 data-lazy-src 到 src),并手动处理脚本加载与事件绑定。

直接说结论:<template></template> 标签本身不触发任何加载行为,它只是个“离线容器”,不能单独实现懒加载;真正起作用的是你用 JS 把它 clone 出来、注入 DOM、再触发资源加载的那一步——而这个过程必须配合 IntersectionObserver 或滚动监听,否则就不是懒加载。
为什么 <template></template> 不等于懒加载
<template></template> 的内容不会被解析、不会发起网络请求、不会执行脚本、也不会渲染。它就像一张没装进打印机的图纸——再精美也没用。常见误解是:“我把 HTML 片段写在 <template></template> 里,等需要时再插入,这就叫懒加载”。错。这只是延迟插入,不是延迟加载:如果片段里包含 <img src="real.jpg"> 或 <script src="heavy.js"></script>,一旦你调用 content.cloneNode(true) 并 append 到 document,浏览器立刻开始下载和执行。
- 不加控制地 clone + append → 所有子资源同步加载,可能阻塞主线程
- 没做预判(比如是否在视口)→ 用户还没滑到,资源已提前加载,浪费带宽
- 没处理重复加载 → 同一片段多次插入,脚本重复执行、图片重复请求
怎么用 <template></template> 配合 IntersectionObserver 实现真懒加载
核心思路:把资源加载逻辑从模板内容中剥离,只保留结构占位;等元素进入视口后,再动态补全真实资源。
- 模板里只放占位结构,比如
<img data-lazy-src="photo.jpg" alt="...">,不写src - 用
IntersectionObserver监听模板实例化后的 DOM 节点(不是<template></template>本身) - 回调中遍历节点内所有
data-lazy-src元素,赋值src;对<script></script>类型则用document.createElement('script')动态创建并设置src - 务必调用
observer.unobserve(target),避免重复触发
const template = document.querySelector('#doc-fragment');
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const fragment = template.content.cloneNode(true);
// 补全图片
fragment.querySelectorAll('[data-lazy-src]').forEach(el => {
if (el.tagName === 'IMG') el.src = el.dataset.lazySrc;
if (el.tagName === 'SCRIPT') {
const script = document.createElement('script');
script.src = el.dataset.lazySrc;
fragment.appendChild(script);
}
});
entry.target.appendChild(fragment);
observer.unobserve(entry.target);
}
});
}, { rootMargin: '200px' });
// 每个待懒加载的容器节点都要 observe
document.querySelectorAll('.lazy-doc-section').forEach(el => observer.observe(el));
容易踩的坑:DOM 注入时机与脚本执行顺序
如果你的模板里有内联脚本(<script>console.log('hi')</script>),cloneNode 后它不会自动执行——这是安全机制,也是常被忽略的点。
- 内联脚本需手动
eval()或创建新<script></script>标签注入,但有 CSP 和 XSS 风险,不推荐 - 外部脚本(
<script src="..."></script>)必须用document.createElement创建并 append,且要监听load事件才能确保执行完成 - 如果片段依赖某个全局库(如
pdf.js),得先确认该库已加载,否则脚本执行会报ReferenceError -
template.content是DocumentFragment,不能直接调用querySelector查找已挂载的节点——必须等 append 到 document 后再查
最常被绕开的复杂点:模板片段里的交互逻辑(比如按钮点击事件)必须在插入 DOM 后重新绑定,或者用事件委托。没人会帮你自动重连 —— <template></template> 只管结构,不管行为。











