页面栈(Page Stack)概念
UniApp 使用一个栈结构(Last In First Out)来管理页面:
页面栈示意图:
栈顶 → [详情页] ← 当前显示的页面
[列表页]
[首页]
栈底 → [启动页]
五种跳转方式的技术解析
uni.navigateTo() - 压栈操作
// 执行前
栈:[首页, 列表页]
// 执行 uni.navigateTo({ url: '/pages/news/detail' })
栈:[首页, 列表页, 详情页] ← 新页面压入栈顶
// 此时:
- 首页:保持原样,仍在内存中
- 列表页:保持原样,仍在内存中
- 详情页:新加载,显示
- 返回时:详情页出栈,显示列表页
本质:将新页面实例压入页面栈顶,原页面实例保留在栈中,所有页面数据状态维持不变。
uni.redirectTo() - 替换栈顶操作
// 执行前
栈:[首页, 列表页] ← 列表页在栈顶
// 执行 uni.redirectTo({ url: '/pages/login/login' })
栈:[首页, 登录页] ← 列表页被移除,登录页放入
// 此时:
- 首页:保持原样
- 列表页:被销毁(触发了 onUnload)
- 登录页:新加载
- 返回时:直接回到首页,无法回到列表页
本质:将当前栈顶页面销毁(释放内存),用新页面替换它的位置。
uni.reLaunch() - 重置整个栈
// 执行前
栈:[启动页, 首页, 列表页, 详情页] ← 4个页面
// 执行 uni.reLaunch({ url: '/pages/index/index' })
栈:[首页] ← 只有新页面
// 此时:
- 所有原页面(启动页、首页、列表页、详情页)全部销毁
- 新首页:重新加载
- 返回时:无法返回,因为栈底只有首页
本质:清空整个页面栈,销毁所有页面实例,创建新页面作为根页面。
uni.switchTab() - 特殊栈操作
// 假设 tabBar 配置了:首页、分类、购物车、个人
// 执行前
栈:[商品详情, 分类, 首页] ← 有3个页面
// 执行 uni.switchTab({ url: '/pages/user/user' })
栈:[个人] ← 清除非 tabBar 页面,保留 tabBar 页面单例
// 此时:
- 非 tabBar 页面(商品详情)被销毁
- 个人页如果是首次打开:创建新实例
- 个人页如果之前打开过:复用之前的实例(保持原有状态)
本质:维持 tabBar 页面的单例模式,清空所有非 tabBar 页面。
uni.navigateBack() - 弹栈操作
// 执行前
栈:[首页, 列表页, 详情页] ← 3个页面
// 执行 uni.navigateBack({ delta: 1 })
栈:[首页, 列表页] ← 详情页出栈并销毁
// 执行 uni.navigateBack({ delta: 2 })
栈:[首页] ← 列表页和详情页都出栈并销毁
本质:将栈顶的 n 个页面弹出并销毁,显示前一个页面。
内存管理角度
| 跳转方式 | 原页面状态 | 内存处理 | 适用场景 |
|---|---|---|---|
| navigateTo | 保留(挂起) | 占用内存,保留数据 | 详情页、表单填写页 |
| redirectTo | 销毁 | 释放内存 | 登录页、支付中间页 |
| reLaunch | 全部销毁 | 完全释放 | 退出登录、切换账号 |
| switchTab | 选择性保留 | 维护 tab 实例 | 底部导航切换 |
| navigateBack | 销毁返回的页面 | 逐步释放 | 返回操作 |
生命周期验证
可以通过页面的生命周期函数来验证这些行为:
// 在页面中
onLoad() { console.log('页面加载', this.$route) }
onShow() { console.log('页面显示', this.$route) }
onHide() { console.log('页面隐藏', this.$route) }
onUnload() { console.log('页面销毁', this.$route) }
// navigateTo: 新页面 onLoad → onShow,原页面 onHide
// redirectTo: 原页面 onUnload,新页面 onLoad → onShow
// reLaunch: 所有页面 onUnload,新页面 onLoad → onShow
// navigateBack: 当前页 onUnload,前一页 onShow
这就是为什么说 navigateTo 是”保留”(页面实例依然存在,只是被隐藏),而 redirectTo 是”关闭”(页面实例被彻底销毁,无法再回来)。
参数
uni.navigateTo(OBJECT)
这是最常用的跳转方式,参数也最丰富:
url(String | 必填):目标页面路径,支持带参数,如'/pages/detail/detail?id=1'。animationType(String | 否):窗口显示的动画效果(仅App平台支持),如"pop-in"、"slide-in-right"。animationDuration(Number | 否):动画持续时间,单位毫秒(仅App平台支持),默认300。events(Object | 否):页面间通信接口,可以监听被打开页面发送的事件。success(Function | 否):跳转成功的回调。fail(Function | 否):跳转失败的回调。complete(Function | 否):跳转结束(成功或失败都会执行)的回调。
uni.redirectTo(OBJECT)
参数相对简洁:
url(String | 必填):目标页面路径,可带参数,但不能是 tabBar 页面。success(Function | 否):跳转成功的回调。fail(Function | 否):跳转失败的回调。complete(Function | 否):跳转结束的回调。
uni.reLaunch(OBJECT)
用于“重启式”跳转:
url(String | 必填):目标页面路径,可带参数。特别注意:如果跳转到 tabBar 页面,则不能带参数。success(Function | 否):跳转成功的回调。fail(Function | 否):跳转失败的回调。complete(Function | 否):跳转结束的回调。
uni.switchTab(OBJECT)
专用于底部导航栏页面:
url(String | 必填):目标 tabBar 页面的路径,路径后不能带参数。success(Function | 否):跳转成功的回调。fail(Function | 否):跳转失败的回调。complete(Function | 否):跳转结束的回调。
uni.navigateBack(OBJECT)
用于返回操作:
delta(Number | 否):返回的页面层数,默认1。如果数字大于页面栈深度,则返回到首页。animationType(String | 否):窗口关闭的动画效果(仅App平台支持),如"pop-out"。animationDuration(Number | 否):动画持续时间,单位毫秒(仅App平台支持),默认300。success(Function | 否):返回成功的回调。fail(Function | 否):返回失败的回调。complete(Function | 否):返回结束的回调。