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

你在做「企业级低代码平台」时,客户把 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 分钟就能让白屏时间变成"可控的等待"。

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

相关推荐
Point9 分钟前
[LeetCode] 最长连续序列
前端·javascript·算法
rookiesx13 分钟前
安装本地python文件到site-packages
开发语言·前端·python
支撑前端荣耀14 分钟前
九、把异常当回事,代码才靠谱
前端
LotteChar22 分钟前
HTML:从 “小白” 到 “标签侠” 的修炼手册
前端·html
趣多多代言人24 分钟前
20分钟学会TypeScript
前端·javascript·typescript
90后的晨仔24 分钟前
⚙️ 《响应式原理》— Vue 是怎么做到自动更新的?
前端·vue.js
结城24 分钟前
深入掌握CSS Grid布局:每个属性详解与实战示例
前端·css
寒..30 分钟前
网络安全第三次作业
前端·css·html
90后的晨仔39 分钟前
🧮《计算属性》— 自动根据其它响应式数据得出结果
前端·vue.js
小白阿龙1 小时前
如何实现缓存音频功能(App端详解)
前端·css·html5