一、项目创建
本文章的项目搭建是基于
create-react-app
进行的搭建。
- 创建项目
create-react-app <项目名称> --typescript
- 安装
storybook
UI 组件的开发环境 :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
导出当前组件的整个目录,title
为menu
的名称,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', () => (
));
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中的配置文件,相当于将addons
与webpack.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.js
中storiesOf(‘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
);
});
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;
}
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之前
枚举和联合类型无法转换,而且还多了很多不属于当前组件的属性
用了loader之后
三、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
}