Vue defineReactive

Vue defineReactive

核心原理

1
2
3
4
5
6
7
8
9
10
11
12
function defineReactive(target, key, value) {
Object.defineProperty(target, key, {
get() {
console.log('getter key = ', key)
return value
}, set(newV) {
console.log(`setter ${key} = ${value}`)
if (newV === value) return value = newV
return value
}
})
}

对于数组和对象有不同的处理方式, 外面套一层

1
2
3
4
5
6
7
8
9
10
11
function Observer(value) {
if (Array.isArray(value)) {
// } else {
this.walk(value)
}
}
Observer.prototype.walk = function (obj) {
for (let key in obj) {
defineReactive(obj, key, obj[key])
}
}

为什么要用构造函数, vue源码是这么写的,😜

如果对象的值不是基本类型, 就无法设置响应式, 所以需要再加一层

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 defineReactive(target, key, value) {
// console.log(value) observe(value)
Object.defineProperty(target, key, {
get() {
console.log('getter key = ', key)
return value
}, set(newV) {
console.log(`setter ${key} = ${value}`)
if (newV === value) return value = newV
return value
}
})
}
function Observer(value) {
if (Array.isArray(value)) {
// } else {
this.walk(value)
}
}
Observer.prototype.walk = function (obj) {
for (let key in obj) {
defineReactive(obj, key, obj[key])
}
}
function observe(value) {
if (typeof value !== 'object') return new Observer(value)
}

基本类型变对象的处理

如果一个key原来的value是基本类型,新设置的value是对象,
也需要进行响应式出里

1
2
3
4
5
6
7
8
9
10
11
12
13
function defineReactive(target, key, value) {
// console.log(value) observe(value)
Object.defineProperty(target, key, {
get() {
console.log('getter key = ', key)
return value
}, set(newV) {
console.log(`setter ${key} = ${value}`)
if (newV === value) return value = newV
observe(value) // 就是这一步 return value
}
})
}

新增一个键值对

1
2
3
function set(target, key, value) {
defineReactive(target, key, value)
}

对于数组的响应式处理

1
2
3
4
5
6
7
8
9
10
11
/** * 数组响应式 */const arrayProto = Array.prototypeconst arrayMethods = Object.create(arrayProto)
// 改变了数组本身const methodsToPatch = [
'push', 'pop', 'unshift', 'shift', 'splice', 'sort', 'reverse']
// 只拦截这七个, 其他的可以继续从原型链查找methodsToPatch.forEach(method => {
Object.defineProperty(arrayMethods, method, {
value: function (...args) {
const ret = arrayProto[method].apply(this, args)
console.log('array reactive')
return ret
}, configurable: true, writable: true, enumerable: false, })
})

完整代码

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
42
43
44
45
46
47
48
49
50
51
function defineReactive(target, key, value) {
// console.log(value) observe(value)
Object.defineProperty(target, key, {
get() {
console.log('getter key = ', key)
return value
}, set(newV) {
console.log(`setter ${key} = ${value}`)
if (newV === value) return value = newV
observe(value)
return value
}
})
}
function Observer(value) {
if (Array.isArray(value)) {
value.__proto__ = arrayMethods
} else {
this.walk(value)
}
}
Observer.prototype.walk = function (obj) {
for (let key in obj) {
defineReactive(obj, key, obj[key])
}
}
function observe(value) {
if (typeof value !== 'object') return new Observer(value)
}
function set(target, key, value) {
defineReactive(target, key, value)
}
/** * 数组响应式 */const arrayProto = Array.prototypeconst arrayMethods = Object.create(arrayProto)
// 改变了数组本身const methodsToPatch = [
'push', 'pop', 'unshift', 'shift', 'splice', 'sort', 'reverse']
methodsToPatch.forEach(method => {
Object.defineProperty(arrayMethods, method, {
value: function (...args) {
const ret = arrayProto[method].apply(this, args)
console.log('array reactive')
return ret
}, configurable: true, writable: true, enumerable: false, })
})
const obj = {
t: 't value', t1: {
tt1: 'tt1 value' }, arr: [1, 2, 3]
}
observe(obj)
obj.tobj.t1.tt1obj.t = { ttt: 'tttt value' }
set(obj, 't2', 't2 value')
obj.arr.push(5)

下面是输出

1
2
3
4
5
6
7
getter key =  t
getter key = t1
getter key = tt1
setter t = t value
getter key = t2
getter key = arr
array reactive