各位老铁,大家好!我是你们的老朋友,今天咱们聊聊在大团队里,怎么把Vue组件库这摊子事儿给搞明白,顺便用Storybook让你的组件文档像明星一样闪耀。
开场白:组件库,团队协作的“普通话”
想象一下,一个团队里,你用“按钮A”,他用“Button甲”,她用“那个圆不溜秋的东西”,这沟通成本得多高?组件库就是团队的“普通话”,大家用一套标准,交流起来才顺畅。
第一章:组件库规范,立规矩才能成方圆
组件库规范不是让你戴上镣铐跳舞,而是给你搭个舞台,让你跳得更漂亮。主要包括以下几个方面:
1.1 命名规范:见名知意,好记性不如烂笔头
- 组件命名: 使用 PascalCase(帕斯卡命名法,也叫大驼峰命名法),例如
MyButton
,UserProfileCard
。这样一眼就知道是个组件。 - 事件命名: 使用 kebab-case(短横线命名法),例如
on-click
,on-value-change
。 - Prop 命名: 使用 camelCase(驼峰命名法),例如
userName
,defaultValue
。 - 文件命名: 和组件命名保持一致,例如
MyButton.vue
。
代码示例:
// MyButton.vue
<template>
<button @click="$emit('on-click')">{{ label }}</button>
</template>
<script>
export default {
name: 'MyButton', // 组件名称
props: {
label: {
type: String,
default: '按钮',
},
userName: { // Prop 名称
type: String
}
},
emits: ['on-click'] // 事件名称
};
</script>
1.2 目录结构:整整齐齐,看着就舒服
建议采用原子化的目录结构,把组件拆解成最小可复用的单元。
components/
├── atoms/ # 最小的、不可再分的组件,例如按钮、输入框
│ ├── MyButton.vue
│ └── MyInput.vue
├── molecules/ # 由 atoms 组成的简单组件,例如带有标签的输入框
│ ├── LabeledInput.vue
│ └── MyFormItem.vue
├── organisms/ # 由 molecules 或 atoms 组成的复杂组件,例如表单、导航栏
│ ├── UserProfileForm.vue
│ └── MainNavigation.vue
└── templates/ # 页面模板
└── UserProfilePage.vue
1.3 Prop 类型和默认值:清清楚楚,明明白白
- 类型: 必须明确声明 Prop 的类型,例如
String
,Number
,Boolean
,Array
,Object
,Function
,Symbol
。 - 默认值: 尽可能提供默认值,避免组件使用时出现意料之外的情况。
- 验证: 可以使用
validator
函数对 Prop 的值进行验证,确保传入的值符合预期。 - 必填项: 使用
required: true
声明必填的 Prop,强制使用者传入值。
代码示例:
<script>
export default {
props: {
// 字符串类型,默认值为空字符串
userName: {
type: String,
default: '',
},
// 数字类型,默认值为 0
age: {
type: Number,
default: 0,
},
// 布尔类型,默认值为 false
isActive: {
type: Boolean,
default: false,
},
// 数组类型,默认值为空数组
items: {
type: Array,
default: () => [], // 必须使用函数返回,避免多个组件共享同一个数组
},
// 对象类型,默认值为空对象
config: {
type: Object,
default: () => ({}), // 必须使用函数返回,避免多个组件共享同一个对象
},
// 必填项
title: {
type: String,
required: true,
},
// 验证器
status: {
type: String,
validator: (value) => {
return ['active', 'inactive', 'pending'].includes(value);
},
},
},
};
</script>
1.4 事件:沟通的桥梁
- 命名: 使用 kebab-case,例如
on-click
,on-value-change
。 - 参数: 明确事件传递的参数,并在组件文档中说明。
- emit: 使用
this.$emit()
触发事件。
代码示例:
<template>
<button @click="handleClick">点我</button>
</template>
<script>
export default {
methods: {
handleClick() {
// 触发 on-click 事件,并传递一个参数
this.$emit('on-click', 'Hello from MyButton!');
},
},
};
</script>
1.5 插槽:灵活的填充物
- 命名: 尽量使用具名插槽,方便使用者填充内容。
- 默认内容: 为插槽提供默认内容,避免使用者不填充内容时出现空白。
代码示例:
// MyCard.vue
<template>
<div class="card">
<div class="card-header">
<slot name="header">
<!-- 默认内容 -->
<h2>Card Header</h2>
</slot>
</div>
<div class="card-body">
<slot>
<!-- 默认内容 -->
<p>Card Body Content</p>
</slot>
</div>
<div class="card-footer">
<slot name="footer"></slot>
</div>
</div>
</template>
使用:
<template>
<MyCard>
<template #header>
<h3>My Custom Header</h3>
</template>
<p>My Custom Body Content</p>
<template #footer>
<button>Save</button>
</template>
</MyCard>
</template>
1.6 样式:保持一致,拒绝个性
- CSS 命名: 使用 BEM(Block Element Modifier)命名规范,例如
block__element--modifier
。 - 样式隔离: 使用 scoped CSS,避免样式冲突。
- 主题: 支持主题切换,方便适应不同的业务场景。
- CSS 变量: 使用 CSS 变量(Custom Properties)来定义颜色、字体等,方便统一管理。
代码示例:
<template>
<button class="my-button my-button--primary">
{{ label }}
</button>
</template>
<style scoped>
.my-button {
/* ... */
background-color: var(--primary-color);
}
.my-button--primary {
/* ... */
color: var(--white-color);
}
</style>
1.7 文档:清晰易懂,事半功倍
- 组件描述: 描述组件的功能和用途。
- Prop 说明: 详细说明每个 Prop 的类型、默认值和作用。
- 事件说明: 详细说明每个事件的触发时机和传递的参数。
- 插槽说明: 详细说明每个插槽的作用和使用方法。
- 示例: 提供多个示例,展示组件的不同用法。
第二章:Storybook,组件的“写真集”
Storybook 是一个强大的组件文档工具,可以帮助你:
- 独立开发组件: 在隔离的环境中开发组件,不受项目代码的干扰。
- 可视化展示组件: 以交互的方式展示组件,方便开发者和设计师预览。
- 自动生成文档: 根据组件的 Prop 和事件自动生成文档。
- 测试组件: 可以使用 Storybook 提供的 Addons 来测试组件。
2.1 安装 Storybook
npx sb init
2.2 创建 Story
在 src/components/MyButton.stories.js
文件中创建一个 Story:
// MyButton.stories.js
import MyButton from './MyButton.vue';
export default {
title: 'Atoms/MyButton', // Storybook 菜单中的路径
component: MyButton,
argTypes: {
label: { control: 'text' }, // 控制器类型,用于在 Storybook 中修改 Prop 的值
onClick: { action: 'clicked' }, // 用于在 Storybook 中监听事件
},
};
const Template = (args) => ({
components: { MyButton },
setup() {
return { args };
},
template: '<my-button v-bind="args" @on-click="args.onClick" />',
});
export const Primary = Template.bind({});
Primary.args = {
label: 'Primary Button',
};
export const Secondary = Template.bind({});
Secondary.args = {
label: 'Secondary Button',
};
2.3 运行 Storybook
npm run storybook
打开浏览器,访问 Storybook 地址(通常是 http://localhost:6006
),你就可以看到你的组件文档了。
2.4 Storybook Addons
Storybook 提供了丰富的 Addons,可以增强 Storybook 的功能。例如:
@storybook/addon-controls
: 提供 Prop 的控制器,方便修改 Prop 的值。@storybook/addon-actions
: 监听事件,并在 Storybook 中显示事件的触发情况。@storybook/addon-docs
: 自动生成组件文档。@storybook/addon-viewport
: 模拟不同的屏幕尺寸。@storybook/addon-a11y
: 检查组件的可访问性。
2.5 使用 MDX 创建文档
除了使用 JavaScript 文件创建 Story,还可以使用 MDX 文件创建 Story。MDX 是一种可以在 Markdown 文件中嵌入 JSX 的语法,可以方便地编写文档和示例。
代码示例:
{/* MyButton.mdx */}
import { Meta, Story } from '@storybook/addon-docs';
import MyButton from './MyButton.vue';
<Meta title="Atoms/MyButton" component={MyButton} />
# MyButton
This is a description of the MyButton component.
<Story name="Primary">
<MyButton label="Primary Button" />
</Story>
<Story name="Secondary">
<MyButton label="Secondary Button" />
</Story>
第三章:团队协作,众人拾柴火焰高
组件库的建设不是一个人的战斗,需要团队成员共同参与。
3.1 代码审查
所有提交到组件库的代码都需要经过代码审查,确保代码质量和规范。
3.2 文档贡献
鼓励团队成员为组件库贡献文档,完善组件的描述、Prop 说明、事件说明和示例。
3.3 定期维护
定期维护组件库,修复 Bug,更新依赖,优化性能。
3.4 持续集成
使用持续集成工具(例如 Jenkins, GitLab CI, GitHub Actions)自动构建和测试组件库,确保代码的稳定性和可靠性。
第四章:总结,组件库是长期投资
组件库的建设是一个长期的过程,需要持续投入和维护。一个好的组件库可以提高开发效率,降低维护成本,增强用户体验。
关键点回顾:
规范项 | 描述 |
---|---|
命名规范 | 组件、事件、Prop、文件命名统一,易于理解。 |
目录结构 | 原子化目录结构,方便查找和管理组件。 |
Prop 类型 | 明确 Prop 的类型、默认值和验证规则,避免错误使用。 |
事件 | 命名清晰,参数明确,方便组件之间的通信。 |
插槽 | 使用具名插槽,提供默认内容,增强组件的灵活性。 |
样式 | 使用 BEM 命名规范,scoped CSS,支持主题切换,使用 CSS 变量,保持样式一致性。 |
文档 | 详细描述组件的功能、Prop、事件和插槽,提供多个示例,方便使用者理解。 |
Storybook | 使用 Storybook 创建组件文档,可视化展示组件,自动生成文档,测试组件。 |
团队协作 | 代码审查,文档贡献,定期维护,持续集成,确保组件库的质量和可靠性。 |
希望今天的分享对大家有所帮助! 记住,组件库不是一蹴而就的,需要持续的投入和维护。 祝大家开发愉快!