JavaScript延迟加载
JavaScript延迟加载的方式
⭐ defer属性详解
sequenceDiagramparticipant HTML解析participant 脚本下载participant 脚本执行HTML解析->>HTML解析: 开始解析HTMLHTML解析->>脚本下载: 遇到defer脚本,开始下载HTML解析->>HTML解析: 继续解析HTML(不阻塞)脚本下载->>脚本下载: 后台下载脚本HTML解析->>HTML解析: 完成HTML解析HTML解析->>脚本执行: DOM构建完成脚本执行->>脚本执行: 按顺序执行defer脚本style HTML解析 fill:#ffcc99style 脚本执行 fill:#ffcc99
<!-- defer属性使用示例 -->
<script defer src="script.js"></script>
defer特性:
- ✅ 并行下载脚本,不阻塞HTML解析
- ✅ 等待HTML解析完成(DOMContentLoaded事件前)执行
- ✅ 多个defer脚本按照它们在文档中的顺序执行
- ✅ 仅适用于外部脚本(有src属性)
🌟 各种延迟加载方式对比
1. async属性
<script async src="analytics.js"></script>
sequenceDiagramparticipant HTML解析participant 脚本下载participant 脚本执行HTML解析->>HTML解析: 开始解析HTMLHTML解析->>脚本下载: 遇到async脚本,开始下载HTML解析->>HTML解析: 继续解析HTML(不阻塞)脚本下载->>脚本下载: 后台下载脚本脚本下载->>HTML解析: 下载完成,暂停HTML解析脚本下载->>脚本执行: 立即执行脚本脚本执行->>HTML解析: 执行完成,继续HTML解析style 脚本执行 fill:#ffcc99
async特性:
- ✅ 并行下载脚本,不阻塞HTML解析
- ❌ 下载完成后立即执行,会中断HTML解析
- ❌ 多个async脚本执行顺序不保证(谁先下载完谁先执行)
- ✅ 适用于独立的脚本,不依赖DOM和其他脚本
2. 动态创建script标签
function loadScript(url, callback) {const script = document.createElement('script');script.src = url;if (callback) {script.onload = callback;}document.body.appendChild(script);
}// 使用示例
loadScript('script.js', function() {console.log('脚本加载完成');
});
动态创建特性:
- ✅ 可以完全控制脚本加载时机
- ✅ 可以添加回调函数处理加载完成事件
- ✅ 默认异步加载(相当于async行为)
- ✅ 可以通过设置async=false实现类似defer的行为
3. 将脚本放在页面底部
<!DOCTYPE html>
<html>
<head><title>页面标题</title>
</head>
<body><!-- 页面内容 --><!-- 脚本放在底部 --><script src="script.js"></script>
</body>
</html>
底部放置特性:
- ✅ HTML解析不会被中断
- ✅ 简单易实现
- ❌ 脚本下载延迟到HTML解析到底部
- ❌ 不是真正的并行下载
4. 使用模块化导入
<script type="module" src="module.js"></script>
// 动态导入
import('./module.js').then(module => {module.init();
});
模块化特性:
- ✅ 默认具有defer行为
- ✅ 支持按需加载(动态import)
- ✅ 严格模式和作用域隔离
- ❌ 浏览器兼容性要求较高
5. 使用事件监听器
document.addEventListener('DOMContentLoaded', function() {// 加载脚本const script = document.createElement('script');script.src = 'script.js';document.body.appendChild(script);
});
事件监听特性:
- ✅ 确保DOM完全构建后加载
- ✅ 适合需要操作DOM的脚本
- ❌ 不是真正的并行下载
- ❌ 代码量较多
📊 加载方式对比表
加载方式 | HTML解析阻塞 | 执行时机 | 执行顺序 | 兼容性 | 使用场景 |
---|---|---|---|---|---|
defer | 不阻塞 | DOM就绪后,DOMContentLoaded前 | 保持顺序 | 良好 | 依赖DOM且有依赖关系的脚本 |
async | 不阻塞下载,执行时阻塞 | 下载完成后立即执行 | 不保证 | 良好 | 独立脚本,如统计分析 |
动态创建 | 通常不阻塞 | 可控制 | 可控制 | 极佳 | 按需加载,条件加载 |
底部放置 | 不阻塞 | HTML解析后 | 按放置顺序 | 极佳 | 简单项目,无特殊需求 |
模块化 | 不阻塞 | 类似defer | 保持顺序 | 一般 | 现代应用,模块化代码 |
🎯 如何选择正确的延迟加载方式
📚 总结
-
defer (最推荐):
- ⚠️ 并行下载,不阻塞HTML解析
- ⚠️ DOM构建完成后,DOMContentLoaded前执行
- ⚠️ 保证执行顺序
- ⚠️ 适合依赖DOM且有依赖关系的脚本
-
async:
- 并行下载,下载完立即执行
- 适合独立的脚本,如统计分析
-
动态创建script:
- 灵活控制加载和执行时机
- 适合条件加载和按需加载
-
底部放置:
- 简单有效,确保DOM已加载
- 但下载延迟到底部
-
模块化导入:
- 现代化方案,具有defer特性
- 支持按需和条件导入