前端需要加载一个大体积的文件时,可以这么优化

简介: 前端需要加载一个大体积的文件时,可以这么优化

前言

我们平时开发的过程中,常常会遇到性能优化的要求。而对于前端的性能优化,我理解为有两大类,一类是网络层面的优化,即加速你的资源加载;另一类也是代码层面上的优化,即根据不同的业务场景写出更高性能的代码。

网络层面上的优化常常令人体验最深,想象一下你把站点的包从 10M 压缩到了 1M ,那么用户打开页面的时间也会大大缩短,相比这样用户的体验也会更好。

image.png

本文会以加载 FFmpeg.wasm 为例,阐述在实际项目中遇到大体积的包时如何优化加载速度。

初探

开始之前,先来介绍一下 FFmpeg.wasm 是什么。 FFmpeg.wasm 使用 WebAssembly 技术将 FFmpeg 的功能集成到 Web 应用程序中,使开发者能够在浏览器环境中处理音频和视频。

它的一些关键特点和用途如下:

  • 多媒体处理:  FFmpeg.wasm 允许在浏览器中进行媒体处理,如音频和视频的解码、编码、剪辑、合成、添加字幕等操作。
  • 转码: 可以在 Web 应用程序中实现实时的音视频转码
  • 独立性:  FFmpeg.wasm 可以独立运行,不需要服务器端的支持,因此可以直接在客户端进行媒体处理,降低服务器负担。

简要来说 FFmpeg 是一个处理音视频的库,常规的音视频处理任务常常放在服务端执行,这些任务十分耗费服务端的 CPU 、内存资源。得益于 WebAssemblyFFmpeg 可以移植到浏览器端使用——即 FFmpeg.wasm ,也就是说这些耗费资源的任务可以放在客户端去执行,也无疑是帮我们省掉了很多服务端资源。

image.png

在它的使用文档中,我们发现了这么一句话。

image.png

我把这个文件下载了下来,果真是超过30M的体积

image.png

也就是说加载这个库时我们需要加载将近 30M 的资源,假设我们的服务器下行带宽是 4M ,在用户能跑满这个带宽的情况下,那么加载这个库大概需要 60秒 左右。如果每一次进来都要等待加载 60秒 的话,那用户估计早就受不了。

如果你的团队里有 FFmpeg 的大佬,那么可以根据你们业务的要求去裁剪一个 FFmpeg ,这样的包体积应该会减少不少。本文还是会采用一些常规的思路去做优化,即压缩与缓存。

PS:如果你是 vite 用户,在跑上面的官方 demo 时遇到了这个报错的话,请把这段配置加到你的 vite 配置文件中。

image.png

optimizeDeps: {
    exclude: ["@ffmpeg/ffmpeg"],
},

压缩

在现代化构建工具中,我们会发现打包出来的产物中往往存在 .gz 后缀名的这种类型的产物。它就是今天我们需要介绍的主角—— gzip 压缩。

gzip 使用 DEFLATE 算法进行数据压缩。 DEFLATE 算法是一种无损数据压缩算法,它基于 霍夫曼编码LZ77 算法的组合。压缩后的文件通常以 .gz 作为扩展名。 gzip 可以压缩单个文件或多个文件,并将它们打包成一个压缩文件。

压缩文件可以使用gzip filename命令,默认情况下, gzip 在压缩文件时会不保留原始文件,可以加上 -k 选项可以防止删除原始文件, gzip 支持不同的压缩级别,通过指定 -1-9 之间的数字来调整。级别越高,压缩比越高,但耗费的时间也越多。解压文件则可以使用:gunzip filename.gzgzip -d filename.gz 命令。

接下来就可以使用 gzip 压缩去压缩我们的 wasm 文件,即 gzip -9 ffmpeg-core.wasm ,压缩后的文件体积降到了原文件体积的 1/3 左右。

image.png

我使用的服务器是 nginx ,要在 nginx 中启用 gzip 压缩,需要填入以下配置:

http{
    gzip on;
    gzip_comp_level 9;
    gzip_min_length 1100;
    gzip_buffers 16 8k;
    gzip_proxied any;
    gzip_types application/wasm;
}

