利用StoryBook開發(fā)UI組件管理

前言

最近掐指一算發(fā)現(xiàn)本月還有篇技術(shù)博文沒寫~,雖然隨便拿一篇日常積累的文章非迹,或者把最近重構(gòu)的一些點(diǎn)拿出來講都可以糊弄過去蹦魔,但是我決定還是搞一點(diǎn)事情。娄帖。。

近期就有一個需求是這樣的昙楚,我手里進(jìn)行的一個重構(gòu)項(xiàng)目里近速,有一些組件我想抽離,給未來其它項(xiàng)目使用堪旧,然后我還需要開發(fā)兩個前端項(xiàng)目削葱,他們有一些共同的組件需求。純靜態(tài)頁面是不適合的淳梦,因?yàn)槲椰F(xiàn)在技術(shù)棧上了React + Typescipt析砸。我想做到即插即用。順便把props,state這些東西定義好爆袍,以后改一改就能上項(xiàng)目首繁。原本是立了個flag準(zhǔn)備自己搞一點(diǎn)東西出來,但是在微信群里螃宙,有人扔了一個鏈接出來storybook,

粗略一看好像就說我需要的蛮瞄。因此今天目標(biāo)就是搗鼓一份開發(fā)環(huán)境。

確定需求

Storybook是UI組件的開發(fā)環(huán)境谆扎。它允許您瀏覽組件庫挂捅,查看每個組件的不同狀態(tài),以及交互式開發(fā)和測試組件堂湖。

但是官方github的介紹非常貧瘠闲先,因此建議大家看Introduction to Storybook?來了解更多状土。

以及guide

我們明確一下我們的需求:

支持載入ant-design等UI庫

支持Typescript

支持redux

支持參數(shù)調(diào)試

正式開始

根據(jù)思路先創(chuàng)建一個支持ts的react項(xiàng)目

create-react-app my-app --scripts-version=react-scripts-ts

然后更新依賴包

yarn upgrade

然后按照 storybook

npm i -g @storybook/cli

cd my-app

getstorybook

之后直接運(yùn)行yarn run storybook就可以看到界面

然而事實(shí)并沒有那么簡單。因?yàn)橹С謙s的是項(xiàng)目本身伺糠,而storybook是獨(dú)立出來的蒙谓。因此你需要按照配置進(jìn)行各種修改。

首先在.storybook目錄下建立webpack.config.js

里面加載typescript-loader

// load the default config generator.

const genDefaultConfig = require('@storybook/react/dist/server/config/defaults/webpack.config.js');

module.exports = (baseConfig, env) => {

? ? const config = genDefaultConfig(baseConfig, env);

? ? // Extend it as you need.

? ? // For example, add typescript loader:

? ? config.module.rules.push({

? ? ? ? test: /\.(ts|tsx)$/,

? ? ? ? loader: require.resolve('awesome-typescript-loader')

? ? });

? ? config.resolve.extensions.push('.ts', '.tsx');

? ? return config;

};

然后對package.json進(jìn)行改造

{

? "name": "my-app",

? "version": "0.1.0",

? "private": true,

? "dependencies": {

? ? "react": "^16.0.0",

? ? "react-dom": "^16.0.0",

? ? "react-scripts-ts": "2.7.0"

? },

? "scripts": {

? ? "start": "react-scripts-ts start",

? ? "build": "react-scripts-ts build",

? ? "test": "react-scripts-ts test --env=jsdom",

? ? "eject": "react-scripts-ts eject",

? ? "storybook": "start-storybook -p 6006",

? ? "build-storybook": "build-storybook"

? },

? "devDependencies": {

? ? "@storybook/addon-actions": "^3.2.12",

? ? "@storybook/addon-links": "^3.2.12",

? ? "@storybook/react": "^3.2.12",

? ? "@types/jest": "^21.1.2",

? ? "@types/node": "^8.0.39",

? ? "@types/react": "^16.0.12",

? ? "@types/react-dom": "^16.0.1",

? ? "@types/storybook__react": "^3.0.5",

? ? "awesome-typescript-loader": "^3.2.3"

? }

}

