小程序原理探究

712 阅读9分钟

0. 背景

在微信推出小程序之前,一直是使用H5-webview在微信app中动态网页的。
原因1: 「用户体验问题」,我们知道浏览器中 “GUI渲染引擎和JS引擎的互斥关系”,加上本身移动端比pc端还多了一个webview启动的额外耗时,因此在用户体验不好,主要表现在: 白屏问题 / 页面切换的生硬 / 点击的迟钝感。
原因2: 「WebView本地功能使用限制」,毕竟不是native-app,所以如果想要使用原生功能还是有限制的(这个问题后面微信推出了JS-SDK来解决webview功能限制问题)。 因此,由于体验问题无法从JS-SDK方面解决,因此微信需要从系统层面来体验问题,且同时保证原生功能使用的提供。

一、微信小程序生命周期 微信小程序按照编码级别划分可以分为三个层级:项目级别、页面级别、组件级别。每个级别有对应的生命周期以及特点。
项目级别 页面级别 组件级别

1、打开微信小程序的生命周期执行次序
(App)onLaunch --> (App)onShow --> (Page)onLoad --> (Page)onShow --> (Page)onReady。
2、当进入下一个页面的生命周期执行次序
(当前页面)onHide --> (下一个页面)onLoad --> (下一个页面)onShow --> (下一个页面)onReady。
3、当返回上一个页面的生命周期执行次序
(当前页面)onUnload --> (上一个页面)onShow。
4、当离开小程序的生命周期执行次序
(App)onHide。
5、当再次进入小程序的生命周期执行次序
微信小程序未销毁 --> (App)onShow --> (Page)onLoad --> (Page)onShow --> (Page)onReady;小程序被销毁-->(App)onLaunch--> (App)onShow --> (Page)onLoad --> (Page)onShow --> (Page)onReady。

小程序运行机制

在认识小程序运行机制之前,我们需要有一个认知,就是小程序的启动分为冷启动和热启动,两者的区别在于是小程序是否彻底被销毁,进而决定是否需要重新进行初始化。而热启动显然是更有利于短期内用户重复使用的用户体验。如果我们说小程序从前台切到了后台,那么就认为此时为热启动,如果我们说「启动」那么就认为是小程序的冷启动(被卸载了需要重新初始化)。即从小程序生命周期的角度来看,我们一般讲的「启动」专指冷启动,热启动一般被称为后台切前台。

小程序原理初探

参考文献:cloud.tencent.com/developer/a…

小程序模型 小程序中渲染层和逻辑层分别用来渲染界面的UI和处理界面的数据逻辑。其中渲染层运行的环境是webview。逻辑层运行的环境是 JsCore。需要注意的是逻辑层不管多少逻辑都始终在一个 JsCore/ V8 引擎中,但是如果是多个界面,那么就需要多个webview。如下图所示:多个界面时候,webview对应多个但JsCore始终只有一个。此外,渲染层与逻辑层之间的信息通信是通过native作为中介。对于网络请求也是通过native来实现请求转发。因此可以认为:native是一切信息通信的中介。 image.png

一、渲染机制:双线程架构

1.1 什么是双线程?

1.1.1 浏览器-运行时单线程

我们知道,在浏览器中一个Tab进程下,存在以下五个线程:

  • 1.GUI线程:负责将文档内容渲染为可视化UI
  • 2.JavaScript引擎线程:负责解析和运行JavaScript逻辑代码
  • 3.定时触发器线程:负责处理setTimeout/setInterval 定时器
  • 4.异步http请求线程:处理XHR异步请求,比如回调函数注册进事件队列
  • 5.事件触发线程:主要用来控制事件循环

这里我们再来了解一下“JS是单线程 && 浏览器多线程
1.JS是单线程,是指JS引擎是单线程的。因为如果js是多线程的话,那么如果一个线程新增dom,另一个线程删除dom,那么这个dom就会出现“操作悖论”。因此将js设计为单线程。 2.浏览器多线程,指打开一个浏览器tab,会有五个线程(如上所示)

那为什么常说JS是单线程?或者说浏览器是单线程? 其实,说js是单线程没有错,但是说浏览器是单线程是错误的,显然浏览器有五个线程,而这么说的人一般是基于“浏览器中的GUI渲染线程和JS引擎线程两者互斥”这一互斥准则,这样同一时间只能执行一个线程,所以说是单线程。其实准确的说,浏览器应该是“运行时单线程”,即GUI渲染线程和JS引擎线程不能在同一时间运行。
需要关注的点是:GUI线程和JS引擎线程是互斥的,具体表现在js的执行会阻塞UI的渲染。

