uni-app中tabbar页面的参数传递

2,404 阅读5分钟

简介

本文以微信小程序环境为例,介绍了tabbar页面的参数传递给普通Web前端开发者带来的困扰。并尝试抹平tabbar页面与普通页面这一层的差异,给出了笔者自己的实践方案。

对于普通的页面

对于一个普通的uni-app页面来说,页面的query参数指的是页面url上携带的query参数,

/**
 * @file /home/menu/index.vue
 * 当前页面
 */

// 跳转至 /product/detail/index 页面,并传递参数 spuId
uni.navigateTo({
  url: '/product/detail/index?spuId=xxxx'
})

我们主要通过Page.onLoad来获取:

/**
 * @file /product/detail/index.vue
 * 目标页面
 */

export default {
  onLoad(option) {
    const { spuId } = option
    
    console.log('spuId: ', spuId)
  }
}

这个模型是与“打开一个Web页面”的心智模型是相符的:

拼接新页面的url参数 -> 在新页面中通过 window.location.search获取

对于tabbar页面

tabbar页面的特征

对于tabbar页面,先介绍几个与本文相关的特征:

  1. tabbar 第一次加载时会触发当前页面的onLoad,之后再次切换tabber页面只会触发每个页面的onShow,不会再触发onLoad
  2. 跳转tabbar页面只能使用 uni.switchTab 而不能使用 uni.navigateTouni.redirectTo。使用 <navigator /> 组件时也必须设置 open-type="switchTab"
  3. 当然你可以使用 uni.reLaunch ,但使用 uni.switchTabuni.reLaunchurl 路径都不可携带参数。
  4. 当 tabbar页面作为小程序卡片被分享打开或者扫小程序码打开时,路径上的参数可在Page.onLoad中正常获取,与上述的普通页面一致。

tabbar页面的异常case

由上面的特征可知,如果你希望像普通页面一样给给tabbar页面传递参数,那么在很多情况下的表现可能并不如你所愿。

比如说:

  • 某个按钮的跳转链接是动态配置的(服务端下发),那么无法简单的使用 uni.navigateTo,而这样的情况是非常常见的。
  • 跳转至tabbar页面无法传递参数,很多场景下页面需要根据参数进行页面初始化
  • tabbar页面间的切换不会触发 onLoad,更是加大了我们对页面初始化时机控制的难度

tabbar页面的解决思路

  • 对应用内的页面间跳转方式进行封装,将 uni.navigateTouni.switchtab 统一起来
  • 进一步,对于 uni.switchtab 的情况,将参数分离出来存放于临时的全局变量中,保证一次性存取。
  • 对于tabbar页面,需要针对于“第一次加载”和“非第一次加载”做区分。(如果依赖页面参数的话)推迟在onShow中处理页面初始化的业务逻辑。

代码实现

Vue的实例上注入页面跳转与参数获取的统一入口

/**
 * @file page-query.service.js
 * 示例
 * - this.navigateTo('/menu/index', { id: 1 }) 跳转页面
 * - this.getTabPageQuery() tabbar页面获取参数
 */

// 用于暂存tabPage的query, 解决 wx.switchTab: url 不支持拼接参数的问题
let tabPageQuery = null

/**
 * 跳转到应用内的某个页面。
 * 如果目标页面是 tabbar 页面,则会调用 switchTab 跳转到目标页面(即不会保留当前页面)
 * @param {string} url - 跳转的目标页面路径(不带参数)
 * @param {object} extraParams - 页面参数
 */
function navigateTo(url, params = {}) {
  if (isTabbarPage(url)) {
    // 跳转tabUrl之前,先把参数存下来
    _tabPageQuery = params
    uni.switchTab({ url })
    return
  }

  // 跳转普通页面,直接拼接参数并调用navigateTo
  uni.navigateTo({
    url: `${url}?${queryStringfy(params)}`,
  })
}

/**
 * tabbar页面获取参数
 */
function getTabPageQuery() {
  const tabQuery = this._tabPageQuery || {}

  // 清空tabPageQuery
  _tabPageQuery = null

  return tabQuery
}

