首先热烈庆祝 2022 北京冬奥会开幕式盛大举行!北京冬奥盛典以二十四节气为序曲,从“雨水”开始,一路倒数,最终行至“立春”, 即标志这春天的到来,也预示新的一年开始, 祝大家新年快乐。
这篇文章主要记录下,目前市场上通用的运行时架构的设计方案及运行时的一些基本原理。
🐴 什么是小程序?
现在的生活或工作中,会遇到越来越线上店铺或业务,开始采用小程序形态进行投放,在各种线下场景里,我们也经常遇到通过扫码或链接的方式,在一些宿主软件中打开一个具备胶囊的页面。那么小程序到底是什么呢,可以通过不同的角度来看:
从开发的角度来说,小程序其实是很像一个移动端解决方案,整个生态对比 web 不仅提供了大量的基础组件和 API, 更桥接了诸多的设备能力及应用生命周期的托管能力。
从在商业的角度再看,小程序不仅具备商业运营属性,也具备监控主体的性质。例如微信结合微信小程序,就形成一定程度的商业护城河的效果,既可以包罗万象,又能极大程度降低平台对于项目的监管及质量管控成本
🌏 与移动 web 的差异
那么站在技术的角度,小程序对比传统 web 的差异到底是什么呢?
在传统 web 开发中,大概可以分为 SPA (单页) 和 MPA(多页) 两类:
在 MPA 下,往往是一个 webview 管理一个页面,但是这种方式会导致跨页通讯成本高、JS 对象无法共享等问题。 因此,逐步兴起的 SPA 框架 (vue, react, angular ...) 及路由控件 (vue-router, react-router, react-naviagtion 等) ,帮助我们可以在一个 webview 中管理多个页面,并且可以共享 js 内存,自定义转场动画等等...
这种模式似乎很不错了,但就移动应用来说,我们还会更加期待 web 能够在移动设备上拥有高性能的表现,并且有不输于原生 app 流畅性的体验。但是复杂且繁重的 javascript 的进程会严重影响应用流畅性,细致的优化也会给开发者带来诸多挑战。
除此以外,JS 的灵活性及移植性,也是一把双刃剑, js 基于 DOM, BOM 对象能够随意操作页面,DOM 行为几乎无法预测及监管。因此,需要提供一个类似 sandbox (沙箱) 的能力来隔离及感知到 js 的各种行为。
基于上述各方面的考虑, 在 JS 中只有线程隔离, 即:service-worker这一条路了,于是
我们将丰富的设备能力,更好的体验,开放安全的平台,线程的隔离,商业的运营能力,快速投放能力打包,便形成了小程序的基础生态雏形
小程序运行时
那么小程序的运行时是什么呢?
运行时在不同语言中含义有所不同,但基本可以概括为: 「运行在代码执行阶段的代码」
这就类似与 vue-runtime 提供了对于页面状态的劫持,生命周期的解析,method 的调用能力、nodejs 提供了 Javascript 运行时执行的能力等
那么小程序运行时则提供了在service-worker中操作页面(或视图) ,正确响应用户交互行为、并调度用户业务逻辑的能力。
运行时架构的基本流程
如何做到这种隔离呢,就需要考虑将用户对于页面的描述(XML)及操作页面的逻辑 (JS)分别 bundle 放入不同的线程当中执行
例如对于下面这个代码包,在构建侧解析后,会分别打出:
- 运行在 worker 中的 index.worker.js
- 以及运行在 webview 中的 index.render.js
在代码运行阶段,这两个 bundle 会分别注入到不同容器中所加载,如图所示:
在不同平台容器的载体不同,web 为 nw 或 serviceWorker, 在 native 则可能是 jscore 或 v8
容器加载的同时,运行时基础框架也会完成消息通道及 framework 层的初始化,在时间轴轴线上,整个小程序的启动流程示意如下:
小程序的架构下,当用户点击后,native 会下载相关资源包,包含基础库 + 用户资源包。native 将业务逻辑和渲染逻辑分别在 v8 / js-core 、webview 容器中执行。后续则依赖 js-bridge 在线程中进行相互通讯及逻辑的调度。当容器初始化完成后,native 会发起页面 load 消息,后续则根据 load 消息的参数进行 initialData 的收集,渲染层拿到 initialData 后,就可以解析 xml 或 dom 指令进行页面渲染了
后续框架侧将依赖 jsBridge 与容器侧完成双线程消息通讯,正确响应用户操作及各种逻辑的触发
总结
小程序并没有创造新轮子,而是基于传统开发方式并结合商业体系而产生的一种架构理念。
小程序的价值往往紧贴应用主体的流量,而应用主体本身,又依赖小程序的灵活性及低成本的特点快速完成流量的转换。这种模式的成功在微信,支付宝等各大流量平台都已验证
而基于双线程架构在不同平台,不同厂商,拥有不同玩法,比如多线层架构等等,核心的目的是一样的:
- 为了隔离 dom, bom 对象,限制开发能力及代码安全
- 为了更为丰富的设备能力及更好的体验
- 结合宿主 app, 打造私域流量及精细化运营