评论

onShow与onLoad的一些理解和实践

onShow与onLoad的理解与使用

基本介绍

onShow、onLoad与onReady都是小程序页面生命周期函数。

onLoad 在页面加载时调用,仅一次;

onShow页面显示/切入前台时触发,两个生命周期非阻塞式调用。

onReady 是页面初始化数据已经完成后调用的,并不意味着onLoad和onShow执行完毕。

调用顺序是onLoad > onShow > onReady


根据对应的执行机制,我们预期有三种执行的逻辑

A. 页面每次出现都会执行

  1. 从其他页面返回
  2. 手机锁屏唤醒,重新看到小程序页面
  3. 把当前小程序页面重写切换到前台(多任务)

B. 页面加载后只需执行一次(页面第一次载入)

C. 只在页面非第一次执行时才执行(A情况的子集,页面非第一次展示时)

需求与问题

逻辑1:

因为onLoad和onShow是非阻塞执行的,当我们有一个这样的需求:页面载入执行A方法,页面展示执行B、C、D方法时,A需要在BCD之前执行,此时把A放在onLoad中,BCD放在onShow中就无法实现需求

逻辑2:

还有一种需求是:页面第一次执行A,非第一次执行R-A,这里onLoad和onShow并没有非第一次的逻辑,需要手动判断。

一种实践方法

下面是纯粹使用onShow代替onLoad,完成所有逻辑的示例,保证了业务逻辑的执行顺序可控。

options获取使用其他方式代替。

为了保持onShow中逻辑的清晰性,尽量使用EventChannel去替代原本onShow+globalData的逻辑。

data:{
  first: true
},
async onShow(){
   //代替onLoad中的options的获取
   const pages = getCurrentPages();
  const currentPage = pages[pages.length - 1];
  const options = currentPage.options;

  this.funD() // C2 页面每次都调用的逻辑
   if(this.data.first){
    this.data.first = false;
    await this.funA(); //A 仅在页面初次调用的逻辑(按需是否阻塞调用)
  }else{
    await this.funB(); //B 仅在页面非初次时调用的逻辑
  }
  await this.funC(); //C1 页面每次都调用的逻辑
}

另外一种使用实践

data:{
  first: true
}
onShow(){
  this.funD() //页面每次都调用的逻辑(仅非阻塞)
  if(!this.data.first){
    this.funC() //仅在页面非初次时调用的逻辑
  }
  await this.funE() //页面每次都调用的逻辑(可阻塞,可非阻塞)
},
onLoad(){
  //仅在页面初次调用的逻辑
   this.funA();
  await this.funB();
}
onReady(){
  this.data.first = false;
}


如有错误,恳请指出。

最后一次编辑于  2022-09-23  
点赞 2
收藏
评论

2 个评论

  • 张业贵
    张业贵
    2022-10-05

    实践1中,设置 first 后执行 funA, 如果失败了怎么办?要不要再次执行?

    实践2中,onReady 和函数执行没有确定的时间关系,此处设置 first 作用不明确

    2022-10-05
    赞同
    回复 7
    • simple
      simple
      2022-10-05
      给你点赞,我是这样考虑的:
      第一个问题:实际await都应该用try...catch包裹的,即使失败也不会影响正常流程;
      第二个问题onReady确实和函数执行没有时间关系,但这里总体是onShow是在onReady之前执行的,而对于if(!this.data.first)是在同步代码中的,所以是可以保证这个判断总在onReady之前执行的。
      2022-10-05
      回复
    • 张业贵
      张业贵
      2022-10-06回复simple
      第一个问题,是不会中断运行,一旦进入catch 就比较麻烦,first不能表示已完成初始化的意思,可以看下面的回复中 init 的写法,使用 first 的反向状态来做好一些
      第二个问题,要求funD 必须为同步函数,这就是定编码规则了,必须人工核查,这个稍微有点费劲
      2022-10-06
      回复
    • simple
      simple
      2022-10-06
      第一个问题: catch是异常处理的一部分,对主逻辑没有影响,first是可以表示已完成初始化的,见上一个解释
      第二个问题:要求funD为同步函数,确实不是强制规则,这里的原则是约定大于配置,也就是说让开发人员自己遵守即可。
      2022-10-06
      回复
    • 张业贵
      张业贵
      2022-10-06回复simple
      第一个问题我们看的角度不同。从代码逻辑来看,first 不仅仅表示页面初始化,更应该保证 funA 运行只运行一次。否则 funA 被catch了,后面又没有重试执行的部分,first 就没有达到效果。所以这里有两个状态,一是执行前锁定进入pending,二是执行成功后的 finished,修改如下
      2022-10-06
      回复
    • 张业贵
      张业贵
      2022-10-06
      改一下逻辑,我们把函数做成无状态函数,但是这种写法还是很复杂
      2022-10-06
      回复
    查看更多(2)
  • 张业贵
    张业贵
    2022-10-05

    文中逻辑和代码逻辑说得有点复杂,最开始的部分比较清晰,后面的代码实践处理 first 还是会存在隐患。

    归总,问题是两个,一是时机,二是顺序。时机只能选择,顺序问题是唯一要解决的问题。

    顺序问题可以通过 Promise 解决,比较轻松。

    只执行一次的问题,可以通过状态检查,放在顺序中。

    2022-10-05
    赞同
    回复 5
    • simple
      simple
      2022-10-05
      这个思考主要解决的问题是一个逻辑在onShow和onLoad中有关联的情况,假设A-B 和A-D 这个两个里面有共同的前置逻辑A的情况,写两次或者写在onShow或onLoad中都可能会有异常的情况,如果你有更好的思路,欢迎贴代码
      2022-10-05
      回复
    • 张业贵
      张业贵
      2022-10-06回复simple
      2022-10-06
      回复
    • simple
      simple
      2022-10-06回复张业贵
      这个看起来确实是没有问题的,但是呢,如果有一个逻辑在onShow中在if(this.data.init == 1)之前阻塞执行,此时,极端情况onLoad所有函数都执行完了,init已经修改为1,是不是// 一种复用结果的方式 这部分代码也会执行呢
      2022-10-06
      回复
    • 张业贵
      张业贵
      2022-10-06回复simple
      你举得这个例子有可能。小程序有一个隐含的用户体验要求就是小而快,尤其是界面响应要快。如果onLoad把数据准备都完成了(网络操作比较耗时),onShow做本地展示的部分还在阻塞,那就说明onShow做了太多事情,很明显影响到用户体验了,要修改逻辑或者拆分功能。在设计和验收上都不应该允许这种情况出现。
      2022-10-06
      1
      回复
    • simple
      simple
      2022-10-06回复张业贵
      你说的也有道理,也应该思考逻辑的合理性
      2022-10-06
      回复
登录 后发表内容