babel-ui库介绍
babel-ui是通天塔可视化使用ui库。目前有Alert
、Alink
、Button
、Checkbox
、Messages
、Modal
、ToolTip
等组件
而这个对于可视化经常使用的ui库,却有以下两个糟点
-
没有文档介绍各个组件具体使用方法
目前为止一直没有文档介绍组件的使用方法。这就导致每次开发都需要去看各个组件的propTypes,甚至去看各个组件的源码实现。
-
没法脱离于业务开发
有的需求需要改动公共组件时,需要在node_modules里调试完之后再同步到babel-ui中。
其实搭建一个类似antdesign的组件官网,就解决以上两个问题。但目前我们ui库受众小且没有专人维护,去投入人力开发并维护一个官网并不是明智之举。
而storybook正好可以快速解决以上问题。
Storybook是啥
Storybook 是React,Vue和Angular最受欢迎的UI组件开发工具。它可以在隔离的环境中开发和设计应用程序;也可以那个使用它来快速构建ui组件的文档
Storybook提供了众多的插件来实现一些高级功能,这些插件都是由核心维护者和众多的社区开发人员维护的。在git上收获了4W+的star。
讲了这么多,来看下babel-ui使用Storybook之后的页面,也可以查看一些示例
Storybook开发流程简介
Storybook提供两种接入方式,一个是使用@storybook/cli
工具快速识别package.json
中所用框架,自动生成一些相应文件;第二种就是针对不同框架从头开始搭建;
我这边讲下第二种一步步搭建的步骤
-
npm安装
@storybook/react
-
package.json设置scripts
"scripts": { "storybook": "start-storybook -p 8000" }
-
根目录下创建
.storybook
文件夹,并依次创建图示文件(center.js和index.styl)
下面会有关于各个文件的详细介绍,这边先忽略
-
新建
stories
文件并写story这里主要配合插件写各个组件的story(是一个可以返回可以渲染到屏幕上的函数)。
.storybook文件夹介绍
Addons.js
这个文件主要是用来注册Storybook的各个插件
import "@storybook/addon-knobs/register";
import "@storybook/addon-notes/register-panel";
import "@storybook/addon-actions/register";
import "@storybook/addon-links/register";
center.js
用来居中中间展示区域,也可使用居中插件代替
import React from "react";
const style = {
maxWidth: 680,
margin: '0 auto'
}
export default function(renderStory) {
return <div style={style}>{renderStory()}</div>
}
config.js
这个文件中主要用来动态加载stories中的文件。同时也可以在此文件中配置storybook的主题以及为所有的story添加统一的插件
import { configure } from "@storybook/react";
//指定story的位置
const req = require.context('../src/stories',true,/\.stories\.js$/)
function loadStories() {
req.keys().map(fileName=>req(fileName))
}
configure(loadStories, module);
index.styl
我这边这个文件主要为babel-ui引入一些字体文件,也可以引入一些公用的样式
$fa-font-path = '../node_modules/font-awesome-stylus/fonts'
@import '~font-awesome-stylus/index'
webpack.config.js
Storybook有自己的webpack配置,我们可以在通过此文件自定义webpack配置(V4版本)。使用webpack-merg
合并默认配置以及自己的配置
addons(Storybook插件)
Storybook有着丰富的插件去满足所需的需求。目前Storybook5.1版本官方支持的常用react插件如下(详细各个插件信息请点击链接):
-
使用storybook-ui将用户输入的值代替props等传入自己的组件。
knobs提供了内置的text、boolean、number、color、object、array、select、radios、options、files、date、button类型供选择
import { text } from '@storybook/addon-knobs' storiesOf('Icon', module).add('default', () => { const name = text('name', 'refresh') return ( <Icon name={name} /> ) })
这样就可以通过storybook内置的text文本输入来控制icon的name
-
用于显示storybook中事件处理程序接收到的数据
当事件触发时,可以查看事件接收的参数
-
为storyies添加文本或者markdown
-
展示组件的各种详细信息,自动将propTypes,defaultProps生成相应的表格信息
-
State状态
-
更改背景
-
居中显示
-
故事之间相互跳转
获取故事的url
-
storybook配置项(storybook v5以上直接在配置)
-
为全局或者单独的story添加样式
-
是否展示story源码
-
为storybook提供各种机型,更好的进行响应式开发
-
单元测试
-
jest或屏幕快照
-
…
如何写Story
在写之前先大概了解下各个方法
StoriesOf
import { storiesOf } from '@storybook/react'
storiesOf('Button', module)
StoriesOf接收两个参数,第一个是story类的名字,第二个参数是用来模块热替换(不传,则每次更改story,都需要手动刷新浏览器)
add
add用来添加story类下的子story;可以链式调用在story类下写多个story
参数1:story的名字
参数2:story在中间区域渲染函数
storiesOf('Button', module)
.add('default',()=>111)
.add('story2',()=>2222)
addDecorator
story的装饰器方法(装饰器是一种用一组通用组件包装故事的方法)
-
每个story单独使用
import { storiesOf,addDecorator } from '@storybook/react' storiesOf('Button', module) .addDecorator(storyFn => <div style={{ textAlign: 'center' }}>{storyFn()}</div>) .add('default',()=>111) .add('story2',()=>2222)
-
在.storybook/config.js全局使用
import { configure, addDecorator } from "@storybook/react"; import center from './center.js' addDecorator(center); const req = require.context('../src/stories',true,/\.stories\.js$/) function loadStories() { req.keys().map(fileName=>req(fileName)) } configure(loadStories, module);
addParameters
给Storybook添加参数(和addDecorator使用方法类似)
-
每个story单独使用
-
在.storybook/config.js全局使用
具体配置信息查看
import { addParameters } from '@storybook/react' //使用addParameters添加Storybook配置 addParameters({ options: { isFullscreen: false, showNav: true, showPanel: true, panelPosition: "right", hierarchySeparator: /\/|\./, hierarchyRootSeparator: /\|/, sidebarAnimations: true, enableShortcuts: true, isToolshown: true, theme: create({ base: "light", brandTitle: "babel-ui", brandUrl: "http://betah5.m.jd.com/active/babelTower/index.html#/" }) } });
在stories文件中写各个组件的story
npm run storybook
启动Storybook之后,就可以在stories文件中写各个组件的story了。下面用一个例子介绍如何配合插件写一个简单的story
import React from 'react'
import { Button, SpiritButton } from 'Shared'
import { storiesOf } from '@storybook/react'
import { boolean, radios, select } from '@storybook/addon-knobs'
import { action } from '@storybook/addon-actions'
import { enumConstants } from 'Utils/common'
// 默认值
const defaultData = {
type: '无',
size: 'large',
disabled: false,
spritDisabled: false,
loading: false,
spritType: 'primary',
spritSize: 'large'
}
// button type类型
const typeOptions = enumConstants('primary', 'gray', '无')
// SpiritButton type类型
const spritButtonType = enumConstants('primary', 'ghost', 'transparent')
// button大小类型
const typeSizeOptions = enumConstants('small', 'large', 'biglarge')
// spritbutton大小类型
const spritSizeOptions = enumConstants('small', 'large', '无')
storiesOf('Button', module)
.add('default', () => {
const type = radios('type', typeOptions, defaultData.type)
const disabled = boolean('disabled', defaultData.disabled)
const size = select('size', typeSizeOptions, defaultData.size)
return (
<Button
{...(type != '无' ? { type } : null)}
size={size}
disabled={disabled}
onClick={action('button被点击')}>
Hello Button
</Button>
)
})
.add('SpiritButton', () => {
const loading = boolean('loading', defaultData.loading)
const disabled = boolean('disabled', defaultData.spritDisabled)
const type = radios('type', spritButtonType, defaultData.spritType)
const size = select('size', spritSizeOptions, defaultData.spritSize)
return (
<SpiritButton
onClick={action('button被点击')}
loading={loading}
type={type}
disabled={disabled}
{...(size != '无' ? { size } : null)}>
Hello SpiritButton
</SpiritButton>
)
})
未来与展望
随着Storybook不断更新,5.2版本即将到来,到时将会支持docs插件用来代替info插件。docs可以快速将story自动转换为世界级的文档。这也是为啥目前并没有使用notes插件写md文档的原因之一。由于5.2版本还在开发中,对docs插件感兴趣的同学可查看博客、插件git地址
总结
本文只是学习Storybook的一个简短总结,在熟悉各个插件之后接入storybook并不是很难;对于解决babel-ui
之前无文档、无法独立业务开发组件的问题还是很不错的实践体验。
Storybook官方文档写的还是不错的,对Storybook有兴趣的同学可以点击链接参看