第一章:React:从0到1组件库构建——storybook篇

一、项目创建

本文章的项目搭建是基于create-react-app进行的搭建。

  • 创建项目create-react-app <项目名称> --typescript
  • 安装storybookUI 组件的开发环境 :npx -p @storybook/cli sb init

二、storybook简介及使用

Storybook是 UI 组件的开发环境,它允许开发者浏览组件库,查看每个组件的不同状态,以及交互地开发和测试组件。

Storybook在 app 之外运行,这允许开发者独立地开发 UI 组件,这可以提高组件的重用性、可测试性和开发速度。所以可以快速构建,而不必担心应用程序特定的依赖关系

2-1、安装

  npx -p @storybook/cli sb init

安装成功以后我们可以通过git diff来查看一下我们的文件与之前的差异

  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "storybook": "start-storybook -p 9009 -s public",
    "build-storybook": "build-storybook -s public"
  },

这时候我们发现我们的scripts中增加了2条命令storybook、build-storybook其中第一条命令的作用是本地调试的命令,第二条命令是将在线的版本打包成一个静态页面共我们进行访问

2-2、基本使用

当我们安装了storybook后我们的src目录下会新增一个stories文件,这个文件用来存放我们需要调试的组件,以及本地生成文档的组件
1、export default导出当前组件的整个目录,titlemenu的名称,component为当前组件
2、一个文件中可以导出多个组件的类型,例如 Text. Emoji
3、action是一个动作,熟悉redux的同学应该不需要我多介绍
4、简单的理解 export default导出去的是一级目录,export到出去的是二级目录

import React from 'react';
import { action } from '@storybook/addon-actions';
import { Button } from '@storybook/react/demo';

// 导出组件 其中title为menu的名称 component为组件
export default {
  title: 'Button',
  component: Button,
};

export const Text = () => ;

export const Emoji = () => (
  
);

2-3、storybook支持Typescript

