想搞個組件可視化好久了,一開始就是想作個和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之間的相互跳轉,用法也很簡單,只須要參考文檔就好
目前比較好的插件列表以下