2018 年 11 月 25 日,由又拍云主办的 Open Talk | 2018 小程序开发者沙龙杭州站圆满结束,二维火前端开发工程师糕头在活动上作了《二维火小程序初探》的分享。

“2018 小程序开发者沙龙”是又拍云 Open Talk 继“2018 音视频技术沙龙”后推出的重磅活动,与大部分偏重营销、流量的小程序活动不同,又拍云 Open Talk 主办的 2018 小程序开发者沙龙更热衷于分享小程序开发过程的种种有趣经历和有益经验。

糕头,二维火前端开发工程师,主要负责二维火平台小程序、商家小程序等项目的开发、维护及拓新工作, 支付宝小程序项目负责人,本次主要分享了二维火对 WebSocket 的架构升级、授权流程的优化,以及第三方开发小程序的流程。

以下是分享全文:

小程序与 H5 的对比

image.png

小程序与 H5 优劣势对比图

小程序的优势是用户的成本非常低,即点即用,因为它已经集成在了微信的平台上,而且可以调用更多的手机硬件,例如录音、GPS 等;而 H5 开发的优点是开发速度快、成本低。那么如何把 H5 的优点集成到小程序上呢?这就要讲一下小程序的一个组件“内嵌”。二维火采用的是混合开发的模式,不仅解决了项目过大的问题,并且能够快速上线,同时也解决了工程维护的痛点。但是这样做要在小程序所属的微信公众平台上配置一系列的域名白名单,并且需要牺牲部分的业务,比如广告等,因此二维火也在逐步转向原生页面。

使用 WebSocket 解决页面实时更新的问题

二维火旗下基于微信小程序专为餐饮客户开发的应用程序,用户可通过小程序二维码进行扫码点餐、点外卖、排队、分享、查看订单等流程,因此原生页面需要解决购物车等页面的实时更新状态的问题,早期二维火使用轮询来实现业务,但是更新情况却不是实时的,总会有一定的时间间隔,所以使用了微信的 WebSocket ,但是微信对 WebSocket 有数量限制,从 2017 年的 1 个到现在的 2 个,虽然有增加,但是还不足以满足我们的需求。

首先介绍在老版本的 WebSocket 机制上,二维火所走过的坑。

image.png

老版本 WebSocket 机制

在老的流程上,每进入一个新的页面就要打开一个实例,然后再进入新的实例又要断开老的,每个页面都要反复操作这种行为。虽然业务的功能都实现了,但是这种情况下就会有痛点形成,它会极大增加链路次数以及导致信息丢失,因此需要解决这个问题。

image.png

新版本 WebSocket 机制

在新版本的流程中,全局只通过持续连接一个 WebSocket 实例,并能实现所有业务,此外引入了“房间”的概念,从打开新实例到断开实例,改为“进入新房间”和“离开房间”,目的是保持链路数只有一条,对此我们进行了架构的升级。

image.png

二维火 WebSocket 架构升级

二维火在原来的 WebSocket 架构升级,通过引入“房间”,以“房间”切割消息推送范围。前面提到在老的流程中会增加 WebSocket 的链接数,解决这个问题我们需要当前链接的地址是不变的,这样使得服务端根本无法区分,如何解决呢?我们通过发送事件,并携带上“房间”的类型和房间参数,比如购物车和订单页这两个房间类型决定了是在哪一个项目页面,门店的 ID 和座位号这两个房间参数决定了是在哪家店的哪一个座位,这样就能够让服务端区分出唯一性。简单来说,“房间”的作用是为了让服务端能够区分出每个人的不同

赋予客户端订阅消息的能力和限定服务端推送只能推送客户端订阅的消息,当连接上 WebSocket 的服务端,并不会马上推送消息给 C 端,只有我们发送了订阅消息给服务端,服务端接收到订阅消息后,才会往 C 端传送信息,并且传送的信息只能是 C 端订阅的消息,从而杜绝了盲目推送和限定了推送的内容。

第四,增加了异常处理的约定,对异常处理的约定和有 token 的失效,服务端拒绝链接等,因为业务的不同,有不同的约定内容,在这里就不一一细说。

在技术底层上,二维火为了兼容 H5 和小程序,分别用了两个不同的架构。H5 用的是 socket.io ,小程序用的是 socket.io-mp-client。

image.png

时序图

