使用 Vite 开发项目,build 后发现所有的逻辑文件都被打包在一个入口 js 文件中,如图:
这样的打包结果显示不是合理的,当项目大了之后,js 入口文件会变得非常大,影响加载速度。还有就是对于项目中长期不变的 js 引入文件,我们不应该使其每次打包都重新生成 hash 名,这样会导致每次重新 build 后,客户端因为文件 hash 值的变化,都要重新下载这些固定文件内容,在网速不好的情况会影响使用体验。所以针对这点,需要做 build 优化,拆分模块文件。
但是 Vite 文档中没有找到这种功能的配置,其实 Vite 没有实现这个功能,它的打包是依赖 rollup 的,所以我们可以查找 rollup 的相关功能。Vite 打包是支持配置 rollup 功能以改变打包结果的,在vite.config.js 中配置 build.rollupOptions即可。
在查看 rollup 文档后得知,拆分包功能需要配置 output.manualChunks。manualChunks 是一个对象,对象的每一个属性名都是要生成的 chunk 名前缀,而属性值则是需要打入的模块包名。例如我们需要将 vue 文件单独拆分出来,则可以配置:
{
// 忽略其他配置
build: {
rollupOptions: {
output: {
manualChunks: {
vue: ['vue']
}
}
}
}
}
此时再执行打包,结果如下:
可以看到 vue 文件是被单独拆分出来了,且后续再修改项目代码时,这个文件指纹是不会再变的,因为开发中不可能修改 vue 源码。同样的道理,对于引用的第三方库,我们都应该拆分出来,可以放在一个 chunk 中,也可以单独拆分,如下:
{
// 忽略其他配置
build: {
rollupOptions: {
output: {
manualChunks: {
// 放在一个 chunk 中
chunk: ['vue','vue-router','vuex'],
// 也可以单独细分
vue:['vue'],
vuex:['vuex'],
'vue-router':['vue-router'],
}
}
}
}
}
至此,我们解决了基本需求。还有一个问题就是如果项目中需要拆分的模块很多,我们一个个去列举模块也不太现实,这时候就可以换个配置模式。manualChunks 是支持配置成一个函数的,函数的入参是每一个被打包的模块id,我们可以判断这个 id 是否是来自node_modules 路径的,如果是则代表是第三方库,就可以统一打包进一个chunk中,配置如下:
{
// 忽略其他配置
build: {
rollupOptions: {
output: {
manualChunks(id) {
if (id.includes("node_modules")) {
return "vendor";
}
},
},
},
}
}
更多关于 manualChunks 的配置,可参考文档:output-manualchunks
在写自己的一个 Vite+Vue 项目时,考虑加入自动路由和自动导入功能。分别使用插件:unplugin-vue-router 和 unplugin-auto-import。
unplugin-vue-router 实现自动路由
自动路由要达到的效果是:无需手动添加路由路径,而是根据文件路径自动生成路由配置。
这里以 Vite 项目为例,安装好插件后,在 vite.config.ts 中配置如下:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// 引入 vite 适配插件
import VueRouter from 'unplugin-vue-router/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [VueRouter(), vue()] // VueRouter 插件必须放在 Vue 插件之前
})
如果是 Typescript 项目,运行项目后,会在项目中自动生成一个 typed-router.d.ts文件,这个文件在后续开发中会自动生更新路由映射类型,为防止 ts 报错,所以需要将这个文件引入到 tsconfig.json 文件中,同时需要将 moduleResolution 项设置为 bundler :
{
"include": [
// other files...
+ "./typed-router.d.ts"
],
"compilerOptions": {
// ...
+ "moduleResolution": "bundler",
// ...
}
}
另外,如果项目中有 env.d.ts文件,例如 Vite + Typescript 创建的项目就会有 vite-env.d.ts文件,则需要添加类型到其中:
/// <reference types="vite/client" />
+ /// <reference types="unplugin-vue-router/client" />
配置完成后,需要修改 router.ts 文件,不再直接使用 vue-router 模块:
- import { createRouter, createWebHistory } from 'vue-router'
+ import {createRouter,createWebHistory} from 'vue-router/auto'
+ import {routes} from 'vue-router/auto-routes'
const router = createRouter({
history:createWebHistory(),
- routes: [
- {
- path: '/',
- component: () => import('src/pages/Home.vue'),
- }
- ]
+ routes,
})
export default router
上面代码中的 routes 变成自动生成的,它是基于文件系统的,默认路由文件映射目录是 src/pages,测试创建 pages/index.vue文件,可以看到 typed-router.d.ts文件中,已自动生成路由的映射:
排除目录
有些时候,pages目录中可能会有一些其他的非页面内容的目录,比如 components目录,我们不希望它生成路由映射。unplugin-vue-router 支持排除目录设置,在 exclude数组中,列出你不想要生成路由的文件即可:
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import VueRouter from "unplugin-vue-router/vite";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
VueRouter({
+ routesFolder: "src/pages", // 这里可指定页面目录
+ exclude: ["src/pages/components/**/*.vue"], // 这里排除了pages目录中components目录文件的路由映射
}),
vue(),
],
});
至此,自动路由配置完成,更多配置功能,详见文档:configuration。
unplugin-auto-import 实现自动导入
自动导入要达到的效果是:使用过一些公共库或者是频繁导入使用的模块时,无需手动添加导入就可直接使用。
还是 Vite 项目为例,安装插件后,在 vite.config.ts 中配置:
// vite.config.ts
import AutoImport from 'unplugin-auto-import/vite'
export default defineConfig({
plugins: [
AutoImport({
/* options */
})
]
})
想要自动导出模块,需要在插件中具体配置。不是所有的插件或库都支持自动导入,首先官方默认提供了一些预设 presets,在这个预设表中的插件,都可以直接配置成字符串即可。比如我希望自动导入 vue 和 vue-router中的方法,即可配置成:
// vite.config.ts
import AutoImport from 'unplugin-auto-import/vite'
export default defineConfig({
plugins: [
AutoImport({
imports: ['vue', 'vue-router']
})
]
})
此时如果是 Typescript 项目,运行项目后,会自动生成 auto-imports.d.ts文件,文件中会根据配置自动生成类型声明,防止 ts 报错,同样我们需要将此文件包含到 tsconfig.json 文件中:
{
"include": [
// other files...
+ "./auto-imports.d.ts"
],
}
观察 auto-imports.d.ts文件,会发现已经有vue和 vue-router的内置模块的类型声明了:
现在使用 vue 模块开发时,就不需要再手动导入了:
<template>
<div>{{ str }}</div>
</template>
<script setup lang="ts">
- import { ref } from "vue";
const str = ref("hello world!");
</script>
结合 unplugin-vue-router 使用
如果使用的插件库不在官方预设内,有些插件会自己实现,例如 unplugin-vue-router。
在使用 unplugin-vue-router 时,我们不直接使用 vue-router,希望是自动导入插件中的方法,而此插件刚好也暴露了自己实现的自动导入预设,所以可修改配置如下:
port { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import VueRouter from "unplugin-vue-router/vite";
import AutoImport from "unplugin-auto-import/vite";
+ import { VueRouterAutoImports } from 'unplugin-vue-router'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
VueRouter({
routesFolder: "src/pages",
exclude: ["src/pages/components/**/*.vue"],
}),
vue(),
AutoImport({
imports: [
"vue",
- "vue-router",
+ VueRouterAutoImports
],
}),
],
});
再查看auto-imports.d.ts文件,发现也已经有了对应方法的类型声明:
自动导入自己写的工具模块
有时候我们也希望可以将自己写的工具方法实现自动导入,好在插件中可以配置。
举个例子,假设我们有一个 utils目录,其中有文件date.ts是封装了一些日期方法,有文件storage.ts文件封装了缓存方法,再来一个 index.ts作为入口文件:
export function getTime() {
return new Date().getTime()
}
export function getStorage(key: string) {
return localStorage.getItem(key) || sessionStorage.getItem(key)
}
export { getTime } from './date'
export { getStorage } from './storage'
这个时候,我们希望 utils目录中所有的方法都能自动导入,就可以配置 dirs项:
AutoImport({
imports: ['vue', 'vue-router'],
dirs: [
'src/utils' // 配置工具类的目录即可
]
})
配置完成后,观察 auto-imports.d.ts文件,可以发现所有的方法都已声明了类型,可以直接使用了。