4

What

Storybook官网
隔离UI组件,支持独立调试&测试,统一展示项目组件库。

Why

项目中抽离的组件较多,但并没有集中展示出来具体有些什么,而是分布在各个使用的业务场景中。对于新接触项目的开发人员来说,由于不清楚业务场景,考虑复用组件时不是很直观。

How

最新搭建方案(2020.12.30)

官方README

一个组件接入storybook的示例

1.三行命令迅速启动示例页
npx -p @storybook/cli sb init

# 启动
npm run storybook

# 启动时报错提示找不到这个包 安装下下再启动
npm install @vue/babel-preset-app --save-dev

成功启动:
image.png

跟旧版本相比,自动生成的文件中demo更完善,上手更方便啦
image.png

2.package.json中添加脚本
{ 
    "scripts": { 
        "storybook": "start-storybook -p 6006",
        "build-storybook": "build-storybook -o ./docs"
    } 
}

./docs是输出的静态文件夹,不指定的话默认是storybook-static

旧版本搭建方案(@storybook/vue v5.3.x)

参考官方指南,选择Manual setup

1.添加依赖
$ cnpm install @storybook/vue -S

$ cnpm install vue-loader vue-template-compiler @babel/core babel-loader babel-preset-vue -S
2.package.json中添加脚本
{ 
    "scripts": { 
        "storybook": "start-storybook" 
    } 
}
3.添加less-loader,注入全局less变量
// .storybook/main.js
const path = require('path')

module.exports = {
  webpackFinal: async (config, { configType }) => {
    // `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION'
    // You can change the configuration based on that.
    // 'PRODUCTION' is used when building the static version of storybook.

    // Make whatever fine-grained changes you need
    config.module.rules.push({
      test: /\.less/,
      use: [
        'style-loader',
        'css-loader',
        'less-loader',
        {
          loader: 'style-resources-loader',
          options: {
            patterns: [
              path.resolve(__dirname, '../src/assets/styles/variable.less')
            ]
          }
        }
      ],
      include: path.resolve(__dirname, '../')
    })

    return config
  }
}
4.添加路径别名
// .storybook/main.js
config.resolve.alias = {
    ...config.resolve.alias,
    '@': path.resolve(__dirname, '../src'),
    '@com': path.resolve(__dirname, '../src/components')
}
5.启动
$ npm run storybook

最小版本组件
storybook运行示例.png

项目结构

项目结构.png

// .storybook/config.js
import { configure } from '@storybook/vue'

configure(require.context('./stories', true, /\.stories\.js$/), module)
// .storybook/stories/global.stories.js
import Vue from 'vue'

import Toggle from './../../src/components/toggle/index.vue'

Vue.component('Toggle', Toggle)

export const toggle = () => `
 <toggle :data="[
        { label: 'toggle1', value: '1' },
       { label: 'toggle2', value: '2' }
      ]"
    />
`

export default { title: 'Global' }

最小版本over~
接下来把组件们都写进stories就大功告成啦~

一个小插曲

几天之后,npm run storybook启不起来一直报错缺依赖,直接rimraf node_modules/重新npm i 完事儿~

Addons

Knobs

动态交互展示组件属性

安装
cnpm install @storybook/addon-knobs -S

.storybook/main.js中引入插件

module.exports = {
  addons: ['@storybook/addon-knobs/register']
}
使用
// .storybook/stories/panel.stories.js
import { storiesOf } from '@storybook/vue'
import { withKnobs, boolean, text } from '@storybook/addon-knobs'

import JhlDialogDetail from '@com/jhl-dialog-detail/index.vue'

export default {
  title: 'Panel',
  decorators: [withKnobs]
}

