Vite:性能优化-分包策略

1,948 阅读6分钟

介绍

在我们项目开发完成后,需要进行项目的打包优化,本文讲解如何在Vite中进行分包,提升项目的性能。 分包策略涉及的优化点有如下几点:首次加载、加载时间、浏览器缓存策略、代码体积。接下来讲一下分包解决的哪些问题,以及使用代码和工程图进行项目分包处理。

分包策略解决的问题

首次加载

在项目中,如果我们的项目中依赖了很多的第三方库或写了很多的代码,在没进行分包处理的情况下,所有代码都是直接打包到一个js文件中的,浏览器在请求到该js文件之后需要一次性加载所有的代码。这样就会在项目很大的情况下,页面加载很久或白屏加载一段时间再展示加载后的内容。这样就会带来很不好的用户体验。

加载时间

和首次加载一样,文件中代码依赖太多,加载时间就会越久。

浏览器缓存策略

在项目中如果a页面中引用了c页面的代码,b页面也引用了c页面的代码。在浏览器中访问a和b页面时,会重复请求c页面的代码。这样就出现了不必要的代码请求和加载。

image.png

Vite进行分包配置

在Vite中配置项目打包后的分包配置,是通过配置build.rollupOptions.output来实现的。在Vite中配置打包的内容是通过rollup来进行配置的,所以需要去看看rollup的官网文档哦 Configuration Options | Rollup

manualChunks 配置选项 |汇总 --- Configuration Options | Rollup

参数:对象数组 / 函数

{ [chunkAlias: string]: string[] } | ((id: string, {getModuleInfo, getModuleIds}) => string | void)

manualChunks(对象数组形式)

参数:对象数组

manualChunks: {
  xxx: ['yyy'],
},

其中xxx就是分包名,数组中的yyy可以是当前项目中的任意文件(相对路径)、第三方库的名称。 对于对象数组这种形式,支持指定key值对应多个内容,打包后的结果就是将数组中的内容全部打包到名称为key.xxxx.js中。

写法如下:

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { viteMockServe } from 'vite-plugin-mock';

// https://vite.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    viteMockServe({
      mockPath: 'mock',
    }),
  ],
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          vue: ['vue', 'vue-router', 'pinia'],
          echarts: ['echarts'],
          lodash: ['lodash'],
          a: ['./src/modules/a'],
          b: ['./src/modules/b'],
          c: ['./src/modules/c'],
          App: ['./src/App.vue'],
        },
        entryFileNames: `js/[name]-[hash].js`,
        chunkFileNames: `js/[name]-[hash].js`,
        assetFileNames(assetInfos) {
          if (assetInfos.name.endsWith('.css')) {
            return `css/[name]-[hash].css`;
          }
          return `[ext]/[name]-[hash].[ext]`;
        },
      },
    },
  },
});

上述代码中我们配置的manualChunks最终的打包结果如下:

  • vue.xxxx.js文件:该文件中包含'vue', 'vue-router', 'pinia'代码。
  • echarts.xxxx.js文件:包含echarts代码。
  • lodash.xxxx.js文件:包含lodash代码。
  • a.xxxx.js文件:包含./src/modules/a代码。
  • b.xxxx.js文件:包含./src/modules/b代码。
  • c.xxxx.js文件:包含./src/modules/c代码。
  • App.xxxx.js文件:包含./src/App.vue代码。 打包结果图如下所示:

image.png

manualChunks(函数形式)

参数:函数

manualChunks: (id,{getModuleInfo,getModuleIds})=>{
    xxxx操作...
    return string|void
}

函数参数:

  • id 依赖包的绝对地址 (常用
  • getModuleInfo 获取当前id对应的模块信息 (不常用)
  • getModuleIds (不常用)

写法如下:

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { viteMockServe } from 'vite-plugin-mock';

// https://vite.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    viteMockServe({
      mockPath: 'mock',
    }),
  ],
  build: {
    rollupOptions: {
      output: {
        manualChunks(id, config) {
          console.log(id);
          if (id.includes('node_modules')) {
            return id
              .toString()
              .split('node_modules/.pnpm')[1]
              .split('/')[0]
              .toString();
          }
        },
        entryFileNames: `js/[name]-[hash].js`,
        chunkFileNames: `js/[name]-[hash].js`,
        assetFileNames(assetInfos) {
          if (assetInfos.name.endsWith('.css')) {
            return `css/[name]-[hash].css`;
          }
          return `[ext]/[name]-[hash].[ext]`;
        },
      },
    },
  },
});

