最近公司要开发自己的移动端组件库, 是基于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
- 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;
}
- 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>
- 哪些组件是 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
-
- 标题
- 一个#是一级标题,二个#是二级标题,以此类推。支持六级标题
#
##
###
####
#####
######
-
- 代码块
``` ```
-
- 添加链接 图标名称
-
- 列表
// 绿色
_string_
// | 写法
_number \| string_
// 第一列 显示版本号, 其他列 灰色显示
`v2.5.3`
-
- md 英文版? 应该有产品提供英文文案
5. 组件代码
5.1 组件顶部
-
- util - 引入工具库
-
- Mixins - 引入Mixins
-
- components - 引入依赖的组件
-
- 引入定义的 type 类型,定义相关type
-
- props, slot 类型处理
-
- 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;
-
- 小组件
function LineIcon() {
if (line) {
return (
<i
class={bem('line-icon')}
/>
);
}
}
-
- 事件处理
function onClick(event: Event) {
emit(ctx, 'click', event); // 事件触发
functionalRoute(ctx); // 跳转处理
}
-
- 函数返回
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>
);
-
- 属性挂载
Cell.props = {
...cellProps,
...routeProps,
};
-
- 导出组件
export default createComponent<CellProps, CellEvents, CellSlots>(Cell);
5.3 非函数式组件
-
- createComponent({})
-
- 函数结构
- mixins
- props
- data
- computed
- watch
- mounted
- methods
- render
- 函数结构
5.4 组件开发 tips
-
- ref 和 自定义事件的区别 自定义事件一般携带参数 ref 主要处理组件内部的状态
-
- touch 事件需要引入 TouchMixin
-
- FieldMixin
-
- 父子组件的传参
// 父组件
mixins: [ParentMixin('vanSteps')],
// 子组件
mixins: [ChildrenMixin('vanSteps')],
-
- addUnit 的使用
-
- hairline 0.5px边框
5.5 组件分享
- 1.cell
- 2.pullRefresh
- 3.progress
- 4.form
- 5.slider
- 6.steps
- 7.tabs
- 8.pagination
- 9.upload
6. 怎么读懂一个组件
-
- 介绍, 即使用场景
-
- 组件 demo 有哪些功能
-
- 需要提供哪些 api (props, events, slots)
-
- render 函数
7. 函数解析
- createNamespace
- createComponent => isFunction => transformFunctionComponent => SlotsMixin => install
- createBEM
- createI18N => camelize