之后最重要的一點(diǎn)是將根目錄下的stories目錄移到src目錄之下.

里面寫入一個index.tsx

import React from 'react';

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

import { action } from '@storybook/addon-actions';

import { linkTo } from '@storybook/addon-links';

import { Button, Welcome } from '@storybook/react/demo';

storiesOf('Welcome', module).add('to Storybook', () => );

storiesOf('Button', module)

? .add('with text', () => Hello Button)

? .add('with some emoji', () => ?? ?? ?? ??);

之后再運(yùn)行yarn run storybook就可以實(shí)現(xiàn)支持ts語法了训桶。

之后我們需要考慮我們的ui組件該如何組織累驮,通過大量翻看gitHub上的源碼,大體上兩種方式舵揭。 一種是在同名組件下直接添加.stories.ts的文件

./Button.jsx

./Button.stories.ts

一種是stories目錄下建立index.ts,引用其他組件內(nèi)容谤专。

我們采取后一種,這是為了方便管理午绳。而且直接在我們現(xiàn)有代碼基礎(chǔ)上就可以進(jìn)行置侍。

我們考慮做個demo,現(xiàn)在react?+?redux?的demo都是用todolist來完成拦焚。但是我們這里直接代入一個成熟的redux方案蜡坊。

首先我們看src目錄下現(xiàn)在的結(jié)構(gòu):

.

├── App.css

├── App.test.tsx

├── App.tsx

├── index.css

├── index.tsx

├── logo.svg

├── registerServiceWorker.ts

├── stories

│? └── index.jsx

├── webpack.config.1.js

└── webpack.config.js

很明顯 典型create-app的結(jié)構(gòu)。 然后我們直接看加入redux之后的項(xiàng)目結(jié)構(gòu):

── src

│? ├── actions

│? │? └── index.ts

│? ├── components

│? ├── constants

│? │? └── index.ts

│? ├── containers

│? │? └── App

│? ├── index.tsx

│? ├── logo.svg

│? ├── reducers

│? │? ├── index.ts

│? │? └── info.ts

│? ├── registerServiceWorker.ts

│? ├── store

│? │? └── index.ts

│? ├── stories

│? │? └── index.jsx

│? ├── typing.d.ts

│? ├── webpack.config.1.js

│? └── webpack.config.js

這里還需要注意的是你需要對tsconfig做一些整個赎败,而且為了支持Less,我對webpack也做了一些修改秕衙。

之后我們寫一段簡單的action to reducer

action:

import { INFO_LIST } from '../constants/index'

const saveList = (data: Object) => ({

? type: INFO_LIST,

? data: data,

})

export function infoListRemote () {

? const info = {

? ? data: {

? ? ? item: 'Hello LinShuiZhaoYing',

? ? ? cnItem: '你好, 臨水照影'

? ? }

? }

? return (dispatch: any) => {

? ? dispatch(saveList(info))

? ? return info

? }

}

reducer:

import { INFO_LIST } from '../constants';

const initialState = {

? info:''

}

const info = (state = initialState, action: any) => {

? // console.log(action)

? switch (action.type) {

? ? case INFO_LIST:

? ? ? return {

? ? ? ? ...state,

? ? ? ? info:action.data.data

? ? ? }

? ? default:

? ? ? return state

? }

}

export default info;

App:

index.tsx:

import * as React from 'react';

import { Button, Icon } from 'antd';

import { connect } from 'react-redux';

import { infoListRemote } from '../../actions/index';

import './index.css';

