1. 生命周期
小程序的生命周期
- onLaunch:小程序初始化完成时触发,全局只触发一次。
- onShow:小程序启动,或从后台进入前台显示时触发。
- onHide:小程序从前台进入后台时触发。
页面的生命周期
- onLoad:页面加载时触发。一个页面只会调用一次,可以在 onLoad 的参数中获取打开当前页面路径中的参数。
- onShow:页面显示/切入前台时触发。
- onReady:页面初次渲染完成时触发。一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互。
- onHide:页面隐藏/切入后台时触发。
- onUnload:页面卸载时触发。
- onRouteDone:路由动画完成时触发。
2. 登录流程
- 调用
wx.login()
获取code传递给服务端; - 服务端根据
code
换取unionId
或openid
,同时生成登录凭证accessToken
返回给小程序; - 小程序将
accessToken
缓存到本地,后续请求都带上。
3. 支付流程
- 小程序调用服务端的下单接口;
- 服务端生成订单,并请求
小程序支付统一下单
接口,生成prepay_id
等参数后返回给小程序; - 小程序利用获取到的
prepay_id
等参数,调用wx.wx.requestPayment()
拉起支付; - 用户支付成功后,服务端可接收到微信的支付回调信息,进而修改订单的支付状态。
4. 手机号授权流程
旧流程
- 调用
wx.login()
获取code
,发送给后端,后端根据code
得到session_key
; - 将
button
组件open-type
的值设置为getPhoneNumber
,当用户点击并同意之后,可以通过bindgetphonenumber
事件回调获取到微信服务器返回的加密数据, 发送给后端; - 后端结合
session_key
以及app_id
进行解密获取手机号。
新流程
- 将
button
组件open-type
的值设置为getPhoneNumber
,当用户点击并同意之后,可以通过bindgetphonenumber
事件回调获取到动态令牌code
,然后把code传到后端; - 后端调用微信后台提供的
phonenumber.getPhoneNumber
接口,消费code
来换取用户手机号。每个code
有效期为5分钟,且只能消费一次。
5. 小程序的底层原理
小程序的运行环境分为两部分:逻辑层
和 渲染层
。 WXML
模板和 WXSS
样式工作在渲染层,JS
脚本工作在逻辑层。
通信模型
小程序的渲染层和逻辑层分别由2个线程管理:渲染层的界面使用了 WebView
进行渲染;逻辑层采用 JsCore
线程运行JS脚本。一个小程序存在多个界面,所以渲染层存在多个WebView
线程,这两个线程的通信会经由 Native
(微信客户端)做中转,逻辑层发送网络请求也经由 Native
转发,
小程序与客户端通信原理
- 视图层组件
内置组件中有部分组件是利用到客户端原生提供的能力。既然需要客户端原生提供的能力,那就会涉及到视图层与客户端的交互通信。这层通信机制在iOS
和安卓系统的实现方式并不一样,iOS
是利用了WKWebView
的提供messageHandlers
特性,而在安卓则是往WebView
的window
对象注入一个原生方法,最终会封装成WeiXinJSBridge
这样一个兼容层,主要提供了调用(invoke)和监听(on)这两种方法。 - 逻辑层接口
逻辑层与客户端原生通信机制与渲染层类似,不同在于,iOS平台可以往JavaScripCore框架注入一个全局的原生方法,而安卓方面则是跟渲染层一致的。
数据驱动原理
在渲染层,宿主环境会把WXML转化成对应的JS对象,在逻辑层发生数据变更的时候,需要通过宿主环境提供的setData
方法把数据从逻辑层传递到渲染层,再经过对比前后差异,把差异应用在原来的Dom树上,渲染出正确的UI界面。
事件触发原理
6. 小程序性能优化
启动性能优化
- 首次加载
小程序启动时,微信会为小程序展示一个固定的启动界面,界面内包含小程序的图标、名称和加载提示图标。此时,微信会在背后完成几项工作:下载小程序代码包
、加载小程序代码包
、初始化小程序首页
。下载到的小程序代码包不是小程序的源代码,而是编译、压缩、打包之后的代码包。 - 加载顺序
预加载(JS引擎/WebView初始化、公共库注入)—> 小程序启动(代码包下载、业务代码注入、渲染首屏)
由上可知,提升启动性能最直接的方法就是控制小程序包的大小。方法有:
- 勾选开发者工具中上传代码时,自动压缩
- 及时清理无用的代码和资源文件
- 减少代码包内的资源,尽量使用CDN
- 避免非必要的全局自定义组件和插件,因为这些内容会在小程序启动时随主包一起下载和注入 JS 代码,影响启动耗时
- 采用分包加载,访问率高的页面放在主包,访问率低的页面放入子包
- 采用分包预加载,提前下载在当前页可能用到的子包数据
- 对于需要从子包启动的分包,采用独立分包
首屏渲染的优化方法:
- 避免引用未使用的自定义组件
- 异步请求放在
onLoad
,不要等onReady
之后才请求数据 - 精简首屏数据,优先展示页面的关键部分,对于非关键部分或者不可见的部分可以延迟更新
- 利用
storage
API,对变动频率比较低的数据进行缓存,二次启动时可以使用缓存数据进行初始化渲染,然后网络请求后再进行数据更新 - 尽量避免展示空白页面,而是先通过骨架屏展示页面的大致结构,请求数据返回后再进行页面更新。以提升用户的等待意愿
运行时性能优化
- 合理使用 setData
data
中 应只包括渲染相关的数据,渲染无关的数据,应挂在非 data 的字段下setData
只传发生变化的数据,并控制调用的频率
- 避免不当的使用onPageScroll
每一次事件监听都是一次视图到逻辑的通信过程,所以只在必要的时候监听pageSrcoll。同时避免在onPageScroll
中执行复杂的逻辑及多次调用setData
。 - 控制 WXML 节点数量和层级
一个太大的 WXML 节点树会增加内存的使用,样式重排时间也会更长,影响体验。一个页面 WXML 节点数量应少于 1000 个,节点树深度少于 30 层,子节点数不大于 60 个。 - 避免在 onHide/onUnload 执行耗时操作
页面切换时,会先调用前一个页面的 onHide 或 onUnload 生命周期,然后再进行新页面的创建和渲染。如果 onHide 和 onUnload 执行过久,可能导致页面切换的延迟。若必须要进行部分复杂逻辑,可以考虑用 setTimeout 延迟进行。 - 及时解绑事件监听及清理定时器
事件监听结束后,应及时解绑监听器,比如摇一摇,定位等;开发「秒杀倒计时」等功能时,可能会使用 setInterval 设置定时器,页面或组件销毁前,需要调用 clearInterval 方法取消定时器。