defer仅对外部脚本(含src属性)生效,执行于dom构建完成之后、domcontentloaded之前,按html顺序执行;内联脚本加defer被忽略,async与defer混用时defer被丢弃。

defer 属性只对外部脚本生效,且执行时机固定:DOM 构建完成之后、DOMContentLoaded 之前。它不推迟下载,只推迟执行;不适用于内联脚本,混用 async 会直接忽略 defer。
为什么加了 defer 却没延迟?
最常见原因是脚本类型错误或写法违规:
-
<script defer>console.log('hi')</script>—— 内联脚本加defer被浏览器完全忽略,既不延迟也不报错 -
<script src="app.js" defer></script>——defer是布尔属性,不能赋值,写成defer="false"等价于defer="",即启用 -
<script src="a.js" async defer></script>——async优先级更高,defer被静默丢弃
真正触发 defer 行为的唯一条件是:src 存在 + defer 存在(无值)。
defer 脚本能安全操作 DOM 吗?
可以,而且无需额外监听事件:
-
document.querySelector、document.body.appendChild等调用不会报Null或undefined - 但注意:图片、CSS、字体等资源尚未加载完成,
window.onload比它更晚 -
document.write()在defer脚本中会被静默忽略,或直接抛错(取决于浏览器),务必禁用
它的执行时机本质是“HTML 解析结束、DOM 树就绪、但非阻塞资源未就绪”,这个边界比很多人想的要窄。
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
多个 defer 脚本如何保证顺序?
严格按 HTML 中的声明顺序执行,与下载完成时间无关:
<script src="utils.js" defer></script><script src="main.js" defer></script>
即使 main.js 先下载完,也必须等 utils.js 执行完毕才轮到它——这是 defer 和 async 的根本分水岭。如果你依赖函数定义、全局状态或模块初始化顺序,defer 是唯一可靠选择。
构建工具里 defer 很容易被绕过
现代打包流程常默认注入内联脚本或强制 async,手动加 defer 可能白费功夫:
- Vite 默认使用
inlineDynamicImports,生成的是内联<script></script>,defer无效 - Webpack 的
html-webpack-plugin需显式配置scriptLoading: 'defer'才生效 - Rollup 插件如
@rollup/plugin-html通常不自动加defer,得自己改模板
检查最终生成的 HTML 源码,确认 <script src="xxx" defer></script> 确实存在,而不是被插件覆盖或转成内联内容——这是最容易被忽略的落地环节。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










