Skip to content

uni-app 生命週期詳解

本文將詳細介紹 uni-app 的生命週期,包括應用生命週期、頁面生命週期和元件生命週期,幫助您更好地理解和使用 uni-app。

概述

生命週期是指應用程式從建立到銷燬的整個過程中,特定時間點觸發的函數,開發者可以在這些函數中執行自己的程式碼邏輯。

uni-app 生命週期分類

型別說明監聽位置
應用生命週期應用級別的生命週期函數App.vue 中監聽
頁面生命週期頁面級別的生命週期函數頁面中監聽
元件生命週期元件級別的生命週期函數元件中監聽

應用生命週期

應用生命週期是指 uni-app 從啟動到解除安裝的過程中,特定時間點觸發的函數。這些函數在 App.vue 中定義。

生命週期函數

函數名說明引數適用場景
onLaunch應用初始化完成時觸發(全域只觸發一次)options(啟動引數)初始化應用資料、檢查更新、獲取使用者資訊
onShow應用啟動或從後臺進入前臺時觸發options(啟動引數)恢復應用狀態、重新整理資料、統計使用時長
onHide應用從前臺進入後臺時觸發-暫停音視訊播放、儲存應用狀態、暫停定時器
onError應用執行報錯時觸發err(錯誤資訊)錯誤日誌收集、異常上報
onUniNViewMessage監聽 nvue 頁面傳送的資料event(訊息物件)接收 nvue 頁面傳送的訊息

應用生命週期示例

vue
<script>
export default {
  onLaunch: function(options) {
    console.log('App Launch')
    console.log('啟動引數:', options)
    
    // 初始化應用資料
    this.checkUpdate()
    this.getUserInfo()
  },
  
  onShow: function(options) {
    console.log('App Show')
    console.log('顯示引數:', options)
    
    // 恢復應用狀態
    this.resumeAudio()
    this.refreshData()
  },
  
  onHide: function() {
    console.log('App Hide')
    
    // 儲存應用狀態
    this.saveAppState()
    this.pauseAudio()
  },
  
  onError: function(err) {
    console.error('App Error:', err)
    
    // 錯誤上報
    this.reportError(err)
  },
  
  methods: {
    checkUpdate() {
      // 檢查應用更新
      uni.getUpdateManager().onCheckForUpdate((res) => {
        if (res.hasUpdate) {
          console.log('發現新版本')
        }
      })
    },
    
    getUserInfo() {
      // 獲取使用者資訊
      uni.getStorage({
        key: 'userInfo',
        success: (res) => {
          this.userInfo = res.data
        }
      })
    },
    
    resumeAudio() {
      // 恢復音訊播放
      if (this.audioContext) {
        this.audioContext.play()
      }
    },
    
    pauseAudio() {
      // 暫停音訊播放
      if (this.audioContext) {
        this.audioContext.pause()
      }
    },
    
    refreshData() {
      // 重新整理資料
      this.$store.dispatch('refreshData')
    },
    
    saveAppState() {
      // 儲存應用狀態
      uni.setStorage({
        key: 'appState',
        data: this.appState
      })
    },
    
    reportError(err) {
      // 錯誤上報
      uni.request({
        url: 'https://api.example.com/error/report',
        method: 'POST',
        data: {
          error: err,
          timestamp: Date.now()
        }
      })
    }
  }
}
</script>

啟動引數說明

不同平台的啟動引數有所不同:

小程式平台

javascript
onLaunch: function(options) {
  // 微信小程式
  console.log('場景值:', options.scene)
  console.log('路徑:', options.path)
  console.log('查詢引數:', options.query)
  
  // 支付寶小程式
  console.log('啟動引數:', options.query)
}

App平台

javascript
onLaunch: function(options) {
  // App平台
  console.log('啟動來源:', options.referrerInfo)
  console.log('啟動引數:', options.query)
}

頁面生命週期

頁面生命週期是指頁面從載入到銷燬的過程中,特定時間點觸發的函數。這些函數在頁面元件中定義。

生命週期函數

函數名說明引數觸發時機
onLoad頁面載入時觸發options(頁面引數)頁面初次載入時
onShow頁面顯示時觸發-頁面顯示或從後臺進入前臺時
onReady頁面初次渲染完成時觸發-頁面初次渲染完成時
onHide頁面隱藏時觸發-頁面隱藏或進入後臺時
onUnload頁面解除安裝時觸發-頁面路由跳轉離開時
onPullDownRefresh使用者下拉重新整理時觸發-下拉重新整理操作時
onReachBottom頁面滾動到底部時觸發-頁面滾動到底部時
onShareAppMessage使用者點選分享時觸發-點選分享按鈕時
onPageScroll頁面滾動時觸發Object(滾動位置)頁面滾動時
onResize頁面尺寸變化時觸發Object(尺寸資訊)頁面尺寸變化時
onTabItemTap點選 tab 時觸發Object(tab資訊)點選底部 tab 時

頁面生命週期示例

