-
Notifications
You must be signed in to change notification settings - Fork 19
Open
Labels
Description
刚开始学习vue的时候,之前总是听别人说vue实现了双向绑定,主要凸显就是表单那里,后来在学习完vue了以后,我发现vue实现的双向绑定的效果react也能实现实时更新视图的效果,也就一直陷入了react也是双向绑定的怪圈。后来看了和两篇博客之后也对双向绑定有了进一步的理解
对于如何实现vue的双向绑定,网上已经有很多的博客去介绍了,大致思路就是依靠Object.defineProperty来在set和get里面去执行回调函数,虽然网上已经有了很多的博客,但还是要自己去实现一遍才能深入理解这其中的问题。
- 首先是如果变量是一个基本数据类型,那么它是基于值进行传递的,我们可以直接进行Object.defineProperty
- 如果变量是一个对象类型的话,那么我们需要做一次递归
基于此,我们就可以检测一个普通对象的变化,但是做到这里,我们却没有办法对数组进行监测,在vue中,如果你是将一个值push进入数组中,vue是可以在页面上实时更新的,但你用arr[3] = 4这种方式去更新数组时,数组确实得到了改变,但是却不能更新视图。也就是说当我们调用Array.prototype里面的方法时,他才会进行数据的双向绑定。当然我们可以重新写Array.prototype.push = funvction(){},但是这对于用这个框架的人而言就是一件很恶心的事情了,而且速度也没法和原生的进行比较。一个数组对象的__proto__本身是应该指向Array.prototype,但是在这中间,我们可以再加一层,也就是fakePrototype,我们在这个里面去调用原生的Array.prototype.
具体代码实现如下
const OP = Object.prototype
const oam = ['push', 'pop']
class observer {
constructor(obj, callback) {
if (OP.toString.call(obj) !== '[object Object]') {
console.error(obj + ' is not object')
}
this.$callback = callback
this.observe(obj)
}
observe(obj) {
if (OP.toString.call(obj) === '[object Array]') {
this.overwriteArray(obj)
}
Object.keys(obj).forEach(function(key,index,array){
let val = obj[key]
Object.defineProperty(obj, key, {
get: function() {
console.log('get', val)
return val
},
set: (function(newVal) {
console.log('set', this)
this.$callback(val)
val = newVal
}).bind(this)
})
if (OP.toString.call(obj[key]) === '[object Object]' || OP.toString.call(obj[key]) === '[object Array]')
{
this.observe(obj[key])
}
},this)
}
overwriteArray(array) {
let original = Array.prototype,
result,
self = this,
overwrite = Object.create(Array.prototype)
Object.keys(oam).forEach(function(key,index,array) {
let method = oam[index],
oldVal = []
Object.defineProperty(overwrite, method, {
value: function () {
oldVal = this.slice(0)
let arg = [].slice.apply(arguments)
result = original[method].apply(this, arg)
self.observe(this)
self.$callback(this)
return result
},
writable: true,
enumerable: true,
configurable: true
})
},this)
console.log('overwrite', overwrite)
array.__proto__ = overwrite
}
}