通过上述的概念,我们完成了以上的时序图。在小程序的全局入口,建立连接,并且带上每个用户的唯一 token 进行辨别是否有效。在 token 有效的前提下,连接建立成功后,由客户端发送 join_room 事件,并且带上房间类型和房间参数。服务端监听这个事件,并且生成唯一且确定的房间 ID,加入对应的房间。然后通过和后端约定的协议格式,向服务端发送 subscribe_message 事件,并且带上需要订阅的事件名,然后对该事件名进行监听,之后服务端进行接收并且进行察看,如果有客户端满足要求,那么便对所有满足要求的客户端进行推送消息。

在离开一个页面的时候,就向服务端发送 unsubscribe_message 事件,并且带上订阅事件名,然后离开事件,那么服务端将不再推送该订阅事件。如果关掉小程序,并进行所有链接断开,服务端判断我们离开了所有房间,取消了所有的订阅事件,这样就能够实现服务端的精准配送,减少了有效消息的丢失以及不必要的消息的推送。

总结一下优化思路,第一,始终保持全局只有一个连接的 WebSocket ;第二,掌握主动权,需要服务端才能推送,而不是一连接服务端就完全推送。

隐式授权,提高用户体验

讲到了推送消息,必然需要用到用户的个人信息。那么如何拿到用户的个人信息呢?这里就需要用户授权。但是一直让用户点击授权是十分影响用户体验的,需要做到隐式授权。

隐式授权是尽可能让用户无感知授权,甚至做到无需授权,那么怎么做到这点?我们先来了解一下为什么授权。所谓授权,就是需要以下几个信息:用户的个人信息、用户的openid、用户的 unionid。

我们可以利用微信的 wx.login 接口实现小程序的静默授权。首先它是静默的且易获得 openid,在满足一定条件的情况下甚至能返回 unionid,比如用户关注过公众号等。因此,我们假设如果在拿到用户信息之后,能够缓存 unionid 和用户信息,只要取得 unionid 就可以拿到用户信息。如果在用户授权以后再缓存 openid 和 unionid 的映射关系,那么之后我们只需要调用微信 login 接口,拿到 openid 就可以拿到用户的信息。简单的流程就是通过 openid 去获得 unionid 的映射关系,然后再获得用户的信息

image.png

流程图

上图是最开始也是最简单的一条流程,新用户在进入小程序时,会调用微信的 login 接口,获取加密串给服务端,判断是否能够拿到 unionid。如果没有拿到,再去判断 openid 是否和 unionid 有映射关系。如果在这一步的操作都没有映射关系,那么就需要用户点击授权,这样是必定能够返回 unionid 和用户信息,然后服务端就会进行缓存,用户在下次进来的时候,只需要通过 openid 去获取 unionid 的映射关系,就可以拿到用户信息,不需要再进行一次次的授权,影响用户体验。但是,这个流程必定是需要调用微信 login 接口,而万一出现微信接口挂了,可能会导致业务流程走不下去,所以二维火在这个基础上进行了优化。

image.png

优化后的流程

在优化后的流程中,我们加入了一个 token,通过判断缓存中的 token 是否有效,来减少请求微信授权的次数。首先察看缓存中是否有 token,并且 token 是否有效。如果有效,服务端便会告诉我们这是否是游客,如果不是游客,那么登陆流程便会结束,也是最快最优的流程。如果此账户是游客,或者 token 是失效的情况下,才会调用微信的 login 接口去获取加密串,然后服务端再返回给我们信息。这就是我们优化的流程,通过加入自身的验证 token 来躲过请求微信 login 接口,这样一套完整的授权流程便能够走完,能够使用户登陆的体验更加友好,除了第一次需要用户点击授权之外,之后用户就不需要再点击授权。

提供平台服务能力,让商家拥有自己的小程序

因为很多商家想要自己的小程序 Logo,同时为了解决开放平台绑定公众号的数量有限的痛点,所以二维火需要帮助商家开通开放平台。二维火以第三方服务平台的身份主要为商家做了以下四个功能:一是帮商家创建开放平台,二是打通用户关系,三是优惠券放入微信卡包的生态之中,四是帮商家代注册小程序。具体来看一下实现的思路。

image.png

商家微信开放平台的结构支持包

上图是商家微信开放平台的结构支持包,包含了多个商家公众号、多个商家小程序,一个开放平台绑定的公众帐号是有限定的,所以不可能无限制地将商家的公众号绑定到二维火的开放平台中,这时候就需要帮助商家拥有自己的开放平台,快速建立自己的用户体系。如此,商家能够更好更有针对性地维护用户。

