乐趣区

关于前端:Storybook-组件驱动开发一-基本使用

Storybook 是以后最风行的 UI 组件开发环境,次要性能就是提供组件独立运行环境,不便编写组件应用样例,每个样例称之为一个“story”。有了它,就能够不便的在我的项目中提取通用组件,治理本人的组件库。
并且,它还能够通过丰盛的插件实现可交互测试,响应式布局,文档生成、设计稿预览等性能,让开发工作更加专一,和设计人员的合作更轻松。

装置 Storybook

Storybook 有很丰盛的自定义配置项和大量的插件,能够适配各种开发场景,不过这也让我的项目的搭建变得很简单,很新人劝退。????

好在最近更新的 6.0 版本之后,我的项目能够实现零配置搭建,就像 Spring Boot 对 Spring 的简化,让我的项目创立变得分外简略,当初一行命令就能够实现我的项目搭建。

Storybook 反对各种支流组件框架,这里以 Angular 来论述。

创立 Angular 我的项目

首先默认全局曾经装置了 Angular CLI:npm install -g @angular/cli。顺便一提,Angular 11 大幅晋升了构建速度和打包体积,没有破坏性改变,强烈安利。????

在指定目录(如 D:\projects)下运行 ng new storybook-example 创立一个 Angular 我的项目,依据提醒交互性的配置我的项目生成即可。

初始化 Storybook

接着在这个我的项目门路下(D:\projects\storybook-example)运行命令 npx sb init 即可实现 Storybook 的搭建。它会依据我的项目的依赖主动配置 Storybook。只须要这么一行命令,Storybook 就能够主动装置到我的项目中去。

装置结束后,运行 npm run storybook 即可看到 storybook 胜利运行了,so easy!????

初始化做了什么?

尽管我的项目运行起来了,不过忽然主动创立了一堆未知的文件,也让人心里没底,来看看我的项目初始化做了哪些事件吧。

  • ???? 装置所需的依赖包:因为辨认到当前目录下是一个 Angular 我的项目,所以装置的是 Angular 版本的 Storybook 依赖包。

    • “@compodoc/compodoc”: Angular 的组件文档生成工具。
    • “@storybook/addon-actions”: 用以记录事件触发的插件。
    • “@storybook/addon-essentials”: 官网保护的插件汇合,带有默认配置。
    • “@storybook/addon-links”: 用以给组件 story 创立链接。
    • “@storybook/angular”: storybook 针对 Angular 的依赖。
  • ???? 设置 npm 脚本:

    • “storybook”: 本地运行 Storybook
    • “build-storybook”: 编译打包 Storybook 我的项目
  • ???? 在我的项目根目录创立 .storybook 文件夹,增加默认配置:

    • main.js:我的项目的全局配置文件,定义了 story 的查找门路,以及引入的插件。
    • preview.js:我的项目的渲染配置,包含全局款式的引入,通用性的变量等。
    • tsconfig.json:自动识别我的项目采纳 typescript 后主动生成的配置文件
  • ???? 在 src/stories 目录下创立三个组件(button、header、page),以及它们的 story 示例

编写 stories

story 用于展现组件某个状态,每个组件能够蕴含任意多个 story,用来测试组件的各种场景。依据默认配置,只须要在组件的文件夹中,以 **.component.stories.ts 的格局创立即可。

story 语法

根本编写语法很简略,是 export 任意多个 function,每一个就是一个 story。导出次要分两种:

  1. default export:默认导出,提供组件级别的配置信息,例如以下配置会注明组件的归类,并提供 Button 组件的信息,以便渲染这个组件。

    // Button.stories.ts
    
    import Button from './button.component';
    
    export default {
      title: 'Components/Button',
      component: Button,
    }
  2. named export:命名导出,用以形容 story,如上所说,一个组件能够有若干个 story。

    // Button.stories.ts
    
    // 创立一个模板,不便在后续的 story 中复用
    const Template = (args: Button) => ({props: args,});
    
    export const Primary = Template.bind({});  // 复制 Template
    Primary.args = {background: '#ff0', label: 'Button'};
    Primary.storyName = "次要状态" // 自定义 story 名
    
    export const Secondary = Template.bind({});
    Secondary.args = {...Primary.args, label: '????????????????'}; // 复用上一个 story 的配置
    
    export const Tertiary = Template.bind({});
    Tertiary.args = {...Primary.args, label: '????????????????'} // 再来一个 

    通过复制模板 function,能够创立若干个 story,传入不同的参数,就能够别离渲染出组件的不同状态。每个 story 的名字默认是 function 名,也能够自定义。

