网站已运行 162 · 13小时 · 26 · 27
目录

微信小程序页面生命周期详解:onLoad、onShow、onReady、onHide 与 onUnload

微信小程序页面生命周期加载显示渲染隐藏销毁示意图

微信小程序页面生命周期并不复杂,真正容易出错的是把“一次初始化”和“每次回到前台都要执行”的工作混在一起。最常见的结果是:返回页面后数据不刷新、事件监听重复注册,或者定时器在页面离开后仍继续运行。

这篇文章用 onLoadonShowonReadyonHideonUnload 五个回调,把首次打开、页面跳转、返回和销毁串成一条可验证的执行路径。示例基于原生小程序 Page,不依赖框架。

先记住五个页面生命周期回调

回调触发时机同一页面实例是否可能重复适合处理
onLoad页面实例加载读取路由参数、一次性初始化
onShow页面显示或重新进入前台刷新可能变化的数据、恢复页面任务
onReady首次渲染完成依赖首屏节点或组件就绪的操作
onHide页面进入后台但未销毁暂停定时器、停止可见页任务
onUnload页面实例被销毁解绑监听、释放资源

首次打开一个页面时,常见顺序是 onLoad → onShow → onReady。从下一级页面返回时,当前页面实例通常还在页面栈中,因此只会重新触发 onShow,不会再次执行 onLoadonReady

用一段代码观察完整顺序

Page({
  onLoad(query) {
    console.log('onLoad', query)
  },

  onShow() {
    console.log('onShow')
  },

  onReady() {
    console.log('onReady')
  },

  onHide() {
    console.log('onHide')
  },

  onUnload() {
    console.log('onUnload')
  }
})

把它放进任意页面的 index.js,分别尝试进入下级页面、返回、重定向和切换 Tab。页面的目录和四件套文件不熟悉时,可以先看微信小程序项目目录结构详解;全局与页面配置的边界则可对照微信小程序 app.json 与 page.json 配置实战

不同路由动作会触发什么

路由动作离开的页面目标页面
wx.navigateToonHide新实例:onLoad → onShow → onReady
wx.navigateBackonUnload原页面:onShow
wx.redirectToonUnload新实例:onLoad → onShow → onReady
wx.switchTabonHide已有 Tab 页:onShow
wx.reLaunch页面被销毁新实例:onLoad → onShow → onReady

这里的关键不是死记顺序,而是先判断页面实例有没有被销毁。隐藏的页面仍留在页面栈里,回来时走 onShow;被销毁的页面再次进入时会创建新实例,从 onLoad 开始。

onLoad:只做一次的初始化

onLoad 会收到打开当前页面时携带的查询参数,适合解析 ID、初始化不会随前后台切换而变化的状态,以及注册“页面实例级”的监听。

Page({
  data: {
    orderId: ''
  },

  onLoad(query) {
    const orderId = decodeURIComponent(query.orderId || '')
    this.setData({ orderId })
    this.loadOrderDetail(orderId)
  },

  loadOrderDetail(orderId) {
    // 根据 orderId 请求详情
  }
})

不要把“每次返回页面都必须刷新”的请求只放在 onLoad。从编辑页返回详情页时,详情页实例没有重新加载,旧数据就会继续留在界面上。

onShow:恢复和刷新易变数据

onShow 可能执行多次,适合读取登录状态、刷新列表或恢复被暂停的任务。请求结果写回页面时,仍要控制更新粒度,避免无关字段一起传给 setData;可以结合微信小程序 setData 性能与更新策略检查数据路径。

Page({
  data: {
    orderStatus: ''
  },

  async onShow() {
    if (!this.data.orderId) return

    const orderStatus = await this.fetchOrderStatus(this.data.orderId)
    if (orderStatus !== this.data.orderStatus) {
      this.setData({ orderStatus })
    }
  }
})

onReady:只处理首次渲染后的工作