storybook5版本里,只有一个配置文件,.storybook`文件夹下的main.js。只需要修改里面的stories的后缀名就ok。

  module.exports = {
  stories: ['../src/**/*.stories.tsx'],
  addons: [
    '@storybook/preset-create-react-app',
    '@storybook/addon-actions',
    '@storybook/addon-links',
  ]
};

在storybook4中需要在.storybook中创建webpack.config.js文件进行配置

module.exports = ({ config }) => {
  config.module.rules.push({
    test: /\.tsx?$/,
    use: [
      {
        loader: require.resolve("babel-loader"),
        options: {
          presets: [require.resolve("babel-preset-react-app")]
        }
      }, 
    ]
  });

  config.resolve.extensions.push(".ts", ".tsx");

  return config;
}

2-4、storybok用例编写

storybook的编写写方式有2种,一种是component Story Format(CSF) 另外一种是classic storiesOf 前者是通过函数的方式进行编写,但是不适用于中文,因此我们在这里使用后者
基本使用请参考2-2、基本使用

import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import Button from '../components/Button';

storiesOf('Button Component', module)
  .add('测试Button', () => )
  .add('with some emoji', () => (
    
  ));
第一章:React:从0到1组件库构建——storybook篇_第1张图片
1.png

2-5、storybook下的常用文件说明

  • addons.js :此文件为storybook的插件注册文件,类似于vue中的vue.use类似 。
  • config.js : storybook的全局配置文件
  • webpack.config.js : storybook的webpack配置文件,不过在storybook5中可以在main.js中添加webpackFinal进行webpack的额外配置
  • main.js :此文件是storybook5中的配置文件,相当于将addonswebpack.config.js进行了结合

2-6、storybook常用插件介绍

  • @storybook/addon-actions捕获组件事件,将结果打印出来,用来记录事件日志
  • @storybook/addon-links:页面跳转
  • @storybook/addon-info:用于 story 信息展示,包含当前展示的组件的代码(实时更新)
  • @storybook/addon-knobs:提供一套可以用来动态编辑展示组件属性的控件,这样使用者可以在不改动代码的情况下学习组件使用
  • @storybook/addon-storysource:能够看到当前操作的组件对应的 story 代码
  • storybook-readme:addon-info 是官方的插件,可以用作文档展示也可以使用markdown, 但是在视觉上比较丑,修改视觉有比较复杂,所以找到了这个插件 storybook-readme ,他可以更加美观的展示代码,并且使用 markdown 的文件

2-6-1、插件的全局配置和局部配置

  • 全局安装:.storybook/config.js 中通过 addDecorator(addon) 安装
  • 局部安装:在某个*.stories.jsstoriesOf(‘XXX’, module).addDecorator(addon)安装
  • 部分插件还需要进行参数的配置 这就会用到addParameters() 方法

装饰器为例:

// src/stories/index.js
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import Button from '../components/Button';

const styles = React.CSSProperties = {
  textAlign:'center'
}
// 创建一个装饰器,用于组件的居中显示
const CenterDecorator = (storyFn:any) => 
{storyFn}
storiesOf('Button', module) .addDecorator(CenterDecorator) .add('with text', () => ) ));

如果需要将装饰器作用于全局,我们需要在.storybook文件夹下创建一个config.tsx文件。用于做storybook的全局配置

import { configure, addDecorator } from "@storybook/react"
import React from "react";

const styles = React.CSSProperties = {
  textAlign:'center'
}
// 创建一个装饰器,用于组件的居中显示
const CenterDecorator = (storyFn:any) => 
{storyFn}
addDecorator(CenterDecorator) // 获取src下面所有的stories用例,所有的用例应用次配置项 const loaderFn = () => { const allExports = []; const req = require.context('../src', true, /\.stories\.tsx$/); req.keys().forEach(fname => allExports.push(req(fname))); return allExports; }; configure(loaderFn, module);

2-6-2、@storybook/addon-actions

捕获组件事件,将结果打印出来,用来记录事件日志
安装:npm install --save-dev @storybook/addon-actions
注册:import '@storybook/addon-actions/register';
使用: 如下

import * as React from 'react';
import {storiesOf} from '@storybook/react';
import {action} from '@storybook/addon-actions';
import {LoadingButton} from 'xxx-ui-components';

storiesOf('Button', module)
  .add('LoadingButton', () => {
    return (
      
        Hello Button
      
    );
  });
第一章:React:从0到1组件库构建——storybook篇_第2张图片
2.png

2-6-3、@storybook/addon-knobs

提供一套可以用来动态编辑展示组件属性的控件,这样使用者可以在不改动代码的情况下学习组件使用
安装:npm install --save-dev @storybook/addon-knobs
注册:在addons中 import '@storybook/addon-knobs/register';
使用:如下

import * as React from 'react';
import {storiesOf} from '@storybook/react';
import {withKnobs, object, array, text} from '@storybook/addon-knobs';
import {LoadingButton} from 'xxx-ui-components';

storiesOf('Button', module)
  .addDecorator(withKnobs)
  .add('LoadingButton', () => {
    return (
      
        Hello Button
      
    );
  });

2-6-4、@storybook/addon-storysource

能够看到当前操作的组件对应的 story 代码
安装:npm install --save-dev @storybook/addon-storysource
注册:import '@storybook/addon-storysource/register';

// webpack.config.js
module.exports = ({ config }) => {
    config.module.rules.push({
        test: /\.tsx?$/,
        loaders: [require.resolve('@storybook/addon-storysource/loader')],
        enforce: 'pre',
    });
  
    config.resolve.extensions.push(".ts", ".tsx");
  
    return config;
}
第一章:React:从0到1组件库构建——storybook篇_第3张图片
3.png

2-6-5、storybook-readme

addon-info 是官方的插件,可以用作文档展示也可以使用markdown, 但是在视觉上比较丑,修改视觉有比较复杂,所以找到了这个插件 storybook-readme ,他可以更加美观的展示代码,并且使用 markdown 的文件
安装: npm install --save-dev storybook-readme
注册: import 'storybook-readme/register';
使用: 如下

import { configure, addParameters, addDecorator } from '@storybook/react';
import {withInfo} from '@storybook/addon-info';
import { addReadme, configureReadme } from 'storybook-readme';
import {AreaWrapper, StoryWrapper, DocWrapper, HeaderWrapper, FooterWrapper} from './layout';

// 展示UI上的配置
configureReadme({
  StoryPreview: StoryWrapper,
  DocPreview: DocWrapper,
  HeaderPreview: HeaderWrapper,
  FooterPreview: FooterWrapper
});

// 配置代码主题
addParameters({
  readme: {
    codeTheme: 'hopscotch',
  }
});

// addReadme 要在 withInfo 下边
addDecorator(withInfo);
addDecorator(addReadme);


// 获取src下面所有的stories用例,所有的用例应用次配置项
const loaderFn = () => {
  const allExports = [];
  const req = require.context('../src', true, /\.stories\.tsx$/);
  req.keys().forEach(fname => allExports.push(req(fname)));
  return allExports;
};

configure(loaderFn, module);

2-6-6、@storybook/addon-info

addon-info的主要作用用来展示组件的源码以及组件的配置项
安装:cnpm install @storybook/addon-info @types/storybook__addon-info -D
注册:import '@storybook/addon-info/register'
安装完以后可以通过addDecorator来进行使用这个插件,这时候页面的右上角就会有对应的功能

import React from 'react';
import { action } from '@storybook/addon-actions';
import Button from './index'
import { storiesOf } from '@storybook/react';
// 引入 withInfo
import { withInfo } from '@storybook/addon-info'

export default {
  title: 'Button',
  component: Button,
};

const Text = () => ;


storiesOf("Button Component", module)
    // 使用装饰器
  .addDecorator(withInfo)
  .add('测试Button', () => Text() )

storybook/addon-info常用配置项(增加)

通用配置:通过装饰器addParameters进行配置,
单独组件配置:在add方法中的第三个参数中进行配置

storiesOf("Button Component", module)
  .addParameters({
    info:{
      text:`这是一个通用配置,作用于下面所有组件`,
      inline: true, // 是否打开源代码
    }
  })
  .addDecorator(withInfo)
  .add('测试Button', () => Text(), {
    info:{
      text:`这是当前组件的配置,只作用于当前组件`,
      inline: true,
    }
  })

2-6-7、react-docgen-typescript-loader

对于组件文档的编写React提供了react-docgen供我们进行使用,我们可以通过react-docgen的规范来进行文档的自动生成,规范遵循JSDOC规范,因此下面介绍了JSDOC的基本介绍

安装:cnpm install -D react-docgen-typescript-loader

注意:
1、在使用此插件的时候我们不能通过React.component进行组件的编写,必须通过将Component导出的形式进行编写组件

2、在导出组件的时候我们不仅仅需要通过export default 进行组件的导出,还需要export将组件进行导出

3、在.storybook/main.js中添加webpackFinal配置项,用于webpack的配置


 //正确规范 
import React, { Component } from 'react';

export class MyComponent extends Component {}

export default MyComponent

此loader需要结合webpack进行使用,后面我们会介绍到它的场景,此章节我们只做汇总

shouldExtractLiteralValuesFromEnum:将枚举和联合类型转换成字符串形式
propFilter:过滤属性(只展示自己定义的属性)

module.exports = ({ config }) => {
  config.module.rules.push({
    test: /\.tsx?$/,
    use: [
      {
        loader: require.resolve("react-docgen-typescript-loader"),
        options: {
          shouldExtractLiteralValuesFromEnum: true,
          propFilter: (prop) => {
            if (prop.parent) {
              return !prop.parent.fileName.includes("node_modules");
            }
            return true;
          },
        },
      },
    ],
  });
  config.resolve.extensions.push(".ts", ".tsx");
  return config;
};

没有用loader之前

枚举和联合类型无法转换,而且还多了很多不属于当前组件的属性

第一章:React:从0到1组件库构建——storybook篇_第4张图片
4.png

用了loader之后

第一章:React:从0到1组件库构建——storybook篇_第5张图片
5.png

三、JSDoc

3-1、什么是JSDoc

JSDoc 是一个根据 JavaScript 文件中注释信息,生成 JavaScript 应用程序或模块的API文档的工具。你可以使用 JSDoc 标记如:命名空间方法方法参数等。从而使开发者能够轻易地阅读代码,掌握代码定义的类和其属性和方法,从而降低维护成本,和提高开发效率

3-2、JSDoc注视规则及使用

  • JSDoc注释一般应该放置在方法或函数声明之前,它必须以/ **开始,以便由JSDoc解析器识别
  • @constructor明确一个函数是某个类的构造函数
  • @param我们通常会使用@param来表示函数、类的方法的参数,参数标签可表示一个参数的参数名参数类型参数描述的注释
  • @return表示一个函数的返回值
  • @example通常用于表示示例代码,通常示例的代码会另起一行编写
  • @alias可以给一个变量或者函数指定一个别名,代码提示时会提示该别名
  • @description可以在代码提示时显示被描述变量或者函数的描述信息
  • @extends用于标识继承于某个类型。
  • @property可以描述一个对象的属性
/**
*@constructor
*@param {string} title - 书本的标题
*@param {string} author - 书本的作者
*/

functionBook(title, author){

    this.title=title;

    this.author=author;

}

Book.prototype={
/**
* 获取书本的标题
* @returns {string} 返回当前的书本名称
*/
  getTitle:function(){
    returnthis.title;   
    },
}


(function(){  
/**  
 * @alias foo  
 */  
function _fooBar(){}  
window.foo = _fooBar;  
})();  
foo();

/**  
 * @description 这是一个构造函数  
 * @constructor  
 */  
function Animal(name,weight){  
    this.name = name;  
    this.weight = weight;  
}


/**  
 * @description 这是一个构造函数  
 * @example   
 * var animal = new Animal('恐龙',1000);  
 * @constructor  
 */  
function Animal(name,weight){  
    this.name = name;  
    this.weight = weight;  
}



/**  
 * @extends {Animal}  
 */  
function Dog(){  
    this.say = function(){  
        console.log(this.name+":wang wang wang ...");  
    }  
}  
Dog.prototype = new Animal();


/**  
 * @property {IDString} id id元素  
 * @property {ClassString} classNames class样式  
 */  
var htmlOptions = {  
    id:null,  
    classNames:null  
}  

你可能感兴趣的:(第一章:React:从0到1组件库构建——storybook篇)