Vue.prototype.$navigateTo = navigateTo
Vue.prototype.$getTabPageQuery = getTabPageQuery

在页面组件中添加相应的mixin,统一获取和管理页面参数的逻辑:

/**
 * @file page-query.mixin.js
 * 该 mixin 主要用于在页面中获取 URL 参数,并将其添加到页面实例的 `this.query` 中来进行后续操作。
 * 有两种类型的 mixin:
 * - `NormalPageQueryMixin`: 用于普通的页面
 * - `TabbarPageQueryMixin`: 用于 tabbar 的页面。
 */

// 在普通的页面中使用这个
export const NormalPageQueryMixin = {
  // 直接获取url中的query,存于 `this.query` 中
  onLoad(options) {
    this.query = options
  },
}


// 在tabbar页面中使用这个
export const TabbarPageQueryMixin = {
  // tabbar首次加载时,获取url中的query,存于this._urlQuery中
  onLoad(options) {
    this._urlQuery = options
  },

  onShow() {
    if (this._urlQuery) {
      // _urlQuery 有值时,说明是tabbar首次加载,此时需要将_urlQuery合并到 `this.query` 中
      // 并将_urlQuery置空,避免影响后续的tabbar页面切换时的场景
      this.query = {
        ...this._urlQuery,
        ...this.$getTabPageQuery(),
      }
      this._urlQuery = null
    } else {
      // _urlQuery 无值时,说明是tabbar页面切换,此时只需要获取tabbar页面的query即可
      this.query = this.$getTabPageQuery()
    }
  },
}

使用示例

请注意!! ⚠️⚠️

该 mixin 会在页面中添加一个名为 query 的变量,因此请不要在页面中手动修改该变量,也不要在页面中定义名为 query 的变量。

对于普通的页面

对于 NormalPageQueryMixin,该 mixin 中会在该页面的 onLoad 钩子中获取 url 中的所有参数并将其存储到 query 变量中。 在页面中可以随时使用 this.query 来获取参数。例如,如果 url 中含有参数 name,则可以在 Vue 模板中使用 {{ query.name }} 进行使用。

export default {
  mixins: [NormalPageQueryMixin],

  // 你的页面代码
  ...

  onLoad() {
    // 直接使用 this.query 即可获取参数
    console.log(this.query)
  },

  onShow() {
    // 直接使用 this.query 即可获取参数
    console.log(this.query)
  },
}

对于tabbar页面

对于 TabbarPageQueryMixin,该 mixin 会在 onShow 钩子中进行更新,以确保在 tabbar 切换后也能正常获取参数。同时,该 mixin 中的 $getTabPageQuery 方法可以获取暂存于 _tabPageQuery 中的参数,并将其添加到 query 变量中。

由于tabbar页面的复杂性,推荐在 onShow 中编写页面初始化的业务逻辑。

import { TabbarPageQueryMixin } from '@/mixins/page-query.mixin'

export default {
  mixins: [TabbarPageQueryMixin],

  // 你的页面代码

  onLoad() {
    // ⚠️ 不要在 onLoad 钩子中使用 this.query,去 onShow 钩子中使用
  },

  onShow() {
    // 直接使用 this.query 即可获取参数
    console.log(this.query)
  },
}

总结

本文的想法来源于一个具体的问题:点餐小程序中菜单页的参数管理

如下图所示:

menu-query.png

其中,首页和菜单页都是tabbar页面。

  • 点击首页的外卖按钮即跳转到菜单页的外卖Tab
  • 小程序通过扫码或者分享出去的卡片可以进入点餐页,并直接打开对应商品的弹框

这里我使用上述的方法解决了这里的参数问题,个人觉得方案并不成熟,有一定的局限性。

特别是 mixinquery 参数的注入并不是一种好多方式(其实hooks的方式可以很好的解决这个问题,但是受限于当前的业务暂时无法实践)。

所以本文更着重于问题的分析与描述,抛砖引玉,谢谢你看到这里,欢迎分享你的想法。