怎么开发一个Vant组件

692 阅读2分钟

最近公司要开发自己的移动端组件库, 是基于Vant进行开发的. 本文旨在分享给大家怎么开发一个组件

Vant 组件开发流程

1. 组件目录

cell/
├─ demo/ ..................... 组件demo文件夹
│  └─ index.html ............. 组件demo, bym网站右侧部分
├─ test/ ..................... 组件单元测试
│  ├─ __snapshots__
│  ├─ demo.spec.js ........... demo单元测试
│  └─ index.spec.js .......... 组件单元测试
├─ index.less ................ less文件
├─ index.tsx/index.js ........ 组件
├─ README.md.................. 英文说明文档, bym网站中间部分
├─ README.zh-CN.md ........... 中文说明文档

2. 组件 demo

目录: cell/demo/index.html

  1. demo标签 demo-section 解析
<demo-section></demo-section> === <section class="van-doc-demo-section">
.van-doc-demo-section {
   box-sizing: border-box;
   min-height: calc(100vh - 56px);
   padding-bottom: 20px;
}
  1. demo-block解析
<demo-block :title="t('type')"></demo-block> 
=== 
<div class="van-doc-demo-block"><h2 class="van-doc-demo-block__title domo-***">\*\*\*</h2></div>
  1. 哪些组件是 weapp 没有的
<demo-block v-if="!isWeapp" :title="t('router')">

3. 单元测试

目录: cell/test

demo.spec.js

import Demo from '../demo';
import { snapshotDemo } from '../../../test/demo';

snapshotDemo(Demo);

index.spec.js

// jest api解析
toMatchSnapshot(): 会为expect 的结果做一个快照并与前面的快照做匹配。(如果前面没有快照那就保存当前生成的快照即可)
toHaveBeenCalled(): 事件被执行: ;
toHaveBeenCalledTimes(0): 事件未被执行 ;
toEqual(): 等于
toBeFalsy(): 错误 

4. 说明文档 markdown

    1. 标题
    • 一个#是一级标题,二个#是二级标题,以此类推。支持六级标题
#
##
###
####
#####
######
    1. 代码块
 ``` ```
// 绿色
_string_

// | 写法
_number \| string_

// 第一列 显示版本号, 其他列 灰色显示
`v2.5.3`
    1. md 英文版? 应该有产品提供英文文案

5. 组件代码

5.1 组件顶部

image.png

    1. util - 引入工具库
    1. Mixins - 引入Mixins
    1. components - 引入依赖的组件
    1. 引入定义的 type 类型,定义相关type
    1. props, slot 类型处理
    1. createNamespace()

5.2 函数式组件

  • 1.函数定义
function Cell(
  h: CreateElement,
  props: CellProps,
  slots: CellSlots,
  ctx: RenderContext<CellProps>
){}
  • 2.props 解构
const { icon, size, title, label, value, isLink ,line} = props;
    1. 小组件
   function LineIcon() {
    if (line) {
      return (
        <i
          class={bem('line-icon')}
        />
      );
    }
  }
    1. 事件处理
  function onClick(event: Event) {
    emit(ctx, 'click', event); // 事件触发
    functionalRoute(ctx); // 跳转处理
  }
    1. 函数返回
  return (
    <div
      class={bem(classes)}
      role={clickable ? 'button' : null}
      tabindex={clickable ? 0 : null}
      onClick={onClick}
      {...inherit(ctx)} // 属性继承
    >
      {LeftIcon()}
      {LineIcon()}
      {Title()}
      {Value()}
      {RightIcon()}
      {slots.extra?.()}
    </div>
  );
    1. 属性挂载
Cell.props = {
  ...cellProps,
  ...routeProps,
};

    1. 导出组件
export default createComponent<CellProps, CellEvents, CellSlots>(Cell);

5.3 非函数式组件

    1. createComponent({})
    1. 函数结构
      • mixins
      • props
      • data
      • computed
      • watch
      • mounted
      • methods
      • render

5.4 组件开发 tips

    1. ref 和 自定义事件的区别 自定义事件一般携带参数 ref 主要处理组件内部的状态
    1. touch 事件需要引入 TouchMixin
    1. FieldMixin
    1. 父子组件的传参
// 父组件
 mixins: [ParentMixin('vanSteps')],

// 子组件
mixins: [ChildrenMixin('vanSteps')],
    1. addUnit 的使用
    1. hairline 0.5px边框

5.5 组件分享

  • 1.cell
  • 2.pullRefresh
  • 3.progress
  • 4.form
  • 5.slider
  • 6.steps
  • 7.tabs
  • 8.pagination
  • 9.upload

6. 怎么读懂一个组件

    1. 介绍, 即使用场景
    1. 组件 demo 有哪些功能
    1. 需要提供哪些 api (props, events, slots)
    1. render 函数

7. 函数解析

  1. createNamespace
  2. createComponent => isFunction => transformFunctionComponent => SlotsMixin => install
  3. createBEM
  4. createI18N => camelize