基于 cordova 的高可拓展性组件库方案设计

简述

之前有简单介绍过混合体系,其中底层 cordova 组件库搭建的好坏,会很大程度上影响业务开发效率,今天主要介绍一下我们团队的混合组件库的主要实现。

技术方案

技术栈:ts + webpack + typedoc + QUnit + 私有 npm

所有的内容都是基于 typescript 语法,能够极大程度规范开发者使用;采用 webpack 做打包集成,虽然他慢,但是他好用,各种 ts-loader、babel-loader…用起来很方便;typedoc 来生成组件库文档,只能说生成的内容看是能看,但是基本上只能提取 api,都是英文不是中文文档(看起来你懂得);QUnit 做简单的单元测试,简单方便不多解释(只做了部分接口的前后输出内容比对。);我们将组件库发布到私有 npm 上,下载和使用非常方便。

最后产品情况大概图如下图所示:

核心封装

所有的公共组件或业务组件,都可以基于 nativecall 核心方法去做二次封装,具体代码如下仅供参考。

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
declare global {
const Cordova: any;
}
let deviceReady: boolean = (window as any).deviceReady;
/**
* 是否是HybridApp
* @ignore
*/
const hybrid = /Cordova/i.test(navigator.userAgent);
/** 配置文件参数 */
Object.defineProperty(window, "wdui", {
configurable: false,
writable: true,
value: {},
});
(window as any)["wdui"]["WduiConfig"] = {
debug: false, // 是否开启debug
monitor: false, // 是否开启监控
monitorRecords: [], // 监控记录
monitorLastData: {}, // 最后一条监控数据
};

/**
* 插件服务监控
* @ignore
*/
const monitor = {
// 开始监控
start: (service: string, action: string) => {
return {
service,
action,
startTime: Date.now(),
endTime: Date.now(),
speed: "0ms",
};
},
// 结束监控
end: (monitorData: any) => {
if ((window as any)["wdui"]["WduiConfig"]["monitor"]) {
monitorData.endTime = Date.now();
monitorData.speed = monitorData.endTime - monitorData.startTime + "ms";
(window as any)["wdui"]["WduiConfig"]["monitorRecords"].push(monitorData);
(window as any)["wdui"]["WduiConfig"]["monitorLastData"] = monitorData;
}
},
};

/**
* 是否混合
* @description
* 是否在HybridApp内部
* 要求在使用cordova的APP中添加navigator.userAgent参数
* @return boolean 是否在HybridApp中
* @example console.log(isHybrid())
*/
export function isHybrid() {
return hybrid;
}

/**
* 插件调用
* @description 底层调用 `Cordova` 原生方法
* @param service 调用的插件的插件名
* @param action 调用的插件的方法名
* @param args 调用是传递的参数
* @param success 成功回调
* @param error 失败回调
* @param defaultFn 浏览器模拟数据函数
* @param format 返回数据拦截格式化函数
*/
export function nativeCall(
service: string,
action: string,
args: any[] | null,
success?: (data: any) => void,
error?: (e: any) => void,
defaultFn?: () => any,
format?: (data: any) => any
) {
if (!success) {
return new Promise((resolve, reject) => {
nativeCall(service, action, args, resolve, reject, defaultFn, format);
});
}
if (!error) {
error = (e) => {
console.error(e);
if ((window as any)["wdui"]["WduiConfig"]["debug"]) {
alert(`${e.r}: ${e.m}`);
}
};
}
let monitorData = monitor.start(service, action); // 开始监控
if (isHybrid()) {
ready(() => {
if ((window as any).Cordova) {
Cordova.exec(
(data: any) => {
monitor.end(monitorData);
success(format ? format(data) : data);
},
(err: any) => {
monitor.end(monitorData);
(error as any)(err);
if ((window as any)["wdui"]["WduiConfig"]["debug"]) {
alert(`${err.r}: ${err.m}`);
}
},
service,
action,
args
);
}
}, error);
} else if (defaultFn) {
monitor.end(monitorData);
success(format ? format(defaultFn()) : defaultFn());
} else {
monitor.end(monitorData);
error({
r: -10001,
m: `无法模拟${service}.${action}接口回调,请在App内使用此功能`,
});
}
}

/**
* cordova是否准备完成
* @ignore
*/
if (!deviceReady) {
deviceReady = false;
}

/**
* 准备函数
* @description 判断app中cordova是否准备完成
* @param success 成功回调
* @param error 失败回调,不在app内部
* @example ready(()=>{ console.log('成功回调')},()=>{ console.log('失败')})
* @example ready().then(()=>{console.log('成功')}).catch(()=>{console.log('失败')})
*/
export function ready(
success?: (data?: any) => void,
error?: (e: any) => void
) {
if (!success) {
return new Promise((resolve, reject) => {
ready(resolve, reject);
});
}
if (isHybrid()) {
if (deviceReady) {
success();
} else {
document.addEventListener(
"deviceready",
() => {
deviceReady = true;
success();
},
false
);
}
} else if (error) {
error({
r: -10002,
m: `Cordova未注入app的UA,不在App内?`,
});
}
}

最后

组件库真的是一个团队可以持续开发的核心项目,他能极大的帮助整个团队提高开发效率,最重要的是加班少了。同样的一个组件库的质量是非常重要的,必须让所有开发认为,组件库的内容一定是稳定的,不会有问题的,这样你的组件库才能推广出去,才能让团队成员爱不释手。

文章作者: 方长_beezen
文章链接: https://dongbizhen.com/posts/40365/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 BEEZEN