uni-app 中结合 onLoad 和 getCurrentPages 的高级实战场景,涵盖跨页面数据刷新、动态路由拦截、页面栈管理、表单草稿同步等复杂需求。
一、多级返回 + 非相邻页面数据刷新
场景 :用户从「首页」→「列表页」→「详情页」→「编辑页」。在编辑页保存数据后,希望直接返回到列表页 (跳过详情页),并让列表页刷新、同时更新首页上对应的概要数据。
难点:需要操作页面栈中两个不同的旧页面,并携带返回值。
javascript
// 编辑页( EditPage.vue )
export default {
methods: {
async onSaveSuccess(newData) {
// 1. 获取当前页面栈
const pages = getCurrentPages();
// pages = [首页, 列表页, 详情页, 编辑页]
// 2. 拿到列表页实例( pages[1] )
const listPage = pages[pages.length - 3];
// 3. 拿到首页实例( pages[0] )
const homePage = pages[0];
// 4. 分别调用它们暴露的更新方法(通过 $vm 获取 Vue 实例)
if (listPage && listPage.$vm) {
await listPage.$vm.refreshList(newData);
}
if (homePage && homePage.$vm) {
homePage.$vm.updateHomeSummary(newData);
}
// 5. 控制返回:跳过详情页,直接返回到列表页(关闭编辑页和详情页两层)
// 利用 navigateBack 的 delta 参数,delta = 2 表示关闭两个页面
uni.navigateBack({ delta: 2 });
}
}
}
// 列表页( ListPage.vue )
export default {
data() {
return { list: [] };
},
methods: {
// 专门供外部调用的刷新方法
refreshList(newItem) {
// 重新拉取列表数据,或者将 newItem 插入到顶部
this.fetchList();
uni.showToast({ title: '列表已更新' });
},
fetchList() { /* 请求逻辑 */ }
}
}
// 首页( HomePage.vue )
export default {
methods: {
updateHomeSummary(newData) {
// 更新首页显示数量或摘要信息
this.summaryCount = newData.totalCount;
}
}
}
二、表单草稿自动保存 + 页面跳转时跨页同步
场景 :一个多步骤表单(三步,三个页面)。用户在任意一步修改内容后,希望切换页面时自动把所有已填数据同步到一个全局草稿对象 ,并且返回上一步时能恢复之前填写的内容。
技巧 :在 onLoad 中恢复草稿,在 onHide 或 onUnload 中利用 getCurrentPages 找到上一个页面并传递数据(不通过路由参数)。
javascript
// 全局草稿对象(可以放到 Vuex 或 store 中)
const formDraft = { step1: {}, step2: {}, step3: {} };
// 步骤1页面( Step1.vue )
export default {
data() {
return { formData: { name: '', age: '' } };
},
onLoad() {
// 从全局草稿恢复
if (formDraft.step1) {
this.formData = { ...formDraft.step1 };
}
},
onHide() {
// 页面隐藏时(跳转到下一步时)保存草稿
formDraft.step1 = { ...this.formData };
// 【高级】修改下一步页面的数据(预填充)
const pages = getCurrentPages();
const nextPage = pages[pages.length - 1]; // 当前页面自己
// 但实际上隐藏时,下一个页面已经入栈了吗?不,这里用不到。
// 更好的做法:在跳转前通过事件总线或 store 传递
},
methods: {
goToStep2() {
// 保存当前步骤
formDraft.step1 = { ...this.formData };
// 跳转前将数据注入到全局,让 step2 的 onLoad 能拿到
uni.navigateTo({ url: '/pages/step2/step2' });
}
}
}
// 步骤2页面( Step2.vue )
export default {
onLoad() {
// 恢复自己步骤的草稿
if (formDraft.step2) this.formData = formDraft.step2;
// 如果需要向上一步页面传递数据,可以在 onShow 中利用 getCurrentPages 找到上一步实例并修改
},
onShow() {
// 当从 step3 返回时,可能需要刷新自己展示的数据
this.refreshFromDraft();
},
methods: {
// 返回上一步时,让 step1 页面的数据同步为最新的 step1 草稿
goBackToStep1() {
// 先保存当前 step2 的草稿
formDraft.step2 = { ...this.formData };
uni.navigateBack();
// 但是返回后,step1 的 onShow 并不会自动重新加载草稿,所以要在 step1 的 onShow 中做处理
}
}
}
// 在 step1 的 onShow 中
onShow() {
// 每次页面显示时,重新从草稿读取(防止返回后数据过时)
if (formDraft.step1) {
this.formData = { ...formDraft.step1 };
}
}
三、动态拦截页面返回并注入数据(类似路由守卫)
场景 :用户在一个编辑页面修改了表单,未保存时点击返回,需要弹窗确认。若确认不保存,直接返回;若确认保存,调用保存接口后,将保存结果直接更新到前一个页面 ,而不刷新整个前一个页面。
实现 :利用 onBackPress 生命周期和 getCurrentPages。
javascript
// 编辑页( EditPage.vue )
export default {
data() {
return { isDataChanged: false, form: {} };
},
onBackPress(options) {
// 监听返回事件(安卓物理返回或导航栏返回按钮)
if (this.isDataChanged) {
// 弹窗询问
uni.showModal({
title: '提示',
content: '内容未保存,是否保存后再返回?',
success: async (res) => {
if (res.confirm) {
// 用户选择保存
const savedData = await this.saveData();
// 找到上一个页面实例,并调用其方法传递保存的数据
const pages = getCurrentPages();
const prevPage = pages[pages.length - 2];
if (prevPage && prevPage.$vm) {
prevPage.$vm.onEditPageReturn(savedData);
}
uni.navigateBack();
} else if (res.cancel) {
// 不保存,直接返回
uni.navigateBack();
}
}
});
return true; // 拦截默认返回行为
}
// 无修改,正常返回
return false;
},
methods: {
saveData() { /* 保存逻辑,返回保存后的对象 */ }
}
}
// 列表页( ListPage.vue )
export default {
methods: {
// 供编辑页返回时调用的方法
onEditPageReturn(savedItem) {
// 更新列表中对应的那条数据,避免重新拉取整个列表
const index = this.list.findIndex(item => item.id === savedItem.id);
if (index !== -1) {
this.$set(this.list, index, savedItem);
uni.showToast({ title: '更新成功' });
}
}
}
}
四、页面栈深度限制与自动清理(防止内存过高)
场景 :在复杂的小程序或 App 中,用户可能不断跳转新页面(比如商品详情页),导致页面栈超过 10 层(iOS 上限)。需要当页面栈达到阈值时,自动将中间页面移除 (例如删除第 2 ~ 第 n-1 层,仅保留首页和当前页)。
高级技巧 :使用 getCurrentPages 获取页面栈长度,再通过 uni.reLaunch 或自定义栈替换。
注意 :reLaunch 会关闭所有页面并打开新页面,会丢失状态。更好的办法是利用 redirectTo 替换当前页,但无法直接移除中间页面。这里展示一种"当页面栈过长时,提示用户并跳转到首页再进入"的高阶方案。
javascript
// 在全局路由拦截器(如 uni-simple-router 或自定义封装 navigateTo)中
function smartNavigateTo(url) {
const pages = getCurrentPages();
const MAX_STACK = 8; // 设定阈值
if (pages.length >= MAX_STACK) {
// 警告并执行 reLaunch 到目标页(会关闭所有页面)
uni.showModal({
title: '提示',
content: '页面打开过多,是否清理并直接跳转?',
success(res) {
if (res.confirm) {
uni.reLaunch({ url });
}
}
});
return false;
}
// 正常跳转
uni.navigateTo({ url });
}
// 在页面内需要跳转时使用 smartNavigateTo 而不是直接 uni.navigateTo
更优雅的方案:利用 getCurrentPages 找到首页后面的页面,然后通过 redirectTo 替换当前页面为目标页,但需要重新构造路由。由于 uni-app 没有直接删除中间页面的 API,这种"及时清理"的体验通常用 reLaunch 或 switchTab 替代。
五、页面参数变更而不刷新页面(冷启动防抖)
场景 :从 A 页面进入 B 页面时,某个参数可能会在后续再次从 A 修改(比如用户修改了城市)。希望 B 页面在不重载的情况下收到参数变化。
实现 :利用 getCurrentPages 拿到 B 页面实例,直接调用其方法更新。
javascript
// A页面(城市选择页)
export default {
methods: {
onCityChange(newCity) {
// 保存到全局或 Vuex
this.$store.commit('setCity', newCity);
// 获取页面栈中已存在的 B 页面(如果存在)
const pages = getCurrentPages();
const targetPage = pages.find(page => page.route === 'pages/list/list');
if (targetPage && targetPage.$vm) {
// 直接调用 B 页面的更新方法,无需重新加载页面
targetPage.$vm.updateCity(newCity);
}
}
}
}
// B页面(列表页)
export default {
methods: {
updateCity(city) {
this.currentCity = city;
// 重新请求列表数据(不重新走 onLoad)
this.fetchList();
}
}
}
关键知识点总结
onLoad只执行一次,适合做初始化参数接收。getCurrentPages返回的页面对象:- 通过
page.$vm获取 Vue 实例($vm在 uni-app 中固定)。 - 可以直接调用该页面上的
methods,修改data。 - 也可以访问页面路由 (
page.route)、页面参数 (page.options等)。
- 通过
- 生命周期的配合 :
onShow:每次页面显示都触发,适合恢复状态或同步数据。onBackPress:结合getCurrentPages实现自定义返回逻辑。
- 性能警告 :大量使用
getCurrentPages修改其他页面数据时,要小心不要造成循环更新或数据不一致。建议配合状态管理(Vuex / Pinia)进行数据同步,直接修改页面实例仅用于轻量级、即时的 UI 更新。