返回博客

自定义 tabBar 下组件首屏永远空白:pageLifetimes.show 不触发之谜

首页组件上线了。开发者工具里一切正常,真机冷启动……空白。换一个 tab 再切回来,内容突然出现了。这种「第二次才有数据」的现象,排查方向极容易跑偏——以为是接口慢、以为是 onLoad 时序、以为是缓存没清——实际上根子在微信框架对自定义 tabBar 的生命周期处理上。

现象

  • 小程序冷启动,落在某个 tabBar 页,页内的自定义 Component 一片空白,或停在「加载中…」。
  • 打开 vConsole / 开发者工具,pageLifetimes.show 的回调从未执行
  • 手动点击另一个 tab,再切回来,pageLifetimes.show 正常触发,数据出现。
  • 把同一个 Component 放到非 tabBar 的普通页面,首屏完全正常。

根因

app.json 里设置 "custom": true 启用自定义 tabBar 后,框架对 tabBar 页面的生命周期调度发生了变化:宿主页面完成初始化挂载时,不会向子 Component 派发 pageLifetimes.show,该事件只在后续每次「用户切换 tab 回到此页」时才触发。

换句话说:

  • 普通 tabBar:pageLifetimes.show 在首次进入时触发 ✓
  • custom: true tabBar:pageLifetimes.show 在首次进入时跳过,从第二次进入开始触发

如果组件把所有初始化逻辑都写在 pageLifetimes.show 里,cold start 那一次的数据请求就悄悄丢失了,没有任何报错。

坑:custom tabBar 首屏,pageLifetimes.show 静默跳过

只要 app.jsontabBar.custom === true,子组件的 pageLifetimes.show 在首次挂载时不触发。把初始化请求放在这里,首屏必然空白,且无任何错误日志,极难发现。

正解

首次加载逻辑移至 lifetimes.attached——这是 Component 自身的挂载生命周期,与 tabBar 类型无关,无论何种场景都可靠触发。

错误写法(自定义 tabBar 下首屏静默失效):

Component({
  pageLifetimes: {
    show() {
      // custom tabBar 首次挂载时永远不执行
      this.loadList()
    }
  }
})

正确写法(首屏可靠触发):

const app = getApp()
 
Component({
  data: { list: [] },
 
  lifetimes: {
    attached() {
      const that = this
      app.checkLogin(function () {
        that.loadList()
      })
    }
  },
 
  methods: {
    async loadList() {
      const res = await api.someList({})
      this.setData({ list: res.data })
    }
  }
})

如果除了首屏加载,还需要每次切回 tab 时刷新数据,两个生命周期并用即可:

Component({
  lifetimes: {
    attached() {
      // 首屏初始化,只跑一次
      this.loadList()
    }
  },
  pageLifetimes: {
    show() {
      // 从第二次进入开始刷新(custom tabBar 下首次不触发,符合预期)
      this.refreshIfStale()
    }
  }
})
正解:首次初始化放 lifetimes.attached,刷新逻辑放 pageLifetimes.show

lifetimes.attached 是 Component 挂载生命周期,不受 tabBar 类型影响,首屏必触发。pageLifetimes.show 则专门负责「用户再次切回来时」的刷新,两者职责分离、互不干扰。

一句话外卖

微信小程序自定义 tabBar 页内的 Component,首屏初始化必须放 lifetimes.attachedpageLifetimes.show 在首次挂载时静默跳过——这是框架行为,不是 bug,但文档里几乎没有任何提示。