Vite 打包性能优化
-
- 开始一个 Vite + ts 项目
-
- 分包策略
- gzip 压缩
- cdn 加速
开始一个 Vite + ts 项目
这里我们开始了一个 Vite + ts 的项目,其中关于 ts 的配置直接看内容注释即可
npm init -y
npm i vite -D
npm vite-plugin-checker -D #用来强制提示ts报错
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script src="./src/main.ts" type="module"></script>
</body>
</html>
// tsconfig.json
{
"compilerOptions": {
"skipLibCheck": true, // 跳过node_modules中的ts检查
"module": "ESNext", // 使用最新的js语法
"moduleResolution": "node", // 导入第三方包时候会使用commonjs规范解析
"lib": ["ES2017", "DOM"], // ts默认项目环境不在es6,所以使用es方法会报错,因此这里指定一下es环境
}
}
// package.json
"scripts": {
"dev": "vite",
"build": "tsc --noEmit && vite build"
},
其中
tsc --noEmit
是根据官方提示,在ts报错时不给出打包结果
// vite.config.ts
import { defineConfig } from 'vite'
import VitePluginChecker from 'vite-plugin-checker'
export default defineConfig({
build: {
minify: false, // 打包结果取消minify,方便我们看打包后结果对比
},
plugins: [
VitePluginChecker({
typescript: true, // 当typescript语法错误时浏览器给出错误提示弹窗,强制开发者修改ts错误
})
]
})
// src/main.ts
// 内容
分包策略
npm i lodash
// src/main.ts
import { forEach } from 'lodash'
const mainArr = []
forEach(mainArr, ele => {
console.log('ele', ele)
})
执行打包命令:
npm run build
结果:
题外话:
npx vite preview
可以预览打包后的效果。
项目场景:因为浏览器的缓存策略,当请求的文件名不变时,会直接从缓存中获取文件,所以只要每次 main.ts
中内容发生了变化,打包后的文件名中的 hash 值都会不一样。也就是说,当项目业务代码发生变化,例如 main.ts
中循环体内的内容发生了变化时,打包后的文件名就发生了变化,浏览器就会重新请求文件。但是这里的问题是,每次打包后的内容大部分都是 lodash
库的内容,它的内容是不会变化的(node_modules
中的代码都是不会变化的),我们只是更改了业务代码而已,会导致每次浏览器请求的文件都很大,很大的网络传输损耗。
分包策略就是为了解决这个问题,它会将一些不常规更新变化的文件进行单独打包处理。
// vite.config.ts
import { defineConfig } from 'vite'
export default defineConfig({
build: {
minify: false, // 打包结果是否minify
rollupOptions: { // vite打包是通过rollup来打包的
output: {
manualChunks: (id: string) => {
// 可以在这里打印看一下id的值,这里做个简单处理将node_modules中的包打包为vendor文件
if(id.indexOf('node_modules') > -1) {
return 'vendor'
}
}
}
}
},
plugins: [...]
})
执行打包命令
npm run build
此时,如果去修改 main.ts
中循环体内容,重新打包后,vendor
文件的 hash 值不会变化,变化的只有 index
文件的,而这个文件比一开始的包小了不是一点点,这也就意味着,浏览器会从缓存中获取 vendor
文件,节省了很大的网络性能。
gzip 压缩
上面打包后的终端提示可以拿过来再看一下
每个文件打包后的大小都有标识,后面一个 gzip 表示经过 gzip 压缩可以将这个文件缩小到 xxx 这个大小,这里我们来实践一下:
npm i vite-plugin-compression -D
// vite.config.ts
import { defineConfig } from 'vite'
import VitePluginCompression from 'vite-plugin-compression'
export default defineConfig({
build: {
...
},
plugins: [
...
VitePluginCompression()
]
})
执行打包操作:
npm run build
和上面未使用 gzip 打包给出的大小提示一样,打包的结果是 .gz
文件。此时,需要跟后端商量,当请求文件是 .gz
文件时,需要设置响应头为 content-encoding: gzip
(告诉浏览器这个文件是 gzip 压缩过的) ,浏览器收到响应结果后发现响应头中的这个设置项就会立即解压得到原来的 .js
文件。
当然,这样会使得浏览器需要承担一定的解压时间,所以体积不是很大的时候,不要用 gzip 压缩,免得适得其反。
cdn 加速
分发策略里我们将依赖单独打成一个稳定的包,现在是另外一个场景:比如国内访问一些未被封的国外网站(例如 github、newsela 等)网站时,因为服务器在国外,所以浏览器打开这些网站时请求时间一般都会比较久。即使你分包了,但是如果这个包体积很大,访问时间还是会很长,因为是从国内访问国外了。如果我们将其中一些依赖包经过 cdn 的方式访问,例如 Vue vue-router 这些包通过 cnd 的方式访问,那么这些包就会直接通过 cdn 地址的方式加载,而不是在我们的服务器上去请求,这样我们的项目包就会更小,页面响应就会更快。
// vite.config.js
import { defineConfig } from 'vite'
import ViteCDNPlugin from 'vite-plugin-cdn-import'
export default defineConfig({
plugins: [
ViteCDNPlugin({
modules: [{
name: 'lodash', // 包名
var: '_', // 对应cdn包导出的变量,如jQuery导出的是$
path: 'https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js'
}]
})
]
})
执行打包命令后:
npm run build
没有分包,lodash
也不会被打入包中,打包后的 index.html
会自动插入 cdn 方式加载 lodash
包,项目打包后的体积也明显小了很多。