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

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

前言

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

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

image.png

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

初探

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

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

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

简要来说 FFmpeg 是一个处理音视频的库,常规的音视频处理任务常常放在服务端执行,这些任务十分耗费服务端的 CPU 、内存资源。得益于 WebAssembly , FFmpeg 可以移植到浏览器端使用——即 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.gz 或 gzip -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 压缩级别,范围为 1 到 9
  • 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-type 是 application/wasm ,所以这里还需要有一些额外的配置。

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

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

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

最后

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

相关文章
|
前端开发 JavaScript 索引
前端性能优化:虚拟滚动技术原理与实战
前端性能优化:虚拟滚动技术原理与实战
1772 80
|
缓存 监控 前端开发
前端性能优化:现代框架的关键策略
前端性能优化:现代框架的关键策略
625 74
|
缓存 前端开发 JavaScript
前端性能优化:打造流畅的用户体验
前端性能优化:打造流畅的用户体验
|
存储 前端开发 安全
实现“永久登录”:针对蜻蜓Q系统的用户体验优化方案(前端uni-app+后端Laravel详解)-优雅草卓伊凡
实现“永久登录”:针对蜻蜓Q系统的用户体验优化方案(前端uni-app+后端Laravel详解)-优雅草卓伊凡
482 5
|
前端开发 Cloud Native Java
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现
博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现
|
人工智能 JavaScript 前端开发
Vue 性能革命:揭秘前端优化的终极技巧;Vue优化技巧,解决Vue项目卡顿问题
Vue在处理少量数据和有限dom的情况下技术已经非常成熟了,但现在随着AI时代的到来,海量数据场景会越来越多,Vue优化技巧也是必备技能。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
存储 人工智能 前端开发
前端大模型应用笔记(三):Vue3+Antdv+transformers+本地模型实现浏览器端侧增强搜索
本文介绍了一个纯前端实现的增强列表搜索应用,通过使用Transformer模型,实现了更智能的搜索功能,如使用“番茄”可以搜索到“西红柿”。项目基于Vue3和Ant Design Vue,使用了Xenova的bge-base-zh-v1.5模型。文章详细介绍了从环境搭建、数据准备到具体实现的全过程,并展示了实际效果和待改进点。
1780 14
|
JavaScript 前端开发 程序员
前端学习笔记——node.js
前端学习笔记——node.js
1166 0
|
SpringCloudAlibaba JavaScript 前端开发
谷粒商城笔记+踩坑(2)——分布式组件、前端基础,nacos+feign+gateway+ES6+vue脚手架
分布式组件、nacos注册配置中心、openfegin远程调用、网关gateway、ES6脚本语言规范、vue、elementUI
谷粒商城笔记+踩坑(2)——分布式组件、前端基础,nacos+feign+gateway+ES6+vue脚手架
|
人工智能 自然语言处理 运维
前端大模型应用笔记(一):两个指令反过来说大模型就理解不了啦?或许该让第三者插足啦 -通过引入中间LLM预处理用户输入以提高多任务处理能力
本文探讨了在多任务处理场景下,自然语言指令解析的困境及解决方案。通过增加一个LLM解析层,将复杂的指令拆解为多个明确的步骤,明确操作类型与对象识别,处理任务依赖关系,并将自然语言转化为具体的工具命令,从而提高指令解析的准确性和执行效率。
954 6

热门文章

最新文章

  • 1
    前端如何存储数据:Cookie、LocalStorage 与 SessionStorage 全面解析
    1442
  • 2
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(九):强势分析Animation动画各类参数;从播放时间、播放方式、播放次数、播放方向、播放状态等多个方面,完全了解CSS3 Animation
    715
  • 3
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(八):学习transition过渡属性;本文学习property模拟、duration过渡时间指定、delay时间延迟 等多个参数
    572
  • 4
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(七):学习ransform属性;本文学习 rotate旋转、scale缩放、skew扭曲、tanslate移动、matrix矩阵 多个参数
    580
  • 5
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(六):全方面分析css的Flex布局,从纵、横两个坐标开始进行居中、两端等元素分布模式;刨析元素间隔、排序模式等
    665
  • 6
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(五):背景属性;float浮动和position定位;详细分析相对、绝对、固定三种定位方式;使用浮动并清除浮动副作用
    845
  • 7
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(四):元素盒子模型;详细分析边框属性、盒子外边距
    1653
  • 8
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(三):元素继承关系、层叠样式规则、字体属性、文本属性;针对字体和文本作样式修改
    399
  • 9
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(二):CSS伪类:UI伪类、结构化伪类;通过伪类获得子元素的第n个元素;创建一个伪元素展示在页面中;获得最后一个元素;处理聚焦元素的样式
    1369
  • 10
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(一):CSS发展史;CSS样式表的引入;CSS选择器使用,附带案例介绍
    677