深入理解 Uni-App 的生命周期:应用、页面与组件全解析

208 min read

在 Uni-App 开发过程中,生命周期 是每个开发者需要深刻理解的核心概念。通过对生命周期的掌握,我们可以在应用的不同阶段执行特定的逻辑,从而优化应用性能、提升用户体验。本文将从应用、页面和组件的生命周期三个层面,详细解读 Uni-App 中的生命周期,并结合实际代码展示其用法。

一、生命周期概述

生命周期 类似于人的生命周期,从创建、挂载、更新到销毁。在 Uni-App 中,每个组件、页面和整个应用都有各自的生命周期方法,开发者可以在这些方法中执行初始化、数据获取、清理等操作。

生命周期分类

  1. 应用生命周期:作用于整个应用,仅在应用启动和关闭时触发。
  2. 页面生命周期:作用于单个页面,每个页面在加载、显示、隐藏和卸载时触发。
  3. 组件生命周期:作用于自定义组件,每个组件在创建、挂载、更新和销毁时触发。

二、应用生命周期

应用生命周期方法定义在 App.vue 文件中,仅在应用的整个生命周期中触发一次或多次。

常用应用生命周期方法

  • onLaunch:应用初始化完成时触发,仅触发一次。
  • onShow:应用启动时或从后台进入前台时触发。
  • onHide:应用从前台进入后台时触发。
  • onError:应用发生错误时触发。
  • onUnhandledRejection:应用未捕获的 Promise 拒绝时触发。

示例代码

App.vue

<template>
  <router-view></router-view>
</template>

<script>
export default {
  onLaunch() {
    console.log('应用初始化完成:onLaunch');
    // 在此处进行全局初始化操作,如获取用户登录状态
  },
  onShow() {
    console.log('应用显示:onShow');
    // 应用从后台回到前台时的操作
  },
  onHide() {
    console.log('应用隐藏:onHide');
    // 应用进入后台时的操作
  },
  onError(error) {
    console.log('应用发生错误:', error);
    // 处理全局错误
  },
  onUnhandledRejection(reason, promise) {
    console.log('未处理的 Promise 拒绝:', reason);
    // 处理未捕获的 Promise 拒绝
  }
}
</script>

<style>
/* 全局样式 */
</style>

解析

  • onLaunch:应用启动时执行一次,适合进行全局初始化操作,如设置全局变量、获取用户登录状态等。
  • onShow:每次应用显示时触发,包括首次启动和从后台回到前台。
  • onHide:每次应用隐藏时触发,即应用进入后台。
  • onError:应用发生错误时触发,可以用于全局错误处理。
  • onUnhandledRejection:未捕获的 Promise 拒绝时触发,用于处理异步操作中的错误。

三、页面生命周期

页面生命周期方法定义在各个页面的 .vue 文件中,每个页面在加载、显示、隐藏和卸载时触发。

常用页面生命周期方法

  • onLoad:页面加载时触发,接收页面参数。
  • onReady:页面初次渲染完成时触发。
  • onShow:页面显示时触发,每次页面显示都会触发。
  • onHide:页面隐藏时触发,每次页面隐藏都会触发。
  • onUnload:页面卸载时触发,如使用 redirectTo 关闭页面时。

示例代码

pages/index/index.vue

<template>
  <view class="container">
    <button @click="toggleComponent">显示/隐藏组件</button>
    <button @click="openNewPage">打开新页面</button>
    
    <!-- 使用 v-if 控制组件的显示与隐藏 -->
    <Test v-if="showComponent"></Test>
    
    <text>{{ message }}</text>
  </view>
</template>

<script>
import Test from '@/components/Test.vue'

export default {
  components: {
    Test
  },
  data() {
    return {
      showComponent: false,
      message: '首页'
    }
  },
  onLoad() {
    console.log('页面加载:onLoad');
    // 页面初始化操作,如请求数据
  },
  onReady() {
    console.log('页面渲染完成:onReady');
    // 页面渲染完成后的操作
  },
  onShow() {
    console.log('页面显示:onShow');
    // 页面显示时的操作
  },
  onHide() {
    console.log('页面隐藏:onHide');
    // 页面隐藏时的操作
  },
  onUnload() {
    console.log('页面卸载:onUnload');
    // 页面卸载时的操作
  },
  methods: {
    toggleComponent() {
      this.showComponent = !this.showComponent;
    },
    openNewPage() {
      uni.navigateTo({
        url: '/pages/test-a/test-a'
      });
      
      // 或者使用 redirectTo 关闭当前页面
      // uni.redirectTo({
      //   url: '/pages/test-a/test-a'
      // });
    }
  }
}
</script>

<style>
.container {
  padding: 20rpx;
}
button {
  margin-bottom: 20rpx;
}
</style>