onReady 表示页面首次渲染完成,同一页面实例只触发一次。确实依赖首屏节点或组件已经就绪的操作可以放这里,但普通数据请求通常没有必要等到 onReady 才开始。

Page({
  onReady() {
    const query = wx.createSelectorQuery()
    query.select('#chart').boundingClientRect(rect => {
      if (!rect) return
      this.initChart(rect.width, rect.height)
    }).exec()
  }
})

onHide 与 onUnload:暂停和彻底清理

onHide 说明页面暂时不可见,但以后可能回来;onUnload 则代表实例已经销毁。定时器、事件监听和持续订阅要按资源生命周期处理,否则容易出现后台运行或重复回调。

Page({
  refreshTimer: null,
  messageHandler: null,

  onLoad() {
    this.messageHandler = message => this.handleMessage(message)
    eventBus.on('message', this.messageHandler)
  },

  onShow() {
    this.startRefreshTimer()
  },

  onHide() {
    this.stopRefreshTimer()
  },

  onUnload() {
    this.stopRefreshTimer()
    eventBus.off('message', this.messageHandler)
  },

  startRefreshTimer() {
    this.stopRefreshTimer()
    this.refreshTimer = setInterval(() => this.refresh(), 30000)
  },

  stopRefreshTimer() {
    if (!this.refreshTimer) return
    clearInterval(this.refreshTimer)
    this.refreshTimer = null
  }
})

常见错误:在 onShow 重复注册监听

下面这种写法每次显示页面都会增加一个监听。往返页面几次后,同一消息可能触发多次回调。

// 不推荐
onShow() {
  eventBus.on('message', this.handleMessage)
}

如果监听只属于当前页面实例,在 onLoad 注册并在 onUnload 解绑更清晰;如果业务要求仅在页面可见时监听,就在 onShow 注册、在 onHide 解绑,并保存同一个函数引用。

用静态断言核对五种场景

为了避免把页面栈行为写反,我用 Node.js 22 对照官方路由与生命周期文档,给首次打开、navigateTonavigateBackredirectTo 和已有 Tab 页重访写了 10 条静态期望断言,全部通过。它验证的是文档映射和文章表格,不等同于微信开发者工具里的运行时日志。

import assert from 'node:assert/strict'

const expected = {
  firstOpen: ['onLoad', 'onShow', 'onReady'],
  navigateTo: {
    source: ['onHide'],
    target: ['onLoad', 'onShow', 'onReady']
  },
  navigateBack: {
    source: ['onUnload'],
    target: ['onShow']
  },
  redirectTo: {
    source: ['onUnload'],
    target: ['onLoad', 'onShow', 'onReady']
  },
  tabRevisit: {
    source: ['onHide'],
    existingTarget: ['onShow']
  }
}

assert.deepEqual(expected.firstOpen, ['onLoad', 'onShow', 'onReady'])
assert.deepEqual(expected.navigateBack.target, ['onShow'])
assert.deepEqual(expected.redirectTo.source, ['onUnload'])

排查时按这个顺序检查

  • 先确认当前动作是隐藏页面还是销毁页面。
  • 检查刷新逻辑是否只写在 onLoad,导致返回后不执行。
  • 检查 onShow 是否重复注册监听或重复启动定时器。
  • 确认解绑时传入的是注册时的同一个函数引用。
  • 涉及节点尺寸或组件实例时,再判断是否应放进 onReady
  • 在开发者工具和真机分别记录日志,避免只凭一次预览下结论。

结论

页面生命周期的核心是实例状态:onLoadonReady 面向一次创建,onShowonHide 面向反复显示与隐藏,onUnload 负责最终清理。把数据刷新、可见页任务和资源释放分别放到对应阶段,页面返回不刷新、监听累积和后台定时器这三类问题会少很多。

官方资料:页面生命周期Page 构造器页面路由(访问于 2026-09-10);本文静态期望检查使用 Node.js 22,10 条断言全部通过。

数臻源码猫咪图标
目录
数臻源码猫咪图标

目录

标签云: