多个defer脚本执行顺序严格按html中书写位置决定,与下载快慢无关;必须带src的外部脚本才生效,内联或无src写法均无效,且不可与async混用。

多个 defer 脚本的执行顺序怎么保证?
执行顺序完全由它们在 HTML 中的书写位置决定,和下载快慢、文件大小、网络延迟无关。浏览器会等所有 defer 脚本都下载完,再按标签出现的先后一个接一个执行——哪怕 app.js 比 vendor.js 先下完,也必须等 vendor.js 执行完才轮到它。
常见错误包括:
- 把依赖方写在前面:
<script src="app.js" defer></script>放在<script src="utils.js" defer></script>前面 → 报ReferenceError: utils is not defined - 构建工具自动插入 polyfill 或 runtime 脚本到最前,无意中破坏了你手动排好的顺序
- 用
document.write()动态生成<script defer></script>标签 → 大部分现代浏览器已禁用该方式,且动态脚本不继承defer语义
defer 和 type="module" 能不能一起用?
不能,也不该加。ES Module 脚本(<script type="module" src="main.js"></script>)天然具备 defer 行为:并行下载、DOM 就绪后执行、严格按声明顺序运行。再显式加 defer 属于冗余,部分构建工具(如 Webpack 的模块分析器)可能因此误判依赖关系。
还要注意两点:
-
type="module"脚本默认启用严格模式,且支持顶层await,行为和传统脚本不同,迁移时得验证初始化逻辑 - 它和普通
defer脚本不在同一队列里:如果 HTML 里混着<script src="legacy.js" defer></script>和<script type="module" src="new.js"></script>,两者的执行时机接近但不严格同步,别让它们互相假设对方已执行完毕
为什么写了 defer 还是取不到 DOM 元素?
大概率是用了内联脚本或漏了 src。defer 只对带 src 属性的外部脚本生效,以下写法全部无效:
-
<script defer>init();</script>—— 内联脚本,defer被浏览器忽略,脚本立即执行 -
<script src="" defer></script>或<script src="" defer></script>——src为空或空格,等同于同步阻塞 -
<script src="a.js" defer></script>——defer是布尔属性,赋值会被忽略,实际仍生效(但语义混乱)
真正起作用的只有两个条件:src 存在 + defer 出现(无值)。其余任何变体,浏览器都不会警告,只是默默退回到默认行为。
构建工具生成的 HTML 里 defer 为什么没生效?
现代打包流程常默认绕过 defer:Vite 开启 build.inlineDynamicImports 后,脚本变成内联 <script></script>;Webpack 的 html-webpack-plugin 默认用 async 注入,除非显式配置 scriptLoading: 'defer';Rollup 插件如 @rollup/plugin-html 通常不自动加 defer,得手改模板。
排查方法只有一个:打开浏览器开发者工具,查看最终加载的 HTML 源码,确认是否真实存在 <script src="xxx.js" defer></script>。别信配置文件里的“我以为我加了”。
最容易被忽略的是:即使 defer 生效了,它只保证脚本执行时 DOM 就绪,不保证脚本内部加载的资源(比如 fetch('./config.json')、import('./chart.js'))也准备好了——这层依赖得靠运行时控制,不是 HTML 属性能管的。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











