脑壳疼的react组件可视化

8,834 阅读7分钟

想搞个组件可视化很久了,一开始就是想做个和antd类似的,然后就遇到了一系列的问题

组件可视化的目的:

  1. 历史组件可感知,能够做到信息同步
  2. 能够改变开发流程,能够提前进行组件的视觉验收
  3. 便于后期的视觉规范统一

Q1:如何搞个和antd类似的组件库?

调研发现,antd使用的是阿里系的bisheng这个包


Q2:bisheng的解决的问题是?

目的是将markdown转成静态站点,antd就是这样产生的


Q3:bisheng存在的问题?

  1. 很久无人维护,文档更新等都比较滞后
  2. 且文档比较难读,上手比较麻烦

Q4:除了bisheng市面上还有什么解决方案?

这就是本次要说的storybook了,目前3.7w个star,且生态比较好,插件也不少


Q5:storybook怎么上手?

step1:安装依赖

npm init -y
npm install @storybook/react --save-dev
npm install react react-dom --save
npm install babel-loader @babel/core --save-dev 

step2:添加script脚本

在package.json中添加

{
 "scripts": {
   "storybook": "start-storybook"
 }
}

step3:添加config文件

创建.storybook/config.js文件

import { configure } from '@storybook/react';

function loadStories() {
  require('../stories/index.js');
  // 可以添加多个store
  // You can require as many stories as you need.
}

configure(loadStories, module);

step4:开始写story

创建../stories/index.js文件

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>
  )); 

step5:跑起来

npm run storybook

效果

image.png

  • ok,成功搞定,已经能够看到编写的代码了

总结:storybook按照配置,跑起来还是比较轻松的


Q6:如何设计目录结构?

官方提供了三种,我选择了个人认为比较清晰的方式

image.png

  1. button.js是组件代码
  2. button.stories.js是故事代码

按照上面的设计估计应该就能够满足我们的需求了


Q7:如何按需加载?

只是简单配置可不行,如果不在一开始就搞好的话,随着后面业务的扩展会带来很多问题,像按需加载这种功能还是一开始就设计好比较好

修改.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);

Q8:storybook插件是什么?

  1. 前面有了组件库展示,但距离可查看api、可测试、可调整样式等差距还很大
  2. 这些额外的功能就需要添加依赖来解决了
  3. 在storybook中插件概念叫做 addons

Q9:啥是addons?

  1. addons是添加的意思
  2. 在storybook则用来将其用作表示增加的功能

**
重要的事情说三遍:

在.storybook配置文件中,添加一个addons.js文件
**在.storybook配置文件中,添加一个addons.js文件
**在.storybook配置文件中,添加一个addons.js文件

addons.js文件是用来维护插件的配置文件

image.png


Q10:如何体验第一个addons?

image.png

官网上Addons目录就是官方推荐的插件列表,开始一个个尝试


Q11:什么是addons-knobs?

image.png

这个可就牛逼了,knobs主要是用来提供动态调整交互的能力,实践一下

step1:安装依赖

npm i @storybook/addon-knobs -D 

step2:添加配置项

找到.storybook/addons.js在其中添加一句话

import '@storybook/addon-knobs/register';

step3:修改已有的stories

按照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>);
});

上面一共做了几件事情:

  1. 在stories上添加了一个修饰器
  2. 在button上添加了对属性的包装,例如text()、boolean()等相当于对属性进行了绑定

直接看效果

step4:双向绑定效果

image.png

可以看到,我们订阅的两个数据类型已经显示在了Knobs中了,此时修改内容,组件内容也随之改变
image.png

step5:计算属性

上面例子中,第二个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>);
});

image.png

当修改age后,也会重新渲染
image.png

Knobs极其强大:

  1. 这个计算属性,很像vue中的computed属性,当数据改变即可以改变交互
  2. 同时也可以快速验证多种状态
  3. 也可以快速验证边界情况

Q12:什么是addons-actions

image.png

Actions用来获取用户点击信息,这个具体使用暂时没太想到场景,看了demo,目前是用来区分具体点击的位置

step1:安装依赖

npm i -D @storybook/addon-actions

step2:修改.storybook/addons.js

import '@storybook/addon-actions/register';

step3:绑定单个事件

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>
));

step4:绑定多个事件

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>
  ));

Q13:什么是addons-storysouce

image.png

这个理解起来还是相对简单的,目的就是在控制台中显示故事源

step1:安装依赖

npm i  @storybook/addon-storysource --dev

step2:创建webpack.config.js

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对外暴露,对其添加插件

step3: 查看源码效果

image.png


Q14:什么是addon-info?

image.png

这个info插件也十分强大,能够快速将组件中的配置信息,转换成markdown可阅读模式中组件信息

step1:安装依赖

npm i -D @storybook/addon-info

step2:添加插件