Args(属性)

上一节看到了怎么去写一个 Story 文件,不过外面重复用到的 args 是什么呢?
它代表组件的输出属性(Angular 中的 @input(),Vue/React 中的 props),它有 2 个层级,不便灵便配置。

  1. story 层级:

    // Button.stories.ts
    
    const Template = (args: Button) => ({props: args,});
    
    // 在这个 story 中传入组件属性,只影响以后 story
    export const Primary = Template.bind({});
    Primary.args = {
      primary: true,
      label: 'Primary',
    };
  2. 组件层级:

    // Button.stories.ts
    
    import Button from './button.component';
    
    // 组件层级(默认导出)中传入组件属性,// 这个 Button 组件的所有 stories 的 primary 属性都会是 true
    export default {
      title: "Button",
      component: Button,
      args: {primary: true,},
    }

就像上一节所看到的,不同的 story 的 args 是能够复用的,这里用到了 ES6 的解构语法:

const Primary = ButtonStory.bind({});
Primary.args = {
  primary: true,
  label: 'Button',
}

// 复用 Primary story 的 args,并笼罩 primary 属性
const Secondary = ButtonStory.bind({});
Secondary.args = {
  ...Primary.args, // 合并上一个 args 对象
  primary: false,
}

简略的导出几个 function,这个按钮组件的测试用例就写好了,看看成果

能够看到,这个按钮组件能够独立于我的项目运行了,并且左边工具栏能够自在更改它的属性,实时查看属性扭转后的成果,还能够主动生成组件文档。

有 story 做示例,有实时的控制台,有文档,再也不怕写好的组件他人不晓得怎么用了。????

额定的配置项

除了写给组件写 story,很多时候也会须要配置插件,或者给组件提供额定的性能,这里看看是如何配置的吧。

Parameters(参数)

Parameters 用以配置 Storybook 和 插件,具备全局、组件、story 三个层级。

Story 领有大量的插件,以下以简略的 backgrounds 插件举例,它用来管制组件容器的背景色,默认自带黑 / 白两色。

  1. 全局定义在根目录 .storybook/preview.js 下,会影响所有的 stories。这样配置后,每个 story 界面下都能够抉择红 / 绿两色背景:

    // .storybook/preview.js
     
    export const parameters = {
      backgrounds: {
        values: [{ name: 'red', value: '#f00'},
          {name: 'green', value: '#0f0'},
        ],
      },
    };
  2. 组件层级下定义,会让这个组件的所有 stories 都能够抉择指定的背景色

    // Button.story.ts
    
    export default {
      title: 'Button',
      component: Button,
      parameters: {
        backgrounds: {
          values: [{ name: 'red', value: '#f00'},
            {name: 'green', value: '#0f0'},
          ],
        },
      },
    };
  3. story 层级下的定义只会影响以后 story,其余 story 就只能抉择默认的黑 / 白两色了。

    // Button.story.ts
    
    export const Primary = Template.bind({});
    Primary.args = {
      primary: true,
      label: 'Button',
    };
    // 红绿背景只在这个 story 下能够抉择
    Primary.parameters = {
      backgrounds: {
        values: [{ name: 'red', value: '#f00'},
          {name: 'green', value: '#0f0'},
        ],
      },
    };

Parameters 的配置是能够继承,同名的子级会笼罩父级的定义。

Decorators(装璜器)

每个 Decorator 也是 function,用来包裹 story,放弃原有的 story 不变的状况下,额定给它增加额定的 DOM 元素、引入上下文环境、增加假数据等等。
和 Parameters 一样,它也能够定义在全局 / 组件 /story 三个层级,每个 Decorator 按定义程序顺次执行,从全局到 story。

例如,用一个额定的 <div> 包裹每个 story 的组件渲染:

// button.stories.ts

import {Meta, Story} from '@storybook/angular';
import {ListComponent} from './list.component';

export default {
  title: 'Example/List',
  component: ListComponent,
  decorators: [(storyFunc) => {const story = storyFunc();

      return {
        ...story,
        template: `<div style="height: 60px">${story.template}</div>`,
      };
    }
  ]
} as Meta;

