快应用 生命周期
了解页面的生命周期与状态,APP的生命周期
通过本节,你将学会:
- 页面的生命周期:
onInit
、onReady
、onShow
、onHide
、onDestroy
、onBackPress
、onMenuPress
- 页面的状态:
显示
、隐藏
、销毁
- APP的生命周期:
onCreate
、onDestroy
教程文档对应的项目代码文件:src/Lifecycle目录,src/app.ux文件
生命周期图
页面的生命周期
由于页面通过ViewModel
渲染,那么页面的生命周期指的也就是ViewModel
的生命周期,包括常见的:onInit, onReady, onShow在页面创建时触发调用
onInit()
表示ViewModel
的数据已经准备好,可以开始使用页面中的数据,如下所示:
private: {
// 生命周期的文本列表
lcList: []
},
onInit () {
this.$page.setTitleBar({ text: '生命周期' })
this.lcList.push('onInit')
console.info(`触发:onInit`)
console.info(`执行:获取ViewModel的lcList属性:${this.lcList}`) // 执行:获取ViewModel的lcList属性:onInit
// $app信息
console.info(`获取:manifest.json的config.data的数据:${this.$app.$data.name}`)
console.info(`获取:APP文件中的数据:${this.$app.$def.data1.name}`)
console.info(`执行:APP文件中的方法`, this.$app.$def.method1())
}
onReady()
表示ViewModel
的模板已经编译完成,可以开始获取 DOM 节点(如:this.$element(idxxx)),
如下所示:
onReady () {
this.lcList.push('onReady')
console.info(`触发:onReady`)
console.info(`执行:获取模板节点:${this.$rootElement()}`) // 执行:获取模板节点:<div attr={} style={"flexDirection":"column"}>...</div>
}
onShow(), onHide()
APP中可以同时运行多个页面,但是每次只能显示其中一个页面;这点不同与纯前端开发,浏览器页面中每次只能有一个页面,当前页签打开另一个页面,上个页面就销毁了;不过和SPA开发倒有点相似,切换页面但浏览器全局Context是共享的
所以页面的切换,就产生了新的事件:页面被切换隐藏时调用 onHide(),页面被切换重新显示时调用onShow()
判断页面的显示状态,可以调用ViewModel
的$visible
属性:true
表示显示,false
表示隐藏,示例如下:
onShow () {
this.lcList.push('onShow')
console.info(`触发:onShow`)
console.info(`执行:获取页面显示状态属性:${this.$visible}`) // true
}
onHide () {
this.lcList.push('onHide')
console.info(`触发:onHide`)
console.info(`执行:获取页面显示状态属性:${this.$visible}`) // false
}
onDestroy()
页面被销毁时调用,被销毁的可能原因有:用户从当前页面返回到上一页,或者用户打开了太多的页面,框架自动销毁掉部分页面,避免占用资源
所以,页面销毁时应该做一些释放资源的操作,如:取消接口订阅监听geolocation.susubscribe()
判断页面是否处于被销毁状态,可以调用Viewmodel
的$valid
属性:true
表示存在,false
表示销毁,示例如下:
onDestroy () {
console.info(`触发:onDestroy`)
console.info(`执行:页面要被销毁,销毁状态:${this.$valid},应该做取消接口订阅监听的操作: geolocation.unsubscribe()`) // true,即将销毁
setTimeout(function () {
console.info(`执行:页面已被销毁,不会执行`) // 页面已销毁,不会执行
}.bind(this), 0)
}
提示:
- 在
onDestroy()
中判断$valid
没有意义,因为页面即将被销毁;如果在本页面之外持有该Viewmodel
的引用则可以通过$valid
判断页面状态 setTimeout
之类的异步操作绑定在了当前页面上,因此当页面销毁之后异步调用不会执行
onBackPress()
当用户点击返回实体按键
、左上角返回菜单
、调用返回API
时触发该事件
如果事件响应方法最后返回true
表示不返回,自己处理业务逻辑(完毕后开发者自行调用API返回);否则:不返回数据,或者返回其它数据:表示遵循系统逻辑:返回到上一页,示例如下:
onBackPress () {
console.info(`触发:onBackPress`)
// true:表示自己处理;否则默认返回上一页
// return true
}
onMenuPress()
在 1070 以前的版本,当同时满足当前页面的manifest.json
中的menu
值为 true 与titleBar
值为 true 时,此时屏幕顶部的标题栏会显示右侧的菜单按钮,点击此按钮会触发onMenuPress
回调
1070 版本开始,快应用推出了新的menuBar
胶囊按钮的交互形式,取代了之前标题栏右侧菜单按钮的按钮交互。当manifest.json
中的menu
值为 true 时,点击menuBar
的左侧按钮,也会触发onMenuPress
回调,详见menuBar 文档
注意: 若onMenuPress
回调在当前页面被实现了,且符合上述触发onMenuPress
回调的条件,点击menu
或menubar
的系统弹窗逻辑就会被拦截不触发,示例如下:
// 只要实现了此回调,就会拦截当前页面的menu或menubar的系统弹窗逻辑
onMenuPress(){
prompt.showToast({
message: `我拦截了menu点击`
})
}
onRefresh()
监听页面重新打开。详细说明请参考文档
1.当页面在 manifest 中 launchMode1050+
标识为 'singleTask' 时,仅会存在一个目标页面实例,用户多次打开目标页面时触发此函数。
2.打开目标页面时在 push 参数中携带 flag 'clearTask',且页面实例已经存在时触发。该回调中参数为重新打开该页面时携带的参数。详见页面启动模式
示例如下:
onRefresh() {
console.log('page refreshed!!!')
}
onConfigurationChanged(event)
监听应用配置发生变化。当应用配置发生变化时触发,如系统语言或主题模式改变,详细说明请参考文档
onConfigurationChanged(evt) {
console.log(`触发生命周期onConfigurationChanged, 配置类型:${evt.type}`)
}
页面的状态
如上所述,APP中允许多个页面同时存在并运行,但当前仅显示其中一个,因此每个页面就会处于多个状态的一个状态
- 显示:该页面就是当前APP正在显示的页面,用
$visible
判断 - 隐藏:该页面上打开新页面后,该页面被隐藏,用
$visible
判断 - 销毁:该页面因某原因销毁后,就不会再执行里面的代码,用
$valid
判断
关于接口调用与页面的生命周期与状态,详见文档script 脚本
APP的生命周期
当前为 APP 的生命周期提供了七个回调函数:onCreate()、onRequest()、onShow()、onHide()、onDestroy()、onError()、onPageNotFound() ,可在app.ux
中定义回调函数,
详情及参数,示例如下:
export default {
onCreate() {
console.info('Application onCreate')
},
onRequest() {
console.info('Application onRequest')
},
onShow() {
console.info('Application onShow')
},
onHide() {
console.info('Application onHide')
},
onDestroy() {
console.info('Application onDestroy')
},
onError() {
console.log('Application onError')
},
onPageNotFound(params) {
const { uri = '' } = params
console.error('error uri', uri)
},
// 暴露给所有页面,在页面中通过:this.$app.$def.method1()访问
method1() {
console.info('这是APP的方法')
},
// 暴露给所有页面,在页面中通过:this.$app.$def.data1访问
data1: {
name: '这是APP存的数据'
}
}
在app.ux
中,开发者可以做一些独立于页面的操作。比如:引入公共的JS资源,然后暴露给所有页面
在app.ux
中,通过this
访问app.ux
中定义的数据和方法,示例如下:
console.info(`获取:APP文件中的数据:${this.$def.data1.name}`)
console.info(`执行:APP文件中的方法`, this.$def.method1())
console.info(`获取:manifest.json的应用名称:${this.$def.manifest.name}`)
console.info(`获取:manifest.json的config.data的数据:${this.$data.name}`)
在pageName.ux
中,通过this.$app
访问app.ux
中定义的数据和方法,示例如下:
console.info(`获取:APP文件中的数据:${this.$app.$def.data1.name}`)
console.info(`执行:APP文件中的方法`, this.$app.$def.method1())
console.info(`获取:manifest.json的应用名称:${this.$app.$def.manifest.name}`)
console.info(`获取:manifest.json的config.data的数据:${this.$app.$data.name}`)
关于$app与$app.$def
$app与$app.$def(后面简称$def)是两个不同的对象;
前者代表框架为开发者暴露提供的APP对象;后者代表开发者在app.ux中导出的对象,放置业务相关的全局数据和方法;
初学开发者可以跳过该块学习,待后期深入了解;
前者对象拥有onCreate, onDestroy生命周期;当应用启动时会执行onCreate方法,里面执行this.variable1的赋值是在$app对象上;
后者对象中的onCreate, onDestroy方法并不会执行,作用仅仅只是把方法复制到前者对象上而已;
这些全局方法在页面中:既可以通过this.$app.method1()调用,也可以通过this.$app.$def.method1()调用;不同之处在于前者可以取到之前赋值的variable1变量,而后者不可以取到(因为之前的赋值是在$app对象上执行的);
总结
理解页面与APP的生命周期,有助于更好的组织页面的业务逻辑,方便页面之间的交互与资源释放等的处理