class App extends React.Component {

? constructor (props: any) {

? ? super(props)

? ? this.state = {

? ? ? infoList: '',

? ? }

? }

? componentWillMount() {

? }

? componentDidMount() {

? ? // console.log(this.props)

? }

? componentWillReceiveProps(nextProps: any) {

? ? // console.log(nextProps)

? ? if (nextProps.info) {

? ? ? this.setState({

? ? ? ? infoList: nextProps.info.item

? ? ? })

? ? }

? }

? getInfo = () => {

? ? const { dispatch } = this.props;

? ? dispatch(infoListRemote())

? }

? render() {

? ? return (

? ? ?

? ? ? ?

{this.state.infoList}

? ? ? ? Click Me

? ? ? ?


? ? );

? }

}

const mapStateToProps = (state: any) => ({

? info: state.info.info,

})

let AppWrapper = App

AppWrapper = connect(mapStateToProps)(App);

export default AppWrapper;

然后來看下效果圖:

可以看到數(shù)據(jù)已經(jīng)傳遞成功。

接下來就是寫stories,因?yàn)槲覀冇昧藃edux?所以我們需要用addDecorator來包裝我們的組件

import React from 'react';

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

import { action } from '@storybook/addon-actions';

import { linkTo } from '@storybook/addon-links';

import { Provider } from 'react-redux';

import { Button, Welcome } from '@storybook/react/demo';

import { createBrowserHistory } from 'history';

import configureStore from '../store';

import? AppWrapper? from '../containers/App'

const store = configureStore(createBrowserHistory);

storiesOf('AppWrapper', module)

.addDecorator(story => {story()})

.add('empty App', () => );

storiesOf('Button', module)

? .add('with text', () => Hello Button)

? .add('with some emoji', () => ?? ?? ?? ??);

最后一步就是加入?yún)?shù)調(diào)試螟够,這里我們需要加在一些addon?來增強(qiáng)體驗(yàn)灾梦。

我們可以在More addons這里看到addons列表峡钓。根據(jù)需求來增加妓笙。然后到對應(yīng)的git網(wǎng)站上去看用法加入即可。

首先加入addon-notes能岩,它用來寫組件描述寞宫,而且經(jīng)過測試,它是可以加入Html代碼拉鹃,因此可以先自己定義統(tǒng)一格式辈赋,然后加入內(nèi)容。

還可以自定義一些信息膏燕,比如使用參數(shù)钥屈,暴露出來的接口等等。加載Info Addon?就可以實(shí)現(xiàn)坝辫。

接下來的核心就是增加參數(shù)調(diào)試功能

這里給段示例代碼:

storiesOf('AppWrapper', module)

.addDecorator(withKnobs)

.addDecorator(story => {story()})

.add('knobs App', () =>)

.add('with all knobs', () => {

? const name = text('Name', 'Tom Cary');

? const dob = date('DOB', new Date('January 20 1887'));

? const bold = boolean('Bold', false);

? const selectedColor = color('Color', 'black');

? const favoriteNumber = number('Favorite Number', 42);

? const comfortTemp = number('Comfort Temp', 72, { range: true, min: 60, max: 90, step: 1 });

? const passions = array('Passions', ['Fishing', 'Skiing']);

? const customStyle = object('Style', {

? ? fontFamily: 'Arial',

? ? padding: 20,

? });

? const style = {

? ? ...customStyle,

? ? fontWeight: bold ? 800 : 400,

? ? favoriteNumber,

? ? color: selectedColor,

? };

? return (

? ?

? ? ? I like:

    {passions.map((p, i) =>
  • {p}
  • )}

? ? ?

My favorite number is {favoriteNumber}.

? ? ?

My most comfortable room temperature is {comfortTemp} degrees Fahrenheit.


? );

});

需要在開發(fā)的時候把動態(tài)傳遞的參數(shù)給設(shè)定好篷就。這樣才能即時顯示。效果圖如下:

然后調(diào)用addon options

在config.js里加入

setOptions({

? downPanelInRight: true,

})

