Skip to content

Vue双向绑定的实现原理 #9

@Ray1993

Description

@Ray1993

我们都知道Vue的核心是视图组件化和 数据驱动,本篇主要是对Vue的双向绑定原理的进行学习。

实现数据绑定的做法有大致如下几种:

  • 发布者-订阅者模式(backbone.js)

  • 脏值检查(angular.js)

  • 数据劫持(vue.js)

数据劫持: vue.js 采用数据劫持的方式,结合发布者-订阅者模式,通过 Object.defineProperty() 来劫持各个属性的setter,getter以监听属性的变动,在数据变动时发布消息给订阅者,触发相应的监听回调。

首先我们先来看看Object.defineProperty()怎么用:

语法:

Object.defineProperty(obj, prop, descriptor)

参数说明:

obj:必需。目标对象
prop:必需。需定义或修改的属性的名字
descriptor:必需。目标属性所拥有的特性

返回值:

传入函数的对象。即第一个参数obj

例子:

    var a= {}
    Object.defineProperty(a,"b",{
      value:123
    })
    console.log(a.b);//123

前两个参数不多说了,一看代码就懂,主要看第三个参数descriptor,看看有哪些取值

descriptor

  • value : 属性的值(不用多说了)
  • writable : 如果为false,属性的值就不能被重写,只能为只读了
  • enumerable : 是否能在for...in循环中遍历出来或在Object.keys中列举出来。
  • configurable : 总开关,一旦为false,就不能再设置他的(value,writable,configurable)
  • get : 一会细说
  • set 一会细说
    注意了,当我们只设置了value的值的时候,其他特性的值都默认为false,上面的代码例子可以理解为:
var a= {}
Object.defineProperty(a,"b",{
  value:123,
  writable:false,
  enumerable:false,
  configurable:false
})
console.log(a.b);//123

我们先具体来看看writable ,enumerable ,configurable

writable:
如果设置为fasle,就变成只读了。。

var a = {}; 
Object.defineProperty(a,"b", { 
    value : 123,
    writable : false });

console.log(a.b); // 打印 123
a.b = 25; // 没有错误抛出(在严格模式下会抛出,即使之前已经有相同的值)
console.log(a.b); // 打印 123, 赋值不起作用。

enumerable :
属性特性 enumerable 定义了对象的属性是否可以在 for...in 循环和 Object.keys() 中被枚举。

var a= {}
Object.defineProperty(a,"b",{
  value:123,
  enumerable:true
})
console.log(Object.keys(a));// ["b"]
for(var attr in a){
   console.log(attr)// b
}

//改为false

var a= {}
Object.defineProperty(a,"b",{
  value:123,
  enumerable:false //注意咯这里改了
})
console.log(Object.keys(a));// []
for(var attr in a){
   console.log(attr)// undefined
}

configurable:
总开关,第一次设置 false 之后,,第二次什么设置也不行了,比如说

var a= {}
Object.defineProperty(a,"b",{
  configurable:false
})
Object.defineProperty(a,"b",{
  configurable:true
})
//error: Uncaught TypeError: Cannot redefine property: b

这里要注意一下,上面讲的默认值,如果第一次不设置它,会帮你设置为false,所以再设置他,也会报错哦。。。

现在来看看属性descriptor剩下的两个方法setget

注意:当使用了get或set方法,不允许使用writable和value这两个属性。get或set不是必须成对出现,任写其一就可以。

var obj = {};
var initValue = 'hello';
Object.defineProperty(obj,"newKey",{
    get:function (){
        //当获取值的时候触发的函数
        return initValue;    
    },
    set:function (value){
        //当设置值的时候触发的函数,设置的新值通过参数value拿到
        initValue = value;
    }
});
//获取值
console.log( obj.newKey );  //hello

//设置值
obj.newKey = 'change value';

console.log( obj.newKey ); //change value

所以当我们设置了value的时候 我们获取值的时候 是直接获取我们自己设置的value;当我们设置get方法的时候,获取值的操作,是直接调用get里面的方法。

看完了上面的内容 我们利用数据劫持的方式,结合发布者-订阅者模式来实现一个极简的MVVM:

<body>
  <input   type="text"   id="input_value"/>
  <p  id="p_value"></p> 
</body>
<script>
   let obj = { };
  Object.defineProperty(obj, "a" ,{
      set:(value) => {
             document.getElementById('input_value').value = value ;
             document.getElementById('p_value').innerHTML = value ;     
      } 
  })
  document.addEventListener("keyup",(e) => {
            obj.a = e.target.value ;
  })
</script>

image

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions