参考官方文档:
一、核心概念:uni-app 的路由到底是什么?
1.1 一句话定义
uni-app 的路由(Route) = 框架统一管理的页面注册表 + 页面栈 + 跳转规则。
它不是 Vue Router。uni-app 没有使用 vue-router,而是自己实现了一套类小程序 的路由系统。原因是:小程序平台(微信、支付宝等)本身就有自己的页面栈管理机制,uni-app 必须兼容所有平台,所以用 pages.json 做统一注册,用框架内置的页面栈做统一管理。
1.2 与 Vue Router 的本质区别
| 对比项 |
Vue Router(Web SPA) |
uni-app 路由 |
| 路由配置文件 |
router/index.js 中用代码配置 |
pages.json 中用 JSON 配置 |
| 页面切换方式 |
同一个 HTML 文件内切换组件(SPA) |
真正切换页面(每个页面是独立的 WebView / 原生页面) |
| 页面栈管理 |
浏览器 History 或 Hash |
框架自维护的页面栈(最多 10 层) |
| 路由守卫 |
beforeEach / beforeEnter / beforeResolve |
无内置守卫,需用 uni.addInterceptor 自行拦截 |
| 动态路由 |
支持 :id、* 通配符 |
不支持动态路由,所有页面必须静态注册 |
| 嵌套路由 |
支持 <router-view> 嵌套 |
不支持页面嵌套页面 |
二、路由注册:pages.json(一切的起点)
2.1 官方文档原文核心
uni-app 页面路由为框架统一管理,开发者需要在 pages.json 里配置每个路由页面的路径和页面样式。类似小程序在 app.json 中配置页面路由。
2.2 完整配置示例
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页",
"navigationBarBackgroundColor": "#007AFF",
"enablePullDownRefresh": true
}
},
{
"path": "pages/detail/detail",
"style": {
"navigationBarTitleText": "详情"
}
},
{
"path": "pages/mine/mine",
"style": {
"navigationBarTitleText": "我的"
}
}
],
"tabBar": {
"list": [
{ "pagePath": "pages/index/index", "text": "首页", "iconPath": "static/tab-home.png", "selectedIconPath": "static/tab-home-active.png" },
{ "pagePath": "pages/mine/mine", "text": "我的", "iconPath": "static/tab-mine.png", "selectedIconPath": "static/tab-mine-active.png" }
]
},
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-app",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
}
}
2.3 关键规则
| 规则 |
说明 |
| pages 数组第一项 = 应用首页 |
框架启动时自动加载第一个页面 |
| path 不带文件后缀 |
写 pages/index/index,不写 .vue |
| 所有可跳转页面必须注册 |
未注册的页面跳转无效,控制台报错 |
| tabBar 页面必须同时出现在 pages 数组中 |
否则 tabBar 不生效 |
| tabBar 最少 2 项,最多 5 项 |
平台限制 |
2.4 对标三端原生
| uni-app |
iOS |
Android |
鸿蒙 ArkTS |
pages.json 注册页面 |
Storyboard 中拖入 ViewController 并设 Storyboard ID;或代码中 UIViewController 子类 |
AndroidManifest.xml 中注册 <activity android:name=".DetailActivity"> |
src/main/resources/base/profile/main_pages.json 的 src 数组中注册页面路径 |
pages[0] = 首页 |
window.rootViewController = 根控制器 |
LAUNCHER intent-filter 的 Activity |
main_pages.json 中第一个路径 |
tabBar.list |
UITabBarController + 各 tab 的 UIViewController |
BottomNavigationView / TabLayout + Fragment |
Tabs 组件 + TabContent |
三、页面栈模型(最核心的概念)
3.1 官方文档原文
框架以栈的形式管理当前所有页面,当发生路由切换的时候,页面栈的表现如下:
3.2 页面栈变化表(官方)
| 路由方式 |
页面栈表现 |
触发时机 |
| 初始化 |
新页面入栈 |
uni-app 打开的第一个页面 |
| 打开新页面 |
新页面入栈 |
调用 uni.navigateTo 或使用 <navigator open-type="navigate"> |
| 页面重定向 |
当前页面出栈,新页面入栈 |
调用 uni.redirectTo 或使用 <navigator open-type="redirect"> |
| 页面返回 |
页面不断出栈,直到目标返回页 |
调用 uni.navigateBack 或使用 <navigator open-type="navigateBack"> |
| Tab 切换 |
当前页面出栈,新 Tab 页面入栈(非 tabBar 页面全部出栈) |
调用 uni.switchTab 或使用 <navigator open-type="switchTab"> |
| 重启动 |
所有页面出栈,新页面入栈 |
调用 uni.reLaunch 或使用 <navigator open-type="reLaunch"> |
3.3 图示理解
【初始状态】打开 App,加载首页
┌──────────┐
│ 首页(A) │ ← 栈底 = 栈顶(只有1个页面)
└──────────┘
【navigateTo → 详情页(B)】
┌──────────┐
│ 详情页(B)│ ← 栈顶(当前显示)
│ 首页(A) │ ← 栈底
└──────────┘
【再 navigateTo → 评论页(C)】
┌──────────┐
│ 评论页(C)│ ← 栈顶
│ 详情页(B)│
│ 首页(A) │ ← 栈底
└──────────┘
【navigateBack delta=1】→ 回到 B
┌──────────┐
│ 详情页(B)│ ← 栈顶
│ 首页(A) │ ← 栈底
└──────────┘
【redirectTo → 结果页(D)】→ B 出栈,D 入栈
┌──────────┐
│ 结果页(D)│ ← 栈顶(当前显示)
│ 首页(A) │ ← 栈底(B 被销毁了!)
└──────────┘
【reLaunch → 登录页(E)】→ 全部清空
┌──────────┐
│ 登录页(E)│ ← 唯一的页面
└──────────┘
【switchTab → 我的页】→ 关闭所有非 Tab 页
┌──────────┐
│ 我的页 │ ← 栈顶(tabBar 页面)
└──────────┘
3.4 页面栈上限
页面栈最多 10 层。 超过后 uni.navigateTo 将失效(静默失败,不跳转也不报错)。
3.5 对标三端原生
| uni-app 页面栈 |
iOS |
Android |
鸿蒙 |
| 栈最多 10 层 |
UINavigationController.viewControllers 数组,理论无上限(受内存限制) |
Task Stack(任务栈),默认无硬性上限(但系统可能回收) |
router 路由栈,官方建议不超过 32 层 |
getCurrentPages() 获取栈 |
navigationController.viewControllers |
ActivityManager.getRunningTasks() 或自行维护栈 |
router.getState() 获取当前路由信息 |
| 栈底 = 首页 |
viewControllers[0] = rootViewController |
任务栈底部 = Launcher Activity |
路由栈第一个元素 |
四、两种跳转方式:声明式 vs 编程式
4.1 官方文档原文
uni-app 有两种页面路由跳转方式:
- 使用 navigator 组件(声明式)
- 调用 API(编程式)
4.2 方式一:<navigator> 组件(声明式)
类似 HTML 的 <a> 标签,但只能跳转本地页面。
<!-- 最基础用法 -->
<navigator url="/pages/detail/detail?id=123">去详情</navigator>
<!-- 指定跳转类型 -->
<navigator url="/pages/mine/mine" open-type="switchTab">去我的</navigator>
<!-- 返回上一页 -->
<navigator open-type="navigateBack" delta="1">返回</navigator>
<navigator> 完整属性表(官方)
| 属性名 |
类型 |
默认值 |
说明 |
url |
String |
--- |
应用内的跳转链接,相对路径或绝对路径,不加 .vue 后缀 |
open-type |
String |
navigate |
跳转类型(见下表) |
delta |
Number |
1 |
当 open-type="navigateBack" 时,返回的层数 |
animation-type |
String |
平台默认 |
跳转动画类型(仅 App 端有效) |
animation-duration |
Number |
平台默认 |
动画时长(ms) |
hover-class |
String |
navigator-hover |
点击态样式类 |
hover-start-time |
Number |
50 |
按住后多久出现点击态(ms) |
hover-stay-time |
Number |
600 |
手指松开后点击态保留时间(ms) |
open-type 有效值
| open-type 值 |
等价的 API |
说明 |
navigate |
uni.navigateTo() |
保留当前页,跳转新页面(默认值) |
redirect |
uni.redirectTo() |
关闭当前页,跳转新页面 |
switchTab |
uni.switchTab() |
跳转到 tabBar 页面,关闭所有非 tabBar 页面 |
reLaunch |
uni.reLaunch() |
关闭所有页面,打开新页面 |
navigateBack |
uni.navigateBack() |
关闭当前页面,返回上一级或多级页面 |
exit |
--- |
退出小程序(仅微信小程序 2.1.0+) |
对标三端原生
uni-app <navigator> |
iOS |
Android |
鸿蒙 |
| 声明式跳转组件 |
Storyboard 中的 Segue(拖线连接两个 VC) |
XML 中的 <intent-filter> + startActivity;或 Jetpack Navigation 的 <action> |
Navigation 组件的 navDestination + router.pushUrl |
open-type 切换行为 |
Segue 类型:push / present / replace |
Intent 的 Flag:FLAG_ACTIVITY_NEW_TASK / CLEAR_TASK 等 |
router.pushUrl / replaceUrl / back |
4.3 方式二:API 编程式跳转(重点)
五、5 个路由 API 逐一详解
5.1 uni.navigateTo(OBJECT) --- 压栈跳转(最常用)
官方定义
保留当前页面,跳转到应用内的某个页面。使用 uni.navigateBack 可以返回到原页面。
参数表
| 参数 |
类型 |
必填 |
说明 |
url |
String |
是 |
需要跳转的应用内非 tabBar 页面路径,路径后可带参数,参数与路径用 ? 分隔,多个参数用 & 连接 |
events |
Object |
否 |
页面间通信通道(2.8.9+ 支持) |
animationType |
String |
否 |
窗口动画类型(仅 App 端) |
animationDuration |
Number |
否 |
窗口动画时长(ms,仅 App 端) |
success |
Function |
否 |
接口调用成功的回调 |
fail |
Function |
否 |
接口调用失败的回调 |
complete |
Function |
否 |
接口调用结束的回调(成功或失败都执行) |
代码示例
// 基础跳转
uni.navigateTo({
url: '/pages/detail/detail?id=123&name=test'
})
// 带 EventChannel 的跳转(页面间通信)
uni.navigateTo({
url: '/pages/detail/detail',
events: {
// 为被打开页面注册的监听事件
'onDataChanged': function(data) {
console.log('子页面传回的数据:', data)
}
},
success: function(res) {
// 通过 eventChannel 向被打开页面传送数据
res.eventChannel.emit('initData', { id: 123, title: 'Hello' })
}
})
⚠️ 限制
- 不能跳转到 tabBar 页面(会静默失败)
- 页面栈最多 10 层,超出后此 API 无效
对标三端
|
iOS |
Android |
鸿蒙 |
| 代码 |
self.navigationController?.pushViewController(detailVC, animated: true) |
startActivity(Intent(this, DetailActivity::class.java)) |
router.pushUrl({ url: 'pages/detail' }) |
| 传参 |
detailVC.itemId = 123(直接属性赋值) |
intent.putExtra("id", 123) |
router.pushUrl({ url: '...', params: { id: 123 } }) |
| 动画 |
push 默认从右向左滑入 |
默认从右向左(可自定义 overridePendingTransition) |
默认转场动画 |
| 栈行为 |
新 VC 压入 viewControllers 数组末尾 |
新 Activity 压入 Task Stack 顶部 |
新页面压入路由栈顶部 |
5.2 uni.redirectTo(OBJECT) --- 替换跳转
官方定义
关闭当前页面,跳转到应用内的某个页面。
参数表
| 参数 |
类型 |
必填 |
说明 |
url |
String |
是 |
需要跳转的应用内非 tabBar 页面路径 |
animationType |
String |
否 |
窗口动画类型(仅 App 端) |
animationDuration |
Number |
否 |
窗口动画时长(仅 App 端) |
success/fail/complete |
Function |
否 |
回调 |
代码示例
// 登录成功后跳转到首页,关闭登录页(用户不能返回登录页)
uni.redirectTo({
url: '/pages/home/home'
})
⚠️ 限制
- 不能跳转到 tabBar 页面
- 当前页面会被销毁 (触发
onUnload),无法返回
与 navigateTo 的核心区别
|
navigateTo |
redirectTo |
| 当前页 |
保留在栈中 |
销毁(出栈) |
| 能否返回 |
能(navigateBack) |
不能 |
| 页面栈变化 |
+1 |
±0(一出一进) |
| 典型场景 |
列表→详情 |
中间过渡页→结果页 |
对标三端
|
iOS |
Android |
鸿蒙 |
| 代码 |
navigationController.setViewControllers([newVC], animated: true) 或先 pop 再 push |
startActivity(intent); finish(); |
router.replaceUrl({ url: 'pages/home' }) |
| 本质 |
替换栈顶 VC |
新 Activity 入栈 + 旧 Activity 销毁 |
替换路由栈顶元素 |
| 生命周期 |
旧 VC 触发 viewDidDisappear + dealloc |
旧 Activity 触发 onPause → onStop → onDestroy |
旧页面触发 onPageHide → aboutToDisappear |
5.3 uni.reLaunch(OBJECT) --- 清栈重启
官方定义
关闭所有页面,打开到应用内的某个页面。
代码示例
// 退出登录,回到登录页,清空所有页面栈
uni.reLaunch({
url: '/pages/login/login'
})
⚠️ 特点
- 所有页面全部销毁(包括 tabBar 页面)
- 页面栈清零,只剩新打开的这一个页面
- 可以跳转到 tabBar 页面,也可以跳转到非 tabBar 页面
对标三端
| | iOS | Android | 鸿蒙 |
|------|-----------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------|----------------------------------------------------------|-------------------------------------------------------------|
| 代码 | window.rootViewController = UINavigationController(rootViewController: loginVC) | `Intent intent = new Intent(this, LoginActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent);` | router.clear(); router.replaceUrl({ url: 'pages/login' }) |
| 本质 | 直接换掉根控制器,旧栈全部释放 | 清空整个任务栈,创建新任务 | 清空路由栈,替换为新页面 |
| 内存影响 | 所有旧 VC 被释放 | 所有旧 Activity 被 destroy | 所有旧页面组件被销毁 |
5.4 uni.switchTab(OBJECT) --- Tab 页切换
官方定义
跳转到 tabBar 页面,并关闭所有其他非 tabBar 页面。
代码示例
uni.switchTab({
url: '/pages/mine/mine'
})
⚠️ 关键限制
| 限制 |
说明 |
| 只能跳 tabBar 页面 |
url 必须是 tabBar.list 中声明的页面 |
| 不能传参数 |
url 后面拼参数无效! |
| 关闭所有非 tabBar 页面 |
页面栈中非 Tab 页面全部销毁 |
如果需要给 Tab 页传数据怎么办?
// 方案1:全局状态管理(Pinia / Vuex)
import { useStore } from '@/store'
const store = useStore()
store.selectedId = 123
uni.switchTab({ url: '/pages/mine/mine' })
// 方案2:uni.$emit 事件总线
uni.$emit('updateMineData', { id: 123 })
uni.switchTab({ url: '/pages/mine/mine' })
// 方案3:uni.setStorageSync 本地缓存
uni.setStorageSync('pendingData', { id: 123 })
uni.switchTab({ url: '/pages/mine/mine' })
对标三端
|
iOS |
Android |
鸿蒙 |
| 代码 |
tabBarController.selectedIndex = 2 或 tabBarController.selectedViewController = mineVC |
bottomNavigationView.selectedItemId = R.id.mine 或切换 Fragment |
修改 Tabs 组件的 index 状态变量 |
| 本质 |
UITabBarController 切换当前显示的子控制器 |
切换 BottomNavigationView 选中项 + FragmentTransaction show/hide |
切换 TabContent 显示 |
| 其他 Tab 页 |
不销毁,只是被隐藏(viewDidDisappear) |
不销毁,Fragment 被 hide 或 detach |
不销毁,只是不显示 |
5.5 uni.navigateBack(OBJECT) --- 返回
官方定义
关闭当前页面,返回上一页面或多级页面。可通过 getCurrentPages() 获取当前的页面栈,决定需要返回几层。
参数表
| 参数 |
类型 |
默认值 |
说明 |
delta |
Number |
1 |
返回的页面数,如果 delta 大于现有页面数,则返回到首页 |
代码示例
// 返回上一页
uni.navigateBack({ delta: 1 })
// 返回上3页
uni.navigateBack({ delta: 3 })
// 动态计算:返回到首页
const pages = getCurrentPages()
uni.navigateBack({ delta: pages.length - 1 })
对标三端
| | iOS | Android | 鸿蒙 |
|------|----------------------------------------------------------------------|------------------------------------------------------|----------------------------|-------------------------|
| 返回1层 | navigationController.popViewController(animated: true) | finish() 或用户按返回键 | router.back() |
| 返回多层 | navigationController.popToViewController(targetVC, animated: true) | 连续 finish() 多次;或用 Intent.FLAG_ACTIVITY_CLEAR_TOP | router.back() 多次调用 |
| 返回首页 | navigationController.popToRootViewController(animated: true) | Intent + `FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP` | 循环 router.back() 直到栈底 |
六、5 个 API 终极对比表
| API |
当前页处理 |
页面栈变化 |
能跳 tabBar? |
能传参数? |
典型场景 |
navigateTo |
保留 |
叠加(+1) |
❌ 不能 |
✅ URL 参数 / EventChannel |
列表→详情 |
redirectTo |
关闭 |
替换(±0) |
❌ 不能 |
✅ URL 参数 |
中间页→结果页 |
reLaunch |
全部关闭 |
清零→新建(=1) |
✅ 能 |
✅ URL 参数 |
退出登录→登录页 |
switchTab |
关闭非Tab页 |
仅保留Tab页 |
✅ 只能跳Tab |
❌ 不能传参 |
切换底部Tab |
navigateBack |
关闭当前页 |
减少(-delta) |
--- |
--- |
返回上一页 |
七、页面传参详解
7.1 URL 参数(最基础)
// 发送方
uni.navigateTo({
url: '/pages/detail/detail?id=123&title=' + encodeURIComponent('你好世界')
})
// 接收方(目标页面的 onLoad 中)
onLoad(options) {
console.log(options.id) // '123'
console.log(decodeURIComponent(options.title)) // '你好世界'
}
对标三端
|
iOS |
Android |
鸿蒙 |
| 传参方式 |
直接属性赋值:detailVC.itemId = 123 |
intent.putExtra("id", 123) |
router.pushUrl({ params: { id: 123 } }) |
| 取参方式 |
在目标 VC 中直接读属性 |
getIntent().getIntExtra("id", 0) |
router.getParams()['id'] |
7.2 EventChannel 页面通信(2.8.9+,推荐)
这是 uni-app 独有的机制,不需要全局状态,直接在两个页面之间建立通信管道。
// ===== 页面A(发送方)=====
uni.navigateTo({
url: '/pages/pageB/pageB',
events: {
// 监听页面B发来的事件
'onResult': function(data) {
console.log('B 传回的数据:', data)
}
},
success: function(res) {
// 向页面B发送数据
res.eventChannel.emit('initData', { id: 123, list: [1,2,3] })
}
})
// ===== 页面B(接收方)=====
onLoad() {
const eventChannel = this.getOpenerEventChannel()
// 监听页面A发来的数据
eventChannel.on('initData', function(data) {
console.log('A 发来的数据:', data)
})
// 向页面A发送数据
eventChannel.emit('onResult', { status: 'success' })
}
对标三端
|
iOS |
Android |
鸿蒙 |
| 类似机制 |
闭包回调 / delegate 协议 / NotificationCenter |
startActivityForResult + onActivityResult(旧)/ ActivityResultLauncher(新) |
router 的 back 配合 params 回传;或 EventHub |
| 本质 |
双向通信管道 |
请求-响应模式 |
事件订阅 |
7.3 全局事件总线 uni.$emit / uni.$on
// 任意页面发送
uni.$emit('dataUpdated', { id: 456 })
// 任意页面监听(通常在 onLoad 中注册,onUnload 中注销)
onLoad() {
uni.$on('dataUpdated', this.onDataUpdated)
},
onUnload() {
uni.$off('dataUpdated', this.onDataUpdated)
},
methods: {
onDataUpdated(data) {
console.log('收到更新:', data)
}
}
八、页面生命周期与路由跳转的关系
8.1 页面生命周期一览(官方)
| 生命周期 |
触发时机 |
触发次数 |
onLoad(options) |
页面加载时,可获取 URL 参数 |
一次 |
onShow() |
页面显示/切入前台时 |
多次(每次显示都触发) |
onReady() |
页面初次渲染完成 |
一次 |
onHide() |
页面隐藏/切入后台时 |
多次 |
onUnload() |
页面卸载(销毁)时 |
一次 |
8.2 不同跳转方式触发的生命周期
| 跳转方式 |
旧页面触发 |
新页面触发 |
navigateTo |
onHide |
onLoad → onShow → onReady |
navigateBack |
onUnload(被销毁的那个) |
前一个页面触发 onShow |
redirectTo |
onUnload |
onLoad → onShow → onReady |
reLaunch |
所有页面 onUnload |
onLoad → onShow → onReady |
switchTab |
非 Tab 页面 onUnload;当前 Tab 页 onHide |
目标 Tab 页 onShow(首次还有 onLoad → onReady) |
8.3 对标三端原生生命周期
| uni-app |
iOS UIViewController |
Android Activity |
鸿蒙 ArkTS 页面 |
onLoad |
viewDidLoad |
onCreate |
aboutToAppear |
onShow |
viewWillAppear / viewDidAppear |
onResume |
onPageShow |
onReady |
viewDidLayoutSubviews(首次布局完成) |
onWindowFocusChanged(true) |
页面首次渲染完成回调 |
onHide |
viewWillDisappear / viewDidDisappear |
onPause / onStop |
onPageHide |
onUnload |
dealloc(ARC 释放时) |
onDestroy |
aboutToDisappear |
九、getCurrentPages() --- 获取当前页面栈
官方定义
getCurrentPages() 函数用于获取当前页面栈的实例,以数组形式按栈的顺序给出,第一个元素为首页,最后一个元素为当前页面。
const pages = getCurrentPages()
const currentPage = pages[pages.length - 1] // 当前页面
const prevPage = pages[pages.length - 2] // 上一个页面
console.log(currentPage.route) // 当前页面路由路径,如 'pages/detail/detail'
每个页面实例的属性和方法
| 属性/方法 |
说明 |
平台 |
page.route |
当前页面的路由路径 |
全平台 |
page.$vm |
当前页面的 Vue 实例 |
全平台 |
page.$getAppWebview() |
获取当前页面的 webview 对象实例 |
仅 App |
⚠️ 注意
请勿修改页面栈,以免造成页面状态错误。
十、App 端特有的窗口动画(animationType)
uni.navigateTo 和 uni.redirectTo 在 App 端支持自定义窗口动画:
| animationType 值 |
说明 |
slide-in-right |
从右侧滑入(默认) |
slide-in-left |
从左侧滑入 |
slide-in-top |
从上侧滑入 |
slide-in-bottom |
从下侧滑入 |
fade-in |
淡入 |
zoom-out |
缩放淡入 |
pop-in |
从底部弹出 |
none |
无动画 |
uni.navigateTo({
url: '/pages/detail/detail',
animationType: 'slide-in-bottom',
animationDuration: 300
})
对标三端
|
iOS |
Android |
鸿蒙 |
| 自定义转场动画 |
UIViewControllerTransitioningDelegate + 自定义 UIViewControllerAnimatedTransitioning |
overridePendingTransition(enterAnim, exitAnim) 或 ActivityOptions.makeCustomAnimation() |
pageTransition 中自定义 PageTransitionEnter / PageTransitionExit |
十一、路由守卫(uni-app 没有内置,需自行实现)
uni-app 没有 Vue Router 的 beforeEach 全局守卫。但可以用 uni.addInterceptor 拦截所有路由 API:
// 在 App.vue 的 onLaunch 或 main.js 中
const interceptors = ['navigateTo', 'redirectTo', 'reLaunch', 'switchTab']
interceptors.forEach(method => {
uni.addInterceptor(method, {
invoke(args) {
// 跳转前拦截
const needLogin = ['/pages/order/order', '/pages/mine/mine']
const token = uni.getStorageSync('token')
if (needLogin.some(p => args.url.startsWith(p)) && !token) {
uni.showToast({ title: '请先登录', icon: 'none' })
uni.navigateTo({ url: '/pages/login/login' })
return false // 返回 false 阻止本次跳转
}
}
})
})
对标三端
|
iOS |
Android |
鸿蒙 |
| 路由守卫 |
在 pushViewController 前手动判断;或自定义 Router 类 |
在 startActivity 前判断;或用 ActivityResultContracts 拦截 |
在 router.pushUrl 前判断 |
| 框架级方案 |
无内置(通常自封装 Router) |
Jetpack Navigation 的 NavigationUI + NavigationGuard(非官方) |
无内置 |
十二、完整技术栈对照总表
| 功能维度 |
uni-app |
iOS 原生 |
Android 原生 |
鸿蒙 ArkTS |
| 路由注册 |
pages.json |
Storyboard / 代码注册 VC |
AndroidManifest.xml 注册 Activity |
main_pages.json |
| 页面容器 |
框架页面栈(WebView / nvue) |
UINavigationController |
Task Stack(Activity 栈) |
router 路由栈 |
| 压栈跳转 |
uni.navigateTo |
pushViewController:animated: |
startActivity(intent) |
router.pushUrl() |
| 替换跳转 |
uni.redirectTo |
替换 viewControllers 栈顶 |
startActivity + finish() |
router.replaceUrl() |
| 清栈重启 |
uni.reLaunch |
换 rootViewController |
CLEAR_TASK + NEW_TASK |
router.clear() + replaceUrl() |
| Tab 切换 |
uni.switchTab |
UITabBarController.selectedIndex |
BottomNavigationView / TabLayout |
Tabs 组件 index |
| 返回 |
uni.navigateBack |
popViewController / popToRootViewController |
finish() / onBackPressed() |
router.back() |
| 获取栈 |
getCurrentPages() |
navigationController.viewControllers |
ActivityManager / 自维护 |
router.getState() |
| 声明式跳转 |
<navigator> 组件 |
Storyboard Segue |
Jetpack Navigation <action> |
Navigation + navPathStack |
| 传参 |
URL 参数 / EventChannel |
属性赋值 / delegate |
Intent.putExtra / Bundle |
router.pushUrl({ params }) |
| 双向通信 |
EventChannel |
delegate / closure / NotificationCenter |
startActivityForResult / ActivityResultLauncher |
EventHub / emitter |
| 全局事件 |
uni.$emit / uni.$on |
NotificationCenter |
LocalBroadcastManager / EventBus |
commonEventManager / emitter |
| 路由守卫 |
uni.addInterceptor(自行封装) |
手动判断 / 自封装 Router |
手动判断 / 拦截器模式 |
手动判断 |
| 转场动画 |
animationType 参数(App 端) |
UIViewControllerTransitioningDelegate |
overridePendingTransition / ActivityOptions |
pageTransition |
| 页面生命周期 |
onLoad/onShow/onReady/onHide/onUnload |
viewDidLoad/viewWillAppear/viewDidAppear/viewWillDisappear/dealloc |
onCreate/onResume/onPause/onStop/onDestroy |
aboutToAppear/onPageShow/onPageHide/aboutToDisappear |
| 页面栈上限 |
10 层 |
无硬性限制(受内存) |
无硬性限制(受系统回收) |
建议 ≤ 32 层 |
| Vue Router 兼容 |
不兼容(可在插件市场找 vue-router 适配插件) |
--- |
--- |
--- |
十三、常见踩坑与最佳实践
13.1 必须避免的坑
| 坑 |
原因 |
解决方案 |
用 navigateTo 跳 tabBar 页无反应 |
API 设计限制 |
改用 switchTab |
switchTab 传参拿不到 |
API 设计限制 |
用 Pinia / uni.$emit / Storage |
| 页面栈超 10 层后跳转失效 |
栈满 |
关键节点用 redirectTo 替换;或用 reLaunch 重置 |
onLoad 在 Tab 页只执行一次 |
Tab 页不会重新创建 |
需要每次刷新就写在 onShow 里 |
redirectTo 后无法返回 |
当前页已被销毁 |
这是设计意图,确认业务需要再使用 |
| URL 参数含中文或特殊字符 |
未编码 |
使用 encodeURIComponent() 编码 |
13.2 最佳实践总结
选择跳转方式的决策树:
目标页面是 tabBar 页面?
├── 是 → uni.switchTab
└── 否 → 需要保留当前页面(能返回)?
├── 是 → 页面栈是否已满(≥10)?
│ ├── 是 → uni.redirectTo(替换,防止溢出)
│ └── 否 → uni.navigateTo(最常用)
└── 否 → 是否需要关闭所有页面?
├── 是 → uni.reLaunch
└── 否 → uni.redirectTo
十四、参考文档链接汇总
以上就是基于 uni-app 官方文档的完整梳理,覆盖了路由注册、页面栈模型、5 个 API 的参数/行为/限制、navigator 组件、传参方式、EventChannel 通信、生命周期联动、路由守卫、窗口动画等全部技术点,并逐一对标了 iOS(UINavigationController / UITabBarController)、Android(Activity Task Stack / Intent)、鸿蒙(router / Navigation)三端原生实现。如果某个部分需要更深入的代码级展开,随时告诉我。