前言
最近接了一个类库的锅,需要打包发布,打包打算试试 Vite
选择原因: 团队里有自己手写一些 cli 打包,但是比较老旧了。新项目不选择 Webpack 的原因是据说 Vite 比 Webpack 轻量,作为一个纯 JS/TS 类库,可能打包不需要配置过于繁琐
使用 vite 初始化项目
https://vitejs.cn/
yarn create vite my-app / npm install vite@latest
选择模板 react-ts
vite 默认是构建 Web 项目,会默认使用根目录下 index.html 作为入口文件
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/main.js"></script>
</body>
</html>
Vite 解析 <script type="module" src="..."> ,这个标签指向你的 JavaScript 源码
如何打包一个类库
如果目标是类库,需要开启构建库模式,原因有两个。首先,库的入口不可能以 html 作为 entry。同时,Vite 库模式,能够便捷的确保将那些你不想打包进库的依赖进行外部化处理,例如 vue 或 react
总的来说就是 3 个步骤:
- 开启构件库模式
- 确定打包入口文件
- 优化库打包的依赖
新建
vite.config.ts,并加入build.lib配置项
库模式入口项 entry 为 string 类型。从文档看,vite 默认打包是全量打包,只有一个入口文件,里面引用了你类库的所有源码。vite 会解析入口文件并压缩
// vite.config.ts
const { defineConfig } = require('vite')
const path = require('path')
const outDir = path.resolve(__dirname, 'lib')
module.exports = defineConfig({
plugins: [react()],
build: {
outDir,
lib: {
entry: path.resolve(__dirname, 'lib/main.js'),
name: 'MyLib',
fileName: (format) => `my-lib.${format}.js`
},
}
})
在 rollupOptions 配置项中,配置不想打包的依赖项,并暴露全局变量。这样就可以避免类库将依赖项打包,使体积增大。配置后,类库被引用时,声明一个全局引用,在宿主包中寻找对应的依赖
// vite.config.ts
module.exports = defineConfig({
build: {
...
rollupOptions: {
// 确保外部化处理那些你不想打包进库的依赖
external: ['react'],
output: {
// 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
globals: {
react: 'React'
}
}
}
}
})
针对打包做一些其他的基础配置项,完整的配置如下
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
const path = require('path')
const entryDir = path.resolve(__dirname, 'lib/main.js')
const outDir = path.resolve(__dirname, 'lib')
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
resolve: {
alias: [{ find: '@', replacement: path.resolve(__dirname, './src') }],
},
json: {
// 是否支持从 .json 文件中进行按名导入
namedExports: true,
// 若设置为 true, 导入的 JSON 会被转换为 export default JSON.parse("...") 会比转译成对象字面量性能更好
// 尤其是当 JSON 文件较大时
// 开启此项, 则会禁用按名导入
stringify: false,
},
build: {
outDir,
lib: {
entry: entryDir,
fileName: (format) => `${format}/[name].js`,
formats: ['cjs', 'es'],
},
manifest: false,
emptyOutDir: true,
// chunk 大小警告的限制
chunkSizeWarningLimit: 500,
rollupOptions: {
// 确保外部化处理那些你不想打包进库的依赖
output: {
// 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
globals: {
react: 'React',
},
},
external: ['react'],
}
},
})