vue
<template>
  <view class="page">
    <text>{{ title }}</text>
    <button @click="handleClick">點選我</button>
  </view>
</template>

<script>
export default {
  data() {
    return {
      title: '頁面標題',
      pageData: null
    }
  },
  
  onLoad(options) {
    console.log('頁面載入', options)
    
    // 獲取頁面引數
    this.pageId = options.id
    
    // 載入頁面資料
    this.loadPageData()
  },
  
  onShow() {
    console.log('頁面顯示')
    
    // 恢復頁面狀態
    this.resumePage()
  },
  
  onReady() {
    console.log('頁面渲染完成')
    
    // 執行頁面初始化操作
    this.initPage()
  },
  
  onHide() {
    console.log('頁面隱藏')
    
    // 儲存頁面狀態
    this.savePageState()
  },
  
  onUnload() {
    console.log('頁面解除安裝')
    
    // 清理資源
    this.cleanup()
  },
  
  onPullDownRefresh() {
    console.log('下拉重新整理')
    
    // 重新整理資料
    this.refreshData().then(() => {
      // 停止下拉重新整理
      uni.stopPullDownRefresh()
    })
  },
  
  onReachBottom() {
    console.log('滾動到底部')
    
    // 載入更多資料
    this.loadMoreData()
  },
  
  onShareAppMessage() {
    return {
      title: '分享標題',
      path: '/pages/index/index',
      imageUrl: '/static/share.jpg'
    }
  },
  
  onPageScroll(e) {
    // 頁面滾動事件
    this.scrollTop = e.scrollTop
  },
  
  onTabItemTap(e) {
    console.log('點選tab', e)
  },
  
  methods: {
    loadPageData() {
      // 載入頁面資料
      uni.request({
        url: `/api/page/${this.pageId}`,
        success: (res) => {
          this.pageData = res.data
        }
      })
    },
    
    resumePage() {
      // 恢復頁面狀態
      if (this.audioPlaying) {
        this.resumeAudio()
      }
    },
    
    initPage() {
      // 頁面初始化
      this.setupEventListeners()
      this.loadEssentialData()
    },
    
    savePageState() {
      // 儲存頁面狀態
      uni.setStorage({
        key: `pageState_${this.pageId}`,
        data: this.pageState
      })
    },
    
    cleanup() {
      // 清理資源
      this.removeEventListeners()
      this.clearTimers()
    },
    
    refreshData() {
      // 重新整理資料
      return new Promise((resolve) => {
        uni.request({
          url: `/api/page/${this.pageId}/refresh`,
          success: (res) => {
            this.pageData = res.data
            resolve()
          }
        })
      })
    },
    
    loadMoreData() {
      // 載入更多資料
      if (!this.loading && this.hasMore) {
        this.loading = true
        uni.request({
          url: `/api/page/${this.pageId}/more`,
          data: { page: this.currentPage + 1 },
          success: (res) => {
            this.pageData = this.pageData.concat(res.data)
            this.currentPage++
            this.loading = false
          }
        })
      }
    }
  }
}
</script>

頁面引數處理

javascript
onLoad(options) {
  // 處理頁面引數
  this.id = options.id || ''
  this.type = options.type || 'default'
  this.from = options.from || ''
  
  // 驗證必填引數
  if (!this.id) {
    uni.showToast({
      title: '缺少必要引數',
      icon: 'error'
    })
    uni.navigateBack()
    return
  }
  
  // 根據引數載入不同資料
  this.loadDataByType(this.type)
}

元件生命週期

元件生命週期是指 Vue 元件從建立到銷燬的過程中,特定時間點觸發的函數。這些函數遵循 Vue 3 的生命週期規範。

生命週期函數

函數名說明觸發時機
beforeCreate例項初始化之後,資料觀測之前元件建立前
created例項建立完成後元件建立完成後
beforeMount掛載開始之前被呼叫掛載到DOM前
mounted例項掛載完成後被呼叫掛載到DOM後
beforeUpdate資料更新時呼叫,發生在虛擬DOM重新渲染之前資料更新前
updated由於資料更改導致的虛擬DOM重新渲染完成後資料更新後
beforeUnmount例項銷燬之前呼叫元件銷燬前
unmounted例項銷燬後呼叫元件銷燬後
activated被 keep-alive 快取的元件啟用時呼叫快取元件啟用時
deactivated被 keep-alive 快取的元件停用時呼叫快取元件停用時
errorCaptured捕獲子元件錯誤時呼叫子元件錯誤時

元件生命週期示例

vue
<template>
  <view class="component">
    <text>{{ message }}</text>
    <button @click="updateMessage">更新訊息</button>
  </view>
</template>

