通讯系统设计
最上面提到,视图层和逻辑层通讯是通过Native层。
具体的手段就是
- ios利用 WKWebView 的提供 messageHandlers 特性
- android 是往webview的window对象注入一个原生方法
这两种会统一封装成weixinJSBridge,这和正常h5与客户端通讯手段一致
初始化过程中Native层理论上是微信客户端,分别在视图层和业务逻辑层注入了WeixinJSBridge
生命周期设计
data
逻辑层的data与view是相互绑定的,data是页面第一次渲染使用的初始数据。页面加载的时候,data将会以JSON字符串形式由逻辑层传至渲染层。因此data中的数据必须是可以转成JSON的类型:字符串,数字,布尔值,对象,数组。
图中,渲染层和逻辑层都从start开始出发,自身分别进行初始化操作,都初始化完毕后要怎么做呢?两条线程互相并不知道对方初始化怎么样了。
所以这个时候由渲染层发出信号,发出一个我已经初始化完毕的信号发给逻辑层,并且自身状态进入等待。
逻辑层收到这个信号的时候有两种情况。
- 第一种就是自身还没初始化完,那么收到此信号后只需要初始化完毕后发送初始数据Data到渲染层即可。
- 第二种情况就是逻辑层早已经进入等待状态,那么收到信号后立即发送初始数据Data到渲染层即可。
生命周期
onLoad(Object query)
页面加载时触发,一个页面只会调用一次,可以在onLoad的参数中获取打开当前页面路径中的参数。onShow()
页面显示/切入前台时触发onHide()
页面隐藏/切入后台时触发。 如 wx.navigateTo 或底部 tab 切换到其他页面,小程序切入后台等。onReady()
页面初次渲染完成时触发。一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互。onUnload()
页面卸载时触发。如wx.redirectTo或wx.navigateBack
到其他页面时。
我们结合路由跳转和webview设计去理解
- wx.navigateTo是创建了新的webview,当前webview 进入Hide()
- wx.redirectTo以及wx.navigateBack是通过更新自身webview进行页面转换的,所以当前页面会进行卸载操作,并且重新生成新页面。所以两个页面都会进入完整生命周期序列。
配合整体架构图来看一下生命周期。
路由设计
路由栈
小程序中不像单页面应用,采用多个webview类似多页。
触发路由的行为可以是逻辑层触发,也可以从视图层触发。在视图层中用户可以通过点击回退按钮,或者回退上一页的手势等机制触发。在逻辑层中发出的信号有打开新页面navigateTo、重定向redirectTo、页面返回navigateBack等,开发者通过官网提供的API触发。
无论逻辑层还是视图层,这个行为都会被发送到Native层,有Native层统一控制路由。对于webview的添加或删除都会有一个载体来维护,这就是路由栈。
上图中,逻辑层中发出打开页面行为到Native层,Native层收到行为后通过pageFrame快速创建webview,并且推入路由栈。页面创建完后,底层基础库会立刻执行初始化操作,初始化完毕后会发送一个信号通知Native页面已经创建并初始化完毕,随后Native层发送信号到逻辑层中。
通知的目的有两个:
需要通知开发者页面已经创建成功。
在沙箱中创建新页面的“根组件”,并正式开启新页面的生命周期与渲染的流程。
性能优化
程序的性能又可以分为「启动性能」和「运行时性能」两个主题。「启动性能」让用户能够更快的打开并看到小程序的内容,「运行时性能」保障用户能够流畅的使用小程序的功能。
小程序启动流程
1.资源准备
1.1小程序相关信息准备
微信客户端需要从微信后台获取小程序的头像、昵称、版本、配置、权限等基本信息,这些信息会在本地缓存,并通过一定的机制进行更新。
1.1环境预加载
为了尽可能的降低运行环境准备对启动耗时的影响,微信客户端会根据用户的使用场景和设备资源的使用情况,依照一定策略在小程序启动前对运行环境进行部分地预加载,以降低启动耗时。
1.2代码包准备
从微信后台获取代码包地址,从 CDN 下载小程序代码包
小程序代码包会在本地缓存,并通过更新机制进行更新。
同步下载/异步下载 强制更新/静默更新
为例降低代码包下载的耗时,微信做的一些优化
- 代码包压缩
- 增量更新
- 优先使用QUIC 和HTTP/2
- 预先建立连接:在下载发生前,提前和 CDN 建立连接,降低下载过程中 DNS 请求和连接建立的耗时
- 代码包复用:对每个代码包都会计算 MD5 签名。即使发生了版本更新,如果代码包的 MD5 没有发生变化,则不需要重新进行下载。
2.代码注入
小程序启动时需要从代码包内读取小程序的配置和代码,并注入到 JavaScript 引擎中。
微信客户端会使用 V8 引擎的 Code Caching 技术对代码编译结果进行缓存,降低非首次注入时的编译耗时
code cache
V8 会把编译和解析的结果缓存下来,等到下次遇到相同的文件,直接跳过这个过程,把直接缓存好的数据拿来使用
启动时性能优化
控制代码包体积
- 推荐所有小程序使用分包加载
- 避免非必要使用全局自定义组件和插件
- 会影响按需注入的效果和小程序代码注入的耗时
- 控制资源文件
- 建议开发者在代码包内的图片一般应只包含一些体积较小的图标,避免在代码包中包含或在 WXSS 中使用 base64 内联过多、过大的图片等资源文件。
- 这类文件应尽可能部署到 CDN,并使用 URL 引入。
代码注入优化
- 推荐所有小程序使用按需注入
- 用时注入
- 为自定义组件配置 占位组件,组件就会自动被视为用时注入组件
- 启动过程中减少同步 API 的调用
- 建议优先使用拆分后的 getSystemSetting/getAppAuthorizeSetting/getDeviceInfo/getWindowInfo/getAppBaseInfo 按需获取信息,或使用使用异步版本 getSystemInfoAsync
- getStorageSync/setStorageSync 应只用来进行数据的持久化存储,不应用于运行时的数据传递或全局状态管理。
首屏渲染优化
- 启用「初始渲染缓存」
- 启用初始渲染缓存,可以使视图层不需要等待逻辑层初始化完毕,而直接提前将页面初始 data 的渲染结果展示给用户,这可以使得页面对用户可见的时间大大提前
- 提前首屏数据请求
- 预拉取能够在小程序冷启动的时候通过微信后台提前向第三方服务器拉取业务数据,当代码包加载完时可以更快地渲染页面,减少用户等待时间,从而提升小程序的打开速度
- 周期性更新能够在用户未打开小程序的情况下,也能从服务器提前拉取数据,当用户打开小程序时可以更快地渲染页面,减少用户等待时间,增强在弱网条件下的可用性。
- 缓存请求数据
- 骨架屏
运行时性能优化
合理使用setData
控制频率,范围,内容
页面渲染优化
- 适当监听scroll 事件
- 控制 WXML 节点数量和层级
- 源码中一个页面dom 数目超过16000,肯定会报错
- data层级不要过深,因为需要深度遍历
- 使用 IntersectionObserver 监听元素曝光
页面切换优化
- 避免在 onHide/onUnload 执行耗时操作
- 页面切换时,会先调用前一个页面的 onHide 或 onUnload 生命周期,然后再进行新页面的创建和渲染
- 提前发起数据请求
- 进行页面跳转时(例如 wx.navigateTo),可以提前为下一个页面做一些准备工作。页面之间可以通过 EventChannel 进行通信。类似postMessage
- 例如,在页面跳转时,可以同时发起下一个页面的数据请求,而不需要等到页面 onLoad 时再进行,从而可以让用户更早的看到页面内容。
- 控制预加载下个页面的时机
- 程序页面加载完成后,会预加载下一个页面。默认情况下,小程序框架会在当前页面 onReady 触发 200ms 后触发预加载。
- 预加载会阻塞当前页面setData,我们可以对单个页面的配置增加, handleWebviewPreload 选项,来控制预加载下个页面的时机。
资源加载优化
控制图片大小
内存优化
- 合理分包,既能减少耗时,也能降低内存占用
- 事件监听,定时器记得清除
三方库框架设计
小程序为什么快(与普通h5相比)
我们在对小程序的架构设计时的要求只有一个,就是要快,包括要渲染快、加载快等。当用户点开某个小程序时,我们期望体验到的是只有很短暂的加载界面,在一个过渡动画之后可以马上看到小程序的主界面。
- 双线程,渲染层和逻辑层并行不阻塞
- 多个webview,页面切换更流畅
- webview 预加载
- 安装包缓存
- 以及微信做了大量的优化和看不见的操作
总结与展望
- 小程序拥有接近原生 App 的体验。
- 小程序并不是真正的 “无需下载”,只是小程序的体积很小,在当今高速的网络环境下能够快速下载,用户感知不到,更确切的来说是 “无感下载”。
- 基于移动端布局的局限性,可以高效且简单的开发,迭代快速。
- 小程序是双线程模型,逻辑层和渲染层分别运行在不同的线程中,通过 JSBridge 进行通信。
现在已知的小程序有 微信,支付宝,抖音,qq,虎牙,斗鱼,饿了么,百度,京东等等
已知的这些都是超级app,对用户来说,一般手机只按照常用和必要的app,对于一般的app其实很难推广。开发h5又天生需要借助平台给予流量,体验无论怎么优化,仍旧比不上原生。但是小程序在可以借助平台流量的同时,有较好的用户体验。
目前已经出了三方框架,FinClip 把小程序搬进app。