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 页面必须同时出现在
pages和tabBar.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() {} // 用户点击分享
onPullDownRefresh、onReachBottom是做列表页的两大神器:一个下拉刷新、一个上拉加载,配合pages.json的enablePullDownRefresh使用。
3. 组件生命周期(Vue 标准)
组件里用的是 Vue 的生命周期:onMounted、onUnmounted 等(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 生命周期)。
-
onLoadvsonShow的区别(一次性拿参 vs 每次可见触发)。 - 组合式 API 下页面生命周期需从
@dcloudio/uni-app导入。
六、高频面试题
Q1:navigateTo、redirectTo、reLaunch、switchTab 有什么区别?
A:navigateTo 打开新页面并保留当前页入栈(可返回);redirectTo 关闭当前页再打开新页(不可返回到原页);reLaunch 关闭所有页面打开新页(清空页面栈);switchTab 用于切换 tabBar 页面,会关闭所有非 tab 页面。此外 navigateTo/redirectTo 不能跳转 tabBar 页面,跳 tab 必须用 switchTab 或 reLaunch。
追问:为什么 navigateTo 用多了会出问题?
A:因为小程序页面栈最多 10 层,navigateTo 每次都入栈,层数超限后新页面打不开。常见场景是"详情页里再跳详情页"无限嵌套。解决:同类页面用 redirectTo 替换栈顶,或用 reLaunch 重置,或业务上限制跳转深度。
Q2:onLoad 和 onShow 的区别?举个实际场景。
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 后在目标页读取。