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

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

前言

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

网络层面上的优化常常令人体验最深,想象一下你把站点的包从 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

最后

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

相关文章
|
5天前
|
前端开发 JavaScript API
(前端3D模型开发)网页三维CAD中加载和保存STEP模型
本文介绍了如何使用`mxcad3d`库在网页上实现STEP格式三维模型的导入与导出。首先,通过官方教程搭建基本项目环境,了解核心对象如MxCAD3DObject、Mx3dDbDocument等的使用方法。接着,编写了加载和保存STEP模型的具体代码,包括HTML界面设计和TypeScript逻辑实现。最后,通过运行项目验证功能,展示了从模型加载到保存的全过程。此外,`mxcad3d`还支持多种其他格式的三维模型文件操作。
|
25天前
|
缓存 前端开发 JavaScript
利用代码分割优化前端性能:策略与实践
在现代Web开发中,代码分割是提升页面加载性能的有效手段。本文介绍代码分割的概念、重要性及其实现策略,包括动态导入、路由分割等方法,并探讨在React、Vue、Angular等前端框架中的具体应用。
|
21天前
|
前端开发 安全 UED
2024年前端性能优化新策略
2024年前端性能优化策略涵盖代码分割与环境变量管理。代码分割通过动态导入和按需加载CSS减少初始加载时间;环境变量管理则确保敏感信息安全,简化多环境配置。结合最新工具和技术,可大幅提升Web应用性能与用户体验。
|
14天前
|
缓存 监控 前端开发
探索前端性能优化:关键策略与代码实例
本文深入探讨前端性能优化的关键策略,结合实际代码示例,帮助开发者提升网页加载速度和用户体验,涵盖资源压缩、懒加载、缓存机制等技术。
|
18天前
|
搜索推荐 前端开发 定位技术
前端开发人员SEO优化技术方案
不同的搜索引擎提供了服务后台常见功能来优化网站搜索
43 2
|
23天前
|
数据采集 缓存 监控
如何优化前端框架的数据驱动方式以提高性能?
综上所述,通过多种手段的综合运用,可以有效地优化前端框架的数据驱动方式,提高应用的性能,为用户带来更好的体验。同时,随着技术的不断发展和进步,我们需要不断探索和创新,以找到更适合的优化方法和策略。
|
2月前
|
存储 人工智能 前端开发
前端大模型应用笔记(三):Vue3+Antdv+transformers+本地模型实现浏览器端侧增强搜索
本文介绍了一个纯前端实现的增强列表搜索应用,通过使用Transformer模型,实现了更智能的搜索功能,如使用“番茄”可以搜索到“西红柿”。项目基于Vue3和Ant Design Vue,使用了Xenova的bge-base-zh-v1.5模型。文章详细介绍了从环境搭建、数据准备到具体实现的全过程,并展示了实际效果和待改进点。
168 2
|
2月前
|
JavaScript 前端开发 程序员
前端学习笔记——node.js
前端学习笔记——node.js
46 0
|
2月前
|
人工智能 自然语言处理 运维
前端大模型应用笔记(一):两个指令反过来说大模型就理解不了啦?或许该让第三者插足啦 -通过引入中间LLM预处理用户输入以提高多任务处理能力
本文探讨了在多任务处理场景下,自然语言指令解析的困境及解决方案。通过增加一个LLM解析层,将复杂的指令拆解为多个明确的步骤,明确操作类型与对象识别,处理任务依赖关系,并将自然语言转化为具体的工具命令,从而提高指令解析的准确性和执行效率。
|
2月前
|
存储 弹性计算 算法
前端大模型应用笔记(四):如何在资源受限例如1核和1G内存的端侧或ECS上运行一个合适的向量存储库及如何优化
本文探讨了在资源受限的嵌入式设备(如1核处理器和1GB内存)上实现高效向量存储和检索的方法,旨在支持端侧大模型应用。文章分析了Annoy、HNSWLib、NMSLib、FLANN、VP-Trees和Lshbox等向量存储库的特点与适用场景,推荐Annoy作为多数情况下的首选方案,并提出了数据预处理、索引优化、查询优化等策略以提升性能。通过这些方法,即使在资源受限的环境中也能实现高效的向量检索。