<script>
export default {
  name: 'MyComponent',
  
  props: {
    initialMessage: {
      type: String,
      default: 'Hello'
    }
  },
  
  data() {
    return {
      message: '',
      timer: null,
      count: 0
    }
  },
  
  beforeCreate() {
    console.log('元件建立前')
    // 此時還不能訪問 data 和 methods
  },
  
  created() {
    console.log('元件建立完成')
    // 可以訪問 data 和 methods,但還不能訪問 DOM
    this.message = this.initialMessage
    this.setupTimer()
  },
  
  beforeMount() {
    console.log('掛載前')
    // 模板編譯完成,但還未掛載到DOM
  },
  
  mounted() {
    console.log('掛載完成')
    // 元件已掛載到DOM,可以訪問DOM元素
    this.$nextTick(() => {
      console.log('DOM更新完成')
    })
  },
  
  beforeUpdate() {
    console.log('更新前')
    // 資料即將更新,DOM還未重新渲染
  },
  
  updated() {
    console.log('更新完成')
    // 資料更新完成,DOM已重新渲染
  },
  
  beforeUnmount() {
    console.log('解除安裝前')
    // 元件即將銷燬,清理資源
    this.cleanup()
  },
  
  unmounted() {
    console.log('解除安裝完成')
    // 元件已銷燬
  },
  
  activated() {
    console.log('元件啟用')
    // 被 keep-alive 快取的元件啟用時
    this.resumeTimer()
  },
  
  deactivated() {
    console.log('元件停用')
    // 被 keep-alive 快取的元件停用時
    this.pauseTimer()
  },
  
  errorCaptured(err, instance, info) {
    console.error('捕獲到錯誤:', err)
    // 可以在此處理錯誤或上報
    this.reportError(err, info)
    // 返回 false 阻止錯誤繼續向上傳播
    return false
  },
  
  methods: {
    updateMessage() {
      this.message = `更新後的訊息 ${Date.now()}`
      this.count++
    },
    
    setupTimer() {
      this.timer = setInterval(() => {
        console.log('定時器執行')
      }, 1000)
    },
    
    cleanup() {
      if (this.timer) {
        clearInterval(this.timer)
        this.timer = null
      }
    },
    
    resumeTimer() {
      if (!this.timer) {
        this.setupTimer()
      }
    },
    
    pauseTimer() {
      if (this.timer) {
        clearInterval(this.timer)
        this.timer = null
      }
    },
    
    reportError(err, info) {
      // 錯誤上報
      console.error('元件錯誤:', err, info)
    }
  }
}
</script>

生命週期執行順序

頁面載入時

  1. 應用生命週期onLaunchonShow
  2. 頁面生命週期onLoadonShowonReady
  3. 元件生命週期beforeCreatecreatedbeforeMountmounted

頁面切換時

從頁面A切換到頁面B

  1. 頁面A:onHide
  2. 頁面B:onLoadonShowonReady
  3. 頁面A:onUnload(如果不再返回)

返回頁面A

  1. 頁面B:onHide
  2. 頁面A:onShow

應用狀態變化

應用進入後臺

  1. 當前頁面:onHide
  2. 應用:onHide

應用返回前臺

  1. 應用:onShow
  2. 當前頁面:onShow

最佳實踐

1. 合理使用生命週期

  • onLoad:用於初始化頁面資料
  • onShow:用於恢復頁面狀態
  • onReady:用於執行DOM相關操作
  • onHide/onUnload:用於清理資源

2. 效能優化

  • 避免在生命週期中執行耗時操作
  • 使用非同步操作避免阻塞渲染
  • 合理使用快取減少重複載入
  • 及時清理定時器和事件監聽器

3. 錯誤處理

  • onError 中處理應用級錯誤
  • errorCaptured 中處理元件錯誤
  • 使用 try-catch 捕獲同步錯誤
  • 合理處理非同步操作的錯誤

4. 狀態管理

  • 在合適的生命週期中儲存和恢復狀態
  • 使用 Vuex 管理全域狀態
  • 使用本地儲存持久化重要資料
  • 合理處理頁面間狀態傳遞

常見問題

Q: onLoad 和 created 有什麼區別?

A: onLoad 是 uni-app 頁面特有的生命週期,用於接收頁面引數;created 是 Vue 元件的生命週期,用於元件初始化。在頁面中,通常使用 onLoad 處理頁面引數。

Q: 如何在不同生命週期中傳遞資料?

A: 可以使用以下方式:

  • 頁面引數:透過路由傳遞
  • 全域狀態:使用 Vuex
  • 本地儲存:使用 uni.setStorage
  • 事件匯流排:使用 mitt 或自定義事件

Q: 生命週期函數可以是非同步的嗎?

A: 可以,但需要注意:

  • 非同步操作不會阻塞生命週期執行
  • 需要處理非同步操作的錯誤
  • 避免在生命週期中執行過於耗時的非同步操作

Q: 如何除錯生命週期執行順序?

A: 可以在各個生命週期函數中新增日誌輸出,觀察執行順序:

javascript
onLoad() {
  console.log('onLoad')
}

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

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

掌握生命週期是高效開發uni-app應用的關鍵,希望本文對您有所幫助!

一次開發,多端部署 - 讓跨平台開發更簡單