微信小程序原理
# 小程序的运行环境
微信小程序运行在多种平台上:iOS/iPadOS 微信客户端、Android 微信客户端、Windows PC 微信客户端、Mac 微信客户端、小程序硬件框架和用于调试的微信开发者工具等。
不同运行环境下,脚本执行环境以及用于组件渲染的环境是不同的,性能表现也存在差异:
- 在 iOS、iPadOS 和 Mac OS 上,小程序逻辑层的 JavaScript 代码运行在 JavaScriptCore 中,视图层是由 WKWebView 来渲染的,环境有 iOS 14、iPad OS 14、Mac OS 11.4 等;
- 在 Android 上,小程序逻辑层的 JavaScript 代码运行在 V8 中,视图层是由基于 Mobile Chromium 内核的微信自研 XWeb 引擎来渲染的;
- 在 Windows 上,小程序逻辑层 JavaScript 和视图层都是用 Chromium 内核;
- 在 开发工具上,小程序逻辑层的 JavaScript 代码是运行在 NW.js 中,视图层是由 Chromium Webview 来渲染的。
JavaScriptCore 无法开启 JIT 编译 (Just-In-Time Compiler),同等条件下的运行性能要明显低于其他平台。
# 平台差异
尽管各运行环境是十分相似的,但是还是有些许区别:
JavaScript 语法和 API 支持不一致:语法上开发者可以通过开启 ES6 转 ES5 的功能来规避(详情);此外,小程序基础库内置了必要的 Polyfill,来弥补 API 的差异
WXSS 渲染表现不一致:尽管可以通过开启样式补全来规避大部分的问题,还是建议开发者需要在各端分别检查小程序的真实表现。
# 小程序运行机制
# 小程序的生命周期
小程序从启动到最终被销毁,会经历很多不同的状态,小程序在不同状态下会有不同的表现。
# 小程序启动
从用户认知的角度看,广义的小程序启动可以分为两种情况,一种是冷启动,一种是热启动。
- 冷启动:如果用户首次打开,或小程序销毁后被用户再次打开,此时小程序需要重新加载启动,即冷启动。
- 热启动:如果用户已经打开过某小程序,然后在一定时间内再次打开该小程序,此时小程序并未被销毁,只是从后台状态进入前台状态,这个过程就是热启动。
从小程序生命周期的角度来看,我们一般讲的「启动」专指冷启动,热启动一般被称为后台切前台。
# 前台与后台
小程序启动后,界面被展示给用户,此时小程序处于「前台」状态。
当用户「关闭」小程序时,小程序并没有真正被关闭,而是进入了「后台」状态,此时小程序还可以短暂运行一小段时间,但部分 API 的使用会受到限制。切后台的方式包括但不限于以下几种:
- 点击右上角胶囊按钮离开小程序
- iOS 从屏幕左侧右滑离开小程序
- 安卓点击返回键离开小程序
- 小程序前台运行时直接把微信切后台(手势或 Home 键)
- 小程序前台运行时直接锁屏
当用户再次进入微信并打开小程序,小程序又会重新进入「前台」状态。
# 渲染层和逻辑层
小程序的渲染层和逻辑层分别由 2 个线程管理:
- 渲染层的界面使用了 WebView 进行渲染;
- 逻辑层采用 JsCore 线程运行 JS 脚本。
一个小程序存在多个界面,所以渲染层存在多个 WebView 线程,这两个线程的通信会经由微信客户端(native
)做中转,逻辑层发送网络请求也经由 Native 转发,小程序的通信模型下图所示。
小程序的逻辑层和渲染层是分开的,逻辑层运行在 JSCore 中,并没有一个完整浏览器对象,因而缺少相关的 DOM API 和 BOM API。这一区别导致了前端开发非常熟悉的一些库,例如 jQuery、 Zepto 等,在小程序中是无法运行的
# 可能导致的问题:交互的效果卡顿
有频繁用户交互的效果在小程序上表现是比较卡顿的,例如页面有 2 个元素 A 和 B,用户在 A 上做 touchmove 手势,要求 B 也跟随移动,movable-view 就是一个典型的例子。一次 touchmove 事件的响应过程为:
a、touchmove 事件从视图层(Webview)抛到逻辑层(App Service)
b、逻辑层(App Service)处理 touchmove 事件,再通过 setData 来改变 B 的位置
一次 touchmove 的响应需要经过 2 次的逻辑层和渲染层的通信以及一次渲染,通信的耗时比较大。此外 setData 渲染也会阻塞其它脚本执行,导致了整个用户交互的动画过程会有延迟。
微信小程序给出的解决方案是:WXS响应事件
# JS 代码在小程序与浏览器运行中的区别
小程序开发框架的逻辑层使用 JavaScript 引擎为小程序提供开发 JavaScript 代码的运行环境以及微信小程序的特有功能。
逻辑层将数据进行处理后发送给视图层,同时接受视图层的事件反馈。
开发者写的所有代码最终将会打包成一份 JavaScript 文件,并在小程序启动的时候运行,直到小程序销毁。这一行为类似 ServiceWorker,所以逻辑层也称之为 App Service
。
在 JavaScript 的基础上,我们增加了一些功能,以方便小程序的开发:
- 增加 App 和 Page 方法,进行程序注册和页面注册。
- 增加 getApp 和 getCurrentPages 方法,分别用来获取 App 实例和当前页面栈。
- 提供丰富的 API,如微信用户数据,扫一扫,支付等微信特有能力。
- 提供模块化能力,每个页面有独立的作用域。
注意:小程序框架的逻辑层并非运行在浏览器中,因此 JavaScript 在 web 中一些能力都无法使用,如 window,document 等。
# 生命周期
# WXSS rpx(responsive pixel)
可以根据屏幕宽度进行自适应。规定屏幕宽为 750rpx。如在 iPhone6 上,屏幕宽度为 375px,共有 750 个物理像素,则 750rpx = 375px = 750 物理像素,1rpx = 0.5px = 1 物理像素。
建议: 开发微信小程序时设计师可以用 iPhone6 作为视觉稿的标准。
注意: 在较小的屏幕上不可避免的会有一些毛刺,请在开发时尽量避免这种情况
# 初始渲染缓存工作原理
小程序页面的初始化分为两个部分。
- 逻辑层初始化:载入必需的小程序代码、初始化页面 this 对象(也包括它涉及到的所有自定义组件的 this 对象)、将相关数据发送给视图层。
- 视图层初始化:载入必需的小程序代码,然后等待逻辑层初始化完毕并接收逻辑层发送的数据,最后渲染页面。
在启动页面时,尤其是小程序冷启动、进入第一个页面时,逻辑层初始化的时间较长。在页面初始化过程中,用户将看到小程序的标准载入画面(冷启动时)或可能看到轻微的白屏现象(页面跳转过程中)。
启用初始渲染缓存,可以使视图层不需要等待逻辑层初始化完毕,而直接提前将页面初始 data 的渲染结果展示给用户,这可以使得页面对用户可见的时间大大提前。它的工作原理如下:
- 在小程序页面第一次被打开后,将页面初始数据渲染结果记录下来,写入一个持久化的缓存区域(缓存可长时间保留,但可能因为小程序更新、基础库更新、储存空间回收等原因被清除);
- 在这个页面被第二次打开时,检查缓存中是否还存有这个页面上一次初始数据的渲染结果,如果有,就直接将渲染结果展示出来;
- 如果展示了缓存中的渲染结果,这个页面暂时还不能响应用户事件,等到逻辑层初始化完毕后才能响应用户事件。
利用初始渲染缓存,可以:
- 快速展示出页面中永远不会变的部分,如导航栏;
- 预先展示一个骨架页,提升用户体验;
- 展示自定义的加载提示;
- 提前展示广告,等等。
# 支持的组件
在初始渲染缓存阶段中,复杂组件不能被展示或不能响应交互。
目前支持的内置组件:
<view />
<text />
<button />
<image />
<scroll-view />
<rich-text />
2
3
4
5
6
自定义组件本身可以被展示(但它们里面用到的内置组件也遵循上述限制)。