解释一下上面配置的参数:

这段 nginx 配置主要用于启用 gzip 压缩,并针对一些特定设置进行了配置。以下是对每个配置指令的解释:

  • gzip on;:启用 gzip 压缩功能
  • gzip_comp_level 9;:设置 gzip 压缩级别,范围为 19
  • gzip_min_length 1100;:设置启用压缩的最小文件长度,这里是 1100 字节
  • gzip_buffers 16 8k;:设置用于 gzip 压缩的内存缓冲区数量和大小,指定了 16 个内存缓冲区,每个缓冲区大小为 8k 。这样可以用来调整压缩时的内存使用情况。
  • gzip_proxied any;:设置在响应代理请求时启用 gzip 压缩
  • gzip_types application/wasm;:指定需要进行 gzip 压缩的 MIME 类型

当启用 gzip 压缩时, nginx 会检查是否存在预先压缩过的.gz文件。如果存在,它会直接提供这个预先生成的.gz文件。如果不存在, nginx 会尝试动态地压缩内容,并将压缩后的内容发送给客户端。

为了减少服务器的开销,我这里把提前压缩好的 .gz 文件放到了 nginx 中,前端请求的时候会直接请求压缩好的文件。前端请求的是 gz 文件,但是对于 nginx 的响应来说,我们希望响应的 content-typeapplication/wasm ,所以这里还需要有一些额外的配置。

首先我们可以看到nginx的配置文件中使用了 include       mime.types; 这个来配置 nginx 所能识别的 mime 类型。这个时候你可以查看跟 nginx 配置同目录的 mime.types 文件,看看这个文件中是否包含application/wasm                                 wasm;这一项配置,如果没有的话,我们得手动把它加上,不然 nginx 是不认识这个 mine 类型的,到时候传输的过程中会把它当成application/octet-stream来处理,这样前端接收到的数据是无法正常使用的。

其次是我们在请求 wasm.gz 的时候,需要告诉 nginx ,我虽然请求的是一个 .gz 文件,但是我希望你返回的 content-typewasm 所对应的类型。因此可以加上如下配置:

location /your-path/ {
    index index.html index.htm;

    location ~ \.wasm\.gz$ {
        types {
            application/wasm     wasm;
        }
        default_type application/wasm;
    }
}

