logo头像
Snippet 博客主题

vue数组双向绑定的实现原理

本文于1102天之前发表,文中内容可能已经过时。

原理解析

  • 新增一个缓存对象(arrayMethods),通过Object.defineProperty方法给该对象新增(push,pop,shift,splice等属性),这些属性的值通过apply的方式复制了Array.prototype上面对应的方法,然后调用修改视图层的方法。
  • 将数组的proto属性指向于arrayMethods
  • 这样做的好处是,当数组改变(比如push)的时候会调用arrayMethods上面push方法,在这个方法的实现上面其实做了2件事情,第一件给该数组新增值,第二件事情就是通知视图层去更新。这样就做到了双向绑定。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 需要监听变化的数组
let arr = []
// 通过Object.create新增一个缓存对象arrayMethods
const arrayProto = Array.prototype
const arrayMethods = Object.create(arrayProto)
// Array.prototype的方法集合
const fnArr = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
// 给arrayMethods添加fnArr的各个属性
fnArr.forEach(item => {
Object.defineProperty(arrayMethods, item, {
value: function(){
// 继承数组的方法
const original = arrayProto[item]
const args = Array.prototype.slice.call(arguments)
original.apply(this, args)
// 通知视图层去更新
const ob = this.__ob__
ob.dep.notify()
}
})
})
// 将需要变化的数组的__proto__指向于arrayMethods
arr.__proto__ = arrayMethods
// 对于不支持__proto__的浏览器,直接新增push、pop等属性,这些属性的值为arrayMethods上面对象的方法
fnArr.forEach(item => {
Object.defineProperty(arr, item, {
value: arrayMethods[item]
})
})