小程序双线程模型
小程序开发框架的目标是通过尽可能简单、高效的方式让开发者可以在微信中开发具有原生 APP 体验的服务。
整个小程序框架系统分为两部分:逻辑层(App Service)和 视图层(View)。小程序提供了自己的视图层描述语言 WXML
和 WXSS
,以及基于 JavaScript
的逻辑层框架,并在视图层与逻辑层间提供了数据传输和事件系统,让开发者能够专注于数据与逻辑。
小程序的渲染层和逻辑层分别由2个线程管理:渲染层的界面使用了WebView 进行渲染;逻辑层采用JsCore线程运行JS脚本。一个小程序存在多个界面,所以渲染层存在多个WebView线程,这两个线程的通信会经由微信客户端(下文中也会采用Native来代指微信客户端)做中转,逻辑层发送网络请求也经由Native转发,小程序的通信模型下图所示。
小程序选取的是类似微信JSSDK之后这样的HyBrid技术,页面用Web技术渲染,辅之以大量的接口提供丰富的客户端原生能力。同时,每个小程序的页面都是使用不同的WebView渲染。
如果开发者可以直接通过JS操作界面的DOM树,那么一些敏感数据就毫无安全性可言,故微信提供了一个沙箱的环境来运行开发者的JS代码,这个环境不能有任何的浏览器先关的接口,只能通过JS解释执行环境,类似于HTML5的ServiceWorker启动另一个线程来执行JS。
但由于小程序是多WebView的架构,所以每一个页面都是不同的WebView渲染显示,所以单独创建了一个线程去执行JS,也就是逻辑层,而界面渲染的任务都在WebView线程里执行(渲染层)。即双线程模型。
小程序的双线程模型并不是使用 Web Worker 子线程,而是一个独立的“主线程”,这样能够保证相对较好的性能。
逻辑线程是一个只能够运行 JavaScript 的沙箱环境,不提供 DOM 操作相关的 API,所以不能直接操作 UI,只能够通过 setData 更新数据的方式异步更新 UI。
注意:小程序框架的逻辑层并非运行在浏览器中,因此 JavaScript 在 web 中一些能力都无法使用,如 window,document 等。
事件驱动的通信方式
注意上图渲染线程和逻辑线程之间的通信方式,与 Vue/React 不同的是,小程序的渲染层与逻辑层之间的通信并不是在两者之间直接传递数据或事件,而是由 Native 作为中间媒介进行转发。
整个过程是典型的事件驱动模式:
渲染层(也可以称为视图层)通过与用户的交互触发特定的事件 event;
然后 event 被传递给逻辑层;
逻辑层继而通过一系列的逻辑处理、数据请求、接口调用等行为将加工好的数据 data 传递给渲染层;
最后渲染层将 data 渲染为可视化的 UI。
而这样逻辑与渲染分离的线程分工模式一方面能够保证运行在逻辑线程沙箱内的 JavaScript 代码是线程安全的,另一方面由于渲染线程的计算量非常小从而保证了对用户交互行为的快速响应,提高了用户体验。
总的来说,跟浏览器的线程模型相比,小程序的双线程模型解决了或者说规避了 Web Worker 堪忧的性能同时又实现了与 Web Worker 相同的线程安全,从性能和安全两个角度实现了提升。可以概括地说,双线程模式是受限于浏览器现有的进程和线程管理模式之下,在小程序这一具体场景之内的一种改进的架构方案。
小程序的基础库
小程序的基础库是通过JavaScript编写的。它可以被注入到渲染层和逻辑层运行,主要应用于:
- 在渲染层,提供各类组件来组建界面的元素
- 在逻辑层,提供各类API来处理各种逻辑
- 处理数据绑定,组件系统,事件系统,通信系统等一系列框架逻辑
这个基础库它是提前内置在微信的客户端中的这样做的好处:
- 降低业务小程序的代码包的大小
- 可以单独修复基础库中Bug,无需修改到小程序的业务代码
小程序与普通网页的区别
网页开发渲染线程和脚本线程是互斥的,这也是为什么长时间的脚本运行可能会导致页面失去响应,而在小程序中,二者是分开的,分别运行在不同的线程中。网页开发者可以使用到各种浏览器暴露出来的 DOM API,进行 DOM 选中和操作。而如上文所述,小程序的逻辑层和渲染层是分开的,逻辑层运行在 JSCore 中,并没有一个完整浏览器对象,因而缺少相关的DOM API和BOM API。这一区别导致了前端开发非常熟悉的一些库,例如 jQuery、 Zepto 等,在小程序中是无法运行的。同时 JSCore 的环境同 NodeJS 环境也是不尽相同,所以一些 NPM 的包在小程序中也是无法运行的。