Skip to content

JS 全局监听 globalData 属性变化

Published: at 09:42 AMSuggest Changes

问题

需要监听小程序的 globalData 的某个属性变化,当变化时,执行某个函数

除了 app.js 中要监听,其他页面也要监听。

解决

app.js 文件

App({
  onLaunch: function () {},
  onShow: function () {},
  onHide: function () {
    // wx.stopLocationUpdate()
  },
  watch: function (key, method) {
    var obj = this.globalData;
    // 加个前缀生成隐藏变量,防止死循环发生
    let ori = obj[key]; // obj[key] 这个不能放在 Object.defineProperty 里
    if (ori) {
      //处理已经声明的变量,绑定处理
      method(ori);
    }
    Object.defineProperty(obj, key, {
      configurable: true, // 属性是否可以被删除或者可否被重新定义特性
      enumerable: true, // 是否可以被枚举,简单讲就是在使用 Object.keys() 时,是否能拿到键值
      set: function (value) {
        this['_' + key] = value;
        console.log('是否会被执行 2');
        method(value);
      },
      get: function () {
        // 在其他界面调用 key 值的时候,这里就会执行。
        if (typeof this['_' + key] == 'undefined') {
          if (ori) {
            //这里读取数据的时候隐藏变量和 globalData 设置不一样,所以要做同步处理
            this['_' + key] = ori;
            return ori;
          } else {
            return undefined;
          }
        } else {
          return this['_' + key];
        }
      },
    });
  },
  globalData: {
    count: 0,
  },
});

其他页面

const app = getApp();
Page({
  /**
   * 页面的初始数据
   */
  data: {
    count: 0,
  },
  //
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    app.watch('count', (v) => {
      that.setData({
        count: v,
      });
    });
  },
});

后记

分析一下下面的代码,和上面的代码想比,它有什么问题,为什么?

// 监听全局变量变化
  watch: function (variate, method) {
    var obj = this.globalData;
    let val = obj[variate];// 单独变量来存储原来的值
    Object.defineProperty(obj, variate, {
      configurable: false,
      enumerable: true,
      set: function (value) {
        val = value;// 重新赋值
        method(variate,value);// 执行回调方法
      },
      get: function () {
        // 在其他界面调用 getApp().globalData.variate 的时候,这里就会执行。
        return val; // 返回当前值
      }
    })
  },

小心 Object.defineProperty 中的 set 方法死循环导致栈溢出。在 setobj[key] = value 时将会导致死循环,因为给属性赋值后,会再次调用 set 方法。解决的办法是利用闭包的原理,定义临时变量为 obj[key],在 set 方法中对临时变量赋值。或者在 obj 中声明一个变量的副本,set 中对变量副本赋值,get 中返回变量副本。

还有一段代码写的不错,可以参考一下

// 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,
        });
    });
},

参考文章


Previous Post
JS 监听 DOM 属性变化
Next Post
ElementUI 禁用浏览器自动填充用户名密码