各位朋友,老铁们,大家好!今天咱来聊聊如何用 Vite 的 lib
模式,把你的 Vue 组件库打造成十八般武艺样样精通的绝世高手,既能整体梭哈,也能按需索取,让你的用户用起来倍儿爽!
开场白:为什么选 Vite?
在打包组件库的江湖里,Webpack 绝对是老前辈,经验丰富,但有时候也显得有点笨重。而 Vite,作为后起之秀,就像一位身手敏捷的剑客,轻量级,启动快,热更新速度惊人,打包速度也是杠杠的。 特别是对于组件库这种需要频繁迭代的项目来说,Vite 的速度优势简直是救命稻草。
正题:Vite 的 lib
模式
Vite 的 lib
模式,顾名思义,就是专门用来构建库(library)的。它能帮你把你的 Vue 组件库打包成各种格式,比如:
- ES Module (ESM): 现代浏览器和 Node.js 都支持,是按需引入的最佳选择。
- CommonJS (CJS): 传统的 Node.js 模块格式,为了兼容老项目还是有必要的。
- UMD (Universal Module Definition): 通用模块定义,可以在浏览器和 Node.js 环境中使用,但通常体积较大,不推荐。
- IIFE (Immediately Invoked Function Expression): 立即执行函数表达式,直接生成一个全局变量,简单粗暴,但容易污染全局命名空间,不推荐。
目标:
- 生成 ESM 格式的包,用于按需引入。
- 生成 UMD 或 CJS 格式的包,用于传统项目或 CDN 引入。
- 提供 TypeScript 类型定义文件 (
.d.ts
),方便用户使用。 - 支持 CSS 样式的打包和按需引入。
实战:撸起袖子开始干
1. 初始化项目
首先,我们需要创建一个新的 Vue 项目(如果你已经有组件库项目,可以跳过这一步)。
npm create vite my-vue-component-lib --template vue-ts # 如果你用 TypeScript
# 或者
npm create vite my-vue-component-lib --template vue # 如果你用 JavaScript
进入项目目录:
cd my-vue-component-lib
2. 安装必要的依赖
除了 Vite 之外,我们还需要一些额外的依赖来支持组件库的构建:
npm install vue sass -D #sass是为了css支持,vue是因为需要把vue设置成外部依赖
3. 目录结构
一个典型的组件库项目目录结构可能如下所示:
my-vue-component-lib/
├── src/
│ ├── components/ # 组件目录
│ │ ├── MyButton.vue
│ │ └── MyInput.vue
│ ├── index.ts # 组件库入口文件
│ └── style/ # 样式目录
│ ├── index.scss # 全局样式
│ ├── MyButton.scss # 组件样式
│ └── MyInput.scss # 组件样式
├── vite.config.ts # Vite 配置文件
├── package.json
└── tsconfig.json # TypeScript 配置文件 (如果使用 TypeScript)
4. 编写组件
在 src/components
目录下创建你的 Vue 组件。例如,MyButton.vue
:
<template>
<button class="my-button" @click="handleClick">
{{ label }}
</button>
</template>
<script setup lang="ts">
import { defineProps, defineEmits } from 'vue';
const props = defineProps({
label: {
type: String,
required: true,
},
});
const emit = defineEmits(['click']);
const handleClick = () => {
emit('click');
};
</script>
<style lang="scss" scoped>
.my-button {
padding: 10px 20px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
&:hover {
background-color: #3e8e41;
}
}
</style>
再创建一个 MyInput.vue
:
<template>
<input type="text" class="my-input" :value="modelValue" @input="handleInput" />
</template>
<script setup lang="ts">
import { defineProps, defineEmits } from 'vue';
const props = defineProps({
modelValue: {
type: String,
default: '',
},
});
const emit = defineEmits(['update:modelValue']);
const handleInput = (event: Event) => {
const target = event.target as HTMLInputElement;
emit('update:modelValue', target.value);
};
</script>
<style lang="scss" scoped>
.my-input {
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
}
</style>
5. 组件库入口文件 (src/index.ts
)
这是组件库的入口文件,用于导出所有的组件。
import MyButton from './components/MyButton.vue';
import MyInput from './components/MyInput.vue';
export {
MyButton,
MyInput,
};
// 定义一个 install 方法,方便全局注册组件 (可选)
import { App } from 'vue';
const components = [MyButton, MyInput]
const install = (app:App) => {
components.forEach(component => {
app.component(component.name, component)
})
}
export default {
install
}
6. 配置 Vite (vite.config.ts
)
这是最关键的一步,我们需要配置 Vite 的 lib
模式。
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { resolve } from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
build: {
lib: {
// 入口文件
entry: resolve(__dirname, 'src/index.ts'),
// 组件库名称 (导出的全局变量名称)
name: 'MyVueComponentLib',
// 文件名格式 (umd/es/iife)
fileName: (format) => `my-vue-component-lib.${format}.js`,
},
rollupOptions: {
// 确保外部化处理那些你不想打包进库的依赖
external: ['vue'],
output: {
// 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
globals: {
vue: 'Vue',
},
},
},
// 输出 source map 方便调试
sourcemap: true,
},
});
配置项解释:
entry
: 组件库的入口文件,这里指向src/index.ts
。name
: 组件库的名称,用于生成 UMD 格式的包时,作为全局变量的名称。fileName
: 输出文件的名称格式。external
: 指定哪些依赖不需要打包进组件库。这里我们将vue
设置为外部依赖,因为用户在使用你的组件库时,肯定已经安装了vue
。globals
: 为外部依赖提供全局变量名称。在 UMD 模式下,需要指定外部依赖的全局变量名称。sourcemap
: 生成 sourcemap 文件,方便调试。
7. 配置 package.json
我们需要在 package.json
中添加一些配置,以便更好地发布和使用组件库。
{
"name": "my-vue-component-lib",
"version": "0.0.1",
"description": "A simple Vue component library",
"main": "./dist/my-vue-component-lib.umd.js", // CommonJS 入口
"module": "./dist/my-vue-component-lib.es.js", // ES Module 入口
"types": "./dist/index.d.ts", // TypeScript 类型定义文件
"files": [
"dist"
],
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"preview": "vite preview"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.2.3",
"sass": "^1.64.1",
"typescript": "^5.0.2",
"vite": "^4.4.5",
"vue-tsc": "^1.8.5"
},
"peerDependencies": {
"vue": "^3.0.0"
}
}
配置项解释:
main
: 指定 CommonJS 模块的入口文件。module
: 指定 ES Module 模块的入口文件。types
: 指定 TypeScript 类型定义文件的入口文件。files
: 指定需要发布到 npm 上的文件或目录。peerDependencies
: 指定组件库的 peer dependencies。 peer dependencies 是指你的组件库依赖的,但是不由你的组件库安装的依赖。 用户必须手动安装这些依赖。 这里我们将vue
设置为 peer dependency,表明你的组件库依赖vue
,但是用户需要自己安装vue
。
8. 构建组件库
现在,我们可以运行 npm run build
命令来构建组件库了。
npm run build
构建完成后,你会在 dist
目录下看到生成的各种格式的包:
dist/
├── my-vue-component-lib.es.js # ES Module
├── my-vue-component-lib.umd.js # UMD
├── index.d.ts # TypeScript 类型定义文件
└── style.css # 样式文件 (如果你的组件有样式)
9. 发布组件库 (可选)
如果你想将你的组件库发布到 npm 上,可以执行以下步骤:
- 登录 npm:
npm login
- 发布组件库:
npm publish
注意: 在发布之前,请确保你的 package.json
中的 name
字段是唯一的,并且没有被其他人占用。
按需引入的实现
Vite 默认就支持 ES Module 的按需引入,所以我们只需要确保我们的组件库输出了 ES Module 格式的包即可。
使用方法:
import { MyButton, MyInput } from 'my-vue-component-lib';
// 或者,如果你想更精细地按需引入:
import MyButton from 'my-vue-component-lib/dist/components/MyButton.vue';
import MyInput from 'my-vue-component-lib/dist/components/MyInput.vue';
// 在 Vue 组件中使用
export default {
components: {
MyButton,
MyInput,
},
template: `
<MyButton label="Click me" @click="handleClick" />
<MyInput v-model="inputValue" />
`,
data() {
return {
inputValue: '',
};
},
methods: {
handleClick() {
alert('Button clicked!');
},
},
};
CSS 样式的按需引入
如果你的组件有独立的 CSS 样式文件,你需要手动引入它们。
import 'my-vue-component-lib/dist/style.css'; // 引入全部样式
// 或者
import 'my-vue-component-lib/dist/components/MyButton.css'; // 引入单个组件的样式
import 'my-vue-component-lib/dist/components/MyInput.css'; // 引入单个组件的样式
为了方便用户使用,你可以在你的组件库中提供一个专门用于引入样式的入口文件,比如 src/style/index.ts
:
// src/style/index.ts
import '../components/MyButton.scss';
import '../components/MyInput.scss';
然后在 vite.config.ts
中,将这个文件也打包成一个单独的 CSS 文件:
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { resolve } from 'path';
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(),
cssInjectedByJsPlugin()
],
build: {
lib: {
// 入口文件
entry: {
index: resolve(__dirname, 'src/index.ts'),
style: resolve(__dirname, 'src/style/index.ts'), // 新增样式入口
},
// 组件库名称 (导出的全局变量名称)
name: 'MyVueComponentLib',
// 文件名格式 (umd/es/iife)
fileName: (format, entryName) => {
return entryName === 'style' ? `style.${format}.js` : `my-vue-component-lib.${format}.js`
},
},
rollupOptions: {
// 确保外部化处理那些你不想打包进库的依赖
external: ['vue'],
output: {
// 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
globals: {
vue: 'Vue',
},
},
},
// 输出 source map 方便调试
sourcemap: true,
},
});
现在,你可以通过以下方式引入样式:
import 'my-vue-component-lib/dist/style.es.js'; // 引入全部样式
cssInjectedByJsPlugin 这个插件让css以js的形式插入页面,避免打包成一个css文件。
TypeScript 类型定义
Vite 会自动生成 TypeScript 类型定义文件 (.d.ts
),所以你不需要手动编写。只需要确保你的代码是符合 TypeScript 规范的即可。
高级技巧:代码分割
如果你的组件库非常庞大,你可以考虑使用代码分割来进一步优化按需引入的性能。代码分割可以将你的组件库拆分成更小的 chunk,只有在需要的时候才会加载。
Vite 默认就支持代码分割,你只需要按照一定的规则来组织你的代码即可。例如,你可以将每个组件都放在一个单独的目录下,并使用 ES Module 的 import()
语法来动态加载组件。
总结:打造你的 Vue 组件库
通过 Vite 的 lib
模式,我们可以轻松地将 Vue 组件库打包成各种格式,并支持按需引入。 总结一下步骤:
步骤 | 描述 | 关键配置 |
---|---|---|
1 | 初始化项目 | 使用 npm create vite 创建 Vue 项目。 |
2 | 安装依赖 | 安装 vue 和 sass 等必要的依赖。 |
3 | 创建组件 | 在 src/components 目录下创建你的 Vue 组件。 |
4 | 创建入口文件 | 创建 src/index.ts 文件,导出所有的组件。 |
5 | 配置 Vite (vite.config.ts ) |
配置 build.lib 选项,包括 entry 、name 、fileName 和 rollupOptions.external 。 |
6 | 配置 package.json |
配置 main 、module 、types 和 peerDependencies 字段。 |
7 | 构建组件库 | 运行 npm run build 命令构建组件库。 |
8 | 发布组件库 (可选) | 登录 npm 并发布组件库。 |
9 | 按需引入 | 用户可以通过 ES Module 的 import 语法按需引入组件。 |
希望今天的分享对你有所帮助。 祝你早日打造出自己的 Vue 组件库,成为前端界的武林盟主! 咱们下期再见!