手把手教你快速搭建专属的storybook

栏目: CSS · 发布时间: 5年前

内容简介:Storybook是一个辅助UI控件开发的工具。通过story创建独立的控件,让每个控件开发都有一个独立的开发调试环境。 Storybook的运行不依赖于项目,开发人员不用担心由于开发环境、依赖问题导致不能开发控件。Storybook支持的框架覆盖主流的框架(React、Vue、Angular)。 由于使用React作为技术栈,本文将介绍使用react的项目如何配置Storybook环境。至此,根据
手把手教你快速搭建专属的storybook

什么是Storybook

Storybook是一个辅助UI控件开发的工具。通过story创建独立的控件,让每个控件开发都有一个独立的开发调试环境。 Storybook的运行不依赖于项目,开发人员不用担心由于开发环境、依赖问题导致不能开发控件。

Storybook支持的框架覆盖主流的框架(React、Vue、Angular)。 由于使用React作为技术栈,本文将介绍使用react的项目如何配置Storybook环境。

安装

  1. 全局安装Storybook
npm i -g storybook
复制代码
  1. 执行以下命令安装@storybook/react
npm i --save-dev @storybook/react
复制代码
  1. 在package.json文件中
{
  "scripts": {
    "storybook": "start-storybook -p 9001 -c .storybook"
  }
}
复制代码
  1. 在工程根目录创建 .storybook 目录

  2. .storybook 目录下创建 config.js 文件

import { configure } from '@storybook/react';
import 'index.scss';

function loadStories() {
  require('./stories/userStory');
}

configure(loadStories, module);
复制代码
  1. 创建story

    虽然官方推荐在项目根目录下创建 stories 目录,但我比较喜欢在 .storybook 目录下创建一个 stories 目录。然后根据不同的业务模块创建不同的 stories 目录。 比如有个user模块,那么我会创建一个 stories/userStory 目录。

// stories/userStory/index.jsx
import React from 'react';
import { storiesOf } from '@storybook/react';
import BasicInfo from 'pages/clientDetail/components/BasicInfo';

storiesOf('用户信息', module)
  .add('基础信息', () => <BasicInfo />);
复制代码

至此,根据 Storybook React Guide ,我们配置了一个简单的storybook环境。 其实这个环境已经可以用了,当然,如果还需要一些额外的功能,比如支持 lessscss 等,就需要自定义webpack配置。

storybook 配置

1. 自定义webpack配置

storybook基础webpack配置 只包含以下几项:

  • babel
    • ES2016+ Support
    • .babelrc support
  • Webpack
    • CSS Support
    • Image and Static File Support
    • JSON Loader

有时候默认的webpack配置不能满足我们的项目,因此需要对webpack配置进行扩展。

通常我使用的是 Full Control Mode 对webpack配置进行修改。首先在 .storybook 目录下增加 webpack.config.js 文件

const path = require('path');

module.exports = (storybookBaseConfig, configType) => {
  // 现在应该很多项目会使用`less`或者`scss`等css预处理技术。
  // 这里使用了postcss-loader进行处理
  storybookBaseConfig.module.rules.push({
    test: /\.s?css$/,
    use: ['style-loader', { loader: 'css-loader', options: { importLoaders: 1 } }, 'postcss-loader'],
    include: path.resolve(__dirname, '../'),
  });

  return storybookBaseConfig;
};

复制代码

注意 1:默认配置失效

如果使用自定义的配置,默认配置就会失效,如果没有重新配置 file-loader ,storybook运行起来时候假如有控件引用了图片等文件会报错。

// 默认配置会失效,处理文件需要配置相应的file-loader
  storybookBaseConfig.module.rules.push({
    test: /\.(gif|png|jpe?g|eot|woff|ttf|pdf)$/,
    loader: 'file-loader',
  });
复制代码

注意 2:保留原配置上修改

使用 Full Control Mode 模式虽然可以最大限度修改storybook的 webpack 配置,但是以下配置修改时需要注意在原配置上进行扩展。

  • entry
  • output
  • first loader in the module.loaders (Babel loader for JS)
  • all existing plugins

