Skip to content

生命周期详解 - 页面什么时候加载,什么时候销毁?

⏰ 生命周期就像人的一生,有出生、成长、衰老、死亡。页面也是如此!

什么是生命周期?

生命周期就是页面从创建到销毁的整个过程

想象一下:

  • 你打开一个页面 → 页面"出生"了
  • 页面显示内容 → 页面"成长"了
  • 你切换到其他页面 → 页面"休眠"了
  • 你返回这个页面 → 页面"苏醒"了
  • 你关闭这个页面 → 页面"死亡"了

在这些关键时刻,我们可以执行特定的代码,这就是生命周期的作用!

应用生命周期

应用生命周期是整个应用的生命周期,在App.vue中定义:

javascript
// App.vue
export default {
  onLaunch: function() {
    console.log('应用启动')
    // 应用第一次启动时执行
    // 适合做:初始化全局数据、检查更新、获取系统信息等
  },
  
  onShow: function() {
    console.log('应用显示')
    // 应用从后台进入前台时执行
    // 适合做:刷新数据、重新连接网络等
  },
  
  onHide: function() {
    console.log('应用隐藏')
    // 应用从前台进入后台时执行
    // 适合做:保存数据、暂停音乐等
  },
  
  onError: function(err) {
    console.log('应用错误:', err)
    // 应用发生错误时执行
    // 适合做:错误上报、用户提示等
  }
}

页面生命周期

页面生命周期是单个页面的生命周期,在每个.vue页面中定义:

1. onLoad - 页面加载

触发时机:页面第一次加载时 执行次数:只执行一次 适合做什么:接收参数、初始化数据、调用API

javascript
export default {
  data() {
    return {
      userInfo: {},
      productList: []
    }
  },
  
  onLoad(options) {
    console.log('页面加载,参数:', options)
    
    // 接收页面参数
    const userId = options.userId
    
    // 初始化数据
    this.initData()
    
    // 调用API
    this.getUserInfo(userId)
    this.getProductList()
  },
  
  methods: {
    initData() {
      // 初始化页面数据
    },
    
    async getUserInfo(userId) {
      // 获取用户信息
      try {
        const res = await uni.request({
          url: '/api/user/' + userId
        })
        this.userInfo = res.data
      } catch (err) {
        console.error('获取用户信息失败:', err)
      }
    },
    
    async getProductList() {
      // 获取商品列表
    }
  }
}

2. onShow - 页面显示

触发时机:页面显示时(包括第一次显示和从其他页面返回) 执行次数:可能多次执行 适合做什么:刷新数据、重新获取最新状态

javascript
export default {
  onShow() {
    console.log('页面显示')
    
    // 刷新购物车数量
    this.updateCartCount()
    
    // 检查登录状态
    this.checkLoginStatus()
    
    // 刷新页面数据(如果需要)
    this.refreshData()
  },
  
  methods: {
    updateCartCount() {
      // 更新购物车数量
      const cartCount = uni.getStorageSync('cartCount') || 0
      this.cartCount = cartCount
    },
    
    checkLoginStatus() {
      // 检查登录状态
      const token = uni.getStorageSync('token')
      if (!token) {
        // 未登录,跳转到登录页
        uni.navigateTo({
          url: '/pages/login/login'
        })
      }
    },
    
    refreshData() {
      // 刷新页面数据
      // 注意:不是所有页面都需要在onShow时刷新数据
      // 只有那些可能被其他页面影响的数据才需要刷新
    }
  }
}

3. onReady - 页面初次渲染完成

触发时机:页面初次渲染完成时 执行次数:只执行一次 适合做什么:获取节点信息、设置动画等

javascript
export default {
  onReady() {
    console.log('页面渲染完成')
    
    // 获取节点信息
    this.getElementInfo()
    
    // 设置动画
    this.startAnimation()
    
    // 设置页面标题(动态)
    uni.setNavigationBarTitle({
      title: '动态标题'
    })
  },
  
  methods: {
    getElementInfo() {
      // 获取元素信息
      const query = uni.createSelectorQuery().in(this)
      query.select('.my-element').boundingClientRect(data => {
        console.log('元素信息:', data)
      }).exec()
    },
    
    startAnimation() {
      // 开始动画
      const animation = uni.createAnimation({
        duration: 1000,
        timingFunction: 'ease'
      })
      
      animation.scale(1.2).rotate(45).step()
      this.animationData = animation.export()
    }
  }
}

