前天同事问我公司内部的小程序怎么对接的,我回忆了一下,简单记录了一下前端同学需要注意的点。

背后还有小程序架构、网络策略等等。当时恰逢小程序架构调整,(老架构的时候我就发现了有一个问题点可以优化,但是跟那边人反馈之后,人家表示不要我管😂,新架构时发现这个问题还巧妙的遗留下来了😮)我虽然不负责那块,但是本着这样不优雅的原则,还是跟新架构的对接人讲了我的优化方案,讲明白了之后,同时上报各自直系领导,并建议我领导牵头开会推动。最后,无奈存量数据太多,老架构那边权衡之后决定不改动。(多说了几句,权当记录一下)

1、背景

公司研发的一款服务软件App(姑且称为“大地”),提供了包涵消息、待办、工作台、同事圈和通讯录五大功能模块,其中,工作台里集成了包括公司的移动客户端、PC端以及第三方平台的部分功能/服务(统称为“应用”)。

我今天要讲的是这个集成平台以什么方式展现“应用”,答案是:借鉴了微信的架构,自研了“小程序”接入“应用”。

我司小程序具有一种相对开放能力(面向全公司),赋能业务快速数字化、场景敏捷迭代,并且可在“大地”上便捷的获取和使用,同时具有完善的使用体验(这就是严格的接入审核标准带来的好处)。

在“大地”开发者平台,创建小程序会自动创建配套的公众号(公众号是为了推送消息使用,可订阅)。小程序开发不限制技术选型,开发完成之后按照小程序接入规范打包上架小程序,审核发布。

简单来说,可以把“大地”看成是一个“钉钉”,我现在要把我们的业务功能投放到“大地”上,就需要接入“大地”小程序,以小程序的方式在“大地”上为用户提供服务。

小程序架构:Cordova框架做的WebView,运行我开发的前端程序,通过Nginx帮我把请求代理到微服务网关,由网关转发到目的主机处理请求。它虽然看上去是一个Native App,但只有一个UI WebView,里面访问的是一个Web App,对我来说就是开发一个H5应用调用一些所需的JSBridge,也就是所谓的Hybrid App

下面看一下本地开发中的一些问题,以及我是怎么处理的

2、问题

Hybrid App本地开发过程中没有真实的Native环境的,同样也无法使用JSBridge,这就会带来一个问题:跟原生交互的行为只能发布小程序才可以调试,本地玩不了,这…,相当fuck。

目的是想让本地开发同小程序测试环境具有相同的体验,我的想法是在本地模拟JSBridge的方法,尽管不能带来真实的效果,至少触发了某个行为之后要有个反应,不至于让操作流程看起来像是“脱节”的(实际跟原生的交互行为并不多,比如:拍照、弹窗提示、定位等等)。

因此,我要做的就是本地模拟JSBridge的一些方法,开发时触发了这些原生交互行为之后提示一些信息,等到上架小程序测试环境时,在手机上会用真实的JSBridge方法自动替换掉我模拟实现的方法。

于是我就开始了下面的准备工作。

  1. 搞清楚JSBridge运行的原理
  2. 本地模拟JSBridge的方法
  3. 上架小程序是自动使用真实的JSBridge

3、了解JSBridge

JSBridge:望文生义就是jsNative之前的桥梁,而实际上JSBridge确实是JSNative之前的一种通信方式

简单的说,JSBridge就是定义NativeJS的通信,Native只通过一个固定的桥对象调用JSJS也只通过固定的桥对象调用NativeJSBridge另一个叫法及大家熟知的Hybrid app技术。

Hybrid app本地开发如何调用JSBridge - 图1
了解即可,更多的请参考

下图展示了JSBridge的工作流程👇

Hybrid app本地开发如何调用JSBridge - 图2

上图中左侧部分正式我要做的,具体请看下文

看累了,三连一下,回看不迷路哟😉

3.1、我们的JSBridge

推测“大地”那边的JSBridge应该是自己写的,没有初始化JSBridge的操作

当调用JSBridge时,必须在页面完全加载完成之后才能够拿到全局的JSBridgeCordova框架提供deviceready事件,该事件触发的时候表示全局的JSBridge挂载成功。(注意:这就是我接下来操作的切入点,嘻嘻)

简单写下如下:

  1. document.addEventListener('deviceready', function () {
  2. console.log('deviceready OK!');
  3. JSAPI.showToast(0, '提示信息')
  4. }, false)

需要注意的是,在开发环境,是没有 deviceready 事件的,所以上面的代码并不会执行,只有在app里面运行的时候才会执行。

思考:

JSBridge必须是在deviceready事件触发后方能使用的,因此首先要做的就是自定义deviceready事件,本地环境可以在load事件里触发自定义deviceready事件,生产环境下监听deviceready事件即可

Hybrid app本地开发如何调用JSBridge - 图3

4、JS发起自定义事件

我是用 CustomEvent 构造函数,继承至 Event,文档看这里

  1. 用法
  1. new CustomEvent(eventName, params);
  1. 示例

创建一个自定义事件

  1. const event=new CustomEvent('mock-event');
  1. 传递参数

这里值得注意,需要把想要传递的参数包裹在一个包含detail属性的对象,否则传递的参数不会被挂载

  1. function createEvent(params, eventName = 'mock-event') {
  2. return new CustomEvent(eventName, { detail: params });
  3. }
  4. const event = createEvent({ id: '0010' });
  1. 发起事件

