微信小程序运行机制解析
微信小程序运行机制解析
在微信小程序诞生之前,最流行的技术应该是Hybrid 混合开发。
Hybrid
混合开发有两个优势,一是跨平台,二是热更新。而微信小程序就更像是运行在微信这个特定环境下的
Hybrid
技术。
接下来从四个方面聊一聊小程序的运行机制,看一看,小程序相比Hybrid又有哪些创新点。
相信做过微信小程序开发的小伙伴都知道,微信有一个自己开发的语言
wxs
。那我们也不妨思考一下这么一个问题,既然
JavaScript
是小程序的主要开发语言,为什么微信小程序还要自己在创造一个语言呢?
一、小程序的启动机制
小程序启动会有两种情况
- 冷启动:是指用户首次打开,或者是小程序被微信主动销毁后,再次打开,这时候小程序是需要重新加载启动
- 热启动:假如用户已经打开过某个小程序,过了一段时间以后,用户再次去打开这个小程序,则小程序无需要再次重新启动小程序,只需要把后台运行的小程序切换到前台
这时候你可能会发现这么一个问题:小程序从本地读取缓存,那小程序什么时候可有应用新的版本呢?
小程序在冷启动时,会检测是否有新版本,如果发现有新的版本,就会异步下载新版本的代码包,并且同时用客户端本地的代码进行启动,也就是新版本的小程序,需要等到下一次冷启动时才会用上。如果你需要马上用上新的版本,则可以使用
wx.getUpdateManager
这个接口进行更新处理
那这时候你可能还有一个问题:微信什么时候主动销毁我们的小程序呢?
这里有两种情况,微信会主动销毁我们的小程序:
小程序在后台运行超过一定的时间,目前大概是五分钟
当短时间内,目前是5s 连续上次以上收到系统内存的警告(运行内存不足,请重新打开该小程序 )
当然了,这种情况对用户体验是非常不友好的,必要的时候,我们也可以使用
wx.onMemoryWarning
接口监听内存的警告事件,提请做一些处理
二、小程序的两种状态
前台状态:小程序启动以后,界面被展示给用户,此时小程序处于前台状态
后台状态:当用户点击右上角按钮关闭小程序,或者离开微信时,小程序并不会立刻终止运行,小程序还会再运行一段事件,这时处于后台状态。
这种状态有点类似于浏览器的tab页,没有关闭,只是被切换了
三、小程序的双线程架构
为了安全和管控,小程序使用双线程执行:视图线程和逻辑线程
- View 视图线程:主要提供各类组建、渲染界面
- App Service 逻辑线程:提供 API 处理业务逻辑
两个线程都是通过底层的
WeiXinJSBridge
进行通讯
接下来我们可以通过下面几个问题来理解一下小程序的运行原理
小程序怎么实现视图更新的?
简单来说,它是通过
setData
来实现的在Hybrid 应用中,是通过一个叫
evaluateJavaScript()
的方法来执行js的,在这个方法还可以通过回调获得js 方法的返回值webView.evaluateJavaScript("javascript: main()", new ValueCallback<string>() { @override public void onReceiveValue(String value) { ... } } )
在程序中,视图层和逻辑层的数据传输,实际上都是通过底层的
WeixinJSBridge
,通过原生evaluateJavaScript
实现的。setData
要求更新的数据首先会将这个数据转化成字符串,接着将这个字符串于代码拼接成JavaScript 脚本,最后,把拼接的内容传给evaluateJavaScript()
去执行。当然小程序在视图更新上也是有做
虚拟 DOM
的优化的。所以从数据到达视图层的更新,并不是实时进行的setData
可能遇到的“坑”?从前面我们可以看到,视图线程和逻辑线程是分开的,两个线程之间通过前置的
setData
方法来驱动数据交换,还要通过WeixinJSBridge
进行中转,这个中转的效率是非常低的。所以有时候安卓用户在进行界面滑动时,可能会感觉到页面卡顿,这是因为视图线程一直在努力进行渲染,逻辑层发来的更新请求被阻塞了,当这种阻塞达到200ms 以上时,视图渲染便会卡顿,卡顿不仅仅和更新的频率相关,和更新的数据量也有关系,当setData更新大列表数据时,或者更新一个size很大的图片时,也容易造成卡顿。为什么只说安卓呢?安卓用户表示IOS就不会出现这种情况吗?在iOS中,小程序的页面是由多个WKWebView
组成的,在系统内存紧张时,一部分WKWebView
会被系统内存回收掉,也就是说之前打开过的小程序界面,会退出历史记录,这些页面我们是无法回收的
四、微信是怎么实现视图线程与逻辑线程的?
与小程序的视图线程相关的编译器有两个,
wcc
编译器:wcc
是WXML
的编译器,主要功能是将WXML
文件编译成JavaScript
代码wcsc
编译器:wcsc
是WXSS
编译器,主要是负责将WXSS
文件编译成JavaScript代码小程序的视图层是在
Polymer
框架的基础之上,基于WebComponent
标准实现的。小程序的视图渲染有点类似与Vue
的DOM
渲染,都是通过虚拟DOM
来实现的。不同的是,小程序除了一般视图组件,还有原生组件,小程序将一般视图组件放在下面,将解析后的原生组件放在上面小程序逻辑线程的实现,我们可以从它的生命周期来体会,
小程序的主要生命周期有以下五个:
onLoad
,onShow
,onReady
,onHide
,onUnload
。当一个小程序页面启动时,首先触发的是
onLoad
和onShow
生命周期函数,页面初始化装载完毕之后,Notify
通知逻辑线程
,逻辑线程
将初始化的Data数据发给视图线程
,由视图线程
渲染,完成首次渲染以后,视图线程
通知逻辑线程
渲染完成,派发onReady
事件,监听到这个事件,代表着也没你可以进行交互了。此时,视图进入持续渲染状态,在运行状态下,用户触发了更新事件,如输入等操作,会触发逻辑线程的事件函数,可能触发了
setData()
,又向视图线程
发送更新数据,视图线程
再次执行更新,当用户跳转到其他小程序或者离开微信时,小程序进入后台状态,此时逻辑线程派发onHide
事件。而当后台切入前台时,又会从onShow
事件开始走,如果页面页面被销毁,则会派回onUnload
事件
微信为什么要打造一个WXS语言?
WXS
是
weinxinScript
的缩写,它结合
WXML
可以构造出页面的组件结构,
WXS
不依赖于运行时的基础库版本,可以在所有版本的微信小程序里运行,
WXS
于
JavaSript
是不同的语言。虽然
WXS
也是逻辑代码,但是它不是运行在
逻辑线程
里的,它是运行在
视图线程
里的,通过直接操作视图数据,避免了跨线程的通讯开销。(这时聪明的你就已经发现了,高贵的苹果用户,运行
WXS
的速度就要比安卓的快一些了。)微信打造
WXS
主要是小程序的双线程架构在数据更新上有性能瓶颈,所有才打造了
WXS
,虽然
WXS
可以提交视图数据的更新效率,但是,他也不是没有缺陷的,它存在以下这些问题:
- WXS 运行环境和其他JS代码是隔离的,因此,WXS是不可以调用JavaScript 代码里定义的函数的,也是不可以调用小程序提供的wx 开头的 API接口
- WXS 函数不能作为视图模板中的事件回调句柄。
- 由于设备的差异,WXS 在iOS设备上运行速度要比JS快,大约是2-20倍,但是在安卓设备上,两者几乎没有差异
总结
小程序采用的是双线程架构,一个线程负责视图的渲染,另一个负责业务逻辑的处理。连个线程都是通过
WeixinJSBridge
与微信Native 底层进行通讯。两个线程之间进行的事件与数据的交互,也是通过WeixinJSBridge
来完成的。所有的平台能力和硬件能力,也是通过WeixinJSBridge
间接提供的。由于setData
在频繁更新数据和大数据更新上存在性能瓶颈,影响渲染效率,所以微信就引入了WXS
变成语言。一般开发时我们可以这样处理,一开始,从后端接口接收回来的初始化数据,在页面onLoad
之前,就存放到Data
数据中,用于视图的初始化渲染,后续的是视图交互与更新,如果不与后台有关,我们就使用WXS
直接在视图中进行完成,这样可以提高渲染性能。