简介
本文以微信小程序环境为例,介绍了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页面,先介绍几个与本文相关的特征:
- tabbar 第一次加载时会触发当前页面的
onLoad,之后再次切换tabber页面只会触发每个页面的onShow,不会再触发onLoad。 - 跳转tabbar页面只能使用 uni.switchTab 而不能使用
uni.navigateTo、uni.redirectTo。使用<navigator />组件时也必须设置 open-type="switchTab"。 - 当然你可以使用 uni.reLaunch ,但使用
uni.switchTab或uni.reLaunch时url路径都不可携带参数。 - 当 tabbar页面作为小程序卡片被分享打开或者扫小程序码打开时,路径上的参数可在
Page.onLoad中正常获取,与上述的普通页面一致。
tabbar页面的异常case
由上面的特征可知,如果你希望像普通页面一样给给tabbar页面传递参数,那么在很多情况下的表现可能并不如你所愿。
比如说:
- 某个按钮的跳转链接是动态配置的(服务端下发),那么无法简单的使用
uni.navigateTo,而这样的情况是非常常见的。 - 跳转至tabbar页面无法传递参数,很多场景下页面需要根据参数进行页面初始化
- tabbar页面间的切换不会触发
onLoad,更是加大了我们对页面初始化时机控制的难度
tabbar页面的解决思路
- 对应用内的页面间跳转方式进行封装,将
uni.navigateTo和uni.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)
},
}
总结
本文的想法来源于一个具体的问题:点餐小程序中菜单页的参数管理
如下图所示:
其中,首页和菜单页都是tabbar页面。
- 点击首页的外卖按钮即跳转到菜单页的外卖Tab
- 小程序通过扫码或者分享出去的卡片可以进入点餐页,并直接打开对应商品的弹框
这里我使用上述的方法解决了这里的参数问题,个人觉得方案并不成熟,有一定的局限性。
特别是 mixin 中 query 参数的注入并不是一种好多方式(其实hooks的方式可以很好的解决这个问题,但是受限于当前的业务暂时无法实践)。
所以本文更着重于问题的分析与描述,抛砖引玉,谢谢你看到这里,欢迎分享你的想法。