验证码: 看不清楚,换一张 查询 注册会员,免验证
  • {{ basic.site_slogan }}
  • 打开微信扫一扫,
    您还可以在这里找到我们哟

    关注我们

JS监听变量改变如何实现

阅读:750 来源:乙速云 作者:代码code

JS监听变量改变如何实现

      需求和背景

      在业务中,由于项目采用微前端架构,需要通过A应用的某个值的变化对B应用中的DOM进行改变(如弹出一个Modal),第一个想到的可能是发布订阅模式,其实不如将问题缩小化,采用原生的能力去解决。

      下面给出两种解决方案,同时也是尤大写Vue时的思路

      • ES5 的 Object.defineProperty

      • ES6 的 Proxy

      Object.defineProperty

      Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象

      ——MDN

      用法如下:

      Object.defineProperty(obj, prop, option)

      入参用法:

      • obj:代理对象;

      • prop:代理对象中的key;

      • option:配置对象,getset都在其中配置;

      例子:

      var obj = { 
        name: 'sorryhc' 
      }
      var rocordName = 'sorryhc';
      
      Object.defineProperty(obj, 'name', {
        enumerable: true,
        configurable:true,
        set: function(newVal) {
            rocordName = newVal 
            console.log('set: ' + rocordName)
        },
        get: function() {
            console.log('get: ' + rocordName)
            return rocordName
        }
      })
      
      obj.name = 'sorrycc' // set: sorrycc
      console.log(obj.name) // get: sorrycc

      对一个对象进行整体响应式监听:

      // 监视对象
      function observe(obj) {
        // 遍历对象,使用 get/set 重新定义对象的每个属性值
         Object.keys(obj).forEach(key => {
             defineReactive(obj, key, obj[key])
         })
      }
      
      function defineReactive(obj, k, v) {
         // 递归子属性
         if (typeof(v) === 'object') observe(v)
         
         // 重定义 get/set
         Object.defineProperty(obj, k, {
             enumerable: true,
             configurable: true,
             get: function reactiveGetter() {
                 console.log('get: ' + v)
                 return v
             },
             // 重新设置值时,触发收集器的通知机制
             set: function reactiveSetter(newV) {
                 console.log('set: ' + newV)
                 v = newV
             },
         })
      }
      
      let data = {a: 1}
      // 监视对象
      observe(data)
      data.a // get: 1
      data.a = 2 // set: 2

      整体思路就是遇到子对象就递归,和深拷贝一样的读参顺序。

      缺陷

      如果学习过Vue2源码的同学可能比较熟,基于下面的缺陷,也是出现了$set$get的用法。

      • IE8 及更低版本 IE 是不支持的

      • 无法检测到对象属性的新增或删除

      • 如果修改数组的 length ( Object.defineProperty 不能监听数组的长度),以及数组的 push 等变异方法是无法触发 setter 的

      Proxy

      Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)

      — MDN

      const obj = new Proxy(target, handler)

      其中:

      • target :要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)

      • handler :一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 obj 的行为

      例子

      const handler = {
        get: function(target, name){
            return name in target ? target[name] : 'no prop!'
        },
        set: function(target, prop, value, receiver) {
            target[prop] = value;
            console.log('property set: ' + prop + ' = ' + value);
            return true;
        }
      };
      
      var user = new Proxy({}, handler)
      user.name = 'sorryhc' // property set: name = sorryhc
      
      console.log(user.name) // sorryhc
      console.log(user.age) // no prop!

      并且Proxy提供了更丰富的代理能力:

      • getPrototypeOf / setPrototypeOf

      • isExtensible / preventExtensions

      • ownKeys / getOwnPropertyDescriptor

      • defineProperty / deleteProperty

      • get / set / has

      • apply / construct

      感兴趣的可以查看 MDN ,一一尝试一下,这里不再赘述

      在React中的实践

      这里展示两段伪代码,大概业务流程是,当点击页面某个按钮(打开/关闭弹窗),触发window.obj.showModal的切换,从而被监听到全局变量的变化,从而改变React中的state状态,最终触发Modal的弹窗。

      Object.defineProperty

      window.obj = {
        showModal: false
      }
      
      const [visible, setVisible] = useState(false);
      
      useEffect(() => {
        visible && Modal.show({
          // ...
        })
      }, [visible])
      
      Object.defineProperty(window.obj, 'showModal', {
        enumerable: true,
        configurable:true,
        set: function(newVal) {
          setVisible(newVal);
            console.log('set: ' + newVal)
        },
        get: function() {
            console.log('get: ' + visible)
            return visible
        }
      })
      
      window.obj.showModal = !window.obj.showModal // set: true
      console.log(window.obj.showModal) // get: true

      Proxy

      const [visible, setVisible] = useState(false);
      
      useEffect(() => {
        visible && Modal.show({
          // ...
        })
      }, [visible])
      
      const handler = {
        get: function(target, name){
            return name in target ? target[name] : 'no prop!'
        },
        set: function(target, prop, value, receiver) {
            target[prop] = value;
            setVisible(value);
            console.log('property set: ' + prop + ' = ' + value);
            return true;
        }
      };
      
      window.obj = new Proxy({showModal: false}, handler)
      window.obj.showModal = !window.obj.showModal // property set: showModal = true
      
      console.log(window.obj.showModal) // true
    分享到:
    *特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们: hlamps#outlook.com (#换成@)。
    相关文章
    {{ v.title }}
    {{ v.description||(cleanHtml(v.content)).substr(0,100)+'···' }}
    你可能感兴趣
    推荐阅读 更多>