为什么浏览器那条“假进度”救不了我们?

你在做「企业级低代码平台」时,客户把 200+ 微应用一次性嵌进门户首页。

浏览器自带的进度条只认主文档,微应用懒加载的 JS/CSS/图片它一概不管,用户盯着 100 % 的进度条却白屏 3 秒,投诉直接拉满。

于是,你撸了一条 完全受控的自定义加载进度条,从 0 % 到 100 % 与真实资源一一对应,投诉率当天掉到 0。


方案:15 分钟搭一条"真·进度条"

1. 骨架 HTML(30 秒)

html 复制代码
<!-- index.html -->
<div id="progress-bar">
  <div class="fill"></div>
  <span class="text">0%</span>
</div>

2. 核心 JS(5 分钟)

js 复制代码
// progress.js
class Loader {
  #total = 0
  #loaded = 0
  #fill = document.querySelector('.fill')
  #text = document.querySelector('.text')

  add(url) {
    this.#total++
    fetch(url).then(() => {
      this.#loaded++
      this.#render()
    })
  }

  #render() {
    const p = Math.round((this.#loaded / this.#total) * 100)
    this.#fill.style.transform = `scaleX(${p / 100})`
    this.#text.textContent = `${p}%`
    if (p === 100) this.#fill.parentElement.remove()
  }
}

window.loader = new Loader()

逐行解析

  • add 把每一个资源注册进来,用原生 fetch 自带 Promise 跟踪完成。
  • scaleX 做宽度动画,GPU 加速不掉帧。
  • 100 % 时自毁 DOM,避免污染全局。

3. 使用姿势(1 分钟)

html 复制代码
<script type="module">
  import './progress.js'
  // 🔍 业务代码里显式注册资源
  loader.add('/static/chunk-a.js')
  loader.add('/static/theme.css')
  loader.add('/static/logo.png')
</script>

原理深挖:三层视角看"真进度"

层级 表面用法 底层机制 设计哲学
资源层 fetch 监听 HTTP 缓存协商 + TCP 多路复用 用浏览器能力,不重复造轮子
渲染层 scaleX + requestAnimationFrame 合成层动画,60 FPS 视觉反馈优先,主线程不阻塞
生命周期 100 % 自毁 垃圾回收自动清理 用完即走,零副作用

扩展:3 个真实业务变体

1. Vue3 组合式封装(微应用场景)

ts 复制代码
// useProgress.ts
import { ref, computed } from 'vue'
export function useProgress() {
  const total = ref(0), loaded = ref(0)
  const percent = computed(() => (loaded.value / total.value) * 100 || 0)
  const add = (url: string) => {
    total.value++
    fetch(url).finally(() => loaded.value++)
  }
  return { percent, add }
}

在 <Suspense> 的 @pending 里调用 add,进度与组件懒加载天然同步。

2. 大文件切片上传(断点续传)

js 复制代码
// 伪代码
chunks.forEach((chunk, i) => {
  loader.add(`/upload?index=${i}`) // 每个切片算 1 个进度单位
})

3. Service Worker 离线缓存

js 复制代码
// sw.js
self.addEventListener('install', e => {
  const urls = [...] // 预缓存列表
  e.waitUntil(
    caches.open('v1').then(cache =>
      Promise.all(
        urls.map(u => fetch(u).then(r => cache.put(u, r)))
      )
    )
  )
  // 向主线程 postMessage 更新进度
})

一键复用片段

css 复制代码
/* 进度条样式,直接拷 */
#progress-bar {
  position: fixed; top: 0; left: 0; right: 0; height: 3px;
  background: rgba(0,0,0,.1); z-index: 9999;
}
.fill {
  height: 100%; background: #0076ff;
  transform-origin: left; transition: transform .3s ease;
}
.text {
  position: absolute; top: 4px; right: 8px;
  font-size: 12px; color: #0076ff;
}

小结

浏览器那条"假进度"只能骗自己,自定义进度条才是用户信任的起点。

把上面的 30 行代码丢进任何项目,3 分钟就能让白屏时间变成"可控的等待"。

下次老板再说"体验优化",你就可以把这篇文章甩给他,然后安心下班。

相关推荐
卸任7 小时前
Vibe coding工具使用心得
前端·ai编程
brien97 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance8 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M8 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写8 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李8 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易8 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
清晨Coding8 小时前
删了 Cookie 还能自动登录?Google系网站登录态自动恢复的原理分析
javascript
风骏时光牛马8 小时前
前沿视界
前端
kyriewen8 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员