StoryBook实战

原文链接
对于一名前端开发者,必须面对的就是组件化开发。我做Angular开发已经有些日子了,也曾为自己的项目开发过通用组件,但仅是在项目内部使用,而且是直接用业务界面对组件进行测试。如果其他项目要使用这些组件,也是使用老土的拷贝方式来进行复用。偶然发现StoryBook,研究了下,顿生好感,原来组件开发可以这么简单的管理和测试,还可以编写清晰明了的说明文档,对提升组件开发的效率那是大大滴提升。

本文将以一个基于Material的三级选择组件为例,进行StoryBook实战,实实在在滴体验下StoryBook的强大。

术语解释

StoryBook给开发这提供了一个强大的组件开发的生态环境,涉及组件的测试、实景展示、文档,以及术语。常见术语解释如下:

  1. Addon:类似Plugin,StoryBook中的功能组件以及扩展以Addon形式存在,开发者亦可以自行编写Addon来扩展StoryBook的功能;
  2. Story:类似用例,是组件的各种使用场景;
  3. Decorator:就是给Story做个包装,可以是样式包装、模块元数据包装、类型包装等。
  4. Notes:备注,可以为每个Story设置Notes,支持MarkDown语法。

如下是常用的Addon:

  • addon-actions:组件操作事件,如click、change
  • addon-links:链接,如某个Story中单击按钮,链接到另一个Story中
  • addon-notes:Story的备注
  • addon-options:调整StoryBook的外观
  • addon-knobs:在页面上改变变量

对于StoryBook入门内容,在网上可以找到很多,同时StoryBook for Angular中也有入门级的详细解释,这里不赘述。 下面来个实战演练。

创建自己的组件

三级级联选择组件,在项目中比较常用,比如省市区、多级分类等,通用的UI大多仅提供一级选择组件。三级选择则需要根据业务需求,开发者自己编写。于是我自己写了一个省市区的三级选择的组件。
首先为这个组件创建了独立的Angular工程:

ng new cityselect
cd cityselect

组件目录如下:


目录结构

组件名称为MatCascaderComponent,期望的运行效果如下:


期望的运行效果

StoryBook实战

在cityselect中安装StoryBook,命令如下:

npx -p @storybook/cli sb init --type angular

//同时安装如下Addon
npm install --save @storybook/addon-options

执行 npm run storybook 成功后,访问 http://localhost:6006 可以看到StoryBook的界面以及缺省的Story。

StoryBook界面

写个简单的Story

StoryBook提供了两种Story的写法,第一种是直接使用组件,第二种是使用Html标签。

直接使用组件

由于组件使用到了Materail的相关Module,Story中需要先将这些外部Module引入,这里用到了:moduleMetadata。

可以单独对每个Story设置moduleMetadata:

.add( "直接使用组件", () => ({
    component: MatCascaderComponent,  //直接使用组件
    props: {},
    //仅对当前Story生效
    moduleMetadata:{  
      imports: [BrowserAnimationsModule, MatFormFieldModule, MatSelectModule],
      schemas: [],
      declarations: [],
      providers: [CityCascsdeService, CommonService]
    } 
  }),
  { notes: `缺省是三级地区选择` }
)

亦可使用addDecorator对storiesOf下的所有Story设置moduleMetadata:

.addDecorator(
  //对此storiesOf下的所有Story生效
  moduleMetadata({  
    imports: [BrowserAnimationsModule, MatFormFieldModule, MatSelectModule],
    schemas: [],
    declarations: [],
    providers: [CityCascsdeService, CommonService]
  })
)
.add( "直接使用组件", () => ({
    component: MatCascaderComponent, //直接使用组件
    props: {}
  }),
  { notes: `缺省是三级地区选择` }
);

使用HTML标签

下面我们使用Html标签,这个我们项目中的用法是一样的,所以需要对组件MatCascaderComponent进行声明,同样是在moduleMetadata中:

.addDecorator(
  moduleMetadata({
    imports: [BrowserAnimationsModule, MatFormFieldModule, MatSelectModule],
    schemas: [],
    declarations: [MatCascaderComponent], //这里声明下
    providers: [CityCascsdeService, CommonService]
  })
)
.add("使用HTML标签",() => ({
    template: `<ngx-mat-cascader ></ngx-mat-cascader>`,
    props: {}
  }),
  { notes: `缺省是三级地区选择` }
);

此时看到的StoryBook的效果如下:

CSS未引入

引入外部CSS

上面我们看到组件的样式不对。StoryBook不会自动引入组件需要的CSS文件,需要告诉StoryBook访问哪个静态文件,详情可参见参考文档。

这里给出主要改动:

  1. .storybook 目录下创建名为 preview-head.html 文件,该文件是为HTML添加自定义的Head内容。内容如下:

    <link rel="stylesheet" href="./styles.css" />
    
  2. 在src/styles.css文件中引入Materail的CSS:

    @import "../node_modules/@angular/material/prebuilt-themes/indigo-pink.css"
    @import "../node_modules/bootstrap-material-design/dist/css/bootstrap-material-design.css"
    
  3. package.json文件中修改启动StoryBook的命令,通过 -s 参数指定静态目录

    //指定./ 和 ./src均为静态目录
    "scripts": {
        "storybook": "start-storybook -p 6006 -s ./,./src",
    }
    

