Skip to main content

微信小程序页面跳转和参数传递

小程序有页面之分,所以存在页面栈,它的操作就像的浏览器的历史操作 push, back 那样,小程序也有类似的页面栈操作。

功能 Web浏览器 小程序
跳转新页面 history.push wx.navigateTo
返回上一页 history.back wx.navigateBack
当前页重定向 history.replace wx.redirectTo

上方是页面间的基本操作。不过在具体的开发中,除了跳转,还要处理页面之间的传值。

带参初始化

小程序支持通过带参初始化页面,达到按业务需要自动完成一些动作的效果。

开发者工具指定编译参数:

自定义编译参数,会传递给首个页面

编译后,首个页面初始化后从 onLoad 周期获取到参数,然后自动完成一些页面操作:

onLoad 期间可能无法更新界面,需要等待 onShow 触发后才能操作,所以一般是把 query 暂存

Page({
  onLoad(query) {
    query; // { name: "luoyz" }
    query.name; // luoyz
    if (query.name === 'luoyz'){
      console.log('抽到3星')
    } else {
      console.log('抽到5星')
    }
  }
})

带参跳转

以下是简单的页面传值例子,A页面带参跳转B页面,B页面可以在 onLoad 周期获取到参数:

A 带参跳转到 B

// A 页面
wx.navigateTo({
  url: `/pages/b/b?name=luoyz`
})
// B 页面
Page({
  onLoad (query) {
    query.name // luoyz
  }
})

带参回传

带参回传即 A 跳转到 B 后,B 处理完业务,把结果回传给 A。

A 跳 B;B 回传 A

比起带参跳转,带参回传的实现复杂得多,因为 navigateBack 接口不能带参数。不过网上也有很多可用的方法,在这里说一下我看得最多的解决方案:

回传数据写在 App 全局变量

页面 B 把回传值写在 App 全局变量里面,navigateBack 回到 A 会触发 A 的 onShow,所以在 onShow 中处理回传的数据。

A 页面 onShow 会触发2次:

① 页面初始化触发一次

② navigateBack 回来再触发一次

// App
App({
  globalData: {}
})
// A 页面
Page({
  onShow() {
    const app = getApp()
    app.globalData.surveyResult // 第一次 null,回传后为 true
  },
  onClick() {
    wx.navigateTo({ url: '/pages/b/b' })
  }
})
// B 页面
Page({
  onClick() {
    const app = getApp()
    app.globalData.surveyResult = true
    wx.navigateBack()
  }
})

回调数据通过页面栈写在上一页的 data

这种方法是用小程序获取页面栈的接口 getCurrentPages() 获得当前页面栈。

getCurrentPages() // 页面对象数组 [A, B]
getCurrentPages()[getCurrentPages().length - 1] // 当前页面

在下面的例子,B 是从 A 跳转来的,那么 B 可以访问、修改上一页的页面属性,即 A。

和方法1一样,A 的 onShow 也会触发2次。初始化触发一次,从 B 返回时触发一次

// A 页面
Page({
  data: {
    surveyResult: null
  },
  onShow() {
    this.data.surveyResult // 第一次 null,回传后为 true
  },
  onClick() {
    wx.navigateTo({ url: '/pages/b/b' })
  }
})
// B 页面
Page({
  onClick() {
    // A 跳转到 B,则页面栈 = [A, B]
    const currentPages = getCurrentPages()
    const PageA = currentPages[0]
    const PageB = currentPages[1]
    PageA.setData({ surveyResult: true })
    wx.navigateBack()
  }
})

上方的跳转和回传处理,是在网上找到的比较常见的小程序跳转解决方案。

页面参数传递的过程比较像事件流,不管是初始化,还是跳转、回传,都需要 onLoad、onShow 触发(只有在触发时候,业务操作才有效果)。

我之前写的一篇文章 ❐ 速览: 基于事件实现流程控制 做了个流程控制工具 PromisifyChannel,可以把事件流转换成业务回调。最近趁着业务需要,我试着把 PromisifyChannel 搬到小程序的上,实现小程序页面的跳转和传参。

NavigationChannel 是用于小程序页面跳转工具库,可以将 onLoad、onShow 的事件转换为业务流程。

[Github Gist] 小程序页面跳转库

每个页面都要配置 NavigationChannel 才能实现页面之间的跳转

注入 NavigationChannel

Page({
  ...withNavigationChannel()
})
onLoad(query) {
  this.initNavigationChannel({...}) // 初始化
  this.getNavigationChannel().handleOnLoad(query)
}

onShow() {
  this.getNavigationChannel().handleOnShow()
}

NavigationChannel 必须在 onLoad 期间初始化,初始化完了后,调用 handleOnLoad(query) 接管 onLoad 的参数。

NavigationChannel 自动获取当前页面的路由。

// 页面 A
this.initNavigationChannel({
  handleCmdFunc: (key, cmd) => {
    cmd.fn; // 调用方法名
    cmd.fromPageRoute; // 发起请求的页面
    cmd.targetPageRoute; // 跳转的目标页面
    cmd.encoding; // 编码方式,如果 data 是对象,则应该用 jsonbase64,默认纯文本
    cmd.data; // 调用参数
  },
})
// 页面 B
this.initNavigationChannel({
  handleCmdFunc: (key, cmd) => {
    const channel = this.getNavigationChannel()
    // 发送回传
    // channel.sendRsp(key, {
    //   fromPageRoute: cmd.fromPageRoute,
    //   targetPageRoute: cmd.targetPageRoute,
    //   encoding: 'plain',
    //   data: 'bdata',
    // })

    // 发送回传,自动填写发送者信息
    channel.sendRspWithCmd(key, cmd, {
      encoding: 'plain',
      data: 'bdata',
    })
  }
})
// A 带参 luoyz 跳转 B
this.getNavigationChannel().invoke({
  targetPageRoute: '/pages/b/b', // pages/b/b 也可,会补斜杠
  fn: '开始验证',
  data: 'luoyz'
}).then(rsp => {
  rsp.data // = bdata
})

让 NavigationChannel 处理小程序首个页面的编译参数,此时 fromPageRoute、targetPageRoute 为空。

// 编译参数: fn=setname&data=luoyz
this.initNavigationChannel({
  handleCmdFunc: (key, cmd) => {
    cmd.fn; // setname
    cmd.data; // luoyz
  }
})