4. onHide - 页面隐藏

触发时机:页面隐藏时(跳转到其他页面或应用进入后台) 执行次数:可能多次执行 适合做什么:暂停操作、保存数据等

javascript
export default {
  data() {
    return {
      timer: null,
      formData: {}
    }
  },
  
  onHide() {
    console.log('页面隐藏')
    
    // 清除定时器
    if (this.timer) {
      clearInterval(this.timer)
      this.timer = null
    }
    
    // 暂停视频播放
    this.pauseVideo()
    
    // 保存表单数据
    this.saveFormData()
  },
  
  methods: {
    pauseVideo() {
      // 暂停视频播放
      const videoContext = uni.createVideoContext('myVideo', this)
      videoContext.pause()
    },
    
    saveFormData() {
      // 保存表单数据到本地
      uni.setStorageSync('formData', this.formData)
    }
  }
}

5. onUnload - 页面卸载

触发时机:页面卸载时(页面被关闭) 执行次数:只执行一次 适合做什么:清理资源、取消网络请求等

javascript
export default {
  data() {
    return {
      requestTask: null,
      timer: null
    }
  },
  
  onUnload() {
    console.log('页面卸载')
    
    // 取消网络请求
    if (this.requestTask) {
      this.requestTask.abort()
    }
    
    // 清除定时器
    if (this.timer) {
      clearInterval(this.timer)
    }
    
    // 清理其他资源
    this.cleanup()
  },
  
  methods: {
    cleanup() {
      // 清理资源
      // 移除事件监听器
      // 清理缓存数据等
    }
  }
}

生命周期执行顺序

让我们通过一个完整的例子来看看生命周期的执行顺序:

javascript
export default {
  data() {
    return {
      message: 'Hello UniApp'
    }
  },
  
  // 1. 页面加载(只执行一次)
  onLoad(options) {
    console.log('1. onLoad - 页面加载', options)
  },
  
  // 2. 页面显示(可能多次执行)
  onShow() {
    console.log('2. onShow - 页面显示')
  },
  
  // 3. 页面初次渲染完成(只执行一次)
  onReady() {
    console.log('3. onReady - 页面渲染完成')
  },
  
  // 4. 页面隐藏(可能多次执行)
  onHide() {
    console.log('4. onHide - 页面隐藏')
  },
  
  // 5. 页面卸载(只执行一次)
  onUnload() {
    console.log('5. onUnload - 页面卸载')
  }
}

执行顺序

  1. 第一次打开页面:onLoadonShowonReady
  2. 跳转到其他页面:onHide
  3. 返回这个页面:onShow
  4. 关闭这个页面:onHideonUnload

实际应用案例

案例1:商品详情页

javascript
export default {
  data() {
    return {
      productId: '',
      productInfo: {},
      timer: null,
      viewCount: 0
    }
  },
  
  onLoad(options) {
    // 获取商品ID
    this.productId = options.id
    
    // 加载商品信息
    this.loadProductInfo()
  },
  
  onShow() {
    // 开始计算浏览时长
    this.startViewTimer()
    
    // 刷新库存信息
    this.refreshStock()
  },
  
  onHide() {
    // 停止计算浏览时长
    this.stopViewTimer()
    
    // 上报浏览数据
    this.reportViewData()
  },
  
  onUnload() {
    // 清理定时器
    this.stopViewTimer()
  },
  
  methods: {
    async loadProductInfo() {
      try {
        const res = await uni.request({
          url: `/api/product/\${this.productId}`
        })
        this.productInfo = res.data
      } catch (err) {
        uni.showToast({
          title: '加载失败',
          icon: 'error'
        })
      }
    },
    
    startViewTimer() {
      this.timer = setInterval(() => {
        this.viewCount++
      }, 1000)
    },
    
    stopViewTimer() {
      if (this.timer) {
        clearInterval(this.timer)
        this.timer = null
      }
    },
    
    async refreshStock() {
      // 刷新库存信息
      try {
        const res = await uni.request({
          url: `/api/product/\${this.productId}/stock`
        })
        this.productInfo.stock = res.data.stock
      } catch (err) {
        console.error('刷新库存失败:', err)
      }
    },
    
    reportViewData() {
      // 上报浏览数据
      if (this.viewCount > 0) {
        uni.request({
          url: '/api/report/view',
          method: 'POST',
          data: {
            productId: this.productId,
            viewTime: this.viewCount
          }
        })
      }
    }
  }
}

