评论

小程序app.globalData属性值改变时其它页面的引用响应更新

利用发布-订阅设置模式和Object.defineProperty实现当app.js中globalData的属性值更改后,其它引用页面的值同步更新

前提说明

    小程序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中返回变量副本。

最后一次编辑于  2021-01-16  
点赞 12
收藏
评论

4 个评论

  • 阿旺
    阿旺
    2021-12-16

    这个还需要考虑页面退出之后 取消订阅的处理

    2021-12-16
    赞同 1
    回复 2
    • Z、k
      Z、k
      2022-01-28
      请问该怎样做取消订阅的处理呢
      2022-01-28
      回复
    • 阿旺
      阿旺
      2022-02-28回复Z、k
      就如楼主所用,添加注册的时候用${key}SubscriberList一个数组维护起来了,因为,楼主在用的是一个匿名的箭头函数 :
      app.subscribe(‘userInfo’, (userInfo) => {
              this.setData({
                  userInfo,
              });
          });,
      所以可以考虑在相应的页面改成一个有名的函数替代 然后自己在实现一个app.unsubscribe方法去${key}SubscriberList注册的这个数组里面去搜索找到指定的方法,找到了给删除就行了。 或者继续用匿名箭头函数但是在每个页面注册的时候用这个页面的route作为key去维护,取消订阅的时候直接在onUnload里面根据这个页面的route的去找到相应的数组删除也可以。
      2022-02-28
      回复
  • 天道酬勤
    天道酬勤
    2023-09-02

    解决了,谢谢楼主

    2023-09-02
    赞同
    回复
  • One Eight Nine Four
    One Eight Nine Four
    2021-07-05
    const keys = ['userInfo'];  
    //我用的下面这句代替的
    const keys = Object.keys(this.globalData)
    
    2021-07-05
    赞同
    回复
  • Redwings
    Redwings
    2021-03-25
    obj[`${key}SubscriberList`] = [];
    


    您好请教下,这一句用的是什么语法,我js不是非常熟,``、$、{key},分别是什么语法啊?十分感谢!

    2021-03-25
    赞同
    回复 4
    • vteso
      vteso
      2021-03-27
      百度一下,模板语法
      2021-03-27
      1
      回复
    • Redwings
      Redwings
      2021-04-09回复vteso
      感谢解答!
      2021-04-09
      回复
    • 慌然一梦
      慌然一梦
      2021-05-18
      应该是百度“模板字符串”
      2021-05-18
      1
      回复
    • 法见
      法见
      07-09
      搜rxjs学习吧
      07-09
      回复
登录 后发表内容