Hi guys, 本期介绍的内容是全局通信弹窗的方案
背景介绍
本项目,为论坛型小程序,由于背景原因,使用技术栈较为老旧。
具备用户隐私弹窗、黑名单用户弹窗、权限弹窗等全局性提示。
- 用户隐私弹窗:
- 程序启动时展示,在所有用户动作前触发
- 黑名单用户弹窗:
- 在黑名单用户涉及业务操作时触发
- 权限弹窗:
- 非会员用户访问会员内容时触发
问题描述
在隐私协议这一环节中:
- 弹窗为自定义组件,挂载于tabbar页面上;
- 最新用户隐私协议内容获取(需访问Https请求)/是否签约,在App.vue的
onLaunch
中;
会小概率出现隐私协议未弹起的情况。为什么不使用其他的生命周期因为要更早的拿到数据。
问题原因
App.vue中调用接口获取隐私协议大约200ms,首页弹窗组件挂载至节点时间可能会>200ms。这里补充一张uni-app的生命周期图。
由此可见App.vue的生命周期是早于组件挂载的,但是问题点在于没法确定组件挂载的具体完成时间。
解决步骤
连续监听节点是否已存在,使用uni.createSelectorQuery
。这明显是一个笨(dumber)方法,耗费性能的同时让代码变得无缘无故,显然💩山就是这么来的。但是App.vue没有钩子函数可以直接在组件挂载后调用。
等等!!!钩子函数?! 冷静一下(calm down)既然没有钩子函数那就自己创造一个呗
利用
Promise
对象的async await
语法糖,结合小程本身的全局对象getApp().globalData
,我们做了个简易的钩子函数(甚至可能不算是个钩子函数)。
- 我们先创建一个对象
Confirm
, - 在
Confirm
的constructor
中声明resolve
、reject
、promise
。 - 将其挂载于
getApp().globalData
上 - 当组件的生命周期执行到
mounted
时 - App.vue中获取最新协议的方法
await
这个Promise
- 当
resolve
后触发弹窗
思路介绍清楚了,我们来看下demo代码。
// service.js
export class Confirm {
constructor() {
this.resolve = () => {
// do something
};
this.reject = () => {
// do something
};
this.promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
}
}
// App.vue
import { Confirm } form '../utils/service.js';
globalData: {
appConfirm: new Confirm(),
},
// tipsWindows.vue
mounted() {
getApp().globalData.appConfirm.resolve();
},
// tipsWindows.vue
async alertPolicy() {
await getApp().globalData.appConfirm.promise;
// do something
}
最后
In the end,这个问题肯定有更好的解决方案,但这是目前我所能想到比较好的方案了。在使用这个方案的时候,发现结合上Proxy
的话可以延展出更多的玩法,比如Proxy
弹窗的open方法,在每次弹出前判断是否为重复提示等等。