我是如何把 Vite 的打包时间从 110s 优化到 25s 的

lxf2023-05-23 00:57:01

前言

2022年12月公司因为经营问题,被迫裁员。

产研为裁员重灾区,身为前端开发的我理所当然的没有逃过这一劫。

好在虽被裁,但技术尚可,年龄不大,所以很快找到了新公司。

今年年初入职了现在这家公司,公司规模不大,成立不久,没有技术包袱,前端全部采用当下最新技术开发。

这很不错!!

打包现状

我负责的系统采用 Vue3 + Vite 的组合进行业务开发(尤大开发利器在开发过程中体验很好)

经过一段时间的开发,我发现现有系统在打包时很慢,如下图:

我是如何把 Vite 的打包时间从 110s 优化到 25s 的

我是如何把 Vite 的打包时间从 110s 优化到 25s 的

这是我截取的阿里云效打包时间,大家可以看到,打包开始时间为:13:22:14,打包结束时间为 13:24:04,足足有110s。

这这这,开发启动一秒,打包完成两分钟。

这不能忍啊!!

你要问我为啥不能忍,我只能说阿里云效构建时长有限,每月都悠着用,月底还超额,只能本地构建手动扔到服务器上,你说,这谁能忍。

既然不能忍,那咱们就动手给他优化下构建时长,摆脱每月悠着用的包袱。

分析打包

声明:公司给配的 MacBookPro M1,性能很好,我本地构建时长在 48s 左右 (所以你们知道怎么优化才最快了吧,直接换性能更好的机器啊!),后续的优化对比都将以我本地的开发机为标准

说干就干!!

安装 rollup-plugin-visualizer 插件,此插件可以展示构建时长并 chunk 数量及大小,是分析构建的绝佳利器。

pnpm add rollup-plugin-visualizer -D

vite.config.ts 文件中引入该插件:

import { visualizer } from 'rollup-plugin-visualizer';

export default defineConfig({ 
    plugins: [ 
        // ...
        // 将 visualizer 插件放到最后的位置 
        visualizer() 
    ] 
});

配置好分析插件后,执行 构建命令,此时在根目录下会生成一个 stats.html 文件,此文件就是当前项目打包的chunk 分析图。

我是如何把 Vite 的打包时间从 110s 优化到 25s 的

打包过程分析:

  • Transforms 阶段(即转换阶段,Vue转换、TS转换、高级语法转化等)耗时在 13s 左右
  • Render Chunk 阶段(对代码进行合并、分割、代码分析等操作,生成目标运行代码)耗时在 35s 左右 (耗时长),且chunk数量为90个

我是如何把 Vite 的打包时间从 110s 优化到 25s 的

生成chunk分析:

  • 首屏chunk (scripts/spear.b3758856.js)过大为 2.24M,同时存在很多大于500kb的chunk
  • 部分第三方包固定chunk也进行了打包,完全可以使用CDN引入,例如:video.js 、pdf.js

优化方案

路由动态引入

方案

路由全部采用动态引入的方式加载,减小首页 chunk 生成时间,打包的chunk越大耗时也就越多,从大chunk中分离出小chunk可以有效减少打包时间,同时提高首页的加载速度。

我是如何把 Vite 的打包时间从 110s 优化到 25s 的

效果

我是如何把 Vite 的打包时间从 110s 优化到 25s 的

我是如何把 Vite 的打包时间从 110s 优化到 25s 的

我们可以看到,chunk由原来的90个,分成了178个,说明大chunk从首页chunk中成功分离出来了,同时打包时间缩短了6s左右。从 48s => 42s

配置 CDN 加速

方案

可以看到项目中大于500k的 chunk 有 很多,分析原因后,发现其中引入了一些外部库,Vite将外部库默认打包进了当前 chunk 中,对于这些固定的外部库,可以直接采用CDN的方式引入,无需打包进chunk中。

首先安装 rollup-plugin-external-globals 插件,该插件可以告诉 Rollup 哪些库是不需要打包的。

pnpm add rollup-plugin-external-globals -D

使用方法:在vite.config.ts中添加打包配置


import externalGlobals from 'rollup-plugin-external-globals';

const globals = externalGlobals({
  moment: 'moment',
  'video.js': 'videojs',
  jspdf: 'jspdf',
  xlsx: 'XLSX',
});

export default defineConfig({
    build: {
        rollupOptions: {
            external: ['moment', 'video.js', 'jspdf','xlsx'],
            plugins: [globals],
        }
    }
});

同时需要在 index.html 模版中引入对应库的CDN。

具体的CDN链接请根据自己的需要去官网或是CDN网站查询

    <script src="https://.../moment.min.js"></script>
    <script src="https://.../xlsx.full.min.js"></script>
    <script src="https://.../jspdf.polyfills.umd.js"></script>
    <script src="https://.../jspdf.umd.min.js"></script>
    <script src="https://.../video.min.js"></script>

效果

我是如何把 Vite 的打包时间从 110s 优化到 25s 的

我是如何把 Vite 的打包时间从 110s 优化到 25s 的

可以看到,chunk 现在有174 个。

video.js 、 jspdf 等外部库已经从打包产物中剔除。

打包时间 42s => 25s

优化 polyfill 导入