image.png

微信开放平台的文档

上图是微信开放平台的文档,通过微信给我们开放的接口获取预售码,然后引导商家通过公众号授权给二维火,通过微信提供 API,帮助商家开通开放平台,并且绑定商家的公众号。

image.png

快速注册小程序流程

在帮商家开通了开放平台以后,就需要帮助商家打通商家小程序,也就是属于他们自己的小程序,流程也如上图,简单来说,只有三步,首先获取授权码然后调用微信开放接口进行快速注册小程序,第三步是补充小程序的基本知识,把小程序绑定到商家的开放平台

image.png

掌柜端

上图是二维火的掌柜端,即二维火的手机应用 App,让商家填写小程序一些基本信息的界面,需要用到小程序的名称、头像、简洁描述以及资质等。

接下来就是通过微信提供的API,带上我们之前获得的小程序的 APP ID 以及待注册的开放平台的 APP ID ,就可以把小程序绑定到我们帮商家申请的开放平台上。同样,我们可以通过微信提供的 API 进行版本管理,具体可以参照微信开放平台的文档,这样商家小程序的体系就基本完成了。具体就是先帮商家创建开放平台,然后通过它的公众号绑定在它的开放平台上,再帮助商家创建小程序,再将小程序绑定到开放平台上,这样体系就能够完成。

image.png

平台用户和商家用户打通

如何帮助商家的用户和二维火的用户进行打通呢?这里就要提到平台和商家的二次授权,用户在进入二维火系统的时候,会优先判断是否授权平台公众帐号,然后再查询当前店铺或者当前业务场景下是否有绑定商家公众号。如果有绑定,再判断当前用户是否需要授权商家公众帐号,然后对上面两次授权的信息进行二次关联,这样便完成了业务中的平台和商家的二次授权。完成了商家的二次授权以后,通过 unionid 的映射关系,可以帮助商家打通平台用户和商家公众帐号粉丝的用户。后台进行平台用户管理的同时,可以实现更多的营销功能,如精准配送、定向推送、卡券的功能等,这样商家不仅可以推送到自己的商家用户,还可以推到平台用户上。

image.png

微信卡券

下面介绍微信卡券的功能,如何打通商家卡券与微信卡券的互通?上图是从二维火平台同步到微信卡包,和微信卡包回到二维火平台的实践,需要注意的是,要在开放平台绑定公众号 APPID 和小程序 ID 才能够实现,然后通过微信开放的接口创建卡券和添加卡券这两个功能进行实现。

image.png

实现微信卡券功能的两个关键API

上图是实现微信卡券功能的两个关键API,具体内容可以参考官方文档,文档对这块的使用说明已经十分清楚了。

以上是我今天所分享的内容,希望能够帮助大家解决一些痛点。谢谢大家!

提问环节:

问题:你在分享中提到 WebSocket 在小程序中的应用,其实在各个业务线中,WebSocket 应用的场景还是蛮多的,那你们提出了一个架构是拒绝在每个页面新建一个实例,我想问一下,其中一些小程序在支持 WebSocket 里有一些它的优势和坑,因为我们业务之前有一个音视频的功能,涉及到状态同步,但是我们在做前一部分的时候,发现音频在小程序的推出后台,状态并不能实时变更,会延迟,那么你们是如何规避这个问题的?

糕头:在此过程中,在你们离开小程序的时候,就需要发送信息告知服务端,告诉服务端再进行计算,在下次进入的时候,再进行返回信息。

提问:另外一个问题,我们全局维持了唯一的 WebSocket 实例,它在负责数据同步上会有问题吗?比如说可能客户端已经接收了一部分的增量信息,如果说小程序已经被刷掉了或者说退出后台了,或者用户进行了其他的操作,导致之前的信息全部被移除,那么下一次再建立通讯的时候,这个数据会和服务端进行全量的交互呢,还是它已经存储在本地存储当中,我们只负责增量信息就可以了?

糕头:你所说的问题,其实每次我们进入的时候,先进行接口请求,进行数据对比,然后去发送你所需要的信息,并且携带上,而不是服务端一味地推送,你只要把推送权掌握在自己手上,就可以实现信息不必要的丢失。就像你说到的,因为你的业务增量,就是说做别的会丢失,那么你可以先通过接口来获取目前正确的全量信息,之后在负责增量信息交互就可以

3333.jpg