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

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

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

相关推荐
FreeBuf_9 分钟前
新型域名前置攻击利用Google Meet、YouTube、Chrome及GCP构建流量隧道
前端·chrome
c0detrend14 分钟前
技术架构设计:如何打造一个高性能的Chrome截图插件
前端·chrome
幽络源小助理19 分钟前
8、幽络源微服务项目实战:前端登录跨域同源策略处理+axios封装+权限的递归查询增删改+鉴权测试
前端·微服务·架构
API开发24 分钟前
apiSQL+GoView:一个API接口开发数据大屏
前端·后端·api·数据可视化·数据大屏·apisql
运维开发王义杰26 分钟前
nodejs:揭秘 npm 脚本参数 -- 的妙用与规范
前端·npm·node.js
我是日安33 分钟前
从零到一打造 Vue3 响应式系统 Day 18 - Reactive:深入 Proxy 的设计思路
前端·vue.js
你的人类朋友34 分钟前
🍃说说Base64
前端·后端·安全
小刘鸭地下城37 分钟前
深入探索浏览器缓存键:一次HTTP强缓存失效引发的思考
浏览器
ze_juejin39 分钟前
Node.js 全局变量完整总结
前端
ttyyttemo40 分钟前
Learn-Jetpack-Compose-By-Example---TextFieldValue
前端