当一切就绪后,就要开始进行 API 规划,这一块是整个 Hybrid 框架中非常重要的内容,毕竟对于前端页面来说,只会通过 JS API 来调用功能。 基本上,API 调用起来是否方便简洁影响着整个体验。
这里将内容细分为以下几点:
API 调用关乎着整个体验,我们约定所有 API 统一采用如下调用方式
- quick.模块名.方法({
- 参数1: "",
- 参数2: "",
- success: fucntion(result) {
- // 成功回调
- },
- error: fucntion(error) {
- // 失败回调
- }
- });
约束说明
混合开发框架最重要的一个功能就是__将原生功能以 JS API 形式提供给前端页面调用__
本框架的 API 规划如下:(这个项目中仅规划了部分功能,实际使用中自行拓展即可)
- quick
- |- ui // 系统ui组件
- | |- toast
- | |- alert
- | |- confirm
- | |- prompt
- | |- showWaiting
- | |- closeWaiting
- | |- actionSheet
- | |- pickDate
- | |- pickTime
- | |- pickDateTime
- | |- popWindow
- |- page // 页面(webview)管理
- | |- open
- | |- openLocal
- | |- close
- | |- reload
- |- navigator // 导航栏控制
- | |- setTitle
- | |- setMultiTitle
- | |- hookSysBack
- | |- hookBackBtn
- | |- setRightBtn
- | |- setLeftBtn
- | |- setRightMenu
- |- auth // 权限认证相关
- | |- getToken
- |- device // 设备相关
- | |- setOrientation
- | |- getDeviceId
- | |- getNetWorkInfo
- | |- getVendorInfo
- | |- closeInputKeyboard
- | |- vibrate
- | |- callPhone
- | |- sendMsg
- |- runtime // 运行环境
- | |- launchApp
- | |- getAppVersion
- | |- getQuickVersion
- | |- getGeolocation
- | |- clearCache
- | |- clipboard
- | |- openUrl
- |- util // 其它工具
- | |- scan
- | |- selectImage
- | |- cameraImage
- | |- selectFile
- | |- openFile
上述规划的是最常用到的功能,具体每一个 API 的介绍,传入参数传出参数等会在框架的 API 文档中提到
如果整套框架要对外开放(如允许第三方按规范接入),那么权限认证是必不可少的!
如果没有权限认证?可以想象下,随便一个页面就能调用任意 API,获取敏感信息。。。
那么权限认证应该是怎样的呢?根据不同需求,可以划分一个等级。
当然了,这个框架是后面的项目级别的,大批量的项目都采用的,因此直接简单配置即可(示例中是在原生预留了这个入口,但没有实现)
这里暂不谈具体实现(实现可参考源码),只说说权限认证的流程:(如钉钉、微信中的)
- quick.error(function(error) {
- // 处理错误
- });
- quick.config({
- ...
- });
- quick.ready(function() {
- // TODO: 处理验证成功后的事情,例如调用api
- });
可以看到,和其它框架一样,前端也是 config,ready,error 三步, 然后原生接收到 config 时,内部进行校验(校验内容可以是检测页面地址是否符合域名白名单等等...)
而且,如果 config 失败或者没有校验,那么敏感 API 都无法调用
无需校验就可用的 API
并不是所有 API 都需校验后才能用,我们约定以下 API 默认就可用(这里是一个粗糙的划分,实际上可以精确到每一个 API)
- ui模块的所有API
- page模块的所有API
- navigator模块的所有API
这样做的好处是,如果不涉及到一些敏感数据,可以无需校验,提供效率(校验如果规则毕竟重的话对速度还是有影响的)
有一个全局变量 quick,但 API 并不是直接绑定在全局变量下,而是按模块划分,譬如
- quick.ui
- quick.device
- quick.page
- ...
这里说明一下,用模块化划分,除了前端调用会更清晰外,原生进行 API 定义与组件 API 拓展时也更方便。
组件 API 的拓展机制
关于组件 API 的拓展机制,这里也不具体描述如何实现(实现可参考源码),仅说说大概是一个什么样的东西。
默认情况下,框架会注册以下组件(前面已经规划的所有模块)
- ui
- page
- navigator
- auth
- device
- runtime
- util
但是假设某项目中突然遇到了一个需求,要新增一个支付功能,并且要以 API 的形式提供给 H5 页面调用,该如何实现呢?
注意,这个框架设计的初衷是可以供 N 个项目使用,所以不可能所有的功能都集成进入框架中的,各个项目可以拓展自己的组件
这时候就需要规划这个拓展机制了,如下
- // 1.前端config时,传入需要注册的组件别名
- quick.config({
- jsApiList: ['pay', 'speech']
- });
- // 2.原生框架中,接收到config后,基于传入的别名,去对应项目配置文件中查询路径,然后将对应路径的API实现类注册
- // 对应的组件的API实现类不是放框架中的,而是由各自的项目管理的,到时候框架就是一个固定的库,给各个项目引用
- // 代码实现这里省略
- ...
- // 3.前端中,通过一个固定的方法,调用刚注册的组件API中的功能
- quick.callApi({
- name: 'xxx',
- mudule: 'pay',
- ...
- data: {},
- success: function(result) {},
- error: function(error) {},
- });
通过这一套机制,可以保持框架的可拓展性,就算应用不同的项目中,N 多的功能,也能通过这种方法拓展,保持一致的使用
在大体功能都实现后,接下来还需要做一些优化功能,具体效果可以是简化调用,也可以是方便调试
部分 API 支持 Promise
前面定义的 API 中都是基于普通的回调进行的,在多层回调嵌套时仍然会毕竟麻烦,因此可以拓展支持 Promise 调用方式
在拓展前,先定一个基调:所有短期回调都支持 Promise 调用,长期回调不建议使用 Promise
因为长期回调涉及到多次回调(比如右上角按钮),所以不建议使用 Promise,如果强行要使用,这些 API 调用完毕后马上就会进入 then
具体实现参考源码,这里稍微对比下普通调用与 Promise 调用
- quick.ui.alert({
- title: '提示',
- message: 'sd#ddd测试',
- success: function(result) {
- console.log('点击alert成功');
- },
- error: function(error) {
- console.error('失败:' + JSON.stringify(error));
- }
- });
- quick.ui.alert({
- title: '提示',
- message: 'sd#ddd测试',
- }).then(function(result) {
- console.log('点击alert成功');
- }).catch(function(error) {
- console.error('失败:' + JSON.stringify(error));
- });
上述还只是没有嵌套的对比情况,有嵌套时,区别更大
PC 端调试 API
类似于钉钉的调试页面,这里也规划了一个在 PC 端调试 API 的页面(基于 websocket),后续会有更详细的说明。
原理方面的源码可以先参考 github.com/dailc/node-…
可以先预览下效果
其它
其它优化后续再介绍
github 上这个框架的实现
来源: https://juejin.im/post/5a406b2af265da430c120a50