前提说明
小程序app.js的globalData中定义了userInfo属性,并且在首页和我的tab中引用了,当在其它页面更新userInfo后,在首页和我的中引用的userInfo未更新。
解决思路
利用发布-订阅的设计模式,app.js中的userInfo用Object.defineProperty实现数据劫持,当监听到userInfo值改变时,通知每个订阅者。在首页和我的页面调用app.js中的订阅方法,将更新数据的方法追加到userInfo的订阅者列表中。
实现代码
// app.js
onLaunch: async function () {
this.initObserve();
},
// 监听globalData中属性变化
initObserve() {
const obj = this.globalData;
const keys = ['userInfo'];
keys.forEach(key => {
let value = obj[key];
obj[`${key}SubscriberList`] = [];
Object.defineProperty(obj, key, {
configurable: true,
enumerable: true,
set(newValue) {
obj[`${key}SubscriberList`].forEach(watch => {
watch(newValue);
});
value = newValue;
},
get() {
return value;
}
});
});
},
// 订阅globalData中某个属性变化
subscribe(key, watch) {
watch(this.globalData[key]);
this.globalData[`${key}SubscriberList`].push(watch);
},
// 首页和我的page页
onLoad() {
app.subscribe('userInfo', (userInfo) => {
this.setData({
userInfo,
});
});
},
遇到的问题
小心Object.defineProperty中的set方法死循环导致栈溢出。在set用obj[key] = value时将会导致死循环,因为给属性赋值后,会再次调用set方法。解决的办法是利用闭包的原理,定义临时变量为obj[key],在set方法中对临时变量赋值。或者在obj中声明一个变量的副本,set中对变量副本赋值,get中返回变量副本。
这个还需要考虑页面退出之后 取消订阅的处理
app.subscribe(‘userInfo’, (userInfo) => {
this.setData({
userInfo,
});
});,
所以可以考虑在相应的页面改成一个有名的函数替代 然后自己在实现一个app.unsubscribe方法去${key}SubscriberList注册的这个数组里面去搜索找到指定的方法,找到了给删除就行了。 或者继续用匿名箭头函数但是在每个页面注册的时候用这个页面的route作为key去维护,取消订阅的时候直接在onUnload里面根据这个页面的route的去找到相应的数组删除也可以。
解决了,谢谢楼主
const keys = ['userInfo']; //我用的下面这句代替的 const keys = Object.keys(this.globalData)
obj[`${key}SubscriberList`] = [];
您好请教下,这一句用的是什么语法,我js不是非常熟,``、$、{key},分别是什么语法啊?十分感谢!