
通过 html 的 `defer` 属性,可在浏览器后台并行下载多个脚本,并严格按声明顺序执行,无需手动缓存响应或使用 `eval`,安全、标准且兼容性良好。
在前端开发中,常需兼顾资源加载性能与执行依赖关系:例如 two.js 依赖 one.js 中定义的函数或变量,二者逻辑上必须串行执行,但网络请求本身可并行发起以减少总耗时。传统做法(如用 fetch 获取脚本内容后通过 eval 或 new Function() 执行)不仅存在 XSS 安全风险,还会绕过浏览器的缓存、CSP 策略和调试支持,不推荐用于生产环境。
更优解是利用原生 HTML 特性——<script> 标签的 defer 属性:
<script defer src="one.js"></script> <script defer src="two.js"></script>
✅ 工作原理:
- 浏览器解析 HTML 时立即发起两个脚本的并行 HTTP 请求(充分利用带宽);
- 脚本下载过程不阻塞 DOM 解析和渲染;
- 所有 defer 脚本会在 DOMContentLoaded 事件触发前、按其在 DOM 中的出现顺序依次执行(即 one.js 必先于 two.js 执行);
- 即使 two.js 下载完成早于 one.js,其执行也会被自动排队等待前者完成。
⚠️ 注意事项:
立即学习“Java免费学习笔记(深入)”;
- defer 仅对外部脚本(含 src 属性)生效,内联脚本不支持;
- 不适用于动态插入的 <script> 元素(需显式设置 script.defer = true 并插入到 head 或 body 中,且仍需保证插入顺序);
- 若需在 DOMContentLoaded 前执行(如 DOM 就绪前初始化),defer 不适用,此时应考虑模块化方案(如 ES Modules 的 import 顺序保证);
- 所有现代浏览器均支持 defer(包括 IE10+),兼容性极佳。
? 进阶建议:
对于更复杂的依赖场景(如条件加载、运行时决定脚本路径),可结合 Promise.all() + 动态 import()(ESM)实现可控的并行加载与顺序执行:
Promise.all([
import('./one.js'),
import('./two.js')
]).then(([mod1, mod2]) => {
// 注意:import() 本身不保证执行顺序,此处依赖模块内部导出逻辑
// 更稳妥方式是链式调用:import('./one.js').then(() => import('./two.js'))
});但简单静态依赖场景下,<script defer> 仍是语义最清晰、性能最佳、维护成本最低的标准方案。










