教程 框架 组件 接口 工具 其他

生命周期

了解页面的生命周期与状态,APP 的生命周期

通过本节,你将学会:

  • 页面的生命周期:onInitonReadyonShowonHideonDestroyonBackPressonMenuPressonRefresh(1050+)onConfigurationChanged(1060+)onReachTop(1080+)onReachBottom(1080+)onPageScroll(1080+)onErrorCaptured(1300+)
  • 页面的状态:显示隐藏销毁
  • APP 的生命周期:onCreateonRequestonShowonHideonDestroyonErroronPageNotFoundonErrorHandler(1300+)

注意:生命周期的说明也可以参考这篇文档

生命周期图

生命周期图

页面的生命周期

由于页面通过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.unsubscribe()

判断页面是否处于被销毁状态,可以调用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()

当用户点击返回实体按键左上角返回菜单时触发该事件

如果事件响应方法最后返回true表示不返回,自己处理业务逻辑(完毕后开发者自行调用 API 返回);否则:不返回数据,或者返回其它数据:表示遵循系统逻辑:返回到上一页

示例如下:

onBackPress () {
  console.info(`触发:onBackPress`)
  // true:表示自己处理;否则默认返回上一页
  // return true
}

onMenuPress()

menu_compare

在 1070 以前的版本,当同时满足当前页面的manifest.json中的menu值为 true 与titleBar值为 true 时,此时屏幕顶部的标题栏会显示右侧的菜单按钮,点击此按钮会触发onMenuPress回调

1070 版本开始,快应用推出了新的menuBar胶囊按钮的交互形式,取代了之前标题栏右侧菜单按钮的按钮交互。当manifest.json中的menu值为 true 时,点击menuBar的左侧按钮,也会触发onMenuPress回调,详见menuBar 文档

注意:onMenuPress回调在当前页面被实现了,且符合上述触发onMenuPress回调的条件,点击menumenubar系统弹窗逻辑就会被拦截不触发

示例如下:

// 只要实现了此回调,就会拦截当前页面的menu或menubar的系统弹窗逻辑
onMenuPress(){
  prompt.showToast({
    message: `我拦截了menu点击`
  })
}

onRefresh(query) 1050+

监听页面重新打开。详细说明请参考文档

1.当页面在 manifest 中 launchMode1050+ 标识为'singleTask'时,仅会存在一个目标页面实例,用户多次打开目标页面时触发此函数。
2.打开目标页面时在 push 参数中携带 flag 'clearTask',且页面实例已经存在时触发。
该回调中参数为重新打开该页面时携带的参数。
详见页面启动模式

示例如下:

onRefresh(query) {
  // launchMode 为 singleTask 时,重新打开页面时携带的参数不会自动更新到页面 this 对象上
  // 需要在此处从 query 中拿到并手动更新
  console.log('page refreshed!!!')
}

onConfigurationChanged(event) 1060+

监听应用配置发生变化。当应用配置发生变化时触发,如系统语言或主题模式改变,详细说明请参考文档

onConfigurationChanged(evt) {
  console.log(`触发生命周期onConfigurationChanged, 配置类型:${evt.type}`)
}

onReachTop() 1080+

监听页面是否触顶

示例如下:

onReachTop() {
  console.log('页面到底顶部')
}

onReachBottom() 1080+

监听页面是否触底

示例如下:

onReachBottom() {
  console.log('页面到底底部')
}

onPageScroll(event) 1080+

监听页面滚动

示例如下:

onPageScroll(evt) {
  console.log(`页面滚动距离:${evt.scrollTop}`)
}

onErrorCaptured(err, vm, info) 1300+

  • 注:oppo侧已在 1117+ 引擎版本支持

在捕获到自身以及后代组件的错误时被触发,该生命周期钩子会收到三个参数:错误对象、触发该错误的组件实例以及附加的错误来源信息。可以在回调中 return false 以阻止错误继续向上传播。

注意:

  • 框架层无法捕获到 promise 内部错误以及嵌套多层的 async 函数内部错误,此类情况无法触发 onErrorCapturedonErrorHandler 生命周期,需自行做好错误的捕获处理。
  • 定时器回调、接口回调,这类场景拿不到 vm 对象,所以 onErrorCaptured 无法监听此类错误,需使用 onErrorHandler 进行监听。

错误传播规则

  • 在框架捕获到错误时,如果此时能拿到 vm对象, 错误将会由子组件冒泡到父组件,逐个触发页面或组件的 onErrorCaptured 钩子,直至全局的 onErrorHandler 钩子,中间可通过 return false 来阻止错误继续传递;如果此时拿不到vm对象,错误将直接发送给全局的 onErrorHandler 钩子;

  • 如果 onErrorCaptured 钩子自身抛出了一个错误,则这个新错误和原本被捕获的错误都会发送给全局的 onErrorHandler 钩子。

示例如下:

onErrorCaptured(err, vm, info) {
  console.error('onErrorCaptured error: ', err)
  console.error('onErrorCaptured vm of component: ', vm)
  console.error('onErrorCaptured error info: ', info)

  // 编写线上错误监控代码
  // yourReportErrorCode(err.message, err.stack, vm?._type, info)

  // 可通过 return false 阻止错误继续传递
  // return false
}

注意事项

请注意,以下用法可能会导致 onErrorCaptured 被循环触发,请尽量避免。

  • onErrorCaptured 里修改 data/public/protected/private 里面的属性,该修改引发了新的错误;
  • onErrorCaptured 里面的异步代码引发了新的错误。

如必须使用,请保证以上用法不会产生新的错误,或者自己使用 try/catch 拦截可能存在的错误,不让其冒泡到框架层,否则 onErrorCaptured 可能会有循环被触发的风险。

页面的状态

如上所述,APP 中允许多个页面同时存在并运行,但当前仅显示其中一个,因此每个页面就会处于多个状态的一个状态

  1. 显示:该页面就是当前 APP 正在显示的页面,用$visible判断
  2. 隐藏:该页面上打开新页面后,该页面被隐藏,用$visible判断
  3. 销毁:该页面因某原因销毁后,就不会再执行里面的代码,用$valid判断

关于接口调用与页面的生命周期与状态,详见文档script 脚本

APP 的生命周期

当前为 APP 的生命周期提供了八个回调函数:onCreate()、onRequest()1070+、onShow()1070+、onHide()1070+、onDestroy()、onError()1030+、onPageNotFound()1060+、onErrorHandler()1300+,可在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)
  },
  onErrorHandler (err, vm, info) {
    console.error('onErrorHandler error: ', err)
    console.error('onErrorHandler vm of component: ', vm)
    console.error('onErrorHandler error info: ', info)

    // 编写线上错误监控代码
    // yourReportErrorCode(err.message, err.stack, vm?._type, info)
  }
  // 暴露给所有页面,在页面中通过:this.$app.$def.method1()访问
  method1() {
    console.info('这是APP的方法')
  },
  // 暴露给所有页面,在页面中通过:this.$app.$def.data1访问
  data1: {
    name: '这是APP存的数据'
  }
}

app.ux中,开发者可以做一些独立于页面的操作。比如:引入公共的 JS 资源,然后暴露给所有页面

app.ux中,通过this.$def访问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.$def访问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 的生命周期,有助于更好的组织页面的业务逻辑,方便页面之间的交互与资源释放等的处理

条匹配 "" 的结果

    没有搜索到与 "" 相关的内容