小程序渲染架构设计
One 什么是小程序
Ⅰ 小程序概念 微信小程序算是小程序的鼻祖了,2017年1月9日微信正式上线了小程序。在探究小程序技术架构之前,我们先看看小程序究竟是什么,微信官网对微信小程序的产品定位及功能介绍是: “微信小程序是一种全新的连接用户与服务的方式,它可以在微信内被便捷地获取和传播,同时具有出色的使用体验。”
这个介绍有种看了跟没看一样的感觉。网上对于微信小程序是什么还有一个介绍的版本: “小程序是一种不需要下载安装即可使用的应用,它实现了应用「触手可及」的梦想,用户扫一扫或搜一下即可打开应用。也体现了「用完即走」的理念,用户不用关心是否安装太多应用的问题。应用将无处不在,随时可用,但又无需安装卸载。”
这个概念就更清晰一些,可以看出小程序是众多实例运行在一个宿主应用中,小程序本身也是一种可插拔的外接应用。
Ⅱ 用户角度的小程序 下面从用户使用交互角度来看一下小程序:
(图1)
iOS:从小程序独立性角度(小程序与小程序之间,小程序与宿主应用之间切换)来说,BATT 的小程序与招商银行的小程序基本交互相似。 Android:从交互上来看BATT 的小程序都可以看做是独立的应用程序,独立存在于后台(多进程),可以在小程序与小程序之间,小程序与宿主应用之间切换。可以直观的理解为这类小程序为小程序应用。招商银行的的小程序是与宿主应用共存的,也就是在一个进程中,不能在小程序与小程序之间,小程序与宿主应用之间切换。这类小程序可以直观的理解为小程序页面。
BATT: 微信,支付宝,头条,百度小程序。由于交互相似,所以并称。所以,从用户使用角度来看,BATT的交互体验更有优势。从对小程序概念的理解来看,各app理解有所差异,但这并不影响功能层面的使用。
Ⅲ 平台角度的小程序 最早应用小程序的微信为什么会创造出小程序这个东西呢?它到底有什么作用?在我看来,主要目的还是在于管控为目的,使用了多个手段来实现,主要管控在于两个方面:
UI管控:以微信为例,微信自己定义了一套DSL,而不是用HTML来开发页面。这样就不能让开发者随意开发,而是在微信的DSL框架中开发。开发者写的DSL具体转换成什么,是通过什么渲染,都是微信平台来决定。基于自定义的这套DSL,可以更好的做代码管控方面的工作,比如:请求白名单,代码扫描等。
服务管控:还是以微信为例,微信中的宿主平台提供的服务(比如:支付,微信运动,卡券,发票,用户账号信息等)对于无论是二方使用者还是三方使用者,都有权限管控的需求。目的也是不能让接入的小程序,在没有授权机制的前提下,随意调用微信基础服务。
Two 小程序技术架构 基于从用户角度的体验需求,以及平台角度的管控需求,我们来看看BATT系小程序在技术上做了哪些达到了这些目的。
Ⅰ 渲染流程 下图是小程序的渲染流程,里面包含了部分技术选型,后面的部分会提到:
(图2)
Ⅱ 主要技术点
- DSL(Domain-specific language):
(图3:图片来源于网络)
源代码先解析成AST,解析之前它是遵循语言规则的文本,解析之后成为与输入文本完全相同的树形结构,这个过程是可逆的。然后再对AST遍历以及替换,这个过程对于前端来说类似于DOM树的生成,最后根据修改后的AST生成编译后的代码。我们以JS为例,用acorn生成的AST,同样我们也可以使用其他的解析器,例如:babylon,esprima等,下面是一个简单的例子(限于篇幅,右侧的AST树没有完全展开,读者可以到astexplorer上生成结果):
(图4)
由于小程序的渲染容器有可能是Webview容器,原生Native容器,Flutter容器(虽然Flutter也是Native渲染,为了与原生Native区别,这里把它单独出来,下同),所以我们可以借鉴前面的代码转换器的思路,用AST抹平具体渲染容器的区别,下图是DSL转换的整体思路:
(图5)
有了以上的的设计并不是大功告成,还需要有很多需要做的工作要做。我们可以简单的把DSL的处理分为编译时和运行时,编译时负责把DSL代码预编译为目标代码,目标代码可以在相应的运行时环境执行。生成的目标代码的作用是,可以在具体的运行时通过当前环境的参数来执行出实际的代码,简单的理解就是为了在多渲染环境运行的一个适配器。
对于编译时来说,从零写起肯定是不现实的。首先我们继续上面AST的话题,上面已经提到了几个AST的解析器:acorn,babylon,esprima。当然还有很多其他的例如:cherow,espree,shift等。所以我们不用再造一个轮子,下面用babylon举例,因为babylon在babel中使用,会与最近的JS功能同步,并且API设计良好易于使用。babel是js编译器(并不仅仅是ES6支持的工具包,否则就变成了类似于Android里面的support包了),可以用于代码压缩,语法转换等。对于生成目标代码的过程:解析(babylon),转换(babel-traverse)都有很好的支持。由于不同的渲染容器有不同的组件库和API,同样功能的组件或API的使用也不尽相同,所以需要封装出适配层代码。
对于运行时来说,只需要把编译生成的适配代码生成具体渲染环境的代码执行就可以了。这里比较类似babel把ECMAScript新版代码转换成旧版代码的逻辑比较像。
- Native渲染
- Android多进程:
- 多线程:
(图6)
上图展示了逻辑层与视图层的通信过程,通信通过Bridge中转,利用发布/订阅模式。视图层通过触发UI事件,会把事件通过bridge传递到Native,Native再通过bridge把事件中转给逻辑层,逻辑层处理事件完成后,把数据再通过bridge传递到Native,之后再由bridge返回给视图层做渲染。
优化: 逻辑和渲染分离之后,逻辑线程需要把数据发送给渲染线程,渲染线程需要把事件发送给逻辑线程,这都需要序列化为字符串进行传输。这样会带来一个问题,频繁的数据传输,和单次大数据量传输都会带来性能问题。针对这个问题,支付宝小程序的设计思路比较值得借鉴,支付宝小程序重新设计了V8虚拟机,让逻辑和渲染都有自己的Local Runtime,存放私有的模块和数据。又提供了共享的Global Runtime的Shared Heap来共享数据,这样依然保证了逻辑和渲染的隔离,又减少了序列化和传输成本。
- 预加载:
- 离线包(分包):
(图7)
Ⅲ 技术选型
- IDE
(图8)
对比结果简单的说,两者开源协议都比较友好,如果重视代码安全性或者兼容XP需求,就选择NW.JS,也是国内厂商的选择;如果从开发支持角度来比较,就选择Electron。
- JS引擎
(图9)
微信小程序旧版本用的JScore,新版本用的V8;支付宝小程序用的重新设计的V8;头条小程序也是使用的V8;可以看到V8的中标率还是很高的,而且开源协议也比较友好。
Three 结语 【小程序渲染架构设计】本文算是介绍了一种小程序渲染架构的一种实现方式,就小程序平台本身来说,还有一些其他的功能和优化点,比如:小程序路由,Debug包加载,埋点统计,虚拟Dom的优化等。文章只是介绍了一些主要流程和技术点,真正做一个完善的小程序平台还是需要很多细节需要考虑的,就小程序开发者的角度来说,也是有优化空间的,比如:骨架屏。做一个小程序平台需要多平台多种技术能力的综合应用才能不断完善,随着新技术的涌现,未来会有更多的技术应用到小程序中。
推荐阅读
- 一个小故事,我的思考。
- 家乡的那条小河
- 一个人的碎碎念
- 野营记-第五章|野营记-第五章 讨伐梦魇兽
- 昨夜小楼听风
- 2021-02-17|2021-02-17 小儿按摩膻中穴-舒缓咳嗽
- 基于微信小程序带后端ssm接口小区物业管理平台设计
- 2019.4.18感恩日记
- 那件我们忽略的小事叫感恩
- 你有婚内虐待行为吗()