前置

vue2的响应式原理

它只能监听指定对象的、指定属性的 getter 和 setter。所以当对象或数组新增属性时,vue2无法为新增的属性增加响应性。

1
2
3
4
Object.defineProperty(target, key, {
set(newVal) {},
get(key) {}
})

vue3的响应式系统

reactive

简单实现reactive,核心有几部分

effect:响应式的依赖收集和触发的机制的体现

reactive函数:提供出去要使用的函数

track:收集/追踪依赖

trigger:触发依赖

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
52
53
// effect,接收一个回调函数,执行
let activeEffect = null
class reactiveEffect {
constructor(fn) {
this.fn = fn
}
run() {
activeEffect = this
return this.fn()
}
}
const effect = (fn) => {
const re = new reactiveEffect(fn)
re.run()
}

// reactive,接收一个对象,返回它的代理对象
function reactiveFn(target) {
return new Proxy(target, {
get(target, key, receiver) {
track(target, key)
return target[key]
},
set(target, key, value, receiver) {
target[key] = value
trigger(target, key)
}
})
}

// track,收集依赖
function track(target, key) {
if (!activeEffect) return
let depsMap = targetMap.get(target)
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, (dep = new Set()))
}
dep.add(activeEffect)
}

// trigger,执行依赖
function trigger(target, key) {
let depsMap = targetMap.get(target)
if (!depsMap) return
const dep = depsMap.get(key)
if (dep) dep.forEach(eff => {
eff.fn()
})
}
1
2
3
4
5
6
7
8
9
10
// 使用
const student = {
name: 'Adam',
age: '18'
}
const proxyStudent = reactiveFn(student)
effect(() => {
document.querySelector('.wrap').innerHTML = proxyStudent.name + proxyStudent.age
})
proxyStudent.age = 19

ref

reactive 只能构建复杂数据类型的响应性,proxy就是这样。vue为我们提供了ref来解决这个问题。比较不熟悉的是 gett和set 

  1. ref 方法:接收任意数据
  2. ref 方法:生成RefC实例,并进行返回
  3. ref 方法:利用 get 和set 监听getter、setter 行为,以便执行依赖收集和依赖触发
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class RefC {
constructor(value) {
this._value = value
}

get value() {
if (activeEffect) {
console.error('reffff', ref)
const dep = ref.dep || (ref.dep = new Set())
dep.add(activeEffect)
}
return this._value
}

set value(newVal) {
this._value = newVal
if (ref.dep) ref.dep.forEach(eff => eff.fn())
}
}

function ref(value) {
return new RefC(value)
}
1
2
3
4
5
6
7
// 使用  
const v = '小明'
const refVar = ref(v)
effect(() => {
document.querySelector('.wrap').innerHTML = refVar.value
})
refVar.value = '小红'

参考链接

https://juejin.cn/post/7186248802423013432

编译时、运行时的源码解析:

https://blog.csdn.net/qq_34618600/article/details/140893125