把橫軸的顯示板變成豎著的近忙。

還有非常多有意思的addons竭业,比如Info的提升版本readme.以及一鍵換背景的backgrounds智润。還有現(xiàn)成的Material-UI。還有直接顯示你Jsx源碼的Storybook-addon-jsx.以及控制版本顯示的storybook-addon-versions未辆,讓你直接對比多個版本的區(qū)別窟绷。一鍵生成所有截圖的Storybook Chrome Screenshot Addon。這些社區(qū)的addons都非常實(shí)用咐柜。感興趣可以自己增加兼蜈。

結(jié)尾

最后我們已經(jīng)完美完成了之前的需求,還有了一些意外的驚喜拙友。

根據(jù)我這份環(huán)境配置饭尝,可以自行進(jìn)行擴(kuò)展,雖然我現(xiàn)在基于React開發(fā)献宫。但是StoryBook也是支持Vue的钥平。

最后照慣例放出該工程的github地址

參考資料

前端組件化開發(fā)方案及其在React Native中的運(yùn)用

react-template

playbook_ts_sample

d3-storybook-test

react-storybook-demo

Introduction to Storybook

StoryBook meets redux

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市姊途,隨后出現(xiàn)的幾起案子涉瘾,更是在濱河造成了極大的恐慌,老刑警劉巖捷兰,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件立叛,死亡現(xiàn)場離奇詭異,居然都是意外死亡贡茅,警方通過查閱死者的電腦和手機(jī)秘蛇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來顶考,“玉大人赁还,你說我怎么就攤上這事【匝兀” “怎么了艘策?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長渊季。 經(jīng)常有香客問我朋蔫,道長,這世上最難降的妖魔是什么却汉? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任驯妄,我火速辦了婚禮,結(jié)果婚禮上合砂,老公的妹妹穿的比我還像新娘青扔。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布赎懦。 她就那樣靜靜地躺著雀鹃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪励两。 梳的紋絲不亂的頭發(fā)上黎茎,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機(jī)與錄音当悔,去河邊找鬼傅瞻。 笑死,一個胖子當(dāng)著我的面吹牛盲憎,可吹牛的內(nèi)容都是我干的嗅骄。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼饼疙,長吁一口氣:“原來是場噩夢啊……” “哼溺森!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起窑眯,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤屏积,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后磅甩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體炊林,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年卷要,在試婚紗的時候發(fā)現(xiàn)自己被綠了渣聚。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡僧叉,死狀恐怖奕枝,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情彪标,我是刑警寧澤倍权,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站捞烟,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏当船。R本人自食惡果不足惜题画,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望德频。 院中可真熱鬧苍息,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽味赃。三九已至煤禽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間座咆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留距辆,地道東北人。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓暮刃,卻偏偏與公主長得像跨算,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子椭懊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容

  • core package 概要:Core是所有其他包的基礎(chǔ)包.它提供了大部分功能包括metadata诸蚕,templa...
    LOVE小狼閱讀 2,592評論 0 3
  • 前言 本文 有配套視頻,可以酌情觀看氧猬。 文中內(nèi)容因各人理解不同挫望,可能會有所偏差,歡迎朋友們聯(lián)系我討論狂窑。 文中所有內(nèi)...
    珍此良辰閱讀 11,909評論 23 111
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理媳板,服務(wù)發(fā)現(xiàn),斷路器泉哈,智...
    卡卡羅2017閱讀 134,711評論 18 139
  • 都說青春是跌跌撞撞的旅行蛉幸,有著后知后覺的美麗,當(dāng)時我們并沒有感覺這樣的時光有多么的了不起丛晦,所以在分別時才會說奕纫,后會...
    曉村閱讀 218評論 0 0
  • 函數(shù) 全局變量 獲取全局變量python獲取全局變量直接獲取 修改全局變量python不允許直接修改全局變量如果要...
    AndroidCat閱讀 248評論 0 0