imfe-mui组件开发手册
# imfe-mui组件贡献规范
[TOC]
# 介绍
本手册主要介绍基于Vant2.x版本中增加业务组件的开发流程
# 环境安装
在开始开发前,准备开发环境
# 安装 Python
https://www.python.org/
# 下载源码
https://git.iflytek.com/ZHYL_IMFE/ui/imfe-mui 分支: 2.x
# 安装依赖
npm run install
#或者
yarn
1
2
3
2
3
# 启动开发服务器
npm run dev
1
# 源码目录介绍
基于 Vant Cli 搭建的组件库的基本目录结构如下所示:
project
├─ src # 组件源代码
│ ├─ button # button 组件源代码
│ └─ dialog # dialog 组件源代码
│
├─ docs # 静态文档目录
│ ├─ home.md # 文档首页
│ └─ changelog.md # 更新日志
│
├─ babel.config.js # Babel 配置文件
├─ vant.config.js # Vant Cli 配置文件
├─ pacakge.json
└─ README.md
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
开发组件需要关心的是 vant.config.js 和 src 目录
# vant.config.js
site.locales 中配置的是文档中菜单目录,分为中文和英文两个部分,下面以中文为示例
nav 数组中存放是菜单树结构,在对应的位置添加需要开发的组件配置即可
例如:需要在 业务组件 中添加 title 标题 组件,配置路径如下
path 表示组件路径,对应 src 目录中的组件文件夹
title 表示文档中组件菜单的名称
// vant.config.js
module.exports = {
...
site: {
...
locales: {
'zh-CN': {
...
nav: [
...
{
title: '业务组件',
items: [
{
path: 'title',
title: 'title 标题'
},
...
],
}
]
...
},
},
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# src中添加组件
单个组件的目录如下:
button
├─ demo # 示例目录
│ └─ index.vue # 组件示例
├─ index.vue # 组件源码
└─ README.md # 组件文档
1
2
3
4
5
2
3
4
5
使用 .vue 文件编写组件时,编译后会生成对应的 JS 和 CSS 文件,且 JS 文件中会自动引入 CSS 文件。
如果需要将 JS 和 CSS 解耦,实现主题定制等功能,在编写代码时就要使用独立的 JS 和 CSS 文件,如下所示:
button
├─ demo # 组件示例
│ └─ index.vue # 组件示例入口
├─ index.js # 组件入口,可以为 jsx 或 tsx
├─ index.less # 组件样式,可以为 less 或 scss
└─ README.md # 组件文档
1
2
3
4
5
6
2
3
4
5
6
采用这种目录结构时,组件的使用者需要分别引入 JS 和 CSS 文件,也可以通过 babel-plugin-import 自动引入样式。
通过引入样式源文件(less 或 scss)并修改样式变量,可以实现主题定制功能。(推荐使用)
使用 jsx 或者 tsx 方式编写组件,可以使用Vant Cli封装好的函数式组件的方式
下面以 Title 组件为例
// 创建命名空间
import { createNamespace } from '../utils';
// 实现发送事件的方式,类似 this.$emit
import { emit } from '../utils/functional';
// 引入的外部组件,以及相关类型
import Icon, { IconProps } from '../icon';
import Tag, { TagType } from '../tag';
import Button, { ButtonType } from '../button';
// 引入和定义相关类型
import { CreateElement, RenderContext } from 'vue/types';
import { ScopedSlots } from '../utils/types';
export type TitleSize = 'large' | 'normal' | 'small' | 'mini';
export type Tag = {
type?: TagType;
text?: string;
color?: string;
textColor?: string;
};
export type Button = {
text: string;
key: string | number;
type: ButtonType;
};
export type TitleProps = {
title?: string;
size?: TitleSize;
subtitle?: string;
icon?: IconProps;
tag?: Tag | Tag[];
button?: Button | Button[];
isLink?: boolean;
linkName?: string;
linkIcon?: string;
};
// 以当前组件名称生成组件相关方法
const [createComponent, bem] = createNamespace('title');
// 函数式组件入口,tsx
function Title(
h: CreateElement,
props: TitleProps,
slots: ScopedSlots,
ctx: RenderContext<TitleProps>
) {
const {
title,
size = 'normal',
subtitle,
icon,
tag,
button,
isLink,
linkName,
linkIcon = 'arrow',
} = props;
let tags: Tag[] = [];
if (tag) {
if (tag instanceof Array) tags = tag;
else tags = [tag];
}
let buttons: Button[] = [];
if (button) {
if (button instanceof Array) buttons = button;
else buttons = [button];
}
function renderIcon() {
const sizes = {
large: '28px',
normal: '24px',
small: '20px',
mini: '20px',
};
if (icon) {
return (
<Icon
class={bem('icon')}
{...{ props: icon }}
size={sizes[size]}
></Icon>
);
}
}
function renderTag() {
return tags.map((item) => {
// 只支持3个参数的配置
const tagProps = {
type: item.type,
color: item.color,
textColor: item.textColor,
};
return <Tag {...{ props: tagProps }}>{item.text}</Tag>;
});
}
function renderButton() {
return buttons.map((item) => {
const buttonProps = {
type: item.type || 'primary',
};
return (
<Button
size="mini"
{...{ props: buttonProps }}
key={item.key}
onClick={() => emit(ctx, 'button-click', item.key)}
>
{item.text}
</Button>
);
});
}
function renderLink() {
if (isLink) {
return (
<span class={bem('link-wapper')} onClick={() => emit(ctx, 'link')}>
<span class={bem('link-text')}>{linkName}</span>
<Icon class={bem('link-icon')} name={linkIcon}></Icon>
</span>
);
}
}
function renderTitle() {
if (slots.title) {
return <div class={bem('title-wapper')}>{slots.title()}</div>;
}
return (
<div class={bem('title-wapper')}>
{renderIcon()}
<span class={bem('title')}>{title}</span>
<span class={bem('tag')}>{renderTag()}</span>
</div>
);
}
function renderSubtitle() {
if (slots.subtitle) {
return <div class={bem('subtitle-wapper')}>{slots.subtitle()}</div>;
}
return (
<div class={bem('subtitle-wapper')}>
<span class={bem('subtitle')}>{subtitle}</span>
<span class={bem('btns')}>{renderButton()}</span>
</div>
);
}
return (
<div class={bem([size])}>
{renderTitle()}
{renderSubtitle()}
{renderLink()}
</div>
);
}
// 定义组件的Props
Title.props = {
title: String,
size: {
type: String,
default: 'normal',
},
subtitle: String,
icon: Object,
tag: [Object, Array],
button: [Object, Array],
isLink: {
type: Boolean,
default: false,
},
linkName: String,
linkIcon: {
type: String,
default: 'arrow',
},
};
// 导出组件,需要用到前面命名空间函数返回的createComponent方法
export default createComponent<TitleProps>(Title);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# 打包并发布
编辑 (opens new window)
上次更新: 2022/10/24, 14:17:08