小程序页面栈详解

7,234 阅读6分钟

在做小程序项目的时候不难发现,使用navigateTo进行页面跳转后,点击左上角或使用navigateBack返回,总是会按照之前的页面进入倒序来展示页面,那么问题来了,它们的跳转规则是什么样的呢?结合到实际业务中如何灵活运用呢?

什么是页面栈?

首先先来了解一下微信小程序的运行环境: 小程序的运行环境分成渲染层和逻辑层,其中 WXML 模板和 WXSS 样式工作在渲染层,JS 脚本工作在逻辑层。 小程序的渲染层和逻辑层分别由2个线程管理:渲染层的界面使用了WebView 进行渲染;逻辑层采用JsCore线程运行JS脚本。一个小程序存在多个界面,所以渲染层存在多个WebView线程,这两个线程的通信会经由微信客户端做中转,逻辑层发送网络请求也经由Native转发,小程序的通信模型下图所示。 小程序运行环境

我们可以看到,一个页面使用一个 WebView 线程进行渲染。如果打开10个页面,则会开启 10 个 WebView 线程,此时内存中的十个webView线程我们称之为页面栈。当然小程序也会对这块内存做限制,目前页面栈的限制是不能超过十条。在小程序中页面的路由是小程序框架本身控制的我们不要去手动管理, 小程序框架通过一个页面栈的设计来管理所有的界面,当发生路由跳转时,页面栈就会做出相应的变化,在小程序页面中通过 getCurrentPages() 就可以获取到当前的页面栈。

举个栗子: 在父页面中先获取页面栈:

const page = getCurrentPages(); // 父页面
console.log('父页面', page); //父页面

通过wx.navigateTo跳转子页面,在子页面中再获取页面栈:

const page = getCurrentPages(); // 子页面
console.log('子页面', page); //子页面

输出: 输出

通过上面的例子可以看到,我们可以在页面中通过 getCurrentPages() 方法来获取当前页面栈,并且获取到的是一个数组,其中每个item都是每个页面的Page对象(也就是在页面中的this对象),由此我们引发一些思考……

路由跳转时页面栈表现?

当发生路由切换的时候,页面栈的表现如下:

情景页面栈表现对应路由跳转API
小程序初始化新页面入栈
打开新页面新页面入栈wx.navigateTo 或使用组件
页面重定向当前页面出栈,新页面入栈wx.redirectTo 或使用组件
页面返回页面不断出栈,直到目标页wx.navigateBack 或使用组件或用户按左上角返回按钮
Tab 切换页面全部出栈,只留下新的 Tab 页面wx.switchTab 或使用组件 或用户切换 Tab
重加载页面全部出栈,只留下新的页面wx.reLaunch 或使用组件

我们在做项目的时候,巧妙运用路由跳转和页面栈会节省很多代码,用户体验也会得到相应的提升,所以,在开始项目之前,定好页面跳转规则相当重要。

页面栈的实际运用分析

下面我们分析一下页面栈的变化过程,从分析中,我们需要明白的一个重要问题就是,当客户按返回按钮的时候究竟会跳转到那个界面,这是我们分析页面栈变化的的意义。 首先我们在页面中调用两次navigateTo,页面栈情况如下 图片

这时显示的界面是pageC ,如果客户在此时返回则会一切正常,回退的第一个界面是pageB,然后是pageA。但是如果在pageC 界面调用 wx.redirectTo({url:'pageD'}) 则情况就会不一样,我们先看一下跳转到pageD后页面栈的情况如何。 图片

根据栈的情况,我们可以分析出。如果使用 wx.redirectTo跳转到pageD页面,然后在回退的时候是不能再次回退到pageC的,而会直接回退到pageB。 通过上面对页面栈的分析,我们可以看到栈的变化是会影响客户回退页面的顺序的,所以根据自己的需求合理的使用不同的跳转方法是非常重要的。如果使用不当就会导致跳转混乱让人摸不清头脑 下面分析一种调转重复页面的情况: 图片

如图所示栈中出现了两个相同的pageB界面,这个时候如果用户按退出键就会出现一个页面出现2次的情况,而且有一个界面的数据也是旧的数据。因此为了避免这个问题,我们应该在 PageC 页面避免将 PageB重复压入栈中,所以在pageC页面使用wx.navigateBack({delta:1}); 进行页面回退。而数据刷新的问题则在页面的onShow函数中进行即可。

情景:确认订单页用户点击左上角返回

假设场景:用户在商品详情页直接点击“立即购买”下单购买,进入确认订单页,付款成功后跳转到付款成功页面,此时用户点击左上角箭头进行了返回…… 处理:此时理应进入商品详情页,所以在确认订单页付款成功跳转时应当将确认订单页出栈,新页面入栈,那么就不可以使用wx.navigateTo来进行页面跳转,应当使用wx.redirectTo 图片

情景:确认订单页用户选择已有收货人

假设场景:在确认订单页,用户需要选择已有的收货人,而已有收货人列表在另一个页面,那么用户点击“选择收货人”之后,使用wx.navigateTo跳转到收货人列表,点击某个收货人,带参数返回确认订单页…… 处理:在确认订单页使用wx.navigateTo跳转到收货人列表,然后在收货人列表里click事件中获取页面栈,直接往上一个页面setData,然后退回上一个页面,show code:

const page = getCurrentPages()
if (page.length > 1) {
  page[page.length - 2].setData({
    收货人: 选中的某个收货人详情   //[object]
  })
  wx.navigateBack({
    delta: 1
  })
}

上面例子中提到过,在页面中通过 getCurrentPages() 方法来获取当前页面栈,并且获取到的是一个数组,其中每个item都是每个页面的Page对象,那么我们就可以使用 setData 方法直接改变上一个页面展示的数据,并且直接退回上一个页面。

此时官方提醒: 输出

虽然这种方法简便,但是官方也给出提醒,页面栈数据可以自行修改,但是!一定要慎重,否则会导致页面状态错误。

总结: 总觉得漏了点啥,又想不起来…… 官方文档应有尽有,多研究官方文档,多引发思考并手写demo尝试,总会有一些新的发现,另外,方法千万条,随便选一条,根据自己业务逻辑选用合适的方法。

2023-05-26最后更新

该文档首发于2019年,因为实效性原因,该文档已不适用于实战,但是初学者依然可以从中来理解页面栈和小程序跳转逻辑,而页面传参方式也依然可以使用,只是官方已经提供了其他更优雅的方法,该文档最后的情景演示仅作为理解使用