参考官方文档:
一、核心概念: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'
})
uni.navigateTo({
url: '/pages/detail/detail',
events: {
// 为被打开页面注册的监听事件
'onDataChanged': function(data) {
console.log('子页面传回的数据:', data)
}
},
success: function(res) {
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 页传数据怎么办?
import { useStore } from '@/store'
const store = useStore()
store.selectedId = 123
uni.switchTab({ url: '/pages/mine/mine' })
uni.$emit('updateMineData', { id: 123 })
uni.switchTab({ url: '/pages/mine/mine' })
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 })
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(options) {
console.log(options.id)
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 独有的机制,不需要全局状态,直接在两个页面之间建立通信管道。
uni.navigateTo({
url: '/pages/pageB/pageB',
events: {
'onResult': function(data) {
console.log('B 传回的数据:', data)
}
},
success: function(res) {
res.eventChannel.emit('initData', { id: 123, list: [1,2,3] })
}
})
onLoad() {
const eventChannel = this.getOpenerEventChannel()
eventChannel.on('initData', function(data) {
console.log('A 发来的数据:', data)
})
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() {
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)
每个页面实例的属性和方法
| 属性/方法 | 说明 | 平台 |
|---|
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:
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
}
}
})
})
对标三端
| 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)三端原生实现。如果某个部分需要更深入的代码级展开,随时告诉我。