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

发布于:2025-08-30 ⋅ 阅读:(15) ⋅ 点赞:(0)

在 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初始化完成,启动参数:{...}  // onLaunch

App切换到前台                  // 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条件为true
  this.$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 个高频钩子函数速查表:

层级 钩子函数 核心作用 关键场景
应用层级 onLaunch App 初始化、检查登录状态、全局配置 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 开发中的绝大多数场景,写出高效、稳定的跨平台代码。