用vue的双向绑定简单实现一个todo-list的示例代码

2020-06-14 06:23:57易采站长站整理
数据变动--->视图更新
这个方向的绑定。

1. 数据劫持

不妨让我们自己思考一下,如何实现数据变动,对应绑定数据的视图就更新呢?

答案还是object.defineProperty,通过object.defineProperty遍历设置this.data里面所有属性,在每个属性的setter里面去通知对应的回调函数,这里的回调函数包括dom视图重新渲染的函数、使用$watch添加的回调函数等,这样我们就通过object.defineProperty劫持了数据,当我们对数据重新赋值时,如

this.title = 'hello vue'
,就会触发setter函数,从而触发dom视图重新渲染的函数,实现数据变动,对应视图更新。

2. 发布-订阅模式

那么问题来了,我们如何在setter里面触发所有绑定该数据的回调函数呢?

既然绑定该数据的回调函数不止一个,我们就把所有的回调函数放在一个数组里面,一旦触发该数据的setter,就遍历数组触发里面所有的回调函数,我们把这些回调函数称为订阅者。数组最好就定义在setter函数的最近的上级作用域中,如下面实例代码所示。


Object.keys(this.data).forEach(function(key) {
var subs = []; // 在这里放置添加所有订阅者的数组
Object.defineProperty(this.data, key, { // this.data.title
enumerable: false,
configurable: true,
get: function getter () {
console.log('访问数据啦啦啦')
return this.data[key]; //返回对应数据的值
},
set: function setter (newVal) {
if (newVal === this.data[key]) {
return; // 如果数据没有变动,函数结束,不执行下面的代码
}
this.data[key] = newVal; //数据重新赋值

subs.forEach(function () {
// 通知subs里面的所有的订阅者
})
}
});
}

那么问题又来了,怎么把绑定数据的所有回调函数放到一个数组里面呢?

我们可以在getter里面做做手脚,我们知道只要访问数据就会触发对应数据的getter,那我们可以先设置一个全局变量target,如果我们要在data里面title属性添加一个订阅者(changeTitle函数),我们可以先设置target = changeTitle,把changeTitle函数缓存在target中,然后访问this.title去触发title的getter,在getter里面把target这个全局变量的值添加到subs数组里面,添加完成后再把全局变量target设置为null,以便添加其他订阅者。实例代码如下:


Object.keys(this.data).forEach(function(key) {
var subs = []; // 在这里放置添加所有订阅者的数组
Object.defineProperty(this.data, key, { // this.data.title
enumerable: false,