前端请求代码也需要做出如下修改:

    await ffmpeg.load({
      coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, "text/javascript"),
      wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm.gz`, "application/wasm"),
    });

可以看到经过我们一顿操作猛如虎的配置之后, nginx 已经是可以正确传输压缩过后的文件

image.png

demo 代码也是可以正确的跑起来的

image.png

缓存

聊完压缩之后,可以再看一下缓存。如果你使用的是 nginx ,它会默认启动协商缓存,响应头如下:

image.png

虽然计算 ETag 可能会在一定程度上增加服务器的计算负担,不过在现代网络硬件架构下,这一成本通常相对较小了。但这里还是希望尝试给出另外一种缓存的解决思路——把加载好的文件存入 indexDB ,后续直接从前端缓存中获取,这样做的好处还是从一定程度上减轻服务端的压力。

这里用到了 localforage 来处理缓存的存取,整体思路是先看缓存有没有,如果有,直接返回,如果没有则去读取网络资源,然后写缓存。

image.png

nginx 传输 gz 资源的时候可能会开启分段传输,所以我们需要写一段代码来收集传输回来的所有片段。

const loadFFmpegCore = async () => {
  const cache = await localforage.getItem(FFMPEG_CACHE_KEY);
  console.log("cache", cache);
  if (cache) {
    console.log("load from cache");
    return URL.createObjectURL(cache);
  }
  try {
    const response = await fetch(FFMPEG_CORE_PATH);

    if (!response.ok) {
      throw new Error(`load failed`);
    }
    let receivedLength = 0;
    const chunks = [];
    const reader = response.body.getReader();
    while (true) {
      const { done, value } = await reader.read();
      if (done) {
        break;
      }
      chunks.push(value);
      receivedLength += value.length;
    }

    // 合并所有 chunks
    const arrayBuffer = new Uint8Array(receivedLength);
    let position = 0;
    for (const chunk of chunks) {
      arrayBuffer.set(chunk, position);
      position += chunk.length;
    }

    // 处理 arrayBuffer
    console.log("arrayBuffer", arrayBuffer);
    const blob = new Blob([arrayBuffer], { type: "application/wasm" });
    await localforage.setItem(FFMPEG_CACHE_KEY, blob);
    return URL.createObjectURL(blob);
  } catch (error) {
    console.error("Error fetching data:", error);
  }
};

使用到的地方也需要相应改一下:

await ffmpeg.load({
      coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, "text/javascript"),
      wasmURL: await loadFFmpegCore(),
    });

image.png

可以看到当我们后续加载的时候,就直接从缓存里拿了。

image.png

最后

以上就是本文对于前端大体积文件加载的全部内容,主要还是围绕压缩与缓存展开。如果你觉得有意思的话,点点关注点点赞吧~

相关文章
|
1天前
|
JSON JavaScript 前端开发
vue的 blob文件下载文件时,后端自定义异常,并返回json错误提示信息,前端捕获信息并展示给用户
vue的 blob文件下载文件时,后端自定义异常,并返回json错误提示信息,前端捕获信息并展示给用户
|
1天前
|
缓存 前端开发 UED
实战指南:如何优化前端性能提升用户体验
本文探讨了在当今互联网时代,前端性能优化对于提升用户体验的重要性,以及如何利用各种技术手段实现前端性能的优化。通过介绍前端性能优化的原则、常见的性能优化技巧和工具,以及实际案例分析,帮助开发者深入了解并掌握提升前端性能的方法,从而提升网站的加载速度、响应速度,提高用户的满意度和留存率。
|
1天前
|
Web App开发 缓存 前端开发
如何优化前端网页加载速度:最佳实践和工具推荐
本文探讨了如何通过采用最佳实践和利用先进的工具来优化前端网页加载速度。从压缩资源到使用CDN,从减少HTTP请求到利用缓存策略,我们将介绍一系列提高网页性能的技术手段。同时,我们还将推荐一些广受好评的工具,帮助开发者更轻松地实施这些优化策略。
|
1天前
|
缓存 前端开发 JavaScript
优化前端性能的5个技巧
提高网站的性能是前端开发中的重要任务之一。本文将介绍5个实用的技巧,帮助前端开发者优化网页加载速度、提升用户体验,并降低服务器负载。
|
2天前
|
缓存 前端开发 JavaScript
如何优化前端性能:最佳实践与工具推荐
在当今互联网时代,用户对网页加载速度和性能的要求越来越高。本文将介绍一些优化前端性能的最佳实践,包括代码压缩、资源合并、懒加载等技术,并推荐一些实用的工具,帮助开发者提升网页加载速度和用户体验。
|
5天前
|
前端开发 JavaScript UED
如何优化前端网页加载速度
在当今互联网时代,网页加载速度是用户体验的关键因素之一。本文将探讨如何通过优化前端技术,提升网页加载速度,包括压缩资源、使用CDN加速、减少HTTP请求等方法。
|
6天前
|
缓存 前端开发 JavaScript
优化前端性能的五大技巧
在当今快节奏的网络世界中,优化前端性能是网站开发中至关重要的一环。本文将介绍五种有效的技巧,帮助开发者提升前端性能,提升用户体验和网站效率。
|
9月前
|
Web App开发 前端开发 JavaScript
前端学习笔记202307学习笔记第五十七天-模拟面试笔记react-fiber解决了什么问题
前端学习笔记202307学习笔记第五十七天-模拟面试笔记react-fiber解决了什么问题
98 0
|
9月前
|
前端开发 定位技术
前端学习笔记202305学习笔记第二十三天-地图单线程配置
前端学习笔记202305学习笔记第二十三天-地图单线程配置
68 0
前端学习笔记202305学习笔记第二十三天-地图单线程配置
|
9月前
|
前端开发 API
前端学习笔记202307学习笔记第五十七天-模拟面试笔记react-react-redux的工作流程
前端学习笔记202307学习笔记第五十七天-模拟面试笔记react-react-redux的工作流程
55 0