调用dispatchEvent方法发起事件,传入你刚才创建的方法

  1. window.dispatchEvent(event);
  1. 监听事件
  1. window.addEventListener('mock-event', ({ detail: { id } }) => {
  2. console.log('id',id) // 会在控制台打印0010
  3. });
  1. 示例:
  1. document.body.addEventListener('show', (event) => { console.log(event.detail); });
  2. // 触发
  3. let myEvent = new CustomEvent('show', {
  4. detail: {
  5. username: 'xixi',
  6. userid: '2022'
  7. }
  8. });
  9. document.body.dispatchEvent(myEvent);

了解了自定义事件之后,通过自定义事件模拟触发deviceready事件,这样上面的 deviceready 事件监听就可以执行了。

注意:这里还要确定一个问题,在什么时候触发自定义事件deviceready呢?

5、确定 deviceready 事件执行时机

  • 只需要编写如下代码,查看输出结果即可
  1. window.addEventListener('load', function () {
  2. console.log('load OK!');
  3. }, false);
  4. document.addEventListener('deviceready', function () {
  5. console.log('deviceready OK!');
  6. }, false);
  • 结果输出
  1. load OK!
  2. deviceready OK!

由此可知,执行顺序:load —> deviceready

6、自定义事件模拟Cordova deviceready事件

  1. 自定义deviceready事件
  2. 根据上面测试执行顺序得出的结论,我在load事件里触发自定义事件
  3. 在开发环境下模拟一些用到的JSBridge-API,比如下面写到的 JSAPI.showToast() 方法
  • mockEvent.js
  1. if (process.env.NODE_ENV === 'development') {
  2. // 自定义事件
  3. let myEvent = new CustomEvent('deviceready');
  4. // 模拟JSAPI事件
  5. window['JSAPI'] = {
  6. showToast(type, desc) {
  7. console.log(type, desc);
  8. }
  9. }
  10. // ...
  11. // 开发环境下,在 原生 load 方法之后 触发自定义事件
  12. window.addEventListener('load', function () {
  13. console.log('load OK!');
  14. setTimeout(() => {
  15. document.body.dispatchEvent(myEvent);
  16. }, 100)
  17. }, false);
  18. }

7、封装deviceReady方法

实现在Cordova框架触发deviceready事件的时候感知到,以便于在deviceReady事件触发后执行JS-API

可用于开发环境和非开发环境

7.1、方式一

这里采用链式调用的方式,

以下这种借助 Promise 的实现,在这种场景下其实是不合理的👀
只是形式上类似,其实并不是

  1. 定义
  • mixin.js
  1. deviceReady() {
  2. return new Promise((resolve) => {
  3. window.addEventListener('deviceready', function () {
  4. resolve("ready go!");
  5. }, false);
  6. })
  7. }
  1. 组件内使用JS-API

使用JSAPI可以如下这么写

  1. this.deviceReady().then((res) => {
  2. console.log(res); // ready go!
  3. JSAPI.showToast(0, '提示')
  4. })
  5. this.deviceReady().then((res) => {
  6. JSAPI.getUserInfo((res) => {
  7. console.log(res);
  8. }, (err) => {
  9. console.log(err);
  10. });
  11. })
  1. 开发环境执行效果如下

Hybrid app本地开发如何调用JSBridge - 图4

7.2、方式二(推荐)

改写成通用的事件监听函数,支持链式调用

  • 开发环境下,由mockEvent.js文件里的dispatchEvent触发自定义的deviceready事件;
  • 小程序里运行,则由真实的deviceready事件触发
  1. 定义
  • mixin.js
  1. receiver(type) {
  2. let callbacks = {
  3. fns: [],
  4. then: function(cb){
  5. this.fns.push(cb);
  6. return this;
  7. }
  8. };
  9. document.addEventListener(type, function(ev) {
  10. let fns = callbacks.fns.slice();
  11. for(let i = 0, l = fns.length; i < l; i++){
  12. fns[i].call(this, ev);
  13. }
  14. });
  15. return callbacks;
  16. }
  1. 使用
  1. this.receiver('deviceready').then((ev) => {
  2. console.log(ev);
  3. JSAPI.getUserInfo(
  4. (res) => {
  5. console.log(res);
  6. },
  7. (err) => {
  8. console.log(err);
  9. }
  10. )
  11. })
  12. this.receiver("click")
  13. .then(() => console.log("hi"))
  14. .then(()=> console.log(22));

最后

当应用发布到app上,就是监听的真实的 Cordova框架的 deviceready 事件了,之后也就可以拿到真实的JSAPI了,以上只是为了在开发环境的时候模拟使用JSAPI。防止在开发环境下直接调用JSAPI飘红的情况,当然也是可以加try catch处理的,只不过个人感觉模拟事件使得代码看起来更加优雅别致一点,使用更加丝滑,酌情食用😁。

软件架构非常有意思,感兴趣的可以交流探索,嘻嘻。

相关文章


Hybrid app本地开发如何调用JSBridge - 图5

我是 甜点cc

热爱前端,也喜欢专研各种跟本职工作关系不大的技术,技术、产品兴趣广泛且浓厚,等待着一个创业机会。主要致力于分享实用技术干货,希望可以给一小部分人一些微小帮助。

我排斥“新人迷茫,老人看戏”的现象,希望能和大家一起努力破局。营造一个良好的技术氛围,为了个人、为了我国的数字化转型、互联网物联网技术、数字经济发展做一点点贡献。数风流人物还看中国、看今朝、看你我