比如需要添加一个loader,需要像面那样push一个loader到 module.rules 数组中。

storybookBaseConfig.module.rules.push({
  test: /\.s?css$/,
  use: ['style-loader', { loader: 'css-loader', options: { importLoaders: 1 } }, 'postcss-loader'],
  include: path.resolve(__dirname, '../'),
});
复制代码

2. storybook + Redux

其实组件可以分为2种,具体可以参照这篇文章

  • 展示类,不涉及逻辑只关注样式展示(Presentational)
  • 逻辑类,关注逻辑而不涉及样式展示(Container)

展示类

展示类的控件使用Storybook很简单,根据展示类的控件传入props即可。

逻辑类

在我的项目中通常为react-redux connect后的类。类中操作(如网络操作)都是通过redux进行的。 为了让这个类能正常测试运行,需要进行以下操作。(例子均为 userStory

  1. 在story文件中引入react-redux的 providerstore
import { provider } from 'react-redux';
import store from 'store';
复制代码
  1. 扩展 story
    在storiesOf中增加装饰器,装饰器作用就是把story包裹起来。 因此我们可以利用装饰器的特点把store引入到控件中。
// stories/userStory/index.jsx
import React from 'react';
import { provider } from 'react-redux'
import { storiesOf } from '@storybook/react';
// 引入store
import store from 'store';
import BasicInfo from 'pages/clientDetail/components/BasicInfo';

storiesOf('用户信息', module)
  .addDecorator(storyFn => <Provider store={store}>{storyFn()}</Provider>)
  .add('基础信息', () => <BasicInfo />);
复制代码
  1. mock数据

    团队搭建的 yapi 平台负责mock数据生成。

  2. whistle

    由于mock数据和storybook不在同一个域,js调用mock数据会跨域,需要做请求转发代理。我们团队使用的是 whistle 。whistle是个好东西:+1:,墙裂推荐!!!

    以下是whistle配置的转发规则。

//yourproject.com resCors://*
//localhost:8888 resCors://enable
//yourproject.com http://127.0.0.1:8888/  weinre://
# 9001是storybook的端口
# https://myapi.xxx.com 是yapi所在域名。
http://localhost:9001/** https://myapi.xxx.com/$1
复制代码

addons

什么是addons,其实可以理解扩展storybook功能的插件。

我使用到的addons有

  • addon-actions 用于展示事件处理函数接收到的数据
  • addon-console console输出(log、error、warning)
  • addon-info 这个最有用了,如果控件填写了proptype,直接就能显示到storybook中。
  • addon-viewport 其实就是chrome的device toolbar功能。

注意

addon-actions和addon-viewport都需要在 addons.js 中注册才能使用。

实践

实践代码

这个就是我的 .storybook 目录结构。

.
├── README.md
├── addons.js
├── config.js
├── stories
│   └── userStory
│       └── index.js
└── webpack.config.js
复制代码
  • config.js
import { configure } from '@storybook/react';
import { setConsoleOptions } from '@storybook/addon-console';
import 'index.scss';

setConsoleOptions({
  panelExclude: [],
});

function loadStories() {
  require('./stories/userStory');
}

configure(loadStories, module);
复制代码
  • webpack.config.js
const path = require('path');

module.exports = (storybookBaseConfig, configType) => {
  storybookBaseConfig.module.rules.push({
    test: /\.s?css$/,
    use: ['style-loader', { loader: 'css-loader', options: { importLoaders: 1 } }, 'postcss-loader'],
    include: path.resolve(__dirname, '../'),
  });

  storybookBaseConfig.module.rules.push({
    test: /\.(gif|png|jpe?g|eot|woff|ttf|pdf)$/,
    loader: 'file-loader',
  });

  // 设置别名
  storybookBaseConfig.resolve.alias = {
    antd: path.resolve(__dirname, '..', `node_modules/antd/dist/antd.min.js`),
    antdcss: path.resolve(__dirname, '..', 'node_modules/antd/dist/antd.min.css'),
    antdzhCN: path.resolve(__dirname, '..', 'node_modules/antd/lib/locale-provider/zh_CN.js'),
  };

  // 增加src为绝对路径
  storybookBaseConfig.resolve.modules.push(path.resolve(__dirname, '..', 'src'));

  // 使用source-map
  storybookBaseConfig.devtool = 'source-map';

  storybookBaseConfig.mode = 'development';

  return storybookBaseConfig;
};
复制代码
  • addons.js
import '@storybook/addon-actions/register';
import '@storybook/addon-viewport/register';
复制代码

action和viewport addon均需要在addons.js中注册才能正常使用。

  • userStory
import React from 'react';
import { Provider } from 'react-redux';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { withConsole } from '@storybook/addon-console';
import BasicInfo from 'pages/clientDetail/components/BasicInfo';
import KeyActionItem from 'pages/clientDetail/components/KeyAction/KeyActionItem';
import { withInfo } from '@storybook/addon-info';
import store from 'pages/clientDetail/store';
import zhCN from 'antdzhCN';
import { LocaleProvider } from 'antd';
import 'antdcss';

// redux结合
storiesOf('用户信息', module)
  .addDecorator(withInfo)
  .addDecorator((storyFn, context) => withConsole()(storyFn)(context))
  .addDecorator(storyFn => <Provider store={store}>{storyFn()}</Provider>)
  .addDecorator(storyFn => <LocaleProvider locale={zhCN}>{storyFn()}</LocaleProvider>)
  .add('基础信息', () => <BasicInfo />, {
    info: {
      text: `
        用户基础信息展示,可进行上下翻页。
      `,
    },
  });

storiesOf('行为轨迹item', module)
  .addDecorator(withInfo)
  .addDecorator((storyFn, context) => withConsole()(storyFn)(context))
  .add('行为轨迹item--课程顾问', () => (
    <div style={{ display: 'flex', justifyContent: 'center', paddingTop: '30px' }}>
      <KeyActionItem
        time="2018-12-12 12:12:12"
        user="testUser"
        role="课程顾问"
        type="saler"
        data={[{ title: '备注', content: '备注测试'}]}
        id={1}
        onClickDelete={action('onClickDelete')}
        canDelete
      />
    </div>
  ))
  .add('行为轨迹item--客户', () => (
    <div style={{ display: 'flex', justifyContent: 'center', paddingTop: '30px' }}>
      <KeyActionItem
        time="2018-12-12 12:12:12"
        user="testUser"
        role="家长"
        type="client"
        data={[{ title: '购买记录', content: '测试购买记录'}]}
      />
    </div>
  ))
  .add('行为轨迹item--admin', () => (
    <div style={{ display: 'flex', justifyContent: 'center', paddingTop: '30px' }}>
      <KeyActionItem
        time="2018-12-12 12:12:12"
        user="admin"
        type="admin"
        data={[{ title: '购买记录', content: '测试购买记录'}]}
      />
    </div>
  ));
复制代码

实践截图

手把手教你快速搭建专属的storybook
手把手教你快速搭建专属的storybook

addons-info控件能把组件的prop信息展示出来

结语

我在本文中介绍了最基本的storybook使用。

  1. storybook配置
  2. webpack配置
  3. redux引入
  4. addons介绍

文章中介绍的使用方式实在是很简单,希望能安利更多人使用storybook。更多(高大上)storybook的实践可以参考这里storybook实践

参考

  1. Storybook官网
  2. StoryBook meets redux

以上所述就是小编给大家介绍的《手把手教你快速搭建专属的storybook》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Operating System Algorithms

Operating System Algorithms

Nathan Adams、Elisha Chirchir / CreateSpace Independent Publishing Platform / 2017-4-21 / USD 39.15

Operating System Algorithms will walk you through in depth examples of algorithms that you would find in an operating system. Selected algorithms include process and disk scheduling.一起来看看 《Operating System Algorithms》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具