方案

  • 当前项目是通过插件 @vitejs/plugin-legacy 在构建期间根据对应的文件生成对应的polyfill,一个输出文件就对应着一个polyfill文件,由此导致输出的chunk文件数量非常多,如下所示,文件越多,构建耗时也越长

    • 我是如何把 Vite 的打包时间从 110s 优化到 25s 的
  • 插件内部是使用babel进行代码分析和输出的,babel 的速度是很慢的,转化阶段就占用了很多时间,而且,babel 转化出的chunk文件最后还需要 rollup 进行输出,这又进一步拖慢了构建时间

  • 为了解决该问题,我们可以将polyfill使用CDN引入,避免不必要的构建耗时,使用 polyfill.io 此网站可以生成polyfill文件,按需支持当前浏览器

  • 考虑到 polyfill.io 生成的polyfill文件稳定性还有待考究,所以我们只能在测试环境使用,而在预发布和正式环境还是采用 @vitejs/plugin-legacy 构建生成polyfill的方式,以及线上环境稳定性才是最重要的。

实现

判断当前环境是否为测试环境,测试环境下不使用@vitejs/plugin-legacy插件。

注意构建命令要使用 vite build --mode test

import legacy from '@vitejs/plugin-legacy';

export default defineConfig(({ mode }: { mode: string }) => {
    // 判断当前是否为测试环境,注意构建命令要使用 vite build --mode test
    const isTest = mode === 'test';
    
    return {
        plugins: [
            !isTest && legacy({
              targets: ['ie >= 11'],
              additionalLegacyPolyfills: ['regenerator-runtime/runtime'],
            }),
        ]
    }
})

测试环境下不使用legacy插件,但是还是需要引入Polyfill的,否则无法适配低端浏览器,这里需要在html中引入提前生成好的 Polyfill 链接。(如果你没接触过,请先去这个网站学习下:polyfill.io)

这里我们需要使用vite-plugin-html插件,动态插入该Polyfill链接,具体实现如下:

vite.config.ts文件中

import legacy from '@vitejs/plugin-legacy';

export default defineConfig(({ mode }: { mode: string }) => {
    // 判断当前是否为测试环境,注意构建命令要使用 vite build --mode test
    const isTest = mode === 'test';
    
    return {
        plugins: [
            createHtmlPlugin({
                minify: true,
                entry: 'src/main.tsx',
                template: 'index.html',
                inject: {
                  data: {
                    polyfill: isTest
                      ? 'https://polyfill.io/v3/polyfill.min.js?features=es2015%2Ces2016%2Ces2017%2Ces2018%2Ces2019%2Ces2020%2Ces2021%2Ces2022'
                      : '',
                  },
                },
             }),
        ]
    }
})

index.html 文件中

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <!-- 注意在这里引入插入的polyfill -->
    <script src="<%- polyfill %>"></script>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>

效果

我是如何把 Vite 的打包时间从 110s 优化到 25s 的 我是如何把 Vite 的打包时间从 110s 优化到 25s 的

可以看到,chunk数量来到了88个,减少了将近一半,同时构建时间被缩短为10s

其他构建优化方法

  • 使用 vite-plugin-compress插件对文件进行 gzip 压缩,减小构建包体积
  • 使用 vite-plugin-imagemin 插件压缩图片,减小构建包体积
  • ...

优化构建的方法有很多,以上两种方法可以显著的减小发布包体积,但是需要配置下nginx支持 gzip 文件,由于生成压缩包也需要构建时间,这会增加构建时长,与本次优化的主题背离,所以在这里就不做介绍了,感兴趣的可以自己去搜索下,使用也很简单。

总结

此次优化旨在减少构建时间,减轻构建压力,测试结果如下:

环境初始时间优化后的时间优化率优化前截图优化后截图
Mac book pro(m1芯片)48s10s79.17%我是如何把 Vite 的打包时间从 110s 优化到 25s 的我是如何把 Vite 的打包时间从 110s 优化到 25s 的
线上打包器110s25s77.27%我是如何把 Vite 的打包时间从 110s 优化到 25s 的我是如何把 Vite 的打包时间从 110s 优化到 25s 的我是如何把 Vite 的打包时间从 110s 优化到 25s 的我是如何把 Vite 的打包时间从 110s 优化到 25s 的

目前项目已经在测试和生产环境跑了一个多月,没有任何问题,大家可以放心使用。

本网站是一个以CSS、JavaScript、Vue、HTML为核心的前端开发技术网站。我们致力于为广大前端开发者提供专业、全面、实用的前端开发知识和技术支持。 在本网站中,您可以学习到最新的前端开发技术,了解前端开发的最新趋势和最佳实践。我们提供丰富的教程和案例,让您可以快速掌握前端开发的核心技术和流程。 本网站还提供一系列实用的工具和插件,帮助您更加高效地进行前端开发工作。我们提供的工具和插件都经过精心设计和优化,可以帮助您节省时间和精力,提升开发效率。 除此之外,本网站还拥有一个活跃的社区,您可以在社区中与其他前端开发者交流技术、分享经验、解决问题。我们相信,社区的力量可以帮助您更好地成长和进步。 在本网站中,您可以找到您需要的一切前端开发资源,让您成为一名更加优秀的前端开发者。欢迎您加入我们的大家庭,一起探索前端开发的无限可能!