咳咳,麦克风测试,1、2、3,喂喂喂。大家好,我是今天的主讲人,很高兴能和大家一起聊聊如何打造一个现代化的 Vue 组件库。
今天咱们不搞虚的,直接上干货。目标明确:我们要创建一个支持主题定制、按需引入、TypeScript 类型提示,并且拥有自动化测试的 Vue 组件库。这听起来有点复杂,但别怕,我会尽量用大家都能听懂的语言,一步一步拆解这个过程。
一、项目初始化:从零开始的旅程
首先,咱们得有个家,也就是项目目录。推荐使用 Vue CLI 来初始化项目,它能帮我们省去很多配置上的麻烦。
vue create my-awesome-ui
在选择特性时,务必勾选 TypeScript 和 Unit Testing。如果你想玩点高级的,可以选择 Vue Router 和 Vuex,但对于组件库来说,它们并不是必须的。
初始化完成后,你会得到一个基本的 Vue 项目结构。接下来,我们需要对这个结构进行一些调整,使其更适合组件库的开发。
二、目录结构:井井有条的家
一个清晰的目录结构对于组件库的维护至关重要。我推荐以下结构:
my-awesome-ui/
├── packages/ # 组件源码目录
│ ├── button/ # Button 组件
│ │ ├── src/ # Button 组件源码
│ │ │ └── Button.vue
│ │ ├── index.ts # Button 组件入口
│ │ └── style/ # Button 组件样式
│ │ └── index.scss
│ ├── input/ # Input 组件
│ │ └── ...
│ └── ...
├── src/ # 演示和文档
│ ├── App.vue
│ └── main.ts
├── tests/ # 单元测试
│ └── unit/
│ └── example.spec.ts
├── typings/ # 类型声明文件
│ └── vue-shim.d.ts
├── vue.config.js # Vue CLI 配置文件
├── package.json # 项目依赖和脚本
└── tsconfig.json # TypeScript 配置文件
packages
: 这里存放我们所有的组件源码。每个组件都有自己的独立目录,包含src
(组件源码)、index.ts
(组件入口)和style
(组件样式)。src
: 这个目录主要用于组件库的演示和文档。我们可以通过修改App.vue
和main.ts
来展示我们的组件。tests
: 存放单元测试代码,确保组件的质量。typings
: 存放类型声明文件,为 TypeScript 提供类型支持。
三、编写第一个组件:Hello World!
咱们先从一个简单的 Button 组件开始。在 packages/button/src/Button.vue
中写入以下代码:
<template>
<button class="my-button" :class="typeClass" @click="handleClick">
{{ text }}
</button>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'MyButton',
props: {
text: {
type: String,
default: 'Button',
},
type: {
type: String,
default: 'default',
validator: (value: string) => ['default', 'primary', 'success', 'warning', 'danger'].includes(value),
},
},
computed: {
typeClass() {
return `my-button--${this.type}`;
},
},
methods: {
handleClick() {
this.$emit('click');
},
},
});
</script>
<style lang="scss">
.my-button {
padding: 10px 20px;
border-radius: 4px;
border: 1px solid #ccc;
background-color: #fff;
cursor: pointer;
&--primary {
background-color: #409EFF;
color: #fff;
border-color: #409EFF;
}
&--success {
background-color: #67C23A;
color: #fff;
border-color: #67C23A;
}
&--warning {
background-color: #E6A23C;
color: #fff;
border-color: #E6A23C;
}
&--danger {
background-color: #F56C6C;
color: #fff;
border-color: #F56C6C;
}
}
</style>
这个 Button 组件接收 text
和 type
两个 prop。text
用于设置按钮的文本,type
用于设置按钮的类型(default, primary, success, warning, danger)。
接下来,我们需要创建一个入口文件 packages/button/index.ts
,用于导出这个组件:
import Button from './src/Button.vue';
import { App } from 'vue';
Button.install = (app: App) => {
app.component(Button.name, Button);
};
export default Button;
这个入口文件导出了 Button 组件,并且添加了一个 install
方法。这个方法用于将组件注册为全局组件,方便在 Vue 应用中使用。
四、按需引入:只取所需,不浪费流量
为了支持按需引入,我们需要修改 vue.config.js
文件,配置 Webpack 的 chainWebpack
选项:
const { defineConfig } = require('@vue/cli-service')
const path = require('path');
module.exports = defineConfig({
transpileDependencies: true,
chainWebpack: (config) => {
config.module
.rule('js')
.include
.add(path.resolve(__dirname, 'packages'))
.end()
.use('babel-loader')
.loader('babel-loader')
.tap(options => {
// 修改它的选项...
return options
})
},
configureWebpack: {
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
'packages': path.resolve(__dirname, 'packages')
}
}
}
})
这个配置告诉 Webpack,我们需要处理 packages
目录下的 JavaScript 文件。
然后,在 src/main.ts
中,我们可以按需引入 Button 组件:
import { createApp } from 'vue'
import App from './App.vue'
import Button from 'packages/button'
const app = createApp(App)
app.use(Button); // 全局注册
app.mount('#app')
这样,我们就实现了按需引入 Button 组件。
五、主题定制:千人千面,满足个性化需求
主题定制是组件库的重要特性。我们可以通过 CSS 变量来实现主题定制。
首先,在 packages/button/style/index.scss
中定义一些 CSS 变量:
:root {
--my-button-background-color: #fff;
--my-button-text-color: #333;
--my-button-border-color: #ccc;
--my-button-border-radius: 4px;
}
.my-button {
background-color: var(--my-button-background-color);
color: var(--my-button-text-color);
border: 1px solid var(--my-button-border-color);
border-radius: var(--my-button-border-radius);
padding: 10px 20px;
cursor: pointer;
&--primary {
--my-button-background-color: #409EFF;
--my-button-text-color: #fff;
--my-button-border-color: #409EFF;
}
// ...其他类型的样式
}
然后,在 Vue 应用中,我们可以通过修改 CSS 变量的值来改变组件的主题。例如,在 src/App.vue
中:
<template>
<div class="app">
<my-button text="Default"></my-button>
<my-button type="primary" text="Primary"></my-button>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'App',
});
</script>
<style lang="scss">
.app {
--my-button-background-color: #f0f0f0; // 修改默认背景色
--my-button-text-color: #666; // 修改默认文字颜色
display: flex;
gap: 10px;
padding: 20px;
}
</style>
通过修改 .app
类的 CSS 变量,我们可以改变所有 Button 组件的默认样式。
六、TypeScript 类型提示:代码更健壮,开发更高效
TypeScript 的类型提示可以帮助我们避免很多潜在的错误,提高代码的健壮性。
我们已经在编写 Button 组件时使用了 TypeScript。为了让 TypeScript 能够正确地识别我们的组件,我们需要在 typings/vue-shim.d.ts
文件中添加类型声明:
import { ComponentCustomProperties } from 'vue';
declare module '@vue/runtime-core' {
export interface ComponentCustomProperties {
$myAwesomeUI: {
version: string;
};
}
}
declare module 'vue' {
export interface GlobalComponents {
MyButton: typeof import('packages/button')['default'];
// 其他组件
}
}
这个类型声明告诉 TypeScript,我们有一个全局组件 MyButton
,它的类型是 packages/button
导出的默认类型。
七、自动化测试:质量保障,让代码更安心
自动化测试是保证组件库质量的重要手段。我们可以使用 Jest 和 Vue Test Utils 来进行单元测试。
首先,我们需要创建一个测试文件 tests/unit/Button.spec.ts
:
import { shallowMount } from '@vue/test-utils';
import Button from 'packages/button';
describe('Button.vue', () => {
it('renders text prop correctly', () => {
const wrapper = shallowMount(Button, {
props: {
text: 'Hello World',
},
});
expect(wrapper.text()).toBe('Hello World');
});
it('emits click event when clicked', async () => {
const wrapper = shallowMount(Button);
await wrapper.find('button').trigger('click');
expect(wrapper.emitted('click')).toBeTruthy();
});
it('applies correct class based on type prop', () => {
const wrapper = shallowMount(Button, {
props: {
type: 'primary',
},
});
expect(wrapper.classes()).toContain('my-button--primary');
});
});
这个测试文件包含了三个测试用例:
- 测试 Button 组件是否正确渲染
text
prop。 - 测试 Button 组件是否在点击时触发
click
事件。 - 测试 Button 组件是否根据
type
prop 应用正确的 CSS 类。
然后,我们可以运行以下命令来执行测试:
npm run test:unit
如果所有测试用例都通过了,那么恭喜你,你的 Button 组件已经通过了单元测试。
八、发布你的组件库:让世界看到你的作品
当你的组件库开发完成后,你可以将其发布到 npm 上,让其他开发者也能使用你的作品。
首先,你需要创建一个 npm 账号。然后,在 package.json
文件中添加以下信息:
{
"name": "my-awesome-ui",
"version": "0.0.1",
"description": "A Vue.js component library",
"main": "dist/my-awesome-ui.umd.js",
"module": "dist/my-awesome-ui.es.js",
"exports": {
".": {
"import": "./dist/my-awesome-ui.es.js",
"require": "./dist/my-awesome-ui.umd.js"
}
},
"files": [
"dist",
"packages"
],
"keywords": [
"vue",
"component",
"ui"
],
"author": "Your Name",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/your-username/my-awesome-ui.git"
},
"bugs": {
"url": "https://github.com/your-username/my-awesome-ui/issues"
},
"scripts": {
"build": "vue-cli-service build --target lib --name my-awesome-ui packages/index.ts",
"test:unit": "vue-cli-service test:unit"
},
"peerDependencies": {
"vue": "^3.0.0"
},
"devDependencies": {
// ...
}
}
name
: 组件库的名称,必须是唯一的。version
: 组件库的版本号,遵循 Semantic Versioning 规范。description
: 组件库的描述,用于在 npm 上展示。main
: CommonJS 规范的入口文件。module
: ES Module 规范的入口文件。files
: 需要发布的文件列表。keywords
: 关键词,用于在 npm 上搜索。author
: 作者。license
: 许可证。repository
: 代码仓库地址。bugs
: Bug 提交地址。scripts
: 构建和测试脚本。peerDependencies
: 依赖的 Vue 版本,必须是 Vue 3。
然后,我们需要修改 packages/index.ts
文件,导出所有组件:
import { App } from 'vue';
import Button from './button';
const components = [
Button,
// 其他组件
];
const install = (app: App): void => {
components.forEach(component => {
app.component(component.name, component);
});
};
export {
Button,
// 其他组件
};
export default {
install
};
接下来,运行以下命令来构建组件库:
npm run build
构建完成后,你会得到一个 dist
目录,里面包含了构建后的文件。
最后,运行以下命令来发布组件库:
npm publish
在发布之前,请确保你已经登录了 npm 账号。
好了,到这里,一个支持主题定制、按需引入、TypeScript 类型提示,并且拥有自动化测试的 Vue 组件库就完成了。
九、总结:回顾与展望
今天,我们一起学习了如何从零开始创建一个现代化的 Vue 组件库。我们讨论了项目初始化、目录结构、组件编写、按需引入、主题定制、TypeScript 类型提示、自动化测试和组件库发布。
当然,这只是一个简单的示例。在实际开发中,你可能需要考虑更多的问题,例如:
- 组件的文档和演示。
- 组件的国际化支持。
- 组件的无障碍访问性。
- 组件库的性能优化。
希望今天的分享能够帮助你更好地理解 Vue 组件库的开发。如果你有任何问题,欢迎随时提问。
感谢大家的聆听!