uni-app 生命周期深度解析(iOS / Android / 鸿蒙 / Vue3 四端对照)

📌 本文定位: 面向同时具备 iOS、Android、鸿蒙原生开发经验,正在转向 uni-app 的资深工程师。所有技术点均基于 uni-app 官方文档 及 Vue3 官方文档整理。


一、先搞懂一个 JS 语法问题:为什么说"与 data/methods 平级的叫生命周期"?

很多原生工程师初学 uni-app 时会对这句话感到困惑。我们从 JavaScript 语言层面彻底讲清楚。

1.1 Options API 的本质是一个"配置对象"

在 Vue2/Vue3 的 Options API 中,export default {} 导出的是一个普通 JS 对象。这个对象的每个 key 都有特定语义:

javascript 复制代码
export default {
  // ✅ 数据属性 → Vue 会将其转为响应式
  data() {
    return { count: 0 }
  },

  // ✅ 方法属性 → Vue 会将其绑定到组件实例上
  methods: {
    increment() { this.count++ }
  },

  // ✅ 计算属性 → Vue 会缓存其返回值
  computed: {
    doubleCount() { return this.count * 2 }
  },

  // ✅ 生命周期钩子 → Vue 会在特定时机自动调用
  onLoad(options) {
    console.log('页面加载', options)
  },

  onShow() {
    console.log('页面显示')
  },

  mounted() {
    console.log('DOM 挂载完成')
  }
}

1.2 Vue 内部是如何区分它们的?

Vue 在创建组件实例时,会遍历这个配置对象的所有 key,根据 key 的名称做不同的处理:

key 名称 Vue 内部处理方式 归类
data 执行函数,将返回值做响应式代理(reactive() 数据层
methods 遍历对象,将每个方法绑定 this 并挂载到实例 方法层
computed 创建惰性求值的响应式引用(computed() 计算层
watch 注册侦听器(watch() 监听层
onLoad / onShow / mounted 注册为生命周期回调,在对应时机由框架自动调用 生命周期层
其他自定义 key 直接挂载到实例上,不做特殊处理 自定义属性

💡 核心结论: "生命周期"不是一个 JS 语言概念,而是 Vue/uni-app 框架的约定 。框架通过识别特定的 key 名称,将其从普通属性中"挑出来",注册到内部的生命周期调度系统中。这就是为什么它必须与 datamethods "平级"------因为它们都是同一个配置对象的顶层 key。

1.3 对比原生开发中的类似模式

平台 类似机制 说明
iOS UIViewControllerviewDidLoad()viewWillAppear() 系统通过方法名识别生命周期,开发者重写即可
Android ActivityonCreate()onResume() 框架通过方法签名识别,自动在对应时机回调
鸿蒙 @ComponentaboutToAppear()onPageShow() ArkUI 框架通过装饰器+方法名识别
Vue3 Composition API onMounted()onUnmounted() 等组合式函数 不再依赖对象 key 名,改为显式函数调用注册
uni-app Options API onLoad()onShow() 等作为配置对象顶层 key 框架通过 key 名识别并注册

二、应用生命周期详解(App.vue)

应用生命周期是全局级别 的,只能在 App.vue 中监听。它管理的是整个 App 进程从创建到销毁的全过程。

2.1 完整生命周期流程图

scss 复制代码
App 启动
   │
   ▼
┌──────────┐     ┌──────────┐     ┌──────────┐
│ onLaunch │────►│  onShow  │◄───►│  onHide  │
│ (仅一次)  │     │(每次前台) │     │(每次后台) │
└──────────┘     └──────────┘     └──────────┘
                      │
                 用户关闭App
                      │
                      ▼
              ┌──────────────┐
              │   onExit     │
              │ (仅Android)  │
              └──────────────┘

异常分支:
  ├─ JS/API错误 ──► onError()
  ├─ 页面不存在 ──► onPageNotFound()
  └─ 主题切换   ──► onThemeChange()

2.2 逐个详解 + 四端对照

onLaunch(options)

  • 触发时机: App 初始化完成时触发,全局仅一次
  • 参数: options 包含启动路径 path、场景值 scene、查询参数 query
  • 典型用途: SDK 初始化、登录态检查、全局数据预加载
javascript 复制代码
// App.vue
export default {
  onLaunch(options) {
    console.log('App Launch, 启动参数:', options);
    
    // 初始化推送 SDK
    // #ifdef APP-PLUS
    plus.push.addEventListener('click', (msg) => {
      console.log('推送点击:', msg);
    });
    // #endif
    
    // 检查登录态
    const token = uni.getStorageSync('token');
    if (!token) {
      uni.reLaunch({ url: '/pages/login/login' });
    }
  }
}

onShow(options)

  • 触发时机: App 启动 从后台切回前台时触发
  • 参数:onLaunch
  • 典型用途: 刷新数据、恢复任务、版本检查
javascript 复制代码
export default {
  onShow(options) {
    console.log('App Show, 场景值:', options.scene);
    // 每次回到前台都检查是否有新消息
    this.checkNewMessages();
  }
}

onHide()

  • 触发时机: App 从前台进入后台时触发
  • 典型用途: 暂停任务、保存状态、释放资源
javascript 复制代码
export default {
  onHide() {
    console.log('App Hide');
    // 保存播放进度
    uni.setStorageSync('music_progress', this.currentProgress);
  }
}

onError(err)

  • 触发时机: JS 执行错误或 API 调用失败时
  • 典型用途: 全局错误上报、崩溃监控
javascript 复制代码
export default {
  onError(err) {
    console.error('全局错误:', err);
    // 上报到 Sentry / Bugly
    // reportError(err);
  }
}

onPageNotFound(res)

  • 触发时机: 打开不存在的页面时(如分享链接指向已删除页面)
  • 参数: res.path(不存在的路径)、res.query
  • 典型用途: 降级跳转到首页或404页
javascript 复制代码
export default {
  onPageNotFound(res) {
    console.warn('页面不存在:', res.path);
    uni.redirectTo({ url: '/pages/index/index' });
  }
}

onThemeChange(res)

  • 触发时机: 系统深色/浅色模式切换时
  • 参数: res.theme'dark' | 'light'
javascript 复制代码
export default {
  onThemeChange(res) {
    console.log('主题切换:', res.theme);
    // 通知全局状态更新
    getApp().globalData.isDarkMode = res.theme === 'dark';
  }
}

2.3 应用生命周期四端对照表(重点收藏)

uni-app iOS Android 鸿蒙 (ArkUI) Vue3 App 级 说明
onLaunch application(_:didFinishLaunchingWithOptions:) Application.onCreate() AbilityStage.onCreate() app.onMount() (无直接对应) 进程创建,全局仅一次
onShow applicationDidBecomeActive(_:) / sceneDidBecomeActive(_:) Activity.onResume() / ProcessLifecycleOwner ON_START UIAbility.onForeground() --- 进入前台/可见
onHide applicationDidEnterBackground(_:) / sceneDidEnterBackground(_:) Activity.onPause() / ProcessLifecycleOwner ON_STOP UIAbility.onBackground() --- 进入后台/不可见
onExit applicationWillTerminate(_:) Activity.onDestroy() / Application.onTerminate() UIAbility.onDestroy() app.onUnmount() 进程销毁(不一定触发)
onError NSSetUncaughtExceptionHandler Thread.UncaughtExceptionHandler AbilityStage.onError() app.config.errorHandler 全局异常捕获
onPageNotFound --- --- --- router.onError() 路由兜底
onThemeChange traitCollectionDidChange(_:) onConfigurationChanged() Environment.colorScheme 变化 --- 深色模式切换

⚠️ 关键差异提醒:

  • iOS 的 applicationWillTerminate 在多任务杀进程时不一定被调用 ,uni-app 的 onExit 同理
  • Android 的 onDestroy 也不保证触发,不要在此做关键数据持久化
  • 鸿蒙的 UIAbility.onForeground/onBackground 是 Ability 级别,与 uni-app 的 App 级别不完全等价
  • Vue3 本身没有 App 级别的 show/hide 概念,这是 uni-app 针对移动端扩展的

三、页面生命周期详解(pages/*.vue)

页面生命周期是页面级别 的,在每个 .vue 页面文件中监听。它管理的是单个页面从创建到销毁的过程。

3.1 完整页面生命周期流程图

scss 复制代码
navigateTo / redirectTo / reLaunch
              │
              ▼
        ┌──────────┐
        │  onLoad  │ ← 接收页面参数 options
        └────┬─────┘
             │
             ▼
        ┌──────────┐
        │ onShow   │ ← 每次页面显示都触发
        └────┬─────┘
             │
             ▼
      ┌─────────────┐
      │  onReady    │ ← DOM/原生渲染完成(仅首次)
      └──────┬──────┘
             │
        用户操作/导航
             │
     ┌───────┴───────┐
     ▼               ▼
┌──────────┐   ┌────────────┐
│  onHide  │   │ onUnload   │
│(页面隐藏) │   │(页面销毁)   │
└──────────┘   └────────────┘
     │               │
     ▼               ▼
  再次 onShow     生命周期结束

3.2 逐个详解 + 示例代码

onLoad(options)

  • 触发时机: 页面创建时触发,每个页面实例仅一次
  • 参数: options 为页面路由参数(URL query 解析后的对象)
  • 典型用途: 接收参数、初始化页面数据、发起首次请求
javascript 复制代码
// pages/detail/detail.vue
export default {
  onLoad(options) {
    // options.id = "123" (来自 /pages/detail/detail?id=123)
    this.productId = options.id;
    this.loadProductDetail(options.id);
  },
  data() {
    return { productId: '', product: {} }
  },
  methods: {
    async loadProductDetail(id) {
      const res = await getProductById(id);
      this.product = res.data;
    }
  }
}

onShow()

  • 触发时机: 页面每次显示时触发(包括首次加载和从其他页面返回)
  • 典型用途: 刷新列表、更新状态、埋点曝光
javascript 复制代码
export default {
  onShow() {
    // 每次回到此页面都刷新订单列表
    this.refreshOrderList();
    // 埋点
    uni.reportEvent('page_view', { page: 'order_list' });
  }
}

onReady()

  • 触发时机: 页面初次渲染完成时触发(DOM/原生视图就绪)
  • 典型用途: 操作 DOM、初始化地图/视频/canvas 等需要视图就绪的组件
  • 注意: 仅在首次加载时触发,navigateBack 返回不会再次触发
javascript 复制代码
export default {
  onReady() {
    // 地图组件需要在 ready 后才能调用方法
    this.mapContext = uni.createMapContext('myMap', this);
    this.mapContext.moveToLocation();
  }
}

onHide()

  • 触发时机: 页面隐藏时触发(navigateTo 到新页面、切后台、切 Tab)
  • 典型用途: 暂停动画/视频、取消定时器
javascript 复制代码
export default {
  onHide() {
    // 暂停轮播图定时器
    clearInterval(this.swiperTimer);
  }
}

onUnload()

  • 触发时机: 页面卸载时触发(navigateBackredirectToreLaunch
  • 典型用途: 清理资源、移除事件监听、取消网络请求
javascript 复制代码
export default {
  onUnload() {
    // 清除定时器
    clearInterval(this.timer);
    // 移除全局事件监听
    uni.$off('cartUpdated', this.onCartUpdated);
  }
}

其他页面生命周期

函数 触发时机 用途
onPullDownRefresh() 下拉刷新时 pages.json 开启 enablePullDownRefresh
onReachBottom() 滚动到底部时 分页加载更多
onShareAppMessage() 点击右上角分享时 返回分享内容配置
onPageScroll(obj) 页面滚动时 obj.scrollTop慎用,性能敏感
onResize(obj) 窗口尺寸变化时 横竖屏切换、折叠屏展开
onTabItemTap(obj) 点击当前 TabBar 项时 仅当前 Tab 页有效
onBackPress() 返回键/手势/导航栏返回时 可拦截返回行为,返回 true 阻止默认返回

3.3 页面生命周期四端对照表

uni-app iOS Android 鸿蒙 (ArkUI) Vue3 组件生命周期 说明
onLoad viewDidLoad() / init(coder:) onCreate() aboutToAppear() onBeforeMount 页面创建,接收参数
onShow viewWillAppear(_:) onResume() onPageShow() onMounted (首次) 页面可见
onReady viewDidAppear(_:) (首次) onWindowFocusChanged(true) (首次) onDidBuild() onMounted 视图渲染完成
onHide viewWillDisappear(_:) onPause() onPageHide() onBeforeUnmount (部分) 页面不可见
onUnload deinit / viewDidDisappear (最终) onDestroy() aboutToDisappear() onUnmounted 页面销毁
onPullDownRefresh UIRefreshControl SwipeRefreshLayout Refresh 组件 --- 下拉刷新
onReachBottom scrollViewDidScroll + offset判断 RecyclerView.OnScrollListener List.onReachEnd --- 触底加载
onBackPress navigationController?.popViewController 拦截 onBackPressedDispatcher onBackPress() --- 返回拦截
onPageScroll scrollViewDidScroll(_:) OnScrollListener onScroll() --- ⚠️ 高频触发

四、Vue3 生命周期 vs uni-app 生命周期:两套体系如何共存?

这是很多开发者最容易混淆的点。uni-app 中实际上存在两套生命周期系统,它们各司其职:

4.1 双轨制对照

类别 Vue3 标准生命周期 uni-app 扩展生命周期 职责划分
组件级 onBeforeMount / onMounted / onBeforeUpdate / onUpdated / onBeforeUnmount / onUnmounted --- 管理 DOM/虚拟节点 的创建、更新、销毁
页面级 --- onLoad / onShow / onReady / onHide / onUnload / onPullDownRefresh / onReachBottom 管理 页面路由、导航、交互 相关逻辑
应用级 app.onMount / app.onUnmount onLaunch / onShow / onHide / onError 管理 App 进程 的全局状态

4.2 执行顺序(关键!)

当一个页面首次加载时,两套生命周期的执行顺序如下:

scss 复制代码
onLoad (uni-app)          ← 最早,接收参数
    │
    ▼
onBeforeMount (Vue3)     ← 虚拟 DOM 即将挂载
    │
    ▼
onMounted (Vue3)         ← 虚拟 DOM 挂载完成
    │
    ▼
onReady (uni-app)        ← 原生视图/真实 DOM 渲染完成
    │
    ▼
onShow (uni-app)         ← 页面可见

⚠️ 重要区别:

  • onMountedonReadyonMounted 表示 Vue 虚拟 DOM 挂载完成,但原生视图可能还没渲染好;onReady 才表示真实视图就绪
  • 操作 canvas/map/video 等原生组件,必须在 onReady ,而非 onMounted
  • onShowonReady 之后触发,且每次页面显示都会触发

4.3 Composition API 中的写法

如果你使用 Vue3 <script setup> 语法,两套生命周期都可以直接使用:

xml 复制代码
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import { onLoad, onShow, onReady, onUnload } from '@dcloudio/uni-app';

const productId = ref('');
const product = ref({});

// ✅ uni-app 页面生命周期
onLoad((options) => {
  productId.value = options.id;
  console.log('onLoad:', options);
});

onShow(() => {
  console.log('onShow');
});

onReady(() => {
  console.log('onReady - 可以操作原生组件了');
});

onUnload(() => {
  console.log('onUnload - 清理资源');
});

// ✅ Vue3 组件生命周期
onMounted(() => {
  console.log('onMounted - 虚拟DOM已挂载');
});

onUnmounted(() => {
  console.log('onUnmounted - 组件卸载');
});
</script>

📖 注意:<script setup> 中使用 uni-app 页面生命周期,需要从 @dcloudio/uni-app 导入。这些是 uni-app 对 Vue3 的组合式函数扩展。


五、JS 架构深层理解:Options API vs Composition API 的生命周期注册机制

作为资深工程师,理解框架"怎么做到的"比"怎么用"更重要。

5.1 Options API:基于对象 key 名的约定式注册

javascript 复制代码
// 你写的代码
export default {
  data() { return { count: 0 } },
  methods: { add() { this.count++ } },
  onLoad(opts) { /* ... */ },
  onShow() { /* ... */ }
}

框架内部伪代码:

ini 复制代码
function createComponentInstance(options) {
  const instance = {};
  
  for (const key in options) {
    if (key === 'data') {
      // 响应式处理
      instance.data = reactive(options.data());
    } else if (key === 'methods') {
      // 绑定 this
      Object.keys(options.methods).forEach(m => {
        instance[m] = options.methods[m].bind(instance);
      });
    } else if (LIFECYCLE_HOOKS.includes(key)) {
      // 🔑 关键:识别生命周期 key,注册到调度器
      registerLifecycleHook(instance, key, options[key]);
    } else {
      // 其他属性直接挂载
      instance[key] = options[key];
    }
  }
  return instance;
}

const LIFECYCLE_HOOKS = [
  'onLoad', 'onShow', 'onReady', 'onHide', 'onUnload',
  'onPullDownRefresh', 'onReachBottom', 'onShareAppMessage',
  'onPageScroll', 'onResize', 'onTabItemTap', 'onBackPress',
  'mounted', 'beforeMount', 'unmounted', 'beforeUnmount'
];

5.2 Composition API:基于显式函数调用的声明式注册

javascript 复制代码
// 你写的代码
import { onLoad, onShow } from '@dcloudio/uni-app';
import { onMounted } from 'vue';

onLoad((opts) => { /* ... */ });
onShow(() => { /* ... */ });
onMounted(() => { /* ... */ });

框架内部伪代码:

ini 复制代码
// @dcloudio/uni-app 中的实现
let currentInstance = null;

export function onLoad(callback) {
  // 获取当前正在创建的组件实例
  const instance = getCurrentInstance();
  // 将回调注册到实例的生命周期队列中
  instance.lifecycleHooks.onLoad = callback;
}

export function onShow(callback) {
  const instance = getCurrentInstance();
  instance.lifecycleHooks.onShow = callback;
}

5.3 两种方式的本质区别

维度 Options API Composition API
注册方式 对象 key 名约定 显式函数调用
识别机制 框架遍历 key 名匹配 开发者主动调用注册函数
灵活性 固定结构,不易拆分 可按逻辑关注点自由组织
TypeScript 支持 较弱(需额外类型声明) 天然支持(函数有明确类型)
代码复用 Mixin(命名冲突风险) Composables(清晰无冲突)
推荐场景 简单页面、快速原型 复杂业务、团队协作、长期维护

六、实战最佳实践与避坑指南

6.1 数据初始化放哪里?

场景 推荐位置 原因
依赖 URL 参数的数据 onLoad 只有这里能拿到 options
不依赖参数的静态数据 data() / ref() 声明即初始化
每次进入都需要刷新的数据 onShow 返回时也能刷新
需要 DOM 就绪的数据 onReady 如地图中心点、canvas 尺寸
全局共享数据 App.globalData / Pinia 跨页面共享

6.2 常见坑点速查

坑点 现象 解决方案
onLoad 中跳转白屏 App 未完全初始化 改用 onShowsetTimeout 延迟跳转
onMounted 中操作 map 报错 原生视图未就绪 改用 onReady
onShow 中拿不到最新参数 onShowoptions 参数 onLoad 中存到 dataonShow 中读取
onPageScroll 卡顿 高频触发导致通信开销 用 CSS position: sticky 替代,或节流处理
onUnload 不触发 使用了 switchTab 而非 navigateBack Tab 页用 onHide 代替
globalData 修改后页面不更新 非响应式 onShow 中重新赋值,或改用 Pinia
H5 端 onLoad 参数丢失 浏览器刷新导致 H5 端建议用 Vue Router 的 route.query 辅助

6.3 生命周期选择决策树

scss 复制代码
需要在某个时机执行代码?
    │
    ├─ 与 App 进程相关?
    │   ├─ 初始化/SDK ──► onLaunch
    │   ├─ 前后台切换 ──► onShow / onHide
    │   └─ 全局错误 ──► onError
    │
    ├─ 与页面导航相关?
    │   ├─ 接收参数 ──► onLoad
    │   ├─ 每次显示刷新 ──► onShow
    │   ├─ 返回拦截 ──► onBackPress
    │   └─ 页面销毁清理 ──► onUnload
    │
    ├─ 与视图渲染相关?
    │   ├─ 操作原生组件 ──► onReady
    │   ├─ DOM 操作(Vue) ──► onMounted
    │   └─ 组件卸载 ──► onUnmounted
    │
    └─ 与用户交互相关?
        ├─ 下拉刷新 ──► onPullDownRefresh
        ├─ 触底加载 ──► onReachBottom
        ├─ 滚动监听 ──► onPageScroll (⚠️慎用)
        └─ 分享 ──► onShareAppMessage

七、总结:四端生命周期全景对照表

uni-app iOS Android 鸿蒙 Vue3 层级
onLaunch didFinishLaunching Application.onCreate AbilityStage.onCreate --- App
onShow didBecomeActive onResume onForeground --- App
onHide didEnterBackground onPause onBackground --- App
onExit willTerminate onDestroy onDestroy app.onUnmount App
onLoad viewDidLoad onCreate aboutToAppear onBeforeMount Page
onShow (页面) viewWillAppear onResume onPageShow onMounted Page
onReady viewDidAppear onWindowFocusChanged onDidBuild onMounted Page
onHide (页面) viewWillDisappear onPause onPageHide --- Page
onUnload deinit onDestroy aboutToDisappear onUnmounted Page

💡 给多端原生工程师的一句话: uni-app 的生命周期设计是对 iOS/Android/鸿蒙三者最大公约数的抽象。onLoad ≈ viewDidLoad ≈ onCreate ≈ aboutToAppearonShow ≈ viewWillAppear ≈ onResume ≈ onPageShow。掌握了这个映射关系,你就能在四种技术栈之间自如切换。框架在变,生命周期的本质思想从未改变。


📚 参考资料:

相关推荐
钛态2 小时前
AI 辅助:前端框架反模式:过度封装、状态滥用与副作用失控
前端·vue·react·web
by__csdn2 小时前
Vue vs React vs Angular:前端框架终极对决
前端·vue.js·react.js·前端框架·vue·react·angular
钛态2 小时前
前端框架选型决策框架:React、Vue、Svelte、Solid 的 2026 年横评
前端·vue·react·web
2501_926102863 小时前
深入浅出Vue.js前端框架设计:核心原理与实践指南
前端·vue.js·前端框架
程序员黑豆4 小时前
使用AI编程开发鸿蒙应用:从环境搭建到实战示例
前端·harmonyos
程序员黑豆7 小时前
鸿蒙应用开发:一次开发多端部署之响应式布局完全指南
前端·harmonyos
小月土星8 小时前
2.0 React Router 前端路由深度解析:从传统后端路由到现代 SPA 的完整演进
前端
badhope33811 小时前
当所有AI都在抄同一份作业:前端模板的趋同、困局与破局
前端·ai·ai编程·前端开发·tailwind·ui设计
光影少年11 小时前
RN的Fabric 渲染流程
运维·前端·javascript·react native·react.js·fabric