原生无法直接用于页面加载进度,因为它不感知资源加载过程,仅接受value和max属性;window.onload和domcontentloaded是离散事件,无中间状态;document.readystate只有三态,无法映射0–100%;需用performanceobserver监听resource或预估资源数并计数更新。

为什么原生 <progress></progress> 无法直接用于页面加载进度
因为 window.onload 和 DOMContentLoaded 都是“完成即触发”的离散事件,没有中间状态;<progress></progress> 本身也不感知资源加载过程。它只接受 value 和 max 两个数字属性,不会自动从 HTML 解析、脚本下载、图片加载中提取进度值。
常见翻车点:
– 直接写 <progress value="0"></progress> 就以为能动
– 用 document.readyState 判断,但它只有 "loading"/"interactive"/"complete" 三态,无法映射到 0–100%
– 把 fetch 数据加载当成“页面加载”,漏掉 CSS、字体、图片等关键资源
如何手动统计真实页面加载进度
必须主动监听所有外部资源的加载完成事件,并维护一个计数器或加权总耗时模型。推荐用 PerformanceObserver(Chrome 80+)捕获真实资源加载数据,比手写钩子更可靠:
- 在
尽早初始化,否则会错过早期资源(如首屏 CSS) - 监听
"resource"类型条目,过滤掉initiatorType === "xmlhttprequest"的请求(那是 API,不是页面资源) - 用
entry.duration和entry.startTime(都相对于navigationStart)算出每项占比,加权汇总 - 动态插入的资源(如懒加载图片、
import())必须单独注册load事件并计入计数器
注意:服务端需返回 Content-Length,否则 XMLHttpRequest.onprogress 的 event.loaded/event.total 全为 0。
怎么把进度值同步到 <progress></progress> 或自定义 DOM
更新必须操作 DOM 属性(el.value = 65),不能用 setAttribute("value", "65") —— 后者写的是 HTML attribute,不触发渲染更新。同时要显式设 max="100",别依赖默认值,尤其当后端返回小数(如 0.73)时,得先 Math.round(percentage * 100) 再赋值。
若用 <div> 模拟进度条:<br>– 推荐用 <code>transform: scaleX(0.73) 而非 width: 73% 做动画,更平滑且硬件加速友好
– iOS 15 之前对 scaleX(0) 有渲染 bug,起始值建议设为 scaleX(0.001)
– 避免在 scroll 或资源 load 回调里直接改样式,先存变量,再用 requestAnimationFrame 统一提交
滚动进度条和加载进度条容易混淆的点
滚动进度条是 window.scrollY / (document.body.scrollHeight - window.innerHeight) * 100,反映用户位置;页面加载进度条是资源加载完成比例——两者计算逻辑、触发时机、数据源完全不同,不能复用同一套 JS 逻辑。
最常被忽略的是:移动端 Safari 对 PerformanceObserver 的支持虽好,但部分安卓 WebView 不支持 "resource" 类型监听;如果必须兼容老环境,降级方案只能是预估资源数量 + Promise.all([...imgPromises, ...scriptPromises]) 计数,但精度差、无法反映大文件拖慢的真实体验。











