React 17 + StoryBook 打造自己团队的UI库
Posted 小铭同学丶
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React 17 + StoryBook 打造自己团队的UI库相关的知识,希望对你有一定的参考价值。
前言
为了提升团队前端工作效率,打造一套团队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.02
npx 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
运行后的样子是这样的,从此你就可以开始扩展组件库了。
以上是关于React 17 + StoryBook 打造自己团队的UI库的主要内容,如果未能解决你的问题,请参考以下文章
为啥 redux-thunk 在 Storybook 中不起作用?
使用 Storybook 进行 React-table[v7] 渲染