双向绑定基础原理——Object.defineProperty()的使用


1、极简双向绑定示例

前端有关VUE的笔试和面试中,观察订阅者模式和VUE中双向绑定的原理这两道题几乎是必考的了。

Vue双向绑定vm视图模型简单来说就是利用了Object.defineProperty(),通过劫持setter,实现model到view,view到model则是一堆事件监听 输入框的input,选择组件的 change等等。具体复杂些的实现就要看观察订阅设计模式了。

核心是通过Object.defineProperty() + addEventListener,对data的每个属性进行了get、set的拦截。其实只用Object.defineProperty()已经可以实现双向绑定,只是这样做效率很低。

1.1 Object.defineProperty()

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。该方法有三个参数,(属性所在的对象,你要操作的属性,被操作的属性的特性),示例如下。

// 示例
let _xxObj = {}

Object.defineProperty(_xxObj, 'xx_val', {
    get: function (_n) {
          console.log('读取时get被触发')
        	// 可以在这里操作
          return _n;
        }
})

1.2 实现过程

1.2.1 单个DOM与单个Obj

这里定义了一个input框,一个用来显示值的span块,并定义了一个名为_xxObj的Object,现在利用Object.defineProperty() + addEventListener 来实现input中的value与该xxObj中的一个名为xx_val的属性的绑定。原理大致如下图。

img

代码如下。

 <body>
    <h1>极简双向绑定</h1>
    <input id="id_input_01" type="text"/>
    <h3>当前xx_value的值:<span id="id_text_01"></span></h3>

    <script>
      // -------------------获取元素--------------------------
      let eInput01 = document.getElementById("id_input_01");
      let eText01 = document.getElementById("id_text_01");
      
      // 定义一个obj
      let _xxObj = {
        xx_val: null,  // 这里的定义xx_val将会被之后的defineProperty()修改
      };

      // ---------------往Obj里写修改Dom的操作-------------------
      /* 
      Object.defineProperty() 方法会直接在一个对象上定义一个新属性,
      或者修改一个对象的现有属性,并返回此对象。
      */
      Object.defineProperty(_xxObj, "xx_val", {
        get: function () {
          console.log('读取时,get被触发')
        },
        set:function(_n){
          console.log("写入时,set被触发")

          // 直接在这里操作DOM 相当于在这里单向绑定了一次 Obj->DOM
          // _xxObj -> eInput01 eText01
          eInput01.value = _n;
        
          // 这里是单向绑定,用于查看当前value的值
          eText01.innerHTML = _n;
        }
      });
            
      // -----------------给DOM绑定监听事件----------------------------  
      eInput01.addEventListener("keyup", function(e){
        console.log(e.target.value);

        // 在这里操作Obj,相当于反向绑定了一次 eInput01->_xxObj
        _xxObj.xx_val = e.target.value;
        
      })
      

    </script>
  </body>

效果图如下。

img

1.2.2 两个DOM与单个Obj

现在新增加一个id为"id_input_02"的input框,并与_xxObj.xx_val双向绑定,示意图如下。

img

代码如下。

 <body>
    <h1>极简双向绑定</h1>
    <input id="id_input_01" type="text"/>
    <h3>当前xx_value的值:<span id="id_text_01"></span></h3>
    <input id="id_input_02" type="text"/>

    <script>
      // -------------------获取元素--------------------------
      let eInput01 = document.getElementById("id_input_01");
      let eInput02 = document.getElementById("id_input_02");
      let eText01 = document.getElementById("id_text_01");
      
      // 定义一个obj
      let _xxObj = {
        xx_val: null,  // 这里的定义xx_val将会被之后的defineProperty()修改
      };

      // ---------------往Obj里写修改Dom的操作-------------------
      /* 
      Object.defineProperty() 方法会直接在一个对象上定义一个新属性,
      或者修改一个对象的现有属性,并返回此对象。
      */
      Object.defineProperty(_xxObj, "xx_val", {
        get: function (_n) {
          console.log('读取时get被触发')
          return _n;
        },
        set:function(_n){
          console.log("写入时set被触发")
          
          // 直接在这里操作DOM 相当于在这里单向绑定了一次 Obj->DOM
          // _xxObj -> eInput01 eInput02 eText01
          eInput01.value = _n;
          eInput02.value = _n;

          // 这里是单向绑定,用于查看当前value的值
          eText01.innerHTML = _n;
        }
      });
            
      // -----------------给DOM绑定监听事件----------------------------  
      eInput01.addEventListener("keyup", function(e){
        console.log(e.target.value);

        // 在这里操作Obj,相当于反向绑定了一次 eInput01->_xxObj
        _xxObj.xx_val = e.target.value;
      })

      eInput02.addEventListener("keyup", function(e){
        console.log(e.target.value);

        // 在这里操作Obj,相当于反向绑定了一次 eInput02->_xxObj
        _xxObj.xx_val = e.target.value;
      })

    </script>
  </body>

实现效果如下,这两个输入框都和_xxObj.xx_value完成了双向绑定。

img

2、思考

当有多个DOM和多个Obj需要双向绑定的时候呢?

img

上面这种模式有什么问题?

答案显而易见,每当要新增加一个Obj和DOM的双向绑定时,都要到对应的Obj定义和DOM定义的地方新加代码。涉及到的DOM和Obj多了,维护起来就变得非常麻烦,而且代码也变得很不易读,是不符合“高内聚,低耦合”的原则的。于是,需要引入观察-订阅者模式来简化代码。

阅者模式在双向绑定中扮演了什么角色呢?它让双向绑定更有效率!

秉持着文章“简洁”的原则,这篇文章我就只介绍到这里,之后我会专门写一篇有关观察订阅者模式和一篇详解vue双向绑定原理的文章…敬请期待。

另外,值得注意的是,观察者模式和订阅发布模式是有一些差别的…
规律大致为:Publishers + Subscribers = Observer Pattern
在这里插入图片描述

3.参考资料

剖析Vue原理&实现双向绑定MVVM

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值