1.1.2 小程序-运行时双线程

浏览器中存在着GUI渲染线程和js引擎线程的互斥关系,但是在小程序中,渲染线程和引擎线程则不存在这种互斥关系,两个线程是可以同时进行的。 问题:浏览器两个线程互斥是因为js可以操作dom,然后GUI渲染会有影响,所以必须互斥。那小程序中不互斥,怎么解决dom问题呢? 小程序中的双线程架构是指:小程序中一条线程负责渲染层工作,一条线程负责逻辑层工作,两个线程之间通过native层进行通信。

(1) 渲染线程 - webview

渲染层用来渲染页面结构,主要由 WebView 进行渲染,一个小程序可以存在多个界面,所以渲染层可能存在多个 WebView 线程。

(2) 逻辑线程 - JsCore

逻辑层采用 JSCore 线程运行 JS 脚本。逻辑层主要用来逻辑处理、数据请求、接口调用等。

1.2 为什么双线程渲染

1.安全方面:安全管控(首要原因),如果不是双线程会存在什么问题?
2.交互方面:相比于纯web有更好的交互体验
首先说小程序的交互体验肯定是比不上原生app的,app的响应速度肯定是最快的,相对指的的h5 web,网页开发的渲染线程和脚本线程是互斥的,二者是共享一个线程的,也就是说在运行脚本线程的时候可能会让页面失去响应,所以这也是为什么我们在开发网页的时候需要将script脚本的引入放在body的后面然后winow.onload去知道已经渲染完的节点。而在小程序中渲染线程和逻辑(脚本)线程相互独立,不能直接干扰对方,渲染线层和逻辑线程可以同时运行。联想一下,这是不是从设计层面就规避了react16推出fiber架构所为了解决的最重要的问题问题(一次大的更新任务会长时间占据着当前线程的资源,导致页面无法响应带来的交互问题!)。 总结:

  1. 浏览器单线程的问题,就是渲染线程和js线程互斥,那么就会存在js线程执行的时候,渲染线程就得等待,而渲染等待的过程就是更新等待的过程,因此这个时间越长越会导致不好的用户体验。因此小程序使用双线程,这样js执行的过程,渲染可以响应用户交互,不会有卡顿。
  2. 浏览器如果使用双线程问题。浏览器既然不使用双线程主要是因为,如果渲染线程和js线程同时操作dom的时候,就会产生一个矛盾操作,虽然解决多线程同步问题,可以引入“锁”机制,但是这同时会增加JS的复杂度,与之前设计js简单的初衷相悖,因此为了避免这种情况所以使用单线程互斥设计。但是互斥线程带来的交互响应延迟的问题有没有其他办法解决呢?react中采用了React Fiber进行处理 / 或者HTML5引入web worker,但是worker为了保证线程安全,不允许js操作dom。
    重点参考文献:www.finclip.com/news/f/1371…
  3. 既然小程序要改进单线程的问题,那么一定要首先保证单线程原有功能的基础上,更进一步优化其他问题。而不能解决这个问题,又把之前的问题暴露出来。因此小程序既然选择单线程就要解决之前双线程操作dom矛盾的问题。而小程序的做法是:禁止JS操作dom,那么也就不存在两个线程操作dom矛盾的问题。那是不是为了解决这个矛盾操作而粗暴的禁止呢?不,是为了安全,因为开发者可以操作dom的话,那么数据的安全性就无法保证,开发者可以拿到用户的信息等,因此不允许js操作dom,这样之前的双线程矛盾问题也就随之不复存在了。而小程序禁止的实现手段是:将js线程放在一个沙箱中,只给这个沙箱提供一个JS解释器以及对应的可操作的API接口,没有其余的操作。此外,小程序这样的双线程结构,若真能允许使用者操作 DOM 树,那么每次对 DOM 增删改查的操作,都会引发线程通信问题,只能让调用过程非常缓慢,这个设计就失去了意义。
    注意:web单线程并不是指单线程运行,而是指线程之间的互斥!

3.版本迭代方面:原生版本迭代更为便捷 小程序选择的是webview+原生组件的形式,hybrid方式,既享受到了webview页面的低门槛和在线更新,🈶️可以使用部分流畅的native原生组件,并且最重要的是空对开发的内容进行一定程度按的管控,同时在安全问题从设计层面就予以了解决。

2.JsBridge原理