Skip to content

图片加载阻塞进程

在做活动或者做年度总结的活动页面时,需要自己做一个加载器,来确保用户正式进入浏览后有一个比较好的阅读体验,特别是针对图片部分,处理不好就会遇见页面白屏或者加载动画消失不见,特别是针对ios 上的webkit机制。

  • 正常的视频如下(文章丢失了,所以找不到了)

alt text

  • 异常下视频所示,没有进度条了(文章丢失了,所以找不到了)

alt text

  • 我写的脚本如下
js
   loadResources(resources, onComplete) {
      resources = Object.assign(resources, this.cqLinks);
      const keys = Object.keys(resources);
      const total = keys.length;
      let loaded = 0;
      // 预加载图片
      keys.forEach((key) => {
        const img = new Image();
        img.src = resources[key];
        img.onload = () => {
          loaded++;
          const progress = Math.round((loaded / total) * 100);
          this.resourceProcess = progress;
          if (loaded === total) {
            onComplete && onComplete();
          }
        };

        img.onerror = () => {
          console.error(`Failed to load image: ${resources[key]}`);
          loaded++;
          const progress = Math.round((loaded / total) * 100);
          this.resourceProcess = progress;
          if (loaded === total) {
            onComplete && onComplete();
          }
        };
      });
    },

为什么会产生这种现象

webview 在处理并发的图片onload回调,会占用主线程,而是 iOS WKWebView 在“并发图片 onload 回调”这个层面本身就扛不住。 单只这个样的现象

  • iOS WebView(WKWebView)在「并发 Image onload」时,会长期占用主线程

  • UI 渲染被完全饿死

  • 最开始我使用了throttle/raf以为会解决问题,但其实没有,必须不让同时触发

alt text

该怎么避免这种问题

减少并发onload的使用,给浏览器一些空闲的时间,代码如下

js
async loadResources(resources, onComplete) {
  resources = Object.assign({}, resources, this.cqLinks);
  const urls = Object.values(resources);
  const total = urls.length;

  let loaded = 0;
  let index = 0;
  const concurrency = 3;

  const updateProgress = () => {
    this.resourceProcess = Math.round((loaded / total) * 100);
  };

  const worker = async () => {
    while (index < urls.length) {
      const current = index++;
      await new Promise((resolve) => {
        const img = new Image();
        img.src = urls[current];
        img.onload = img.onerror = () => {
          loaded++;
          updateProgress();
          resolve();
        };
      });

      await new Promise(r => requestAnimationFrame(r));
    }
  };

  await Promise.all(
    Array.from({ length: concurrency }, worker)
  );

  onComplete && onComplete();
}

参考文章

requestAnimationFrame
Reflow
页面解析规则

上次更新于: