当前位置: 首页 > news >正文

uni-app 常用钩子函数:从场景到实战,掌握开发核心

在 uni-app 跨平台开发中,钩子函数是串联 “业务逻辑” 与 “页面 / 组件生命周期” 的关键纽带。新手开发者常因钩子函数繁多而困惑,其实日常开发中仅需掌握12 个高频钩子函数,就能应对 90% 以上的场景。本文将剔除冷门钩子,聚焦应用、页面、组件三大层级的常用钩子,通过 “作用解析 + 场景示例 + 代码实战” 的形式,帮你快速上手并灵活运用。

一、先搞懂:钩子函数的 “层级逻辑”

在学习具体钩子前,必须明确 uni-app 的钩子函数按 “作用范围” 分为三大层级,不同层级的钩子互不干扰、各司其职:

  • 应用层级:控制整个 App 的全局行为(如启动、前台 / 后台切换),定义在App.vue中,全局仅执行一次。

  • 页面层级:控制单个页面的生命周期(如加载、显示、卸载),定义在页面组件(pages/xxx/xxx.vue)中,每次页面跳转都会触发。

  • 组件层级:控制自定义组件的生命周期(如创建、渲染、销毁),定义在组件(components/xxx.vue)中,基于 Vue 2 生命周期设计,与页面钩子完全独立。

记住这个层级逻辑,能避免后续混淆 “页面钩子” 与 “组件钩子” 的使用场景。

二、应用层级:3 个常用钩子,掌控全局

应用层级的钩子函数虽少,但作用至关重要,直接影响 App 的全局初始化与资源管理。日常开发中高频使用的仅有 3 个:onLaunchonShowonHide

2.1 onLaunch:App 初始化的 “第一入口”

  • 调用时机:App 首次启动时触发(全局仅执行一次,关闭 App 后重新打开才会再次触发)。

  • 核心作用:初始化全局状态(如 Vuex)、检查登录状态、加载全局配置(如接口基础 URL)。

  • 注意事项:此时页面尚未渲染,不能操作 DOM 或调用页面相关 API(如uni.navigateTo需延迟执行)。

场景示例:App 启动检查登录状态
<!-- App.vue -->
<script>
export default {onLaunch(options) {console.log("App初始化完成,启动参数:", options); // 可获取启动时的参数(如小程序跳转参数)// 1. 初始化Vuex全局状态(如加载用户信息)this.$store.dispatch("initGlobalState");// 2. 检查登录状态(从本地存储获取token)const token = uni.getStorageSync("token");if (!token) {// 延迟跳转:避免页面未就绪导致跳转失败setTimeout(() => {uni.redirectTo({ url: "/pages/login/login" }); // 跳转到登录页}, 500);}}
};
</script>

2.2 onShow:App 切换前台的 “唤醒开关”

  • 调用时机:App 从后台切换到前台时触发(如用户按 Home 键返回桌面后,再次点击 App 图标打开),每次切换都会触发。

  • 核心作用:恢复页面状态(如刷新购物车数量)、重启全局定时器 / 监听(如实时消息推送)。

  • 场景对比:与页面的onShow不同,应用的onShow是 “全局级” 的,适合处理整个 App 的前台恢复逻辑。

场景示例:前台切换时刷新全局数据
<!-- App.vue -->
<script>
export default {data() {return {globalTimer: null // 全局定时器};},onShow() {console.log("App切换到前台");// 1. 刷新全局数据(如购物车数量、未读消息数)this.$store.dispatch("refreshCartCount");this.$store.dispatch("refreshUnreadMessage");// 2. 重启全局定时器(如实时获取时间)this.globalTimer = setInterval(() => {this.$store.commit("updateCurrentTime", new Date().toLocaleTimeString());}, 1000);}
};
</script>

2.3 onHide:App 切换后台的 “休眠按钮”

  • 调用时机:App 从前台切换到后台时触发(如用户按 Home 键、切换到其他应用),每次切换都会触发。

  • 核心作用:暂停耗时操作(如视频播放、音频播放)、清除全局定时器 / 监听(避免后台耗电、内存泄漏)。

场景示例:后台切换时释放全局资源
<!-- App.vue -->
<script>
export default {data() {return {globalTimer: null};},onHide() {console.log("App切换到后台");// 1. 清除全局定时器clearInterval(this.globalTimer);// 2. 暂停全局媒体播放(如视频、音频)const videoContext = uni.createVideoContext("global-video");videoContext.pause();// 3. 取消全局事件监听(如WebSocket)this.$store.dispatch("closeWebSocket");}
};
</script>

三、页面层级:6 个常用钩子,掌控页面交互

页面层级的钩子函数是日常开发中使用频率最高的,涵盖页面从 “加载” 到 “卸载” 的全流程。高频使用的有 6 个:onLoadonShowonReadyonHideonUnloadonPullDownRefresh(含onReachBottom)。

3.1 onLoad:页面 “初始化” 的核心钩子

  • 调用时机:页面加载完成时触发(仅执行一次,即使页面隐藏后重新显示,也不会再次触发)。

  • 核心作用:接收页面跳转参数、请求页面初始化数据、初始化页面状态(如分页参数)。

  • 关键能力:通过参数options获取跳转时携带的参数(如/pages/detail/detail?id=123中的id)。

场景示例:页面加载时获取详情数据
<!-- pages/detail/detail.vue(商品详情页) -->
<script>
export default {data() {return {goodsDetail: null // 商品详情数据};},// 接收跳转参数(如?id=123)onLoad(options) {console.log("页面加载,商品ID:", options.id); // { id: "123" }// 请求商品详情数据this.getGoodsDetail(options.id);},methods: {getGoodsDetail(goodsId) {uni.request({url: `https://your-server.com/api/goods/${goodsId}`,success: (res) => {this.goodsDetail = res.data.data;},fail: () => {uni.showToast({ title: "获取详情失败", icon: "none" });}});}}
};
</script>

3.2 onShow:页面 “显示” 的触发开关

  • 调用时机:页面显示时触发(每次页面从隐藏状态切换到显示状态都会触发,如从详情页返回列表页)。

  • 核心作用:刷新页面数据(如列表页返回后刷新列表)、重启页面级定时器 / 监听(如倒计时)。

  • 与 onLoad 的区别onLoad仅执行一次,onShow可多次执行,适合处理 “页面重新显示时需要更新” 的逻辑。

场景示例:列表页返回后刷新数据
<!-- pages/list/list.vue(商品列表页) -->
<script>
export default {data() {return {goodsList: [] // 商品列表数据};},onLoad() {this.getGoodsList(); // 首次加载数据},// 从详情页返回时,重新获取列表数据onShow() {this.getGoodsList();},methods: {getGoodsList() {uni.request({url: "https://your-server.com/api/goods/list",success: (res) => {this.goodsList = res.data.data;}});}}
};
</script>

3.3 onReady:页面 “渲染完成” 的标志

  • 调用时机:页面渲染完成时触发(仅执行一次,此时页面 DOM 已生成,可操作 DOM 元素)。

  • 核心作用:操作页面 DOM(如获取元素高度、初始化第三方组件)、执行需要 DOM 支持的逻辑(如地图渲染)。

  • 注意事项:uni-app 中不支持document/window,需用uni.createSelectorQuery()获取 DOM。

场景示例:获取页面元素高度
<!-- pages/index/index.vue(首页) -->
<script>
export default {onReady() {console.log("页面渲染完成,可操作DOM");// 获取首页轮播图高度uni.createSelectorQuery().in(this) // 绑定当前页面上下文(必须).select(".swiper-container") // 选择器.boundingClientRect((rect) => {if (rect) {console.log("轮播图高度:", rect.height); // 如 300px// 可根据高度动态调整其他元素样式}}).exec(); // 执行查询}
};
</script>

3.4 onHide & onUnload:页面 “隐藏 / 卸载” 的资源清理

这两个钩子常配合使用,核心作用都是 “释放资源”,但触发时机不同:

  • onHide:页面隐藏时触发(如跳转到其他页面,但页面仍保留在页面栈中,未被销毁),需清除 “临时资源”(如定时器,后续可能重启)。

  • onUnload:页面卸载时触发(如关闭页面、跳转到其他页面且当前页面被销毁),需清除 “永久资源”(如接口请求、全局事件监听)。

场景示例:页面隐藏 / 卸载时清理资源
<!-- pages/timer/timer.vue(倒计时页面) -->
<script>
export default {data() {return {countdown: 60,timer: null // 倒计时定时器};},onLoad() {// 开启倒计时定时器this.startCountdown();},// 页面隐藏时:清除定时器(后续返回可重启)onHide() {clearInterval(this.timer);},// 页面卸载时:彻底清除资源(如取消接口请求)onUnload() {clearInterval(this.timer);this.cancelUnfinishedRequest(); // 取消未完成的接口请求},methods: {startCountdown() {this.timer = setInterval(() => {if (this.countdown > 0) {this.countdown--;} else {clearInterval(this.timer);}}, 1000);},cancelUnfinishedRequest() {// 实际项目中可使用axios的CancelToken等机制console.log("取消未完成的接口请求");}}
};
</script>

3.5 onPullDownRefresh & onReachBottom:下拉刷新与上拉加载

这两个钩子是列表页的 “标配”,用于实现下拉刷新数据、上拉加载更多的功能:

  • onPullDownRefresh:用户下拉页面时触发,需在pages.json中配置enablePullDownRefresh: true开启。

  • onReachBottom:用户上拉页面触底时触发,可在pages.json中配置onReachBottomDistance调整触底距离(默认 50px)。

场景示例:列表页下拉刷新与上拉加载
<!-- pages/list/list.vue(商品列表页) -->
<script>
export default {data() {return {goodsList: [],page: 1, // 当前页码pageSize: 10, // 每页条数isLoading: false // 加载状态锁(避免重复请求)};},onLoad() {this.getGoodsList();},// 下拉刷新:重新请求第一页数据onPullDownRefresh() {this.page = 1;this.getGoodsList(() => {uni.stopPullDownRefresh(); // 关闭下拉刷新动画});},// 上拉触底:请求下一页数据onReachBottom() {if (this.isLoading) return; // 若正在加载,跳过this.page++;this.getGoodsList();},methods: {getGoodsList(callback) {this.isLoading = true;uni.request({url: `https://your-server.com/api/goods/list?page=${this.page}&size=${this.pageSize}`,success: (res) => {const newList = res.data.data;if (this.page === 1) {this.goodsList = newList; // 第一页:覆盖数据} else {this.goodsList = [...this.goodsList, ...newList]; // 后续页:追加数据}},fail: () => {uni.showToast({ title: "请求失败", icon: "none" });if (this.page > 1) this.page--; // 请求失败,页码回退},complete: () => {this.isLoading = false;callback && callback(); // 执行回调(如下拉刷新关闭动画)}});}}
};
</script><!-- pages.json 配置 -->
{"pages": [{"path": "pages/list/list","style": {"enablePullDownRefresh": true, // 开启下拉刷新"onReachBottomDistance": 100 // 触底距离调整为100px}}]
}

四、组件层级:3 个常用钩子,掌控组件行为

组件层级的钩子函数基于 Vue 2 生命周期,日常开发中高频使用的有 3 个:createdmountedbeforeDestroy(含watch监听)。

4.1 created:组件 “初始化” 的核心

  • 调用时机:组件实例创建完成时触发(数据已初始化,但 DOM 未渲染)。

  • 核心作用:初始化组件私有数据、请求组件专属数据、绑定组件内部事件。

  • 注意事项:此时组件未挂载到 DOM,不能操作 DOM 元素。

场景示例:组件初始化时请求数据
<!-- components/GoodsCard.vue(商品卡片组件) -->
<script>
export default {props: {goodsId: {type: String,required: true // 父组件必须传递商品ID}},data() {return {goodsInfo: null // 组件私有数据(商品信息)};},// 组件创建完成,请求商品信息created() {this.getGoodsInfo(this.goodsId);},methods: {getGoodsInfo(id) {uni.request({url: `https://your-server.com/api/goods/${id}`,success: (res) => {this.goodsInfo = res.data.data;}});}}
};
</script>

4.2 mounted:组件 “渲染完成” 的标志

  • 调用时机:组件挂载到 DOM 后触发(此时组件 DOM 已生成,可操作 DOM)。

  • 核心作用:操作组件 DOM(如初始化组件内部的第三方插件)、绑定 DOM 事件(如点击、滚动)。

  • 与页面 onReady 的区别mounted是组件级的,仅在组件渲染完成后触发;onReady是页面级的,在所有子组件渲染完成后触发。

场景示例:组件挂载后初始化日历插件
<!-- components/DatePicker.vue(日期选择组件) -->
<script>
// 假设引入了第三方日历插件
import Calendar from "@/utils/calendar.js";export default {mounted() {console.log("组件挂载完成,初始化日历插件");// 获取组件内部DOM元素,初始化日历const calendarEl = this.$el.querySelector(".calendar-container");this.calendar = new Calendar(calendarEl, {minDate: new Date(), // 最小日期为今天onSelect: (date) => {// 日期选择回调,向父组件发送事件this.$emit("date-select", date);}});}
};
</script>

4.3 beforeDestroy:组件 “销毁前” 的资源清理

  • 调用时机:组件销毁前触发(此时组件实例仍可用,可访问数据和方法)。

  • 核心作用:清除组件内部的定时器、取消事件监听、释放第三方插件资源(避免内存泄漏)。

  • 注意事项:组件销毁后无法再操作实例,所有资源清理逻辑需在此钩子中完成。

场景示例:组件销毁前清理资源
<!-- components/DatePicker.vue(日期选择组件) -->
<script>
import Calendar from "@/utils/calendar.js";export default {data() {return {calendar: null,timer: null // 组件内部定时器};},mounted() {// 初始化日历插件const calendarEl = this.$el.querySelector(".calendar-container");this.calendar = new Calendar(calendarEl, { /* 配置项 */ });// 开启组件内部定时器(如实时更新日期)this.timer = setInterval(() => {this.calendar.updateCurrentDate();}, 60000);},// 组件销毁前清理资源beforeDestroy() {console.log("组件即将销毁,清理资源");// 1. 销毁第三方插件实例this.calendar.destroy();// 2. 清除定时器clearInterval(this.timer);// 3. 取消事件监听(如组件内绑定的全局事件)uni.off("global-event", this.handleGlobalEvent);},methods: {handleGlobalEvent() {// 处理全局事件的逻辑}}
};
</script>

4.4 补充:watch 监听(组件数据同步的 “利器”)

虽然watch不是严格意义上的 “生命周期钩子”,但它常与组件钩子配合使用,用于监听propsdata的变化,是组件数据同步的核心工具。

  • 核心作用:当props(父组件传递的数据)或data(组件私有数据)变化时,执行自定义逻辑(如同步更新组件内部状态)。

  • 常用配置

*   `deep: true`:深度监听对象 / 数组内部属性的变化。*   `immediate: true`:初始值赋值时立即触发监听(默认仅变化时触发)。
场景示例:监听 props 变化同步数据
<!-- components/GoodsCard.vue(商品卡片组件) -->
<script>
export default {props: {goodsId: {type: String,required: true}},data() {return {goodsInfo: null};},created() {this.getGoodsInfo(this.goodsId);},// 监听goodsId变化(如父组件切换商品时)watch: {goodsId: {handler(newGoodsId) {// 当goodsId变化时,重新请求对应商品信息this.getGoodsInfo(newGoodsId);},immediate: true // 初始赋值时也触发(确保created中无需重复调用)}},methods: {getGoodsInfo(id) {uni.request({url: `https://your-server.com/api/goods/${id}`,success: (res) => {this.goodsInfo = res.data.data;}});}}
};
</script>

五、关键:钩子函数的 “执行顺序”(避坑必备)

日常开发中,很多问题源于不了解钩子函数的执行顺序(如 “数据未初始化就使用”)。以下是 3 个高频场景的执行顺序,必须牢记:

5.1 场景 1:App 首次启动(应用 + 首页钩子)

  1. 应用层级:onLaunch(App 初始化)→ onShow(App 切换前台)

  2. 页面层级:onLoad(首页加载)→ onShow(首页显示)→ onReady(首页渲染完成)

示例控制台输出:
App初始化完成,启动参数:{...}  // onLaunchApp切换到前台                  // onShow(应用)页面加载,商品ID:123          // onLoad(首页)页面显示                      // onShow(首页)页面渲染完成,可操作DOM        // onReady(首页)

5.2 场景 2:页面跳转(A 页面→B 页面)

  1. A 页面:onHide(A 页面隐藏)

  2. B 页面:onLoad(B 页面加载)→ onShow(B 页面显示)→ onReady(B 页面渲染完成)

  3. 从 B 页面返回 A 页面:

  • B 页面:onUnload(B 页面卸载)

  • A 页面:onShow(A 页面重新显示)

示例控制台输出:
// A→B跳转A页面隐藏                      // onHide(A)B页面加载,参数:{type: "1"}   // onLoad(B)B页面显示                      // onShow(B)B页面渲染完成                  // onReady(B)// B→A返回B页面卸载                      // onUnload(B)A页面显示                      // onShow(A)

5.3 场景 3:页面加载组件(页面 + 组件钩子)

  1. 页面层级:onLoad(页面加载)

  2. 组件层级:created(组件创建)→ mounted(组件挂载)

  3. 页面层级:onShow(页面显示)→ onReady(页面渲染完成)

示例控制台输出:
页面加载,参数:{}             // onLoad(页面)组件创建完成,请求商品信息      // created(组件)组件挂载完成,初始化日历插件    // mounted(组件)页面显示                      // onShow(页面)页面渲染完成,可操作DOM        // onReady(页面)

六、避坑指南:5 个高频问题与解决方案

6.1 问题 1:onLaunch 中跳转页面失败

现象:在onLaunch中调用uni.navigateTo跳转页面,无响应或报错。

原因onLaunch执行时页面栈尚未初始化,无法承载页面跳转。

解决方案:用setTimeout延迟 500ms 跳转,优先使用uni.redirectTo(不依赖页面栈深度):

onLaunch() {if (!uni.getStorageSync("token")) {setTimeout(() => {uni.redirectTo({ url: "/pages/login/login" });}, 500);}
}

6.2 问题 2:onPullDownRefresh 不触发

现象:下拉页面无刷新动画,onPullDownRefresh未执行。

原因:未在pages.json中开启当前页面的下拉刷新配置。

解决方案:在pages.json中添加enablePullDownRefresh: true

{"pages": [{"path": "pages/list/list","style": {"enablePullDownRefresh": true,"backgroundColor": "#f5f5f5" // 刷新区域背景色}}]
}

6.3 问题 3:组件 mounted 中获取 DOM 为 null

现象:在组件mounted中用this.$el.querySelector获取元素,返回null

原因:元素通过v-if控制,mounted执行时v-if条件为false,元素未渲染。

解决方案:用this.$nextTick延迟执行 DOM 操作,确保元素已渲染:

mounted() {this.showElement = true; // 先让v-if条件为truethis.$nextTick(() => {// 延迟到DOM更新后获取元素const el = this.$el.querySelector(".target-element");console.log("元素:", el);});
}

6.4 问题 4:watch 监听对象不触发

现象:监听的对象内部属性变化时,watch未执行。

原因:未配置deep: truewatch默认仅监听对象引用变化,不监听内部属性。

解决方案:添加deep: true配置:

watch: {userInfo: {handler(newVal) {console.log("用户信息变化:", newVal);},deep: true // 深度监听内部属性}
}

6.5 问题 5:页面隐藏后定时器仍运行

现象:页面跳转后,定时器继续执行,导致内存泄漏。

原因:仅在onUnload中清除定时器,而onUnload仅在页面卸载时触发(uni.navigateTo跳转时页面仅隐藏,不卸载)。

解决方案:在onHide中清除定时器,onShow中重新开启:

onLoad() {this.startTimer();
}onShow() {this.startTimer(); // 页面重新显示时重启定时器
}onHide() {clearInterval(this.timer); // 页面隐藏时清除定时器
}onUnload() {clearInterval(this.timer); // 页面卸载时彻底清除
}methods: {startTimer() {this.timer = setInterval(() => {// 定时器逻辑}, 1000);}
}

七、总结:常用钩子函数 “速查表”

为方便快速查阅,整理了本文讲解的 12 个高频钩子函数速查表:

层级钩子函数核心作用关键场景
应用层级onLaunchApp 初始化、检查登录状态、全局配置App 首次启动
onShow刷新全局数据、重启全局定时器App 切换前台
onHide暂停全局操作、清除全局定时器App 切换后台
页面层级onLoad接收参数、请求初始化数据页面首次加载
onShow刷新页面数据、重启页面定时器页面重新显示(如返回页面)
onReady操作 DOM、初始化第三方组件页面渲染完成
onHide清除页面临时资源(如定时器)页面隐藏(如跳转其他页面)
onUnload释放页面永久资源(如接口请求)页面卸载(如关闭页面)
onPullDownRefresh下拉刷新数据用户下拉页面
onReachBottom上拉加载更多数据用户上拉触底
组件层级created初始化组件数据、请求组件专属数据组件实例创建
mounted操作组件 DOM、初始化组件插件组件挂载完成
beforeDestroy清除组件资源(定时器、事件监听)组件销毁前
辅助工具watch监听数据变化、同步组件状态propsdata变化时

八、实战建议:如何高效使用钩子函数?

  1. 按层级划分逻辑:全局逻辑(如登录检查)放应用钩子,页面逻辑(如列表加载)放页面钩子,组件逻辑(如卡片渲染)放组件钩子,避免混乱。

  2. 资源 “成对” 处理:开启定时器 / 监听时,明确在哪个钩子清除(如onShow开启→onHide清除,mounted开启→beforeDestroy清除),避免内存泄漏。

  3. 优先使用高频钩子:非特殊场景下,优先使用本文讲解的 12 个高频钩子,冷门钩子(如onErrorbeforeCreate)尽量少用,降低复杂度。

  4. 结合业务场景选择:如 “页面重新显示需刷新数据” 用onShow,“仅首次加载需请求数据” 用onLoad;“组件数据同步” 用watch+created

掌握这些常用钩子函数及实战技巧,你就能轻松应对 uni-app 开发中的绝大多数场景,写出高效、稳定的跨平台代码。

http://www.dtcms.com/a/357051.html

相关文章:

  • MySQL 深分页:性能优化
  • 每周AI看 | 微软开源VibeVoice-1.5B、OpenAI历史性交棒、网易云商出席AICon全球人工智能开发与应用大会
  • MCP Java Sdk 添加key认证
  • CMake构建学习笔记22-libxml2库的构建
  • 【链表 - LeetCode】146. LRU 缓存
  • Prometheus+Grafana入门教程:从零搭建云原生服务器监控系统
  • 如何管理跨境电商多语种素材?数字资产本地化指南
  • nacos单机部署并开启鉴权
  • #医疗AI时代的生物医学Go编程:高性能计算与精准医疗的案例分析(五)
  • 机器学习 - Kaggle项目实践(5)Quora Question Pairs 文本相似
  • OpenCV轮廓近似与Python命令行参数解析
  • 玳瑁的嵌入式日记D29-0829(进程间通信)
  • ZooKeeper 安装配置
  • idea2025.2中maven编译中文乱码
  • Altium Designer 22使用笔记(10)---PCB铺铜相关操作
  • c++ const 关键字
  • 聊聊Prompt Engineering (提示词工程)
  • 【工具类】得到多个数组中的相同元素
  • 考研数据结构Part3——二叉树知识点总结
  • Vue学习Ⅳ
  • 二手车估值查询-二手车估值api接口
  • el-table实现双击编辑-el-select选择框+输入框限制非负两位小数
  • HunyuanVideo-Foley视频音效生成模型介绍与部署
  • 非标设计 机架模板 misumi 设计组合案例
  • 浏览器自动化工具怎么选?MCP 控制浏览器 vs Selenium 深度对比
  • 预测模型及超参数:3.集成学习:[1]LightGBM
  • LangChain实战(三):深入理解Model I/O - Prompts模板
  • 顶会顶刊图像分类的云服务器训练方法
  • 闭包与内存泄漏:深度解析与应对策略
  • Spring boot 启用第二数据源