前言

为了晋升团队前端工作效率,打造一套团队UI库是有一个无效的办法。既能够缩小重复劳动,又能够进步组件的复用率与统一性。

创立了团队UI库后,就很有必要搭建一个动态文档网站,用于标准UI库的开发扩大以及标准应用。毕竟这货色不是你一个人开发的,也不是面向你一个人用的,有手册能够查,当然比每天都去问开发人员或者本人摸索来的快。

以前是比拟趋向于Docz这种动态文档生成插件,Docz的特色是零配置、简略、疾速,它应用Markdown语法的扩大MDX(在 Markdown 里引入 React 组件并渲染出组件)来书写文档,对于相熟Markdown的开发者是能够间接上手的。

然而相似于Docz这种在曾经满足不了团队的需要了,我想要的不仅仅是一个文档手册汇合,我还想要在线调试,实时查看组件成果。这时候就有了Storybook,Storybook是一个UI开发环境,容许开发者设置独自的开发工作、文档以及测试。

如何在React中引入Storybook

1、首先创立一个React + Storybook我的项目。

npx create-react-app myapp --template typescript  //创立React我的项目 v17.02npx sb init // 结构成Storybook我的项目

结构完后的文件夹次要有如下的扭转:

├── .storybook│       │── main.js         //主文件│       └── preview.js      //预览设置文件│├─ src │    └── stories│             │── assets              //动态资源文件夹│             │── button.css          //按钮款式文件│             │── Button.tsx          //按钮主文件│             │── Button.stories.tsx  //按钮文档页面│             │── Introduction.stories.mdx //欢送介绍页面,间接应用mdx文件,相似Docz│             └── .....

因为这个目录构造是主动生成的,对于咱们当前UI治理不太不便,所以咱们略微将文件目录再略微革新下src目录构造。

在src下新建一个compnent文件夹,而后其中再以组件名文件夹分类,方便管理,如下:

├─ src │    │──component│    │      │──  style  //款式文件│    │      │        │── button.scss │    │      │        │── index.scss│    │      │        └──  ... │    │      │──  button│    │      │        │── button.tsx│    │      │        │── Button.stories.tsx│    │      └──  ... //参考上述文件夹│    └── stories│             │── assets              //动态资源文件夹│             │── Introduction.stories.mdx //欢送介绍页面,间接应用mdx文件,相似Docz│             └── .....

2、main.js

因为批改了文件目录机构,所以咱们也要对应的批改main.js的文件匹配机制。

