← 全部文章
uni-app路由生命周期面试

uni-app 系列(二):路由、pages.json 与三级生命周期

2026年8月2日 · 18 分钟

一、pages.json:页面的"户口本"

在 uni-app 里,任何页面都必须先在 pages.json 注册,否则无法访问。它同时管着路由、导航栏、全局样式、底部 tabBar 和分包。

{
  "pages": [
    {
      "path": "pages/index/index",
      "style": {
        "navigationBarTitleText": "首页",
        "enablePullDownRefresh": true
      }
    },
    {
      "path": "pages/detail/detail",
      "style": { "navigationBarTitleText": "详情" }
    }
  ],
  "globalStyle": {
    "navigationBarBackgroundColor": "#ffffff",
    "navigationBarTextStyle": "black"
  },
  "tabBar": {
    "list": [
      { "pagePath": "pages/index/index", "text": "首页" },
      { "pagePath": "pages/user/user", "text": "我的" }
    ]
  }
}

几个要点:

  • pages 数组的第一项就是应用首页(启动页)。
  • 每个页面的 style单独覆盖 globalStyle
  • tabBar 页面必须同时出现在 pagestabBar.list 里,跳转要用专门的 switchTab

二、五种路由跳转 API(重点,别用错)

uni-app 的跳转不是 vue-router,而是一组 uni. API,每个都有明确语义:

API作用页面栈行为能否跳 tabBar
uni.navigateTo打开新页面入栈(保留当前页)
uni.redirectTo关闭当前页再打开替换栈顶
uni.reLaunch关闭所有页面再打开清空栈
uni.switchTab切换到 tabBar 页清非 tab 页✅(只能跳 tab)
uni.navigateBack返回上一页/多页出栈
// 打开详情页并传参
uni.navigateTo({ url: "/pages/detail/detail?id=10&from=list" });

// 返回上一页
uni.navigateBack();

// 返回两层
uni.navigateBack({ delta: 2 });

注意页面栈上限:小程序页面栈最多 10 层,一味 navigateTo 会栈溢出打不开。列表→详情→详情→…这种同类跳转,常用 redirectTo 或在业务上做去重。

三、页面间传参的三种方式

1. URL query(最常用,适合简单参数)

uni.navigateTo({ url: "/pages/detail/detail?id=10" });

接收方在 onLoad 拿到:

onLoad((query) => {
  console.log(query.id); // "10"(注意是字符串)
});

URL 传复杂对象要 encodeURIComponent(JSON.stringify(obj)),且有长度限制,不推荐传大对象。

2. eventChannel(页面间双向通信,适合回调)

// A 页跳转并监听 B 页传回的数据
uni.navigateTo({
  url: "/pages/b/b",
  events: {
    acceptData(data) { console.log("B 传回:", data); },
  },
  success(res) {
    res.eventChannel.emit("toB", { msg: "A 传来的" });
  },
});

3. 全局状态(跨多页共享,用 Pinia,第四篇细讲)

大对象、跨多页共享的数据,别塞 URL,放全局 store 最干净。

四、三级生命周期(面试核心)

uni-app 有三层生命周期,很多人只知道组件的,这是面试区分度所在:

1. 应用生命周期(App.vue,全局唯一)

// App.vue
export default {
  onLaunch() {},   // 应用初始化(全局只触发一次)
  onShow() {},     // 应用启动,或从后台进入前台
  onHide() {},     // 应用从前台进入后台
};

2. 页面生命周期(页面 .vue,uni-app 特有)

onLoad(query) {}          // 页面加载,能拿到路由参数(只一次)
onShow() {}               // 页面显示(每次进入都触发)
onReady() {}              // 页面初次渲染完成
onHide() {}               // 页面隐藏
onUnload() {}             // 页面卸载
onPullDownRefresh() {}    // 下拉刷新
onReachBottom() {}        // 触底加载更多
onPageScroll(e) {}        // 页面滚动
onShareAppMessage() {}    // 用户点击分享

onPullDownRefreshonReachBottom 是做列表页的两大神器:一个下拉刷新、一个上拉加载,配合 pages.jsonenablePullDownRefresh 使用。

3. 组件生命周期(Vue 标准)

组件里用的是 Vue 的生命周期onMountedonUnmounted 等(Vue3 组合式),或 created/mounted(选项式)。

三者关系与触发顺序