pages/test-a/test-a.vue

<template>
  <view class="container">
    <button @click="goBack">返回首页</button>
    <text>{{ message }}</text>
  </view>
</template>

<script>
export default {
  data() {
    return {
      message: '测试页面 A'
    }
  },
  onLoad() {
    console.log('测试页面 A 加载:onLoad');
    // 页面初始化操作
  },
  onReady() {
    console.log('测试页面 A 渲染完成:onReady');
    // 页面渲染完成后的操作
  },
  onShow() {
    console.log('测试页面 A 显示:onShow');
    // 页面显示时的操作
  },
  onHide() {
    console.log('测试页面 A 隐藏:onHide');
    // 页面隐藏时的操作
  },
  onUnload() {
    console.log('测试页面 A 卸载:onUnload');
    // 页面卸载时的操作
  },
  methods: {
    goBack() {
      uni.navigateBack();
    }
  }
}
</script>

<style>
.container {
  padding: 20rpx;
}
button {
  margin-bottom: 20rpx;
}
</style>

解析

  • onLoad:页面加载时触发,可用于接收参数和进行初始化操作。
  • onReady:页面首次渲染完成时触发,适合进行界面相关的操作,如动画效果。
  • onShow:每次页面显示时触发,包括首次显示和从后台返回前台。
  • onHide:每次页面隐藏时触发,如打开新页面或切换到其他应用。
  • onUnload:页面被卸载时触发,如使用 redirectTo 关闭页面。

生命周期触发顺序示例

  1. 首次打开首页

    • 应用生命周期
      • onLaunch
      • onShow
    • 页面生命周期
      • onLoad
      • onShow
      • onReady
    • 组件生命周期(首次显示时加载组件):
      • beforeCreate
      • created
      • mounted
  2. 点击“显示/隐藏组件”按钮

    • 显示组件时
      • beforeCreate
      • created
      • mounted
    • 隐藏组件时
      • destroyed
  3. 点击“打开新页面”按钮,使用 navigateTo

    • 当前页面生命周期
      • onHide(首页被隐藏)
    • 新页面生命周期
      • onLoad
      • onShow
      • onReady
  4. 返回首页

    • 新页面生命周期
      • onHide(新页面被隐藏)
    • 首页生命周期
      • onShow
  5. 使用 redirectTo 打开新页面

    • 当前页面生命周期
      • onUnload(首页被卸载)
    • 新页面生命周期
      • onLoad
      • onShow
      • onReady

四、组件生命周期

组件生命周期方法定义在自定义组件的 .vue 文件中,每个组件在创建、挂载、更新和销毁时触发。理解和正确使用这些方法,可以有效管理组件的状态和资源。

常用组件生命周期方法

  1. beforeCreate:实例初始化之后,数据观测和事件配置之前调用。
  2. created:实例创建完成后立即调用,此时已完成数据观测、属性和方法的运算,但挂载阶段尚未开始。
  3. beforeMount:在挂载开始之前调用,此时模板已编译但未挂载。
  4. mounted:组件挂载到页面后立即调用,此时可以进行 DOM 操作。
  5. beforeUpdate:数据更新之前调用,可以在此对数据进行处理。
  6. updated:数据更新后调用,此时 DOM 已更新。
  7. activated:用于 <keep-alive> 缓存组件的激活时调用。
  8. deactivated:用于 <keep-alive> 缓存组件的停用时调用。
  9. destroyed:组件实例销毁后调用,用于清理资源。

示例代码

1. 创建自定义组件 Test.vue

首先,在项目根目录下新建一个 components 文件夹,并在其中创建 Test.vue 组件。

components/Test.vue

<template>
  <view class="test-container">
    <text>{{ message }}</text>
  </view>
</template>

<script>
export default {
  name: 'Test',
  data() {
    return {
      message: '这是一个测试组件'
    }
  },
  beforeCreate() {
    console.log('组件生命周期:beforeCreate');
  },
  created() {
    console.log('组件生命周期:created');
  },
  mounted() {
    console.log('组件生命周期:mounted');
  },
  destroyed() {
    console.log('组件生命周期:destroyed');
  }
}
</script>

<style>
.test-container {
  padding: 20rpx;
  background-color: #f0f0f0;
  border: 1px solid #ccc;
}
</style>

2. 在页面中引用组件

接下来,在某个页面(如 pages/index/index.vue)中引用并使用该组件。

pages/index/index.vue

<template>
  <view class="container">
    <button @click="toggleComponent">显示/隐藏组件</button>
    <button @click="openNewPage">打开新页面</button>
    
    <!-- 使用 v-if 控制组件的显示与隐藏 -->
    <Test v-if="showComponent"></Test>
    
    <text>{{ message }}</text>
  </view>
</template>

<script>
import Test from '@/components/Test.vue'