addon-info采用的是修饰器模式,可以全局添加,也可以局部添加

  1. 全局修饰就是在config中全局添加
addDecorator(withInfo); // Globally in your .storybook/config.js.
  1. 局部修饰的花,就在stories中添加
storiesOf('Component', module)
  .addDecorator(withInfo) // At your stories directly.
  .add(...);

step3:在组件代码中添加propTypes

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按钮

image.png

点击showinfo,就可见
image.png

  1. 自动将组件名称转为了markdown
  2. 自动将组件代码转为了markdown
  3. 自动将propsTypes转为了表格

不过表格中的信息还是有丢失,缺少默认值、描述

step4:在组件代码中添加默认值、描述

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
  1. 默认值通过defaultProps设置
  2. 注释通过在属性上添加注释来实现

image.png


Q15:什么是addon-viewport?

viewport大家都很熟悉,这个就是用来搞移动端的

step1:安装依赖

npm i --save-dev @storybook/addon-viewport

step2:注册插件

在.storyboos/config中注册

import '@storybook/addon-viewport/register';

step3:全局添加

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方法,很方便就可以

image.png

这样默认就有了一批viewpoint,这一块具体配置,就慢慢读文档就好了


Q16:什么是addons-shots

这部分是测试用例相关,短时间内啃不下来,待后面有时间再补充

image.png


Q17:什么是addon-backgrounds

image.png

这个主要是用来设置背景色,非必须,可有可无


Q18:什么是addon-a11y

image.png

这个相当与lint类的功能,在实际项目中还是很有用的

step1:安装依赖

npm i @storybook/addon-a11y --dev

step2:注册插件

修改addons

import '@storybook/addon-a11y/register';

step3:添加修饰器和参数

在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,这样的话,如果书写不符合规范的按钮,则会报错

image.png


Q19:什么是addon-console

image.png

前面很多插件,添加了很多信息到操作台上,所以很多人容易忽略掉chrome控制台内部的信息,所以这个插件很有意义,就是用来把控制台的信息映射到操作台上,具体配置也比较简单,按照文档操作即可


Q20:什么是addon-links

image.png

这个插件解决的问题是,story之间的相互跳转,用法也很简单,只需要参考文档就好


Q21:除了这些官方推荐插件外还有哪些?

目前比较好的插件列表如下

  1. Storyshots: 快照测试。
  2. Specs: 交互测试。
  3. Notes: 在 story 中添加备注。
  4. Info: 用于创建 css 框架手册。
  5. Readme: 将 markdown 导入为 story。
  6. actions: 显示事件的 event 对象。
  7. Intl: 添加 locales 面板,用于切换语言。
  8. State: 添加 state 面板,展示或更新 state,重绘视图。
  9. Props Combinations: 配置可能有的props,一次性绘出多个组件作对比。
  10. Knobs: 页面上变更 props 重绘视图。
  11. Links: 通过 linkTo 函数或 LinkTo 组件链接多个 story,支持点击跳转。
  12. Story-router: 装饰器,支持 storybook 使用 react-router 等路由组件。
  13. Backgrounds: 切换背景图或背景颜色。
  14. i18n tools: 切换文字对齐方式,左对齐或右对齐。
  15. Material-UI: 添加并切换自定义主题。
  16. Host: 装饰器,在页面上以盒模式展示组件。
  17. Chapters: 在同一个 story 中以章节形式展示多个组件。
  18. Options: 调整 storybook 页面外观,切换为全屏等。
  19. Console: 将浏览器控制台 console 信息输出到 storybook log 面板。
  20. JSX preview: 展示及拷贝 JSX 代码。
  21. Versions: 添加版本号,查看各版本的变化。
  22. Apollo: 添加 Apollo client,模拟 GraphQL 查询。
  23. Screenshot: 保存网页截图。
  24. Styles: storybook 预览界面添加自定义样式。
  25. Figma: 添加 Figma 设计面板。

Q22:总结有哪些?

  1. 最爽的点:如果storybook直接扔进项目里,可以共享一套webpack配置,且组件在业务中和story中能够同时同步,实时同步
  2. 解决的问题:
  3. storybook主要解决的是历史组件的展示
  4. 也可改变验收流程,先进行组件验收,再进行业务验收
  5. 同时也有利于新同学上手
  6. 设计划分:
  7. 第一阶段:先把storybook集成在一起,便于提升效能
  8. 第二阶段:提取成熟组件,到公共层面,便于其他项目使用
  9. 第三阶段:组件和story分离,变成两个项目维护,保持组件纯净,story作为文档解决方案

Q23:待办有哪些?

  1. 在showinfo中展示组件
  2. 自定义markdown中的表格样式
  3. 添加测试用例的开发(addons-shots)
  4. background添加颜色

Q24:参考有哪些?

  1. storybook使用指南
  2. storybook官网