用 document.readyState 判断加载阶段比监听 load 更早,因其可在 'loading' 阶段介入并显示进度条,而 load 事件触发时资源已全部加载完毕。
document.readyState 判断加载阶段比监听 load 更早很多人直接监听 window.addEventListener('load', ...),但这时页面资源(图片、字体等)已全部下载完,进度条往往一闪而过。真正想显示“正在加载”的过程,得从 HTML 解析开始介入。document.readyState 有三个值:'loading'(DOM 构建中)、'interactive'(DOM 就绪,脚本可能还在执行)、'complete'(全部资源加载完毕)。在 'loading' 阶段插入进度条 DOM,并配合定时器模拟进度,才能让用户感知到“还没好”。
实操建议:
立即学习“前端免费学习笔记(深入)”;
<div id="progress-bar"></div> 放在 <body> 最顶部,避免被其他样式遮挡或阻塞渲染position: fixed; top: 0; height: 3px; width: 0%; background: #4a6fa5; z-index: 9999;),确保不依赖外部样式表加载<head> 里立即执行一段 <script>,检查 document.readyState 并启动进度逻辑,不要等 DOMContentLoaded
requestAnimationFrame 更新宽度比 setTimeout 更平滑单纯用 setTimeout 每 50ms 加 5%,容易卡顿或跳变,尤其低端设备。浏览器对 requestAnimationFrame 的调度更贴合刷新率,能保证每帧只更新一次宽度,视觉上更自然。
实操建议:
立即学习“前端免费学习笔记(深入)”;
document.readyState === 'complete' 时,触发最终 90% → 100% 的动画(可用 CSS transition 或 rAF),然后 300ms 后移除进度条元素getBoundingClientRect() 或强制触发一次重排(如 offsetHeight),防止浏览器合并样式计算导致首帧不渲染async 或 defer 脚本中初始化进度条如果把进度条逻辑写在 <script async src="progress.js"></script> 里,它可能在 document 还没解析完时就被下载执行,此时 document.body 为空,appendChild 会失败;而 defer 虽然按顺序执行,但已错过 'loading' 阶段。
实操建议:
立即学习“前端免费学习笔记(深入)”;
<script>...</script>),且放在 <head> 底部或 <body> 顶部DOMContentLoaded 后才启动,已经晚了position: fixed 进度条有渲染延迟iOS Safari 在页面滚动或缩放过程中,有时会延迟绘制 fixed 元素,导致进度条“卡住不动”或突然跳到 100%。这不是 bug,是 Safari 对 fixed 定位的优化策略:它会暂缓合成,直到确认不需要频繁重绘。
实操建议:
立即学习“前端免费学习笔记(深入)”;
transform: translateZ(0) 或 will-change: transform,强制启用 GPU 合成层top: 0 和 height: 3px 外还加 border 或 box-shadow,这些会触发软件渲染最常被忽略的是:进度条本身不该增加首屏关键路径耗时。一行内联脚本、不到 20 行代码、无网络请求、不阻塞解析——它存在的唯一目的,是让白屏时间显得更短。一旦它开始影响 LCP 或 CLS,就得删掉。