export default {
  components: {
    Test
  },
  data() {
    return {
      showComponent: false,
      message: '首页'
    }
  },
  onLoad() {
    console.log('页面加载:onLoad');
  },
  onReady() {
    console.log('页面渲染完成:onReady');
  },
  onShow() {
    console.log('页面显示:onShow');
  },
  onHide() {
    console.log('页面隐藏:onHide');
  },
  onUnload() {
    console.log('页面卸载:onUnload');
  },
  methods: {
    toggleComponent() {
      this.showComponent = !this.showComponent;
    },
    openNewPage() {
      uni.navigateTo({
        url: '/pages/test-a/test-a'
      });
      
      // 或者使用 redirectTo 关闭当前页面
      // uni.redirectTo({
      //   url: '/pages/test-a/test-a'
      // });
    }
  }
}
</script>

<style>
.container {
  padding: 20rpx;
}
button {
  margin-bottom: 20rpx;
}
</style>

3. 创建新页面 Test-a.vue

pages 目录下创建 test-a 文件夹,并在其中创建 test-a.vue 页面。

pages/test-a/test-a.vue

<template>
  <view class="container">
    <button @click="goBack">返回首页</button>
    <text>{{ message }}</text>
  </view>
</template>

<script>
export default {
  data() {
    return {
      message: '测试页面 A'
    }
  },
  onLoad() {
    console.log('测试页面 A 加载:onLoad');
  },
  onReady() {
    console.log('测试页面 A 渲染完成:onReady');
  },
  onShow() {
    console.log('测试页面 A 显示:onShow');
  },
  onHide() {
    console.log('测试页面 A 隐藏:onHide');
  },
  onUnload() {
    console.log('测试页面 A 卸载:onUnload');
  },
  methods: {
    goBack() {
      uni.navigateBack();
    }
  }
}
</script>

<style>
.container {
  padding: 20rpx;
}
button {
  margin-bottom: 20rpx;
}
</style>

解析

  • beforeCreate:在组件实例化后、数据观测和事件配置之前调用。
  • created:组件实例创建完成后调用,此时数据已观测,方法和事件已配置,但尚未挂载到页面。
  • mounted:组件挂载到页面后调用,此时可以进行 DOM 操作。
  • destroyed:组件被销毁前调用,用于清理资源,如取消定时器或移除事件监听。

生命周期触发顺序示例

  1. 首次打开首页

    • 应用生命周期
      • onLaunch
      • onShow
    • 页面生命周期
      • onLoad
      • onShow
      • onReady
    • 组件生命周期(首次显示时加载组件):
      • beforeCreate
      • created
      • mounted
  2. 点击“显示/隐藏组件”按钮

    • 显示组件时
      • beforeCreate
      • created
      • mounted
    • 隐藏组件时
      • destroyed
  3. 点击“打开新页面”按钮,使用 navigateTo

    • 当前页面生命周期
      • onHide(首页被隐藏)
    • 新页面生命周期
      • onLoad
      • onShow
      • onReady
  4. 返回首页

    • 新页面生命周期
      • onHide(新页面被隐藏)
    • 首页生命周期
      • onShow
  5. 使用 redirectTo 打开新页面

    • 当前页面生命周期
      • onUnload(首页被卸载)
    • 新页面生命周期
      • onLoad
      • onShow
      • onReady

五、其他组件生命周期方法

除了常用的 beforeCreatecreatedmounteddestroyed,组件还提供了其他生命周期方法,如:

  • beforeMount:在挂载开始之前调用,此时模板已编译但未挂载。
  • beforeUpdate:数据更新之前调用,可以在此对数据进行处理。
  • updated:数据更新后调用,此时 DOM 已更新。
  • activateddeactivated:用于 <keep-alive> 缓存组件的激活与停用。

示例代码

components/AdvancedComponent.vue

<template>
  <view class="advanced-container">
    <text>{{ advancedMessage }}</text>
  </view>
</template>

<script>
export default {
  name: 'AdvancedComponent',
  data() {
    return {
      advancedMessage: '这是一个高级组件'
    }
  },
  beforeCreate() {
    console.log('高级组件生命周期:beforeCreate');
  },
  created() {
    console.log('高级组件生命周期:created');
  },
  beforeMount() {
    console.log('高级组件生命周期:beforeMount');
  },
  mounted() {
    console.log('高级组件生命周期:mounted');
  },
  beforeUpdate() {
    console.log('高级组件生命周期:beforeUpdate');
  },
  updated() {
    console.log('高级组件生命周期:updated');
  },
  activated() {
    console.log('高级组件生命周期:activated');
  },
  deactivated() {
    console.log('高级组件生命周期:deactivated');
  },
  destroyed() {
    console.log('高级组件生命周期:destroyed');
  }
}
</script>

