document.readystate 的三个状态对应浏览器解析html的阶段:loading表示文档正在解析,interactive表示dom构建完成但子资源可能仍在加载,complete表示所有资源加载完毕。

document.readyState 的三个状态值到底对应什么时机
document.readyState 有 loading、interactive、complete 三种值,但它们的触发条件常被误解。关键不是“DOM 是否构建完”,而是浏览器解析 HTML 的阶段:
-
loading:文档仍在解析中,<script></script>同步执行时大概率处于此状态 -
interactive:DOM 构建完成(DOMContentLoaded已触发),但子资源(如图片、样式表、异步脚本)可能还在加载 -
complete:所有资源(包括图片、iframe 等)都加载完毕,等同于window.onload
所以想操作 DOM,interactive 就够了;想等图片渲染完成再做懒加载计算,才需要 complete。
在 script 标签里轮询 readyState 容易掉进的坑
直接写 while (document.readyState !== 'interactive') {} 是典型错误——它会阻塞主线程、卡死页面,且在现代浏览器中可能永远等不到 interactive(比如脚本被插入到 且 HTML 尚未开始解析)。
- 不要用同步轮询;改用
document.addEventListener('readystatechange', handler) - 注意事件可能在监听前就已触发(例如脚本放在
底部),需先检查当前状态再绑定 -
readystatechange在interactive和complete各触发一次,若只关心 DOM 就绪,应立即removeEventListener或加判断
正确写法示例:
if (document.readyState === 'interactive' || document.readyState === 'complete') {
initApp();
} else {
document.addEventListener('readystatechange', () => {
if (document.readyState === 'interactive') {
initApp();
}
});
}
比 readyState 更可靠的选择:DOMContentLoaded vs defer
多数场景下,document.readyState 并非最优解。真正影响首屏的是 DOM 构建完成时间,而 DOMContentLoaded 事件语义更明确、兼容性更好(IE9+),且不会因资源加载延迟而误判。
- 如果脚本在 HTML 中内联,用
addEventListener('DOMContentLoaded', ...)最稳妥 - 如果脚本是外链,优先加
defer属性:<script defer src="app.js"></script>—— 浏览器保证它在 DOM 解析完成后、DOMContentLoaded前执行,无需手动判断状态 -
defer脚本的执行顺序与 DOM 中声明顺序一致,async则不保证,这点容易被忽略
首屏优化中 readyState 的真实价值在哪
它真正的用武之地,是那些无法控制脚本加载方式的场景:比如第三方 SDK 动态注入、运行时 patch、或 SSR 后 hydrate 的边界判断。
- 在 hydration 阶段,需确认服务端吐出的 HTML 已被浏览器解析为可操作 DOM,此时检查
document.readyState === 'interactive'比监听事件更轻量 - 动态插入脚本后,可用
document.currentScript?.readyState(仅 IE 支持)辅助判断,但主流浏览器应统一走Promise.resolve().then(() => {...})微任务延后执行 - 注意 Safari 对
interactive的触发时机略晚于 Chrome/Firefox,在首屏关键路径中建议补一层requestIdleCallback降级兜底
复杂点在于:readyState 不是事件,它反映的是瞬时状态;而首屏优化依赖的是确定的执行时序,所以别把它当调度器用,只当一个轻量的“快照校验”。