entryFileNames 配置选项 |汇总 --- Configuration Options | Rollup

是一个用于定义入口文件生成规则的选项。它允许你自定义输出文件的名字模式,特别是当你有多个入口点时非常有用。通过设置这个选项,你可以控制最终打包后的文件名格式,包括添加哈希值、指定目录结构等。

写法如下:

entryFileNames: `js/[name]-[hash].js`,

其中name和hash是rollup提供的占位符。其他占位符如下:

  • [format]: The rendering format defined in the output options, e.g. es or cjs.
    [format]:输出选项中定义的渲染格式,例如 es 或 cjs
  • [hash]: A hash based only on the content of the final generated entry chunk, including transformations in renderChunk and any referenced file hashes. You can also set a specific hash length via e.g. [hash:10]. By default, it will create a base-64 hash. If you need a reduced character sets, see output.hashCharacters
    [hash]:仅基于最终生成的入口块内容的哈希,包括 renderChunk 中的转换和任何引用的文件哈希。您还可以通过 [hash:10] 设置特定的哈希长度。默认情况下,它将创建一个 base-64 哈希。如果您需要简化的字符集,请参阅 output.hashCharacters
  • [name]: The file name (without extension) of the entry point, unless the object form of input was used to define a different name.
    [name]:入口点的文件名(不带扩展名),除非使用 input 的对象形式来定义不同的名称。

chunkFileNames 配置选项 |汇总 --- Configuration Options | Rollup

chunkFileNames 选项用于定义非入口(non-entry)chunk文件的命名规则。当你使用代码拆分(例如通过动态导入 import())时,Rollup会为每个拆分出来的chunk生成一个单独的文件chunkFileNames 就是用来控制这些文件的命名格式。 写法如下:

chunkFileNames: `js/[name]-[hash].js`,

其中name、hash也是占位符。

assetFileNames 配置选项 |汇总 --- Configuration Options | Rollup

output.assetFileNames 选项用于定义非JavaScript资源(如图片、CSS文件等)的命名规则。当你在打包过程中包含这些资源时(例如通过 import './path/to/image.png' 或者通过插件处理的资源),Rollup会根据你设置的 assetFileNames 模板来命名这些资源文件。

参数类型:string | ((assetInfo: PreRenderedAsset) => string)(字符串 | 函数)

默认值:assets/[name]-[hash][extname]

写法如下:

assetFileNames(assetInfos) {
  if (assetInfos.name.endsWith('.css')) {
    return `css/[name]-[hash].css`;
  }
  return `[ext]/[name]-[hash].[ext]`;
},

完整代码

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { viteMockServe } from 'vite-plugin-mock';

// https://vite.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    viteMockServe({
      mockPath: 'mock',
    }),
  ],
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          vue: ['vue', 'vue-router', 'pinia'],
          echarts: ['echarts'],
          lodash: ['lodash'],
          a: ['./src/modules/a'],
          b: ['./src/modules/b'],
          c: ['./src/modules/c'],
          App: ['./src/App.vue'],
        },
        // manualChunks(id, config) {
        //   console.log(id);
        //   if (id.includes('node_modules')) {
        //     return id
        //       .toString()
        //       .split('node_modules/.pnpm')[1]
        //       .split('/')[0]
        //       .toString();
        //   }
        // },
        entryFileNames: `js/[name]-[hash].js`,
        chunkFileNames: `js/[name]-[hash].js`,
        assetFileNames(assetInfos) {
          if (assetInfos.name.endsWith('.css')) {
            return `css/[name]-[hash].css`;
          }
          return `[ext]/[name]-[hash].[ext]`;
        },
      },
    },
  },
});

知识点总结

  • 使用vite进行代码分包处理是通过配置rollup选项。(vite中是output.xxx)
  • manualChunks:用于手动控制代码拆分的方式,提供对象数组/函数的形式。
  • entryFileNames:配置入口文件名
  • chunkFileNames:配置分包的文件名称
  • assetFileNames:处理非js文件的文件名

推荐

看到了一篇比较好的文章 真就是前端性能优化比较全面的,大家有兴趣可以去看看 各方面都总结了的。 前端性能优化的几个大招(理论+实践,看完就是Leader水平)前言 性能优化,一个掣肘用户体验的关键模块。它没有固定的标 - 掘金 墙裂推荐