Vite 现在是越来越强大,无论是 Vue 工程,还是 React的工程,都可以体验 Vite 极速的服务启动。开发环境下,使用原生 ESM 文件,无需打包。在生产环境下,Vite借助 Rollup 来打包的,所以和其他打包工具一样,也有一个优化的问题。本文就来说说 Vite 工程怎样优化分块,因为我们很在乎打包后的文件大小。
以一个我先前做过的 Vue3 crud 工程为例,这个工程用到了Element plus UI 库,这个库是比较大,仅完整版的 min.js 就有 1M 左右,如果用 Vite 默认打包,会得到下面的警告提示:
✓ 1456 modules transformed.
dist/index.html 0.42 KiB
dist/assets/index.310b9427.css 316.87 KiB / gzip: 43.16 KiB
dist/assets/index.972b2f34.js 1098.90 KiB / gzip: 340.02 KiB
(!) Some chunks are larger than 500 KiB after minification. Consider:
- Using dynamic import() to code-split the application
- Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/guide/en/#outputmanualchunks
- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.
Vite 给出三个优化建议:
- 使用动态
import()
对应用程序进行代码分割。如果我们用到路由,可以优先考虑这种方式。或者我们的应用程序有使用 Element plus,Antd 这种大型 UI 组件库,可以考虑按需导入组件,我们会稍后讲解这一优化。 - 使用
build.rollupOptions.output.manualChunks
来优化分块,这个是我们要重点讲的内容。 - 通过
build.chunkSizeWarningLimit
调整此警告的块大小限制。chunkSizeWarningLimit
参数值默认是 500 KB,调成2M
就不会警告了。有点掩耳盗铃的意思,一般我们不会这样做,除非我们的网速特别快。
本质上,Webpack 可以使用require.context
来动态导入组件,Vite 借助import.meta.glob
可以实现类似功能。
按需导入组件
这里仅以按需自动导入为例,我们需要安装 [unplugin-vue-components]() 插件
npm i -D unplugin-vue-components
Element plus 官方文档的快速开始 说是需要安装 unplugin-vue-components 和 unplugin-auto-import 这两款插件。
然后把下列代码插入到你的 Vite 的配置文件中。
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
const pathSrc = path.resolve(__dirname, 'src')
// https://vitejs.dev/config/
export default defineConfig({
base: './',
resolve: {
alias: {
'~/': `${pathSrc}/`,
},
},
plugins: [
vue(),
Components({
resolvers: [
ElementPlusResolver({
importStyle: 'sass',
})
],
dts: 'src/type/components.d.ts',
})
],
css: {
preprocessorOptions: {
scss: {
additionalData: `@use "~/styles/element/index.scss" as *;`
}
}
}
})
unplugin-vue-components
实现了Vue 组件的按需自动导入。
dts
:设置生成组件的全局声明文件,如果安装了 typescript 包,默认值true
,也接受自定义文件名的路径
本示例中,npm run dev
启动后,会在工程根目录/src/type 下自动生成 components.d.ts
- resolvers:为流行的 UI 库(如 Vuetify、Ant Design Vue 和 Element Plus)提供了几个内置解析器,通过导入相应解析器来启用它们。
本示例中,我们使用 ElementPlusResolver
来支持 Element Plus 组件的自动导入。
我们还通过配置 css.preprocessorOptions.scss.additionalData
来自定义组件主题
现在 npm run build
打包一下,看一下优化效果。
✓ 1505 modules transformed.
dist/index.html 0.42 KiB
dist/assets/index.807e96e6.css 136.75 KiB / gzip: 19.12 KiB
dist/assets/index.9226c9ca.js 743.79 KiB / gzip: 231.22 KiB
不错,index.js,和 index.css 明显小了,不过js bundle 依旧很大。业务代码、组件库代码、Vue.js、和第三方库代码都在一个 bundle 中,文件能不大吗?得分块,也就是之前讲的“代码分割”,将 bundle(包)拆分就成了 多个 chunks(块)。
在 Vite/Webpack 中,一个入口对应一个 bundle,而模块(Modules)对应导入的代码库,比如说 Element Plus/vue.js/lodash。每个 bundle 可以由多个模块组成,但按需导入多个模块的 bundle 不一定比某个模块文件大。所以理想情况是将 bundle 按共享常用的模块分块,比如 Element Plus一个块,因为它文件比较大。Vue.js、和第三方库代码一个块。因为我们用的第三方库代码不多,文件比较小,不宜拆分太多的块。
manualChunks
Webpack 中,我们使用 SplitChunks
插件来分块,有很多的优化配置,感兴趣的同学可以参考我之前写的Webpack 分块优化。Vite 中,功能没有这么多,但够用了,我们可以使用 manualChunks
来分块。
manualChunks 允许创建自定义共享常用块。支持对象形式和函数形式分块。
对象形式
类型:[chunkAlias: string]: string[]
使用对象形式是,每个属性表示一个包含列出的模块及其所有依赖项(如果它们是模块图的一部分)的块,除非它们已经在另一个手动块中。块的名称将由属性键确定。
build: {
rollupOptions: {
output: {
manualChunks: {
element: ['element-plus']
}
}
}
}
重新打包一下,看一下优化效果。
✓ 1505 modules transformed.
dist/index.html 0.49 KiB
dist/assets/index.807e96e6.css 136.75 KiB / gzip: 19.12 KiB
dist/assets/index.e0922e3f.js 270.68 KiB / gzip: 72.58 KiB
dist/assets/element.d52a0af3.js 470.92 KiB / gzip: 157.68 KiB
还行,element.js 从之前的 index.js 拆分了出来,文件大小更合理了。
然而我们如果需要创建一个包含 node_modules
中的所有依赖项的块,用对象形式是不行的,因为node_modules
不是合法的模块名。我们得用更灵活的函数形式进行配置。
函数形式
类型:(id: string, {getModuleInfo, getModuleIds}) => string | void
使用函数形式时,每个解析后的模块 ID 将传递给函数。如果返回一个字符串,则将该模块及其所有依赖项添加到具有给定名称的手动块中。例如,以下代码会额外创建一个命名为 vendor 的 chunk,它包含所有在 node_modules 中的依赖:
manualChunks: (id) => {
// console.log(id)
if (id.includes('element-plus')) return 'element'
if (id.includes('node_modules')) return 'vendor'
}
打印一下 id,我们会看到一大推的文件路径。Vite/Webpack 是以文件路径作为模块 ID 的,这也是 Node.js 的一贯做法。
E:/vue3-crud/node_modules/element-plus/es/components/color-picker/src/components/alpha-slider.mjs
E:/vue3-crud/node_modules/element-plus/es/components/color-picker/src/components/hue-slider.mjs
E:/vue3-crud/node_modules/dayjs/plugin/customParseFormat.js
E:/vue3-crud/node_modules/dayjs/plugin/advancedFormat.js
E:/vue3-crud/node_modules/lodash-es/add.js
E:/vue3-crud/node_modules/lodash-es/after.js
E:/vue3-crud/node_modules/@ctrl/tinycolor/dist/module/index.js
E:/vue3-crud/node_modules/@ctrl/tinycolor/dist/module/css-color-names.js
E:/vue3-crud/node_modules/axios/lib/core/transformData.js
E:/vue3-crud/node_modules/axios/lib/helpers/isAbsoluteURL.js
E:/vue3-crud/node_modules/async-validator/dist-web/index.js
E:/vue3-crud/node_modules/escape-html/index.js
// ...
E:/vue3-crud/src/utils/request.ts
E:/vue3-crud/src/components/AreaCascader/area.ts
// ...
借助getModuleInfo
,getModuleIds
参数可实现更复杂的分块配置。
跑一下 npm run build
看看最终优化效果。
✓ 1505 modules transformed.
dist/index.html 0.63 KiB
dist/assets/index.a995fcfa.css 1.85 KiB / gzip: 0.62 KiB
dist/assets/element.35dfb808.css 134.88 KiB / gzip: 18.68 KiB
dist/assets/index.ad83645e.js 177.41 KiB / gzip: 37.60 KiB
dist/assets/element.de98a909.js 309.86 KiB / gzip: 98.25 KiB
dist/assets/vendor.afd46969.js 256.42 KiB / gzip: 93.77 KiB
vendor.js
、element.css
也拆分出来了,大功告成,这下比较满意了。
总结
本文简述了 Vite 分块优化的背景,重点简述了两种分块优化:通过按需自动导入组件分割代码,设置 manualChunks
手动改进分块。
评论 (0)