const path = require('path');module.exports = {       //应用scss,应用前记得装置对应的loader    webpackFinal: async (config, { configType }) => {        config.module.rules.push({            test: /\.scss$/,            use: ['style-loader', 'css-loader', 'sass-loader'],            include: path.resolve(__dirname, '../'),        });        // Return the altered config        return config;    },    stories: [            '../src/**/*.stories.mdx',             '../src/component/**/*.stories.@(js|jsx|ts|tsx)'        ],    addons: ['@storybook/addon-links', '@storybook/addon-essentials'],};其中次要是stories和addons,更多的配置项能够查看StoryBook Main Config。stories:用来形容你的故事绝对于配置文件的地位,从默认的配置能够看出格局为'*.stories.@(js|jsx|ts|tsx)';addons: 用来形容你要引入的storybook插件。webpackFinal:自定义webpack配置。

3、preview.js

import '../src/component/style/index.scss';  //引入全局款式文件import { configure } from '@storybook/react';//排列目录的程序const loaderFn = () => {    const allExports = [        require('../src/stories/Introduction.stories.mdx'),        require(`../src/component/button/button.stories.tsx`),    ];    return allExports;};configure(loaderFn, module);export const parameters = {    layout: 'centered', // canvas页面示范元素地位居中    actions: { argTypesRegex: '^on[A-Z].*' },    controls: {        expanded: true, //canvas页面显示形容文档的形容,类型,初始值        matchers: {            color: /(background|color)$/i,            date: /Date$/,        },    },};

次要是自定义左侧导航栏的程序,还有一些页面元素的布局和款式引入。

4、组件故事模式

在应用storyBook接触最多的就是Component Story Format(CSF),这是应用了ES6提供的module编写的形式,咱们能够看下一个我编写的例子,去更好的了解这种模式。

import { Meta, Story } from '@storybook/react';import { Button as Component, ButtonProps } from './button';import { ButtonGroup } from '../button-group/button-group';export default {    title: 'Component/Base/Button',    component: Component,    argTypes: {        showGroup: {            description: '是否显示按钮组,仅供测试应用,非正式属性',            defaultValue: false,            control: {                type: 'boolean',            },            table: {                category: 'Test',            },        },        //button        type: {            description: '类型',            defaultValue: 'default',            control: {                type: 'select',                options: ['default', 'primary', 'success', 'warning', 'danger', 'text'],            },            table: {                category: 'Button',                type: { summary: 'default | primary  | success | warning | danger | text' },            },        },        size: {            description: '尺寸',            defaultValue: 'medium',            control: {                type: 'select',                options: ['mini', 'small', 'medium', 'large', 'big', 'huge'],            },            table: {                category: 'Button',                type: { summary: 'mini | small | medium | large | big | huge' },            },        },        prefixIcon: {            description: '前置图标',            control: {                type: 'text',            },            table: {                category: 'Button',            },        },        sufixIcon: {            description: '后置图标',            control: {                type: 'text',            },            table: {                category: 'Button',            },        },        color: {            description: '色彩',            control: {                type: 'color',            },            table: {                category: 'Button',            },        },        textColor: {            description: '文本色彩',            control: {                type: 'color',            },            table: {                category: 'Button',            },        },        nativeType: {            description: '按钮原生类型,可参考<a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attr-type">HTML规范</a>',            defaultValue: 'button',            control: {                type: 'select',                options: ['submit', 'button', 'reset'],            },            table: {                category: 'Button',                type: { summary: "submit | button | reset" },            },        },        block: {            description: '是否为块元素',            control: {                type: 'boolean',            },            table: {                category: 'Button',            },        },        disabled: {            description: '是否禁用',            control: {                type: 'boolean',            },            table: {                category: 'Button',            },        },        plain: {            description: '是否开启幽灵按钮',            control: {                type: 'boolean',            },            table: {                category: 'Button',            },        },        round: {            description: '是否开启圆角',            control: {                type: 'boolean',            },            table: {                category: 'Button',            },        },        loading: {            description: '是否开启期待动效',            table: {                category: 'Button',            },        },        to: {            description: '跳转地址,测试用例只反对文本输出',            control: {                type: 'text',            },            table: {                category: 'Button',            },        },        onClick: {            description: '点击事件,返回Event',            control: {                type: null,            },            table: {                category: 'Button',            },        },        //ButtonGroup        direction: {            description: '方向',            defaultValue: 'horizontal',            control: {                type: 'select',                options: ['horizontal', 'vertical'],            },            table: {                category: 'ButtonGroup',            },        },        //hidden        style: {            table: {                disable: true,            },        },        children: {            table: {                disable: true,            },        },        className: {            table: {                disable: true,            },        },    },    args: {        showGroup: false,        children: 'Preview',    },} as Meta;export const Button = (args: any) => {    const { showGroup, direction, ...other } = args;    if (showGroup) {        return (            <ButtonGroup direction={direction}>                <Component {...other}>PreView</Component>                <Component {...other}>Next</Component>            </ButtonGroup>        );    } else {        return <Component {...other}></Component>;    }};

export default 定义了组件的相干配置,其中别离为:

title:导航栏层级,用'/'号分隔开,而且title不能反复;
component:组件理论元素;
argTypes:文档形容项,默认会显示组件props的入参项,也能够自定减少或者重写,具体配置上面详解。
argTypes配置通过一个例子解释比拟容易:

type: { // 字段名为type   description: '类型',  // 属性形容   defaultValue: 'default',//开发环境默认值   control: {     type: 'select', //文档控制器类型text(文本),select(抉择)... 依据理论状况定义     options: ['default', 'primary', 'success', 'warning', 'danger', 'text'], //选项内容   },   table: {     category: 'Button', //定义形容分类,可多个组件联结开发区分应用     type: { summary: 'default | primary  | success | warning | danger | text' }, // 字段属性类型重写    defaultValue:{summary:'default'} //文档形容显示的默认值   },},

实际效果如下:

每个export const 定义侧边栏子菜单以及相干的文档,例子中是有两个组件的联结调试,能够在argTypes李减少一个showGroup字段,显示单个按钮还是按钮组。

尽管这个办法能够多个组件联结调试,然而性能也是无限的,不如间接代码编辑的快。

5、运行storybook

npm i -D node-sass@5.0 sass-loader@10.0.5 style-loader@1.3.0 //装置相干插件
npm run storybook
运行后的样子是这样的,从此你就能够开始扩大组件库了。