<style>
.advanced-container {
  padding: 20rpx;
  background-color: #e0ffe0;
  border: 1px solid #00cc00;
}
</style>

解析

  • beforeMount:在挂载开始之前调用,此时模板已编译但未挂载,可以进行一些预处理操作。
  • beforeUpdate:数据更新之前调用,可以在此对数据进行进一步处理或验证。
  • updated:数据更新后调用,此时 DOM 已更新,可以进行与 DOM 相关的操作。
  • activateddeactivated:当使用 <keep-alive> 缓存组件时,activated 在组件被激活时调用,deactivated 在组件被停用时调用。

使用 <keep-alive> 缓存组件

pages/index/index.vue

<template>
  <view class="container">
    <button @click="toggleComponent">显示/隐藏高级组件</button>
    
    <!-- 使用 <keep-alive> 缓存组件 -->
    <keep-alive>
      <AdvancedComponent v-if="showAdvancedComponent"></AdvancedComponent>
    </keep-alive>
  </view>
</template>

<script>
import AdvancedComponent from '@/components/AdvancedComponent.vue'

export default {
  components: {
    AdvancedComponent
  },
  data() {
    return {
      showAdvancedComponent: false
    }
  },
  methods: {
    toggleComponent() {
      this.showAdvancedComponent = !this.showAdvancedComponent;
    }
  }
}
</script>

<style>
.container {
  padding: 20rpx;
}
button {
  margin-bottom: 20rpx;
}
</style>

解析

  • :包裹需要缓存的组件,激活和停用时触发 activateddeactivated
  • toggleComponent 方法用于控制高级组件的显示与隐藏,同时触发相应的生命周期方法。

六、注意事项

  • 唯一性:应用生命周期方法只能在 App.vue 中定义,页面生命周期方法应在各自的页面 .vue 文件中定义,组件生命周期方法仅在自定义组件中定义。
  • 避免混淆:不要将应用生命周期方法和页面、组件生命周期方法混淆,确保在正确的位置定义相应的方法。
  • 资源管理:在生命周期方法中合理管理资源,如在 destroyed 中清理定时器或事件监听,避免内存泄漏。
  • 条件编译:在使用条件编译时,确保不同平台下生命周期方法的逻辑完整,避免因平台差异导致的问题。

七、作业与实践

作业

  1. 应用生命周期实践

    • App.vueonLaunch 中初始化全局变量,如用户登录状态。
    • onShow 中检测应用从后台回到前台,刷新部分数据。
  2. 页面生命周期实践

    • 在首页的 onLoad 中请求初始数据。
    • onShow 中刷新页面数据,确保每次显示时数据是最新的。
    • 使用 navigateToredirectTo 打开新页面,观察生命周期方法的触发顺序。
  3. 组件生命周期实践

    • 创建多个自定义组件,分别在不同生命周期方法中添加 console.log 语句,观察它们的触发顺序。
    • 在组件的 mounted 中进行 DOM 操作,如修改元素样式,验证生命周期方法的执行时机。
    • 在组件的 destroyed 中清理资源,如取消定时器或移除事件监听。
  4. 高级组件生命周期应用

    • 创建一个使用 <keep-alive> 缓存的组件,利用 activateddeactivated 方法管理组件状态。
    • beforeUpdateupdated 方法中处理数据变化,确保组件状态的一致性。

实践建议

  • 日志调试:在各个生命周期方法中添加 console.log,帮助理解方法的触发时机和顺序,便于调试和优化。
  • 合理利用生命周期方法
    • beforeCreatecreated:用于初始化数据和配置事件。
    • mounted:进行 DOM 操作或调用第三方库。
    • beforeUpdateupdated:处理数据变化和更新视图。
    • destroyed:清理资源,防止内存泄漏。
  • 组件复用:通过理解组件生命周期,设计高复用性的组件,提高代码的可维护性和效率。
  • 资源管理:在组件销毁前,确保所有资源(如定时器、事件监听)都已正确释放,避免潜在的问题。
  • 条件编译结合生命周期:在不同平台下执行不同的生命周期逻辑,确保应用的跨平台兼容性。

八、总结

通过本节课的学习,掌握了 Uni-App 中生命周期的基本概念和常用方法,包括:

  • 应用生命周期onLaunchonShowonHideonErroronUnhandledRejection
  • 页面生命周期onLoadonReadyonShowonHideonUnload
  • 组件生命周期beforeCreatecreatedbeforeMountmountedbeforeUpdateupdatedactivateddeactivateddestroyed

理解并正确使用生命周期方法,能够帮助开发者在 Uni-App 应用中更好地管理数据、优化性能和提升用户体验。建议同学们通过作业和实际项目,进一步巩固和应用所学知识,为后续更复杂的开发任务打下坚实的基础。


参考资料