这样一来,这个列表组件的所有 story,都会展现出它在一个 60 像素高的容器内的出现成果。

除了给组件包裹额定的元素,再例如为复合组件增加组件依赖:

// List.stories.ts

import {moduleMetadata} from '@storybook/angular';
import {CommonModule} from '@angular/common';

import List from './list.component';
import ListItem from './list-item.component'


// 给 list 组件增加它须要的组件和模块依赖
export default {
  title: 'List',
  component: List,
  decorators: [
    moduleMetadata({declarations: [ListItem],
      imports: [CommonModule],
    }),
  ],
};

const Template = (args: List) => ({
  component: List,
  props: args,
});

就像平时须要在 ngModule 中申明似的,moduleMetadata 装璜器能够轻松测试各种组件,让你能在 Storybook 中从小到大搭建组件库。

组件驱动开发

古代用户界面更加简单,在突飞猛进的技术倒退下,人们更加冀望独特,共性的用户体验。但这也意味着前端和设计须要给 UI 界面嵌入更多的逻辑和设计,这让前端变得原来越重,界面也越加简单难以保护和测试。
将 UI 模块化拆散也成了趋势,拆散页面逻辑到一个个独立的交互模块中,将简单的业务拆分解构,每个模块易于测试,组合和复用。所以当初组件化框架成了相对的支流。类似的,以后微服务和容器化也变得十分风行。

开发流程

那么组件驱动的开发又是如何呢?

  1. 一次编写一个组件 :首先编写一个个独立组件,定义它们的输出属性和输入事件,始于微末(例如 Avatar、Button、Input、Tooltip)。没错,这就是咱们相熟的一个个组件库所做的事件:Material、Antd、ElementUI……
  2. 组合组件 :形成更简单的业务模块,形成新的性能(例如 Forms、Header、List、Table)。
  3. 拆卸页面 :将业务模块构建出页面,应用假数据来模仿页面的各种应用场景和边缘用例。(例如首页、设置页、个人主页)
  4. 集成页面到利用 :将页面与后盾数据接口,业务逻辑服务层对接,形成理论应用程序(例如 xx 监管零碎、xxx 商城、xx 官网)

所以 Storybook 初始化我的项目后,创立了 button、header、page 三个组件示例,它们就代表了这样的开发阶梯流程。

实用场景

没有万精油的开发模式,总是须要按需抉择技术栈和开发模式的,咱们重要去理解每个优劣场景,这个也不例外。

劣势;

  • 高质量: 通过编写各种 stories 来验证组件的运行场景,测试用例越多,组件品质越发牢靠和强壮。
  • 易测试: 在组件级别进行测试,将 debug 细化,比按业务页面测试更省力准确。
  • 开发和设计并行:讲 UI 按组件模块化开发,能够让设计和前端开发更严密的单干,不同我的项目共享资源。

劣势:

  • 工夫周期: 相比针对页面一把梭似的开发,针对组件谨严设计 api,编写 story,思考复用性和边缘场景。我的项目的开发工夫周期必然会进步,这对外包的小伙伴来说,可就不敌对了。
  • 页面类利用:整个利用在开发和设计角度来看,都是几个残缺页面的汇合,页面间没有太多能够复用的元素,整夜开发和划分组件差异不大(开发各种可视化大屏可能会有同感)

所以,组件驱动开发,更适宜复用场景丰盛的系列利用场景,我的项目生命周期短缺,品质要求高,前端和设计单干严密的团队(或者,咱们本人一个人包圆儿的集体我的项目????)

总结

本篇简略介绍了 Storybook 这一组件开发工具,次要蕴含几点:

  • 在已有的 Angular 我的项目下,通过一行命令初始化了 Storybook 开发环境,它主动装置了依赖,创立了默认配置,并在我的项目里生成了三个示例。运行它设置好的命令行,即可看到组件测试界面。
  • 介绍了如何编写组件的 story。

    • 编写按键组件的三个 story 用例,实现用例的复用。
    • 用 parameter 配置背景插件。
    • 用 decorator 给组件增加额定的外层 DOM、传递组件的模块依赖。
  • 介绍组件驱动开发模式,剖析其优劣场景。

纸上得来终觉浅,还是得把它融入咱们理论的开发场景,看看它是如何驱动我的项目的开发的。所以,一起来体验可视化的测试驱动开发,实现一个残缺的业务页面吧,咱们下一篇见!????

退出移动版