拓展:数据的双向绑定原理

主要是通过observe函数对所有数据遍历,同时加上set,get方法,这样就能通过Object.defineProperty()劫持set、get。通过解析器对数据解析, 初始化之后会渲染页面,同时添加Watcher订阅者监听数据的变化.一旦数据变化就会立即收到通知,并且更新视图—(ps:Watch里面会在实例化的Dep中通过notice通知,从而调用update()触发数据更新)

不监控到数组下标的变化,导致通过数组下标添加元素,不能实时响应

一、因为数组的位置不固定,数量多变,正常对象key对应value一般不会变,但是如果数组删除了某个元素,比如第一个元素被删除或者头部增加一个元素,那么将导致后面所有的key对应value错位,如果6个元素,也就会触发5次set。
二、数组元素可能非常非常多,每个元素进行劫持有一定浪费,这可能是Evan you对性能的考虑。
三、Vue将数组的7个变异方法进行了重写,也就是更改了Array原型上的方法达到劫持变化。

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
const arr = [1, 2, 3, 4, 5, 6]
for (let key in arr) {
let value = arr[key]
Object.defineProperty(arr, key, {
get() {
console.log(`get: ${key}`)
return value
},
set(newValue) {
console.log(`set: ${key} to ${newValue}`)
return value = newValue
}
})
}

arr[0] = 1 // 打印:set:0 to 1
arr[3] // 打印:get:3
arr.shift() // 会导致5次前移,所以产生5次get和5次set
/*
get: 1
set: 0 to 2
get: 2
set: 1 to 3
get: 3
set: 2 to 4
get: 4
set: 3 to 5
get: 5
set: 4 to 6
*/

Proxy虽然是劫持的整个对象,但也是浅层劫持,属性值是对象时同样也需要深度遍历,不然该属性对象的变化也无法监测到。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

const obj = {
count: 5,
user: { name: 'JavaScript', age: 22 }
}

const proxyObj = new Proxy(obj, {
get(target, key) {
console.log(`get:${key}`)
return target[key]
},
set(target, key, value) {
console.log(`set:${key} to ${value}`)
return target[key] = value
}
})

proxyObj.user.name = 'JenkinWoo'
/*
get:user
*/

当递归进行深度拦截

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

function deepProxy(obj) {
return new Proxy(obj, {
get(target, key) {
console.log(`get:${key}`)
if (typeof target[key] === 'object' && target[key] !== null) {
return deepProxy(target[key]) // 递归劫持
}
return target[key]
},
set(target, key, value) {
console.log(`set:${key} to ${value}`)
return target[key] = value
}
})
}
const obj = {
count: 5,
user: { name: 'JenkinWoo', age: 22 }
}

const proxyObj = deepProxy(obj)
proxyObj.user.name = 'JenkinWoo'
/*
get:user
set:name to JenkinWoo
*/

所以为什么proxy优于Object.defineProperty?

从以上的例子就能看到,Object.defineProperty必须“预先”劫持属性。被劫持的属性才会被监听到。所以后添加的属性,需要手动再次劫持。

而proxy代理了整个对象,不需要预先劫持属性,而是在获取/修改的时候,通过get/set方法来告诉你key。所以不管如何新增属性,总是能被捕获到。