export const egJhlDialogDetail = () => ({
  components: { JhlDialogDetail },
  props: {
    visible: { default: boolean('visible', true) },
    title: { default: text('Text', '标题') },
    dlgWidth: { default: text('dlgWidth', '66%') }
  },
  data () {
    return {
      data: [
        {
          label: '基本信息',
          children: [
            { label: '中文名', prop: 'labelNameCn', span: 8 },
            { label: '英文名', prop: 'labelNameEn', span: 8 },
            { label: '实体', prop: 'entityName', span: 8 },
            { label: '一级主题', prop: 'firstThemeName', span: 8 },
            { label: '二级主题', prop: 'secondThemeName', span: 8 },
            { label: '三级主题', prop: 'thirdThemeName', span: 8 }
          ]
        },
        {
          label: '加工信息',
          children: [
            { label: '更新时间', prop: 'labelModifiedTime', span: 8 },
            { label: '业务负责人', prop: 'businessOwnerErp', span: 16, render: 'erp' },
            { label: '口径', prop: 'processDesc', span: 24, render: 'html' }
          ]
        },
        { label: '自定义区域', slot: 'quality' }
      ],
      detail: {
        'labelNameCn': 'zm测试',
        'labelNameEn': 'zmTest',
        'entityName': '用户',
        'firstThemeName': '人口属性',
        'secondThemeName': '自然属性',
        'thirdThemeName': '年龄',
        'labelModifiedTime': '2020-05-14 00:00:00',
        'businessOwnerErp': 'zhaozimu',
        'processDesc': '<p>213来源于X表,来源表关联字段X,标签筛选规则是XX,时间窗口为近X天,标签加工逻辑为XX,标签覆盖量为X,标签更新周期为XX</p>'
      }
    }
  },
  template:
    `<jhl-dialog-detail
            :visible.sync="visible"
            :title="title"
            :dlg-width="dlgWidth"
            :data="data"
            :detail="detail"
          >
            <div slot="quality">'slot' is here~</div>
          </jhl-dialog-detail>`
})
效果

knobs测试.jpg

Actions

快速上手

由于组件里都是$emit抛的事件,Actions暂时还不支持,待后续研究补充~

storybook-readme

快速上手

展示markdown文档

安装
cnpm i storybook-readme -D

注册组件

// .storybook/addons.js
import 'storybook-readme/register'
使用

配置

// .storybook/config.js
import { configure, addDecorator, addParameters } from '@storybook/vue'
// UI framework
import Vue from 'vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
// knobs
import { withKnobs } from '@storybook/addon-knobs'
// readme
import { addReadme } from 'storybook-readme/vue'
import { themes, create } from '@storybook/theming'

const basicTheme = create({
  base: 'light',
  brandTitle: 'README addon',
  brandUrl: 'https://github.com/tuchk4/storybook-readme',
  brandImage: null
})

addParameters({
  options: {
    showPanel: true,
    panelPosition: 'right',
    theme: basicTheme
    // theme: themes.dark,
  },
  readme: { codeTheme: 'github' }
})

addDecorator(withKnobs)
addDecorator(addReadme)

Vue.use(ElementUI)

configure(require.context('./stories', true, /\.stories\.js$/), module)

使用

//.storybook/stories/toggle.stories.js
import { boolean, number } from '@storybook/addon-knobs'

import Toggle from '@com/toggle/index.vue'
import ToggleReadme from '@com/toggle/index.md'

export default {
  title: 'Component',
  parameters: {
    readme: { sidebar: ToggleReadme }
  }
}

export const egToggle = () => ({
  components: { Toggle },
  props: {
    isResetIdx: boolean('isResetIdx', false),
    currentIndex: number('currentIndex', 1)
  },
  data () {
    return {
      data: [
        { label: 'toggle1', value: '1' },
        { label: 'toggle2', value: '2' },
        { label: 'toggle3', value: '3', status: 'false' }
      ]
    }
  },
  template:
    `<toggle 
        :data="data"
        :is-reset-idx="isResetIdx"
        :current-index="currentIndex"
    />`
})
效果

文档效果.jpg


沐乃
102 声望20 粉丝

The outer world you see is a reflection of your inner self.