题图:被mpvue周期搞疯的你
之前介绍过美团外卖C端 H5 & 小程序页面大量基于mpvue框架的能力,使用Vue组件打通了原生小程序页面和H5页面的隔阂,再结合小程序自带的原生组件功能,就构成了整个外卖跨端应用的终端部分架构。但是随着页面规模变大,各种 vue组件,小程序组件混用,页面周期从一条路走到黑的单一路径,变成各种混合分享弹出,分享页进入等多种周期混合,同构项目会出现各种奇怪的问题。今天通过介绍mpvue在小程序环境运行时的各种周期来帮助各位同学解决自己业务中可能遇到的问题。
一、H5环境Vue多页应用周期
例A页面,我们在页面加载到js代码后,实例化Vue,传入页面组件和默认值
Root.vue:
...
export default {
data() {
return {
name:'SS'
}
}
}
JS:
new Vue(Root)
实例化后,Vue对象会生成V-dom(虚拟dom节点,下同)树,同时把V-dom赋值data函数里返回的初始值,计算出真实dom给浏览器渲染.
随后我们业务逻辑可能获取ajax数据或者根据入参计算重新对页面值进行更改,vue会把我们的修改同步到V-dom树,然后diff计算最小真实dom修改路径,同步到浏览器中。
...
ContentB.a = 2;
ModalB-1.show = true;
当用户离开这个页面时候,浏览器会回收这个页面tag的JS内存、dom内存、视图窗口。我们的V-dom树就彻底销毁了(正常浏览器实现,各种黑科技容器可能实现有区别)。
当再次进入页面时候,重复这个流程。
整个流程如图![]()
二、小程序页面周期
同样的组件,我们使用mpvue方式编程,运行周期有何不同呢?
访问页面的时候,小程序找路径下面的main.js,调用app.$mount(),在实例化Vue组件的同时使用Page()函数初始化小程序页面,绑定mpvue要用的data和小程序周期函数
Vendor.js:
var app = global.getApp();
global.Page({
// 页面的初始数据 data: {
$root: {}
},
handleProxy: function handleProxy (e) {
return rootVueVM.$handleProxyWithVue(e)
},
onLoad: function onLoad (query) {},
onShow: function onShow () {
// 只有页面需要 setData rootVueVM.$nextTick(function () {
rootVueVM._initDataToMP();
});
},
// 生命周期函数--监听页面初次渲染完成 onReady: function onReady () {},
// 生命周期函数--监听页面隐藏 onHide: function onHide () {},
// 生命周期函数--监听页面卸载 onUnload: function onUnload () {},
// 页面相关事件处理函数--监听用户下拉动作 onPullDownRefresh: function onPullDownRefresh () {},
// 页面上拉触底事件的处理函数 onReachBottom: function onReachBottom () {
callHook$1(rootVueVM, 'onReachBottom');
},
// 用户点击右上角分享 onShareAppMessage:
// Do something when page scroll onPageScroll:
// 当前是 tab 页时,点击 tab 时触发 onTabItemTap:
}
重要的点可以看到几处:
0、在main.js运行时实例化Vue,生成V-dom树
1、在main.js运行时就直接setData({$root:{} }) ,把根数据节点放进去,方便后续塞数据
2、onShow钩子里把带初始化数据的V-dom转化成裸JSON格式通过Page.setData()传入小程序
3、小程序接到setData里的数据结合wxml计算真实dom/native节点,放到小程序渲染webview中
初始化之后,如果我们作出更新操作后,会发生什么呢?
由于mpvue只是runtime层改为小程序代码,其他逻辑复用vue.js,所以监听变动,v-dom触发更新是统一逻辑,只是最后同步dom树从使用document.create, document.append这些浏览器API改成了page.setData小程序模式
这两部看上去很完美,我们的按照官网文档大家也可以顺利的搭建一个同构应用。但是当小程序点击回退,或者点击右上原点关闭后,重新从各种路径返回我们的用例页面时,会发现有时候正常,有时候页面的状态和h5实现不一样。这是为何?![]()
从图我们可以看到,小程序多了黄色的 appService.js. 就是这个进程每次缓存住了我们的页面v-dom装态,每次进入页面即使我们像平时写小程序一样运用 onShow,onLoad这些时机去对页面数据进行赋值修改,第一次进入和第二次进入会有不一样的状态。
但是当我们系统里杀死微信,再进入页面,就恢复如初。这就是由于小程序appService.js对页面内存数据的回收时机和H5不同.
不熟悉小程序底层原理的同学可以看看官网的介绍,下面默认你是对小程序本身生命周期理解到位的老司机,进行进一步的讲解.
针对缓存情况,我们可以借用小程序的页面周期钩子,onUnload进行处理,页面卸载触发后(回退,右上关闭均可触发),通过清理v-dom上的数据来实现和H5多页应用同样的效果,
即unLoad时候把不想要留缓存的数据设为初始值,为下次进入页面做准备
优化后,如下图![]()
三、小程序原生组件的特殊处理
mpvue除了能把Vue组件转换为原生代码块外,框架也支持Vue模版中引入原生小程序组件进行渲染。
但是这里开发同学常犯的一个错误就是对于小程序原生组件onReady钩子的理解。我们很习惯在原生组件布局完成后触发onReady做一些逻辑,比如通知其他组件当前这个组件已经就绪并做数据传递。但是这个onReady其实是小程序原生组件在渲染层真实渲染完节点后就回调,多次删除,插入节点会多次触发
例如:
这个组件每次data.show从false->true的过程就会引发一次组件onReady钩子执行,结合我们之前讲的小程序数据清理,希望大家注意不要清理Vue组件数据的时候无意反复触发原生组件钩子。
最后希望这篇文章对mpvue使用者有帮助,点赞多后续还会更多出品mpvue性能调优,同构经验的文章。happy coding!