.storybook目下的修改,必须重启StoryBook。这时我们看到样式正确了。


正确CSS

设置参数的Story

上面的Story非常简单,没有参数和Action。下面看看如果设置参数和Action。MatCascaderComponent缺省是个省市区的三级选择组件,但是如果提供不同的参数,它就会华丽的变身了:

.add( "设置选择数据",() => ({
  component: MatCascaderComponent,
  props: {
    data: [
      {
        code: "11",
        name: "易耗品",
        children: [
          {
            code: "1101",
            name: "打印机",
            children: [
              { code: "110101", name: "彩色墨盒" },
              { code: "110102", name: "黑色墨盒" }
            ]
          }
        ]
      },
      {
        code: "12",
        name: "食品",
        children: [
          {
            code: "1201",
            name: "快餐",
            children: [
              { code: "120101", name: "薯条" },
              { code: "120102", name: "热狗" }
            ]
          }
        ]
      }
    ],
    level1placeholder: "选择分类",
    level2placeholder: "选择货区",
    level3placeholder: "选择货架",
    allTitle: "全部",
    showAll: true,
    onZoneChange:action('onZoneChange')
  }
  }),
  { notes: '这是一个三级商品选择组件' }
);

效果如下:


ezgif.com-video-to-gif (1).gif

Html标签测试设置参数参见如下示例代码:

.add( "设置初始值", () => ({
    template: `<ngx-mat-cascader [data]="basedatas" [separate]="separate" [(value)]="selectvalue" (onZoneChange)="zoneChange()" ></ngx-mat-cascader>`,
    props: {
      basedatas:[...],
      separate:'-',
      selectvalue:'11-1101',
      zoneChange:action('change')
    }
  }),
  { notes: '这是一个三级商品选择组件' }
);

是不是方便的不能再方便?对拷贝、粘贴深恶痛绝的我,看到了组件开发的春天。

为Story写备注

在上面的示例中,你会发现notes属性,就是备注的意思。一个完美的备注即提升了Story的可读性,也方便后期对组件的维护,更可以通过备注向使用者展示展示自己的组件。
StoryBook提供了两种途径,一是在Story的notes属性中直接使用MarkDown,二是使用一个MarkDown文件。
如下是在notes属性中直接使用MarkDown,需要注意的是折行后前面不能有空格:

.add("设置选择数据", () => ({
    component: MatCascaderComponent,
    props: {...}
  }),
  { notes: ` # 我是一级标题 
## 我是二级标题,行首不能有空格,下同
### 我是三级标题
1. 我是列表1
2. 我是列表2
    ` }
 )

效果如下:


notes中使用MarkDown

如果备注一两句能说描述清楚Story,上述方法可行。但对于复杂的Story,还是一个MarkDown文件更方便。要使用md文件做备注,需要做些改动,要让代码识别出MarkDown文件:

//引入对md文件的支持,在.storybook目录下创建typings.d.ts文件,内容如下:
declare module "*.md" {
  const content: string;
  export default content;
}

// 在.storybook/tsconfig.json文件添加:

"files": [
    "./typings.d.ts"
  ]

// story中引入文件:
  
import * as readme from '../app/components/select/README.md';

.add("选择测试",() => ({
    component: MatCascaderComponent,
    props: {...}
  }),
  { notes: readme }
);

效果如下:


使用MarkDown文件

是不是有点喜欢上StoryBook了?至少我是这样,甚至憧憬着自己开发的组件减轻了更多同行的工作量。

更换主题

StoryBook还可以打包成静态页面放在公网上,供大家品评。但是界面左上角显示的还是StoryBook,需要换下,这就涉及的修改StoryBook的主题了,这里仅提供名字和链接更改的方法:

import { addParameters } from '@storybook/angular';
import logo from '../src/assets/img/dteam.svg';

addParameters({
  options: {
    theme:{
      brandTitle:'DTeam组件库',
      brandUrl: 'https://github.com/dteam-top'
    },
  }
});

效果如下:


更换主题

关于主题的更详细的说明,请参见文档2。

总结

通过对StoryBook的学习和实践,我觉得它的确不错,对于组件的开发、测试、文档化非常方便:

  1. 支持的主流前端框架
  2. 为组件提供独立的开发环境
  3. 多种测试场景,全面测试组件
  4. 使用MarkDown编写备注
  5. 众多的Addon,方便扩展

这些对于前端开发的工作,提供了不少改进:

  • 避免混乱的重复代码,提高代码复用性
  • 测试人员可以直接测试组件
  • 丰富的Story,所见即所得
  • 组件开发,督促开发者提高自身技能
  • Markdown的文档,方便展示组件

但是“人无完人”,我在使用StoryBook过程中,也发现了一些问题:

  1. Addon太多,管理有些混乱
  2. 支持UI框架多,但是有的Andon却不是所有UI框架都支持,比如info,具体可参见文档3
  3. 大版本之间变化大,从网上找到的示例代码比较老,不能用
  4. 不知道是否支持国际化

任何工具都是入门易、深耕难,StoryBook亦是如此,这需要开发人员提升组件化思维,并结合更多的实践,才能让它更好的助力前端开发。

参考文档

  1. StoryBook for Angular
  2. StoryBook Theme
  3. StoryBook addon对各前端平台的支持
  4. StoryBook for Angular在线示例
  5. StoryBook引用静态资源
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342