想搞个组件可视化好久了,一开始就是想作个和antd相似的,而后就遇到了一系列的问题javascript
组件可视化的目的:css
调研发现,antd使用的是阿里系的bisheng这个包vue
目的是将markdown转成静态站点,antd就是这样产生的java
这就是本次要说的storybook了,目前3.7w个star,且生态比较好,插件也很多react
npm init -y
npm install @storybook/react --save-dev
npm install react react-dom --save
npm install babel-loader @babel/core --save-dev
复制代码
在package.json中添加webpack
{ "scripts": { "storybook": "start-storybook" } } 复制代码
建立.storybook/config.js文件git
import { configure } from '@storybook/react'; function loadStories() { require('../stories/index.js'); // 能够添加多个store // You can require as many stories as you need. } configure(loadStories, module); 复制代码
建立../stories/index.js文件github
import React from 'react'; import { storiesOf } from '@storybook/react'; import { Button } from '@storybook/react/demo'; storiesOf('Button', module) .add('with text', () => ( <Button>Hello Button</Button> )) .add('with emoji', () => ( <Button><span role="img" aria-label="so cool">😀 😎 👍 💯</span></Button> )); 复制代码
npm run storybook
复制代码
官方提供了三种,我选择了我的认为比较清晰的方式
web
按照上面的设计估计应该就可以知足咱们的需求了chrome
只是简单配置可不行,若是不在一开始就搞好的话,随着后面业务的扩展会带来不少问题,像按需加载这种功能仍是一开始就设计比如较好
修改.storybook/config.js
import { configure } from '@storybook/react'; const req = require.context('../src/components', true, /\.stories\.js$/); function loadStories() { req.keys().forEach(filename => req(filename)); } configure(loadStories, module); 复制代码
addons
**
重要的事情说三遍:
在.storybook配置文件中,添加一个addons.js文件
**在.storybook配置文件中,添加一个addons.js文件
**在.storybook配置文件中,添加一个addons.js文件
addons.js文件是用来维护插件的配置文件
官网上Addons目录就是官方推荐的插件列表,开始一个个尝试
npm i @storybook/addon-knobs -D
复制代码
找到.storybook/addons.js在其中添加一句话
import '@storybook/addon-knobs/register'; 复制代码
按照github地址,添加示例代码
import React from 'react'; import { storiesOf } from '@storybook/react'; import { withKnobs, text, boolean, number } from '@storybook/addon-knobs'; const stories = storiesOf('Storybook Knobs', module); // Add the `withKnobs` decorator to add knobs support to your stories. // You can also configure `withKnobs` as a global decorator. stories.addDecorator(withKnobs); // Knobs for React props stories.add('button', () => ( <button disabled={boolean('不可用', false)} > {text('文案', '牛逼的knobs')} </button> )); // Knobs as dynamic variables. stories.add('as dynamic variables', () => { const name = text('Name', 'Arunoda Susiripala'); const age = number('Age', 89); const content = `I am ${name} and I'm ${age} years old.`; return (<div>{content}</div>); }); 复制代码
上面一共作了几件事情:
直接看效果
上面例子中,第二个case,包含计算变量
// Knobs as dynamic variables. stories.add('as dynamic variables', () => { const name = text('Name', 'Arunoda Susiripala'); const age = number('Age', 89); const content = `I am ${name} and I'm ${age} years old.`; return (<div>{content}</div>); }); 复制代码
npm i -D @storybook/addon-actions
复制代码
import '@storybook/addon-actions/register'; 复制代码
import { storiesOf } from '@storybook/react'; import { action, configureActions } from '@storybook/addon-actions'; import Button from './button'; storiesOf('Button', module).add('default view', () => ( <Button onClick={action('button-click')}>Hello World!</Button> )); 复制代码
import { storiesOf } from '@storybook/react'; import { actions } from '@storybook/addon-actions'; import Button from './button'; // This will lead to { onClick: action('onClick'), ... } const eventsFromNames = actions('onClick', 'onMouseOver'); // This will lead to { onClick: action('clicked'), ... } const eventsFromObject = actions({ onClick: 'clicked', onMouseOver: 'hovered' }); storiesOf('Button', module) .add('default view', () => <Button {...eventsFromNames}>Hello World!</Button>) .add('default view, different actions', () => ( <Button {...eventsFromObject}>Hello World!</Button> )); 复制代码
npm i @storybook/addon-storysource --dev
复制代码
module.exports = function({ config }) { config.module.rules.push({ test: /\.stories\.jsx?$/, loaders: [require.resolve('@storybook/addon-storysource/loader')], enforce: 'pre', }); return config; }; 复制代码
这个和前面两个插件不一样之处就在这,它须要在webpack中添加一个loader,经过storybook自己的webpack对外暴露,对其添加插件
npm i -D @storybook/addon-info
复制代码
addon-info采用的是修饰器模式,能够全局添加,也能够局部添加
addDecorator(withInfo); // Globally in your .storybook/config.js. 复制代码
storiesOf('Component', module) .addDecorator(withInfo) // At your stories directly. .add(...); 复制代码
import React from 'react'; import PropTypes from 'prop-types'; const Test = (props)=><div>{props.children}</div> Test.propTypes = { text: PropTypes.string.isRequired, onDelete: PropTypes.func, } export default Test 复制代码
添加addons-info插件后,就会增长showinfo按钮
import React from 'react'; import PropTypes from 'prop-types'; const Test = (props)=><div>{props.children}</div> Test.defaultProps = { text: '默认值', onDelete: () => {} }; Test.propTypes = { /** 这里text是注释 */ text: PropTypes.string.isRequired, /** 这里onDelete是注释 */ onDelete: PropTypes.func, } export default Test 复制代码
viewport你们都很熟悉,这个就是用来搞移动端的
npm i --save-dev @storybook/addon-viewport
复制代码
在.storyboos/config中注册
import '@storybook/addon-viewport/register'; 复制代码
import React from 'react'; import { configure,addDecorator,addParameters } from '@storybook/react'; // config.js import { withInfo } from '@storybook/addon-info'; addDecorator(withInfo); addParameters({ viewport: { defaultViewport: 'iphone6' }, }); const req = require.context('../src/components', true, /\.stories\.js$/); function loadStories() { req.keys().forEach(filename => req(filename)); } configure(loadStories, module); 复制代码
这里就是使用addParameters方法,很方便就能够
这部分是测试用例相关,短期内啃不下来,待后面有时间再补充
npm i @storybook/addon-a11y --dev
复制代码
修改addons
import '@storybook/addon-a11y/register'; 复制代码
在config文件中添加
addDecorator(withA11y); addParameters({ a11y: { // ... axe options element: '#root', // optional selector which element to inspect config: {}, // axe-core configurationOptions (https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#parameters-1) options: {} // axe-core optionsParameter (https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#options-parameter) }, }); 复制代码
ok,这样的话,若是书写不符合规范的按钮,则会报错
这个插件解决的问题是,story之间的相互跳转,用法也很简单,只须要参考文档就好
目前比较好的插件列表以下