Vue双向绑定是怎么实现的?

用了Vue长达2年,如果自己去实现一个双向绑定,我可能一个字母都写不出来,是时候探究一下了。

先看data里某对象的输出

 data() {
    return {
      pagination: {
        layout: 'prev,pager,next,jumper,sizes,->, total',
        pageSizes: [10, 20, 30, 40],
        currentPage: 1,
        pageSize: 10,
        total: 0
      }
    }
  },

我们可以看到对象属性都有有两个相对应的get和set方法,为什么会多出这两个方法呢?因为vue是通过Object.defineProperty()来实现数据劫持的。

问题来了,什么是Object.defineProperty()

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。注意:此方法是ES6新增的属性,而IE8不支持ES6语法 所以vue2.0不支持IE8浏览器。

关于es6新增的Object.defineProperty()方法是如何使用,可见

ES6-强大的对象方法 Object.defineProperty() - 简书

以下是本人根据别人的实例敲出来的实例子,自己动下手,加深下印象。

<script>
	  let obj = {
            id: 1,
            pname: '小米',
            price: 1222
        }
function displayDate(){
	Object.defineProperty(obj, 'price', {
        value: 1999
    })

    console.log(obj);
    Object.defineProperty(obj, 'count', {
        value: 100
    })
	console.log(obj) 
	 Object.defineProperty(obj, 'id', {

        // writable修改后值是否可以被重写  默认是false

        writable: false

    })

    obj.id = 3 //重写无效

    console.log(obj);

    Object.defineProperty(obj, 'address', {

        value: '山东',

        // enumerable参数,设置是否可以被遍历 默认是false,通过该方法添加的默认都是false不会被遍历

        enumerable: false,

        // configurable参数,默认为false。不允许删除或修改

        configurable: true //值为false时,即为不可删除或修改

    })
console.log(obj)
    console.log(Object.keys(obj));  //取对象属性的方法,即遍历,因为address设置了不可被遍历
    delete obj.address
    console.log(obj);

}
</script>

结果如下

自己动手,印象果然一下就深刻了。

再来一个示例

<script>
	 let obj = {
            id: 1,
            pname: '小米',
            price: 1222
        }
function displayDate(){
	Object.defineProperty(obj,'pname',{
    get:function(){
        //当读取obj的pname的属性值的时候,会触发这个函数
        console.log('get方法被调用了');
        //return 的值,就是属性值
        return pname;
    },
    set:function(val){
        //set可以接收一个参数,就是你想赋的值
        //当设置obj的pname的属性值的时候,会触发这个函数
        console.log('set方法被调用了');
        //可以通过下面方法赋值,text变量就是obj的text的属性值
        pname = val;
    }
})
obj.pname = '华为';
console.log(obj.pname)

}
</script>

 

写到这里,聪明的你应该是知道如何实现VUE的双向绑定了,是的,它就是利用了2015年ES6标准的Object.defineProperty()方法。

手动实现一个VUE双向绑定,思路:用Object.defineProperty()的set值向input填充,用input的oninput事件触发set事件。代码如下。

var model = {};
Object.defineProperty(model,'txt',{
    get:function(){},
    set:function(val){
        var span = document.getElementsByTagName('span')[0];
        span.innerHTML = val;
    }
})
var input = document.getElementsByTagName('input')[0];
    input.oninput = function(){
    model.txt = input.value;//必然触发set函数
  }

至此,简易版的VUE双向绑定如何实现的大概思路就出来了,但我们的尤雨溪大神,肯定不会这样写。一个流行框架的诞生,所需技术与思想,不是我们常人所能达到的。

双向绑定的完整实现思路是:

我们已经知道实现数据的双向绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。接着,我们还需要有一个指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令对应初始化成一个订阅者Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者Watcher接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。

1.实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。

2.实现一个订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图。

3.实现一个解析器Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器。

图解

1.实现一个Observer

Observer是一个数据监听器,其实现核心方法就是前文所说的Object.defineProperty( )。如果要对所有属性都进行监听的话,那么可以通过递归方法遍历所有属性值,并对其进行Object.defineProperty( )处理。如下代码,实现了一个Observer。

<script>
	function defineReactive(data, key, val) {
    observe(val); // 递归遍历所有子属性
    Object.defineProperty(data, key, {
        enumerable: true,
        configurable: true,
        get: function() {
            return val;
        },
        set: function(newVal) {
            val = newVal;
            console.log('属性' + key + '已经被监听了,现在值为:“' + newVal.toString() + '”');
        }
    });
}
 
function observe(data) {
    if (!data || typeof data !== 'object') {
        return;
    }
    Object.keys(data).forEach(function(key) {
        defineReactive(data, key, data[key]);
    });
};
 
var library = {
    book1: {
        name: ''
    },
    book2: ''
};
observe(library);
library.book1.name = 'vue权威指南'; // 属性name已经被监听了,现在值为:“vue权威指南”
library.book2 = '没有此书籍';
</script>

后继observer的改造,订阅者,解析器的实现本人就不研究了,略过就可以了,如有兴趣传送门。

总结:
在new Vue的时候,在 Observer 中通过 Object.defineProperty() 达到数据劫持,代理所有数据的 getter 和 setter 属性,在每次触发 setter 的时候,都会通过 Dep 来通知Watcher,Watcher 作为Observer数据监听器与Compile模板解析器之间的桥梁,当 Observer 监听到数据发生改变的时候,通过 Updater 来通知 Compile 更新视图,而 Compile 通过 Watcher 订阅对应数据,绑定更新函数,通过 Dep 来添加订阅者,达到双向绑定。

vue的双向绑定原理及实现_深海里搁浅的猫的博客-CSDN博客_vue实现双向数据绑定原理

Vue 是如何实现数据双向绑定的?_吖旭玲的博客-CSDN博客_vue如何实现数据双向绑

​​​​​​vue2核心对象defineProperty_明哥前端的博客-CSDN博客

ES6:Object.defineProperty方法_anyw_的博客-CSDN博客

ES6-强大的对象方法 Object.defineProperty() - 简书

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值