简单易懂的双向数据绑定解读

简单易懂的双向数据绑定解读html

数据更新视图的重点是如何知道数据变了,只要知道数据变了,那么接下去的事都好处理。如何知道数据变了,就是经过Object.defineProperty( )对属性设置一个set函数,当数据改变了就会来触发这个函数,因此咱们只要将一些须要更新的方法放在这里面就能够实现data更新view了前端

实现一个Observeres6

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

Object有一个名为defineProperty的方法,能够设置访问器属性,好比:dom

var obj = {}
Object.defineProperty(obj, 'a', {
    get: function(){
      console.log('this is the getter')
      return 1
    },
    set: function(){
      console.log('change new value')
    }
})
// 获取对象的值
obj.a
// console.log(obj)
// 改变对象a的值
//obj.a = 1
复制代码

当咱们执行obj.a的时候会触发get函数,控制台会打印'this is the getter',当咱们为obj.a赋值的时候,obj.a=2;这是控制台会打印"change new value"你们能够把getter和setter理解成获取对象属性值和给对象属性赋值时的钩子就能够了。函数

双向数据绑定实现思路this

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

1.实现一个监听器Observer,用来劫持并监听全部属性,若是有变更的,就通知订阅者。prototype

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

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

流程图以下:

订阅者模式:订阅者模式也叫“订阅-发布者模式”,对于前端来讲这种模式简直无处不在,好比咱们经常使用的xx.addEventListener('click',cb,false)就是一个订阅者,它订阅了click事件,当在页面触发时,浏览器会做为发布者告诉你,能够执行click的回调函数cb了。

eg: https://jsbin.com/biyulaz/edit?html,output

实现一个Watcher

好比,士兵与长官就是一个订阅与发布者的关系,士兵的全部行动都经过长官来发布,只有长官发号施令,士兵们才能执行对应的行动。

// obj是一个发布者, 发布信息时调用dep的notify方法
obj = {
    pub: function(dep){
        dep.notify()
    }
}

//Dep是连接订阅者和发布者的一个桥梁, 订阅者将本身存入subs中, 发布者经过pub方法来通知subs中的每个订阅者
function Dep(){
    this.subs = []
}
Dep.prototype.notify = function () {
    // 通知订阅者, 并让它们执行update方法
    this.subs.forEach(item => {
        item.update()
    })
}

// 订阅者
function Watcher(msg) {
    this.msg = msg
}
Watcher.prototype.update = function () {
    console.log('dom' + this.msg + '更新');
}

// 建立三个订阅者
var a = new Watcher(1),
    b = new Watcher(2),
    c = new Watcher(3);

var dep = new Dep();

// 将三个订阅者push进Dep.subs中
dep.subs.push(a)
dep.subs.push(b)
dep.subs.push(c)

// 发布者发布
obj.pub(dep)
复制代码

在这段代码中,obj是发布者,Watcher实例是订阅者,Dep用来储存订阅者,以及接受发布者通知的一个媒介

proxy(es6)实现

var nameProxy = new Proxy({
			name: 'gaodeng'
		}, {
			get: function (target, key, value) {
				console.log('触发getter钩子函数, 值为: ', value)
				return target[key];
			},
			set: function (target, key, value) {
				console.log('触发setter钩子函数, 值为: ', value)
				target[key] = value;
			}
		});
// 获取		
nameProxy.name
// 设置
nameProxy.name = '小明'
复制代码

最后一个完整的栗子: https://jsbin.com/yijorunepe/edit?html,output

相关文章
相关标签/搜索