深入vue响应式原理,附详细代码

  把一个普通 Javascript 对象传给 Vue 实例来作为它的  data 选项,Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter。用户看不到 getter/setters,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。
  每个组件实例都有相应的 watcher 程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的  setter 被调用时,会通知  watcher 重新计算,从而致使它关联的组件得以更新。
 
响应式原理实现步骤:
1.轮询data中的所有属性,先将属性值赋值internalValue,并将所有属性设置为访问器属性,
读取属性时需要调用get()函数,写入属性时需要调用set()函数
2.读取属性时调用get()函数,查看是否正在调用watch,是否需要添加函数句柄到subscribers中,
并返回internalValue给属性读取处
3.写入属性时调用set()函数,先将属性值写入internalValue,再执行subscribers中的所有watch调用的函数句柄,
函数句柄中读取属性时又重新通过get()函数获取了internalValue(写入的属性值),并进行计算来实现依赖数据的更新

响应式原理实现代码:可以自行添加打印后查看

 1 let data = { price: 5, quantity: 2 }
 2 let target = null
 3 
 4 class Dep {
 5   // Dep.prototype.custructor 指向Dep构造函数,this指向的属性为实例属性
 6   constructor () {
 7     this.subscribers = [] //存储函数句柄的数组
 8   }
 9 
10   // 定义在Dep.prototype上的depend()方法
11   depend () {
12     // 若果target(函数句柄)不为空且subscribers数组中没有target(函数句柄),则将target(函数句柄)加入subscribers数组中 
13     if (target && !this.subscribers.includes(target)) {
14       this.subscribers.push(target) 
15     }
16   }
17   notify () {
18     //轮询subscribers数组,且执行数组中的每个函数句柄
19     this.subscribers.forEach(sub => sub())
20   }
21 }
22 
23 //获取data中的可枚举的实例属性组成的字符串数组,并且轮询这个数组,数组中的每个属性执行箭头函数
24 Object.keys(data).forEach(key => {
25   //获取每个属性对应的属性值(实时更新)
26   let internalValue = data[key]
27   console.log('1.internalValue:'+internalValue)
28   
29   //创建一个实例dep,dep有subscribers属性(存储函数句柄)和depend()方法、notify()方法
30   const dep = new Dep()
31 
32   //将data对象的数据属性修改为访问器属性,访问属性时会调用相应的get/set方法
33   //在函数内部创建另一个函数即闭包,闭包指有权访问另一个函数作用域中的变量的函数,即get/set函数有权访问箭头函数中的变量
34   Object.defineProperty(data, key, {
35     get() {
36       console.log('getter')
37       console.log('2.internalValue:'+internalValue)      
38       dep.depend() //执行dep.depend()方法,添加targe属性到subscribers数组中 
39       return internalValue // 返回属性值
40     },
41     set(newVal) {
42       console.log('setter')
43       console.log('3.internalValue:'+internalValue)
44       internalValue = newVal //把新的值设置到internalValue中
45       dep.notify() //执行dep.notify()方法,轮询subscribers数组,且执行数组中的每个函数句柄
46     }
47   })
48 })
49 
50 //添加watcher函数声明,将myFun(函数声明)赋值给target,并执行target(函数句柄)
51 function watcher(myFun) {
52   target = myFun
53   target() //调用函数句柄
54   target = null
55 }
56 
57 //将函数声明作为参数传入并执行watcher函数
58 watcher(() => {
59   data.total = data.price * data.quantity //箭头函数不是立即执行,需要手动调用
60 })
61 
62 watcher(() => {
63   data.total1 = data.price * 2 
64 })
65 
66 console.log("total = " + data.total)
67 data.price = 20
68 console.log("total = " + data.total)
69 data.quantity = 10
70 console.log("total = " + data.total)

参考来源:

1.vue官网:http://doc.vue-js.com/v2/guide/reactivity.html

2.JavaScript 响应式与 Proxy:https://mp.weixin.qq.com/s/GktsHoN3q12nz8c-QlfqgQ

猜你喜欢

转载自www.cnblogs.com/shuqiao/p/9855893.html