App 启动首页时,大致顺序:

App onLaunch → App onShow → 页面 onLoad → 页面 onShow
→ 组件 created/mounted → 页面 onReady

关键区分:onLoad 只在页面创建时触发一次且能拿参数;onShow 每次页面可见都触发(比如从详情页返回列表页,列表页会再次 onShow 但不会 onLoad)。这点是列表刷新逻辑的关键。

Vue3 组合式写法

<script setup> 里,页面生命周期要从 @dcloudio/uni-app 导入:

import { onLoad, onShow, onPullDownRefresh } from "@dcloudio/uni-app";

onLoad((query) => { /* ... */ });
onPullDownRefresh(() => { /* ...刷新完 */ uni.stopPullDownRefresh(); });

五、本阶段必须掌握的知识点

  • 页面必须在 pages.json 注册;数组第一项是首页。
  • 五种跳转 API 的语义与区别,尤其 tabBar 页只能用 switchTab、页面栈上限 10 层。
  • 三种传参方式:URL query / eventChannel / 全局状态,及各自适用场景。
  • 三级生命周期:应用(onLaunch/onShow/onHide)、页面(onLoad/onShow/onReady/…)、组件(Vue 生命周期)。
  • onLoad vs onShow 的区别(一次性拿参 vs 每次可见触发)。
  • 组合式 API 下页面生命周期需从 @dcloudio/uni-app 导入。

六、高频面试题

Q1:navigateToredirectToreLaunchswitchTab 有什么区别?

A:navigateTo 打开新页面并保留当前页入栈(可返回);redirectTo 关闭当前页再打开新页(不可返回到原页);reLaunch 关闭所有页面打开新页(清空页面栈);switchTab 用于切换 tabBar 页面,会关闭所有非 tab 页面。此外 navigateTo/redirectTo 不能跳转 tabBar 页面,跳 tab 必须用 switchTabreLaunch

追问:为什么 navigateTo 用多了会出问题?

A:因为小程序页面栈最多 10 层navigateTo 每次都入栈,层数超限后新页面打不开。常见场景是"详情页里再跳详情页"无限嵌套。解决:同类页面用 redirectTo 替换栈顶,或用 reLaunch 重置,或业务上限制跳转深度。


Q2:onLoadonShow 的区别?举个实际场景。

A:onLoad 在页面首次加载时触发一次,且能接收路由参数;onShow 在页面每次显示时都触发(包括首次进入、以及从其他页面返回该页)。场景:列表页 onLoad 拉取首屏数据;进入详情页做了收藏操作后返回列表页,此时列表页触发 onShow(而非 onLoad),可在 onShow 里刷新收藏状态,做到数据同步。

追问:那 onReady 又是什么时机?和 Vue 的 onMounted 区别?

A:onReady页面初次渲染完成的时机,此后才能安全地用 uni.createSelectorQuery 获取节点信息。onMounted组件挂载完成。触发顺序上,页面 onReady 通常在其内部组件 onMounted 之后。要操作 DOM/节点尺寸,页面级放 onReady、组件级放 onMounted


Q3:uni-app 的生命周期分哪几层?为什么要分?

A:分应用、页面、组件三层。应用生命周期(onLaunch/onShow/onHide)管整个 App 的启动与前后台切换,全局唯一;页面生命周期(onLoad/onShow/onReady/onPullDownRefresh/onReachBottom 等)管单个页面的加载、显示、交互;组件生命周期沿用 Vue(mounted 等)。分层是因为小程序/App 是"页面栈"模型,页面有独立于组件的加载/显示/卸载时机(如触底、下拉、分享),这些无法用组件生命周期表达,必须有页面级钩子。

追问:onShow 在组合式 API 里怎么写?

A:从框架包导入即可:import { onShow } from "@dcloudio/uni-app",然后在 setup 里调用 onShow(() => {...})。注意页面级生命周期都来自 @dcloudio/uni-app,而组件级(onMounted 等)来自 vue


Q4:页面间怎么传参?传大对象怎么办?

A:简单参数用 URL query?id=1,接收方 onLoad 里取,注意值是字符串);需要页面回传/双向通信用 eventChannel;跨多页共享或大对象用全局状态(Pinia)不要把大对象塞进 URL——有长度限制且需手动 encodeURIComponent+JSON,既丑又易出错,正确做法是存 store 后在目标页读取。

相关水晶