在使用 React
开发组件时常常会有一些苦恼,好比当一个组件的复杂度逐步上升时,它所拥有的状态不容易追溯;当须要查看某种状态的组件时,可能须要手动更改组件的属性或是更改接口返回的数据(数据驱动的组件)等等。因而我就去了解并学习 Storybook
,而后组织了一次分享会,这也是咱们团队的第一次技术分享。javascript
关于 Storybook
,我在一两年前有接触并尝试使用,当时对组件化开发的理解可能有限,只是为了用而用,并未感觉到它的实用之处;加上通过屡次的迭代,Storybook
已经到了 6.0 版本,能够说是更易用、更优雅了。java
上图是分享会 ppt 的封面,感兴趣的同窗能够私信我,接下来进入正题。react
这篇文章主要给你们分享一下几点:webpack
Next.js
中使用 Storybook由于包含了实践,可能有如下几点要求,不过不用担忧,只要你能看懂就行:git
Next.js
的,这个我在上一篇文章中有讲到如何搭建 Next.js
项目,能够点击这里把我搭建的脚手架克隆到本地,以即可以跟着动手。Typescript
、styled-component
。storybook是一个开源工具,为React、Vue、Angular等框架提供一个沙箱环境,可独立地开发UI组件;它更有组织和高效地构建出使人惊叹的 UIs。github
独立构建组件web
建立组件时不须要竖起屏幕,不须要处理数据,也不须要构建业务逻辑。 shell
模拟难以达到的用例json
在一个应用中渲染关键状态是不容易的 bash
用例做为一个故事
将用例保存为 Javascript 中的故事,以便在开发、测试和QA期间从新访问。
使用插件减小工做流程
使用插件能够更快地构建UI,组件文档化,并简化工做流程。
确保一致的用户体验
每当写一个故事,就获得一种状态的视觉效果。快速地浏览故事,检查最贱 UI 的正确性。
自动回归测试代码
使用官方插件 Storyshots 启动代码快照。
单元测试组件
对组件进行单元测试确保组件能正常工做。
基于每次提交像素级地捕获UI变化
用视觉测试工具查明UI的变化。
在项目中查找任何组件
Storybook 可搜索编写的任何组件,为你的UI组件提供真实信息的单一来源。
开发过程当中得到及时反馈
经过 Storybook 部署到云端,与团队协做实现UI。
跨端跨应用共享组件
每一个故事都是一个用例,团队成员能够找到它并决定是否重用。
生成文档
编写 markdown/MDX,为组件库和设计系统生成可定制化的文档。
下面我会经过一个示例想你们展现 Storybook 是如何工做的,期间也能看到我是如何使用结合 Typescript、styled-components以及个人编码习惯。
假设你已经克隆了这个仓库,首先在项目中安装 storybook
:
# 安装 storybook
yarn add storybook
# 初始化 storybook 项目,会根据项目类型自动地进行配置
npx sb init
# 启动 storybook 服务
yarn storybook
复制代码
以上几步没问题以后,如今就能够在 http://localhost:6006/ 访问 Storybook 提供的 UIs 了:
它默认提供了几个例子,如 Button
、Header
等,例子代码在 src/pages/stories
中:
后缀名为 stories.tsx
的文件就是一个故事,它定义了咱们想要定义的组件的表现状态;你们可能不是很理解一个故事是什么,后面你们看了示例以后就会理解了,我先打个比方,一我的就比如一个故事,当他有不一样的心情时,就会表现出不一样的表情,同一时间只能看到它的一种表情,但我如今用照片记录他所表现的一个个不一样的表情,这有利于我去分析这我的的性格;Storybook 就像是照相机,能够记录组件的不一样状态,便于咱们去追溯。
ProductOptimCard
组件接下来设计并实现 ProductOptimCard
组件,这个组件是数据驱动的,也就是内容是根据数据的变化而变化的,为了方便,我只定义了标题、是否必作、是否完成这三个属性,它们的变化会展现不一样状态下的视图,默认的效果以下:
如下是组件实现代码:
// src/components/towone/ProductOptim/ProductOptimCard/index.tsx
import React from 'react';
import styled from 'styled-components';
interface IProductOptimCardProps {
data: {
isMustDo: boolean;
isFinish: boolean;
title: string;
};
}
const Container = styled.div` width: 452px; height: 276px; background: #fefeff; border: 1px solid #edf0fa; box-shadow: 0px 4px 14px 0px rgba(0, 10, 71, 0.07); `;
const Content = styled.div` height: 225px; background: #fff; padding-top: 21px; padding-left: 20px; position: relative; `;
const Footer = styled.div` height: 50px; background: #f7f8fa; display: flex; align-items: center; justify-content: space-between; padding-right: 10px; padding-left: 20px; `;
const Title = styled.div` font-size: 16px; font-weight: bold; color: #333; margin-bottom: 14px; `;
const Badge = styled.div<{ isMustDo: boolean }>` width: 37px; height: 21px; background: ${({ isMustDo }) => (isMustDo ? '#0af' : '#999999')}; font-weight: bold; color: #fefeff; font-size: 12px; border-radius: 11px 2px 11px 11px; position: absolute; top: 10px; right: 10px; display: flex; align-items: center; justify-content: center; `;
const Text = styled.div` font-size: 14px; color: #666666; margin-bottom: 14px; `;
const MoreText = styled.a` font-size: 14px; color: #333333; `;
const FinishButton = styled.div<{ isFinish: boolean }>` width: 60px; height: 28px; background: ${({ isFinish }) => (isFinish ? '#999' : '#046eff')}; color: #fefeff; font-size: 12px; display: flex; justify-content: center; align-items: center; cursor: pointer; `;
const ProductOptimCard: React.FC<IProductOptimCardProps> = ({ data }) => {
const { isMustDo, isFinish, title } = data;
return (
<Container> <Content> <Title>{title}</Title> <Text>一、尺寸:800 x 800px</Text> <Text>二、卖点提炼文字展现(针对同款多、标品类目)</Text> <Text>三、产品占图片三分之二</Text> <Text>四、参考五家淘宝以及阿里优秀相似款主图(按成交金额排序)</Text> <Badge isMustDo={isMustDo}>必作</Badge> </Content> <Footer> <MoreText>更多教程</MoreText> <FinishButton isFinish={isFinish}>完成了</FinishButton> </Footer> </Container>
);
};
export default ProductOptimCard;
复制代码
而后在首页引入它:
// src/pages/index.tsx
//...
export default function Home() {
return (
<Conotainer> <ProductOptimCard data={{ isMustDo: false, isFinish: false, title: '单品标题优化' }} /> </Conotainer>
);
}
复制代码
执行 yarn dev
启动项目,而后打开 http://localhost:3000/ 查看:
图中红框中的组件就是 ProductOptimCard
的默认样式,组件自己已经实现了不一样状态:如必作、没必要作、已完成、未完成;但我想查看某个状态,将不得不更改 src/pages/index.tsx
中传给 ProductOptimCard
的 data
属性,而这个一般是根据接口返回的数据,要去该代码就显得麻烦不优雅了,不过不用担忧,咱们如今有 Storybook了
,请往下看。
在同级目录新建一个 ProductOptimCard.stories.tsx
文件,为 ProductOptimCard
编写故事,代码以下:
import React, { ComponentProps } from 'react';
import { Story, Meta } from '@storybook/react/types-6-0';
import ProductOptimCard from './';
export default {
title: 'TWOONE/ProductOptim/ProductOptimCard',
component: ProductOptimCard,
} as Meta;
const Template: Story<ComponentProps<typeof ProductOptimCard>> = (args) => (
<ProductOptimCard {...args} />
);
export const DefaultCard = Template.bind({});
DefaultCard.args = {
data: {
isMustDo: false,
isFinish: false,
title: '单品标题优化',
},
};
export const MustDoCard = Template.bind({});
MustDoCard.args = {
data: {
isMustDo: true,
isFinish: false,
title: '单品标题优化',
},
};
export const FinishCard = Template.bind({});
FinishCard.args = {
data: {
isMustDo: false,
isFinish: true,
title: '单品标题优化',
},
};
export const UnFinishCard = Template.bind({});
UnFinishCard.args = {
data: {
isMustDo: false,
isFinish: false,
title: '单品标题优化',
},
};
复制代码
咱们引入了 ProductOptimCard
,并为其编写了四种状态,分别是 DefaultCard
、MustDoCard
、FinishCard
、UnFinishCard
,传入不一样的data
,天然会表现出不一样的状态。而后打开 http://localhost:6006/:
红框是咱们为 ProductOptimCard
编写的故事,点击不一样状态以查看 UI 效果:
能够看到,咱们很容易就知道并查看这个组件的不一样状态,是否是有点跃跃欲试了呢,点击 Docs
可查看文档,其它操做就你们课后本身尝试:
项目中若有使用 alias 为文件夹设置别名,导入形式是这样
import { Box } from '@/styles/common';
,这一般是在咱们的tsconfig.json
中已经配置了,可是 storybook 不认识,也须要配置一下,它支持咱们自定义 webpack 配置,打开.storybook/main.js
,添加以下代码:
// .storybook/main.js
const path = require('path');
module.exports = {
// ...
webpackFinal: async (config, { configType }) => {
config.resolve.alias['@'] = path.resolve(__dirname, '../src');
return config;
},
};
复制代码
到这里咱们已经经过一个示例来了解如何使用 Storybook 了,接下来会简单聊聊个人一些编码心得。
从数据获取的层面看,我将组件分为容器组件和内容组件:
**容器组件:**从接口获取数据。
**内容组件:**接收 props 数据、可编写 story 组件驱动开发。
一般一个组件引入的三方库在最顶部,其次是自定义组件,因此我这里的顺序值得是组件中变量定义的位置,如下是我所习惯的定义顺序(从上往下),每一个区域隔一行:
三方库
自定义组件
图片常量
Typescript 接口
样式组件
组件区
一个最小化的示例代码:
import React from 'react';
import styled from 'styled-components';
import { MySelfComp } from '@/components';
import ICON_LOGO from '@/assets/images/icon.logo.png';
interface IProps {}
const Container = styled.div``;
const DemoComp: React.FC<IProps> = () => {
return <Container></Container>
}
export default DemoComp;
复制代码
目前带你们认识了 Storybook,而且介绍了如何使用,固然这只是基础用法,在项目中你们可能也会遇到不一样的场景,如遇到问题能够查看官方文档,仍是写的挺详细的;原本想将测试流程也写进去,不过感受会有不小的篇幅,之后能够另起一篇文章,我能够先简单介绍一个我以为比较理想的组件开发流程:组件设计并编写 -> 编写 story -> Jest 测试 -> Enzyme 测试,对于后面两个测试库,确实进一步地提高了组件的健壮性,可是会增长不少的工做量,通常的小公司确实用不着,感兴趣的能够课下自行研究。