前言
我们知道在 APP 端,有回到前台的事件监听,在 uniapp 也有对应的 App.onHide() 和 App.onShow() 事件能够实现,但是如果加上全局的弹窗显示,就得进一步处理弹窗的唯一显示。
事件监听
在 Uniapp 中监听前后台切换,只需要使用 App.onHide() 事件注册一个全局后台事件,然后在具体的弹窗组件中监听就行了。
//App.vue
export default {
onHide: function () {
uni.$emit("app:background");
}
}
//Notice.vue
onMounted(async () => {
uni.$on("app:background", async () => {
//这里写弹窗显示逻辑
noticeRef.show();
}
}
唯一弹窗
因为弹窗是全局的,所以不管是 CLI 项目还是原生的 uniapp 项目,在界面加载时都会挂载到界面上。如果不做处理,每个界面都会出现弹窗轰炸,这对用户体验来说是毁灭性的。
状态失控
那么实现唯一弹窗呢?这时候你惯性思维一敲脑袋,想着在 Pinia 中保存一个状态,然后事件触发时,用这个状态做拦截。但是接着你会发现,所有界面的弹窗都在抢着拿到这个状态,弹窗变成在意料之外的界面显示,当前的界面反而不再显示了。
这会你才意识到,所有界面的弹窗组件都是竞争关系,用一个全局状态判断并不可行。而且 App.onHide 和 App.onShow 中触发的事件,可能比组件内部的 onShow 和 onHide 更快执行,也没办法通过内部的生命周期去拦截。
标记控制
比起维护状态,我们应该回到组件本身,不是怎么去控制全局的弹窗显示状态,而是控制什么条件下去显示。我们只需要给每个界面标记打个标签,然后在事件执行的时候,对比一下就行了。最简单的标记就是通过通过 URL 实现。在 uniapp 中,我们可以借助 getCurrentPages() 返回的界面路径来标记。
我们在每个弹窗组件挂载的时候,都把界面路径缓存一份,这样在事件触发的时候再获取最新的 URL,只在 URL 和当前界面相同时,显示弹窗就行了。
//Notice.vue
onMounted(async () => {
// 缓存挂载时的界面路径
const pages = getCurrentPages();
const page = pages[pages.length - 1];
const cachePath = page.route || "";
// APP后台返回,事件显示
uni.$on("app:background", async () => {
const pages = getCurrentPages();
const page = pages[pages.length - 1];
const curPath = page.route || "";
// 拦截历史界面弹窗 -> 根据最上层界面,对比挂载路径,不相同不显示弹窗
if (curPath !== cachePath) {
return;
}
noticeRef.show();
});
});
这样我们就能防止历史界面中的监听的事件也被触发,只保证当前进入后台的界面在回到前台时,显示弹窗。即使我们返回到 旧的界面 或者切换到 被缓存的底栏页 ,也不会出现弹窗二次显示的问题。
使用这种方法处理的弹窗,不管是在 APP 中,还是在 H5 中,都能很好的保持弹窗显示的唯一性。
总结
有时候我们太过于考虑如何去控制所有组件的状态显示,反而把一些无状态的场景复杂化了。当这种方法不再可靠时,抛弃状态控制,用最简单的条件约束去实现一些场景,可能会得到合适的效果。