Storybook
介绍
Storybook 是开发组件库的必备开发工具,翻译为“故事书”。
我们可以把每一个组件想象成一个故事,Storybook就好像在讲一个个故事。
- Storybook 是一个可视化的展示组件的平台
- 使用 Storybook 可以在独立的环境中,创建组件,并在隔离的开发环境中,以交互式的方式展示组件
- Storybook 在主程序之外运行,因此用户可以独立开发组件库,而不必担心应用程序之间的应用关系
- 它把程序的开发和组件的开发分离
- 在 Storybook 中开发组件并预览测试
- 组件开发完毕可以直接在主程序中,或者让其他人使用开发好的组件
- 支持很多框架,可以开发下面这些框架的组件
- React、React Native、Vue、Angular
- Ember、HTML、Svelte、Mithril、Riot
- Storybook 支持很多插件,并提供灵活的 API,可以根据需要自定义 Storybook,还可以构建 Storybook 的静态版本,并将其部署到服务器
在Storybook中可以方便的查看组件,并且以交互的方式展示组件。
安装
- storybook初始化
# 创建项目目录
mkdir my-storybook
# 进入项目目录
cd my-storybook
# 使用story命令初始化项目
npx sb init
# 如果当前项目已经用框架创建了初始项目,例如Vue CLI 或 Create React App
# storybook 会自动检测项目类型进行初始化
# 如果没有,就会提示要手动指定
# 是否要手动选择要安装的storybook项目类型(框架)?
? Do you want to manually choose a Storybook project type to install? Yes
# 选择项目类型:VUE
? Please choose a project type from the following list: VUE
# 也可以在初始化时使用命令强制指定项目类型
# npx sb init --type vue
这个过程会在安装依赖之前,首先初始化一个基本的结构。
然后安装依赖。
6.0.0早期的文档中使用的是:
npx -p @storybook/cli sb init
结果是一样的
- 安装vue
如果项目中没有安装框架,则需要手动安装。
在安装时使用 yarn 安装,因为后面要使用 yarn workspaces。
# 安装vue
yarn add vue
# 安装开发依赖
yarn add vue-loader vue-template-compiler --dev
使用
storybook在package.json中初始化了两个scripts脚本:
storybook
- 启动storybookbuild-storybook
- 打包生成一个静态网站
启动storybook
yarn storybook
它默认提供了几个组件,界面:
初始化的内容
目录结构:
-
.storybook
main.js
storybook的配置文件stories
路径- storybook 就是 stories 的集合
- stories 就是用来创建界面上要呈现的内容
- 默认匹配 stories 目录下的文件
addons
插件- addon-links 设置链接
- addon-essentials 一个插件集合,包括:
- addon-actions 设置事件
- addon-backgrounds 设置背景
- addon-controls 图形化UI,可以动态与组件的参数进行交互
- add-docs 配置文档
- add-viewport Viewport工具栏用于调整iframe尺寸
- add-toolbars 其他工具栏
preview.js
-
stories
存放组件,以Button为例,相关的文件:- Button.vue 定义的组件
- button.css 组件中引入的样式
- Button.stories.js 用于将组件渲染到页面
- 引入了Button组件
- 默认成员导出一个对象,指定component为Button组件
- title 定义了顶级菜单和一级菜单名
- 其他成员导出一些函数
- 每个函数返回一个Vue选项对象
- 每个函数都注册了Button组件
- 每个函数就是一个story,在界面就是二级菜单,菜单名称就是成员变量名称
在项目中可以先开发组件,组件开发完毕后,编写对应的 stories 来查看组件的渲染结果。
// Button.stories.js
// 引入组件
import MyButton from './Button.vue';
export default {
// Example是顶级菜单
// Button是一级菜单
title: 'Example/Button',
// 指定组件
component: MyButton,
argTypes: {
backgroundColor: { control: 'color' },
size: { control: { type: 'select', options: ['small', 'medium', 'large'] } },
},
};
// 返回组件选项对象的公共方法
const Template = (args, { argTypes }) => ({
props: Object.keys(argTypes),
components: { MyButton },
template: '<my-button @onClick="onClick" v-bind="$props" />',
});
// 导出一个展示组件内容的二级菜单
// 变量名就是菜单名
export const Primary = Template.bind({});
// 这里传入props,storybook会将其传入Template方法
Primary.args = {
primary: true,
label: 'Button',
};
export const Secondary = Template.bind({});
Secondary.args = {
label: 'Button',
};
export const Large = Template.bind({});
Large.args = {
size: 'large',
label: 'Button',
};
export const Small = Template.bind({});
Small.args = {
size: 'small',
label: 'Button',
};
package.json
{
"name": "myelement",
"version": "0.1.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
// 使用6006端口启动storybook
"storybook": "start-storybook -p 6006",
// 构建静态网站,构建的结果可以发布到web服务器
"build-storybook": "build-storybook"
},
//...
}
执行build-storybook
构建的结果存放在生成的 storybook-static
目录下。
在这个目录下开启web服务,可以查看结果,与storybook
效果一样。
这个目录可以发布到web服务器。
Monorepo + Storybook
使用 monorepo 的方式管理 storybook 中的代码。
用自己写的组件替换 storybook 默认提供的组件。
stories是根据状态渲染这个组件。
将组件的 stories 放在各自的包下,方便管理。
需要修改 storybook 的配置文件中的 stories 路径。
编写自己的 stories
// packages\button\stories\button.stories.js
// 导入组件的入口文件 index.js
import Button from '../'
export default {
title: 'Button',
component: Button
}
export const ShowMessage = () => ({
components: { Button },
template: '<Button @click="onClick">Show</Button>',
methods: {
onClick() {
console.log('Hello world!')
}
}
})
export const ShowTime = () => ({
components: { Button },
template: '<Button @click="onClick">{{ now }}</Button>',
data() {
return {
now: new Date()
}
},
methods: {
onClick() {
this.now = new Date()
}
}
})
效果:
表单组件 单独安装表单验证依赖
组件中依赖第三方模块的情况
例如 Form 表单中要使用到 Button Input FormItem 等组件。
FormItem 需要表单验证模块 async-validator。
这就需要在 FormItem 包的目录下(/packages/formitem
)安装 async-validator。
yarn add async-validator
如果每个组件都依赖了第三方模块,需要在每个包的目录下安装,这样很麻烦,后面会介绍 使用 yarn workspace 管理重复依赖。
Form中依赖了其他组件,form.stories.js:
// form.stories.js
// 导入组件的入口文件 index.js
import Form from '../'
import FormItem from '../../formitem'
import Input from '../../input'
import Button from '../../button'
export default {
title: 'Form',
component: Form
}
export const FormTest = () => ({
components: {
Form,
FormItem,
Input,
Button
},
template: `
<Form :model="user" :rules="rules" ref="form" class="form">
<Form-Item label="用户名" prop="username">
<Input v-model="user.username" placeholder="请输入用户名"></Input>
</Form-Item>
<Form-Item label="密码" prop="password">
<Input
type="password"
v-model="user.password"
autocomplete="off"
></Input>
</Form-Item>
<Form-Item>
<button type="primary" @click="login()">登 录</button>
</Form-Item>
</Form>
`,
data() {
return {
user: {
username: '',
password: ''
},
rules: {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, message: '密码最少6位', trigger: 'blur' }
]
}
}
},
methods: {
login() {
this.$refs.form.validate(valid => {
if (valid) {
alert('submit!')
} else {
console.log('error submit!!')
return false
}
})
}
}
})