案例2:聊天页面

javascript
export default {
  data() {
    return {
      messageList: [],
      websocket: null,
      heartbeatTimer: null
    }
  },
  
  onLoad(options) {
    // 获取聊天室ID
    this.roomId = options.roomId
    
    // 加载历史消息
    this.loadHistoryMessages()
  },
  
  onShow() {
    // 连接WebSocket
    this.connectWebSocket()
    
    // 标记消息为已读
    this.markMessagesAsRead()
  },
  
  onHide() {
    // 断开WebSocket连接
    this.disconnectWebSocket()
  },
  
  onUnload() {
    // 确保WebSocket已断开
    this.disconnectWebSocket()
  },
  
  methods: {
    connectWebSocket() {
      this.websocket = uni.connectSocket({
        url: `wss://api.example.com/chat/\${this.roomId}`
      })
      
      this.websocket.onMessage((res) => {
        const message = JSON.parse(res.data)
        this.messageList.push(message)
      })
      
      // 开始心跳
      this.startHeartbeat()
    },
    
    disconnectWebSocket() {
      if (this.websocket) {
        this.websocket.close()
        this.websocket = null
      }
      
      // 停止心跳
      this.stopHeartbeat()
    },
    
    startHeartbeat() {
      this.heartbeatTimer = setInterval(() => {
        if (this.websocket) {
          this.websocket.send({
            data: JSON.stringify({ type: 'heartbeat' })
          })
        }
      }, 30000)
    },
    
    stopHeartbeat() {
      if (this.heartbeatTimer) {
        clearInterval(this.heartbeatTimer)
        this.heartbeatTimer = null
      }
    }
  }
}

常见问题和最佳实践

1. 不要在onShow中频繁调用API

javascript
// ❌ 错误做法
onShow() {
  // 每次显示都调用API,浪费资源
  this.loadAllData()
}

// ✅ 正确做法
onShow() {
  // 只刷新必要的数据
  this.refreshCartCount()
  this.checkLoginStatus()
}

2. 及时清理资源

javascript
// ✅ 正确做法
export default {
  data() {
    return {
      timer: null,
      requestTask: null
    }
  },
  
  onHide() {
    // 清理定时器
    if (this.timer) {
      clearInterval(this.timer)
      this.timer = null
    }
  },
  
  onUnload() {
    // 取消网络请求
    if (this.requestTask) {
      this.requestTask.abort()
    }
  }
}

3. 合理使用生命周期

  • onLoad:初始化数据、接收参数
  • onShow:刷新状态、重新连接
  • onReady:操作DOM、设置动画
  • onHide:暂停操作、保存数据
  • onUnload:清理资源、取消请求

小结

今天我们学习了:

  • ✅ 应用生命周期和页面生命周期
  • ✅ 每个生命周期的触发时机和适用场景
  • ✅ 生命周期的执行顺序
  • ✅ 实际应用案例
  • ✅ 常见问题和最佳实践

记住这个口诀

  • onLoad:页面出生,初始化
  • onShow:页面显示,刷新状态
  • onReady:渲染完成,操作DOM
  • onHide:页面隐藏,暂停操作
  • onUnload:页面死亡,清理资源

下一篇预告

下一篇我们将学习《基础组件大全 - 按钮、文本、图片这些怎么用?》,开始学习UniApp的各种组件。

练习作业

  1. 创建一个页面,在每个生命周期中打印日志
  2. 实现一个计时器,在页面显示时开始计时,隐藏时停止
  3. 在页面卸载时弹出确认对话框

生命周期是页面的灵魂,掌握了生命周期,你就能让页面在合适的时机做合适的事情!