Vue 3响应式原理学习笔记(二)

2021-10-12 10:14:14

#前端#vue

0

前言

本博文用于记录学习Vue 3响应式原理课程的学习笔记。 Vue 3响应式原理 Vue 3响应式原理学习笔记(一)

笔记

Proxy and Reflect

  • 如何让我们的响应式引擎自动调用跟踪和触发?
    • 解决方法:
      • 运行effect时,如果访问了产品的属性,或者说是使用了GET时,调用track去保存effect
      • 如果产品的属性改变了,或者说使用了SET,调用trigger来运行那些保存了的effect

如何拦截GET和SET

  • VUE2中使用了ES5的Object.defineProperty()去拦截GET和SET
  • VUE3中使用了ES6 Reflect和 Proxy去拦截(VUE3原生不支持Internet Explore)
  • Proxy是另一个对象的占位符,默认情况下对该对象进行委托
let product = { price: 5, quantity: 2}
let proxiedProduct = new Proxy(product, {})
console.log(proxiedProduct.quantity) //先调用Proxy,Proxy调用product,返回product到console log,最后打印出 2,简单来说就是一个对象委托
new Proxy(对象, handler) //handler处理程序,一个对象,可以传递一个trap(诱捕器),这个trap可以让我们拦截基本操作,如属性查找或枚举或函数调用
  • 使用handler的实例
// 使用handler
let product = { price: 5, quantity: 2}
let proxiedProduct = new Proxy(product, {
  get() {
    console.log('Get was called')
    return 'Not the value'
  }
})

console.log(proxiedProduct.quantity)  //Get was called Not the value

// handler加入参数
let product = { price: 5, quantity: 2}
let proxiedProduct = new Proxy(product, {
  get(target, key) {
    console.log('Get was called with key = ' + key)
    return target[key]
  }
})

console.log(proxiedProduct.quantity)  

// 在Proxy里使用Reflect
let product = { price: 5, quantity: 2}
let proxiedProduct = new Proxy(product, {
  get(target, key, receiver) {  //receiver保证了当我们的对象有继承自其它对象的值或函数,它保证了当我们的对象有继承自其它对象的值或函数时,this指针能正确的指向使用(的对象),可以避免一些我们在Vue2中的响应式警告
    console.log('Get was called with key = ' + key)
    return Reflect.get(target, key, receiver)
  },
  set(target, key, value, receiver) {
    console.log('Set was called with key = ' + key + ' and value = ' + value)
    return Reflect.set(target, key, value, receiver)
  },
})

proxiedProduct.quantity = 4
console.log(proxiedProduct.quantity)  
  • Vue3对handler的封装
function reactive(target) {
  const handler = {
    get(target, key, receiver) {
      console.log('Get was called with key = ' + key)
      return Reflect.get(target, key, receiver)
    },
    set(target, key, value, receiver) {
      console.log('Set was called with key = ' + key + ' and value = ' + value)
      return Reflect.set(target, key, value, receiver)
    }
  }
  return new Proxy(target, handler)
}
let product = reactive({ price: 5, quantity: 2 })
product.quantity = 4 
console.log(product.quantity)

// Set was called with key = quantity and value = 4
// Get was called with key = quantity
// 4
  • 因此Vue3是将对象传递到响应式函数中,响应式函数便会返回一个代理对象
  • 而我们便会将这个代理对象当作原始对象来使用

如何监听GET和SET

reactive函数

function reactive(target) {
  const handler = {
    get(target, key, receiver) {
      let result = Reflect.get(target, key, receiver)
      track(target, key)
      return result
    }
    set(target, key, value, receiver) {
      let oldValue = target[key]
      let result = Reflect.set(target, key, value, receiver)
      if (oldValue != value) {
        trigger(target, key)
      }
      return result
    },
  }
  return new Proxy(target, handler)
}
let product = reactive({ price: 5, quantity: 2 })

过程

// reactive函数返回产品对象的proxy(代理)
let product = reactive({ price: 5, quantity: 2 })
let total = 0

let effect = () => {
  total = product.price * product.quantity
}
// 运行effect函数,因为get了product对象,所以会调用到track函数,而track函数会给产品创建一个新的映射
effect()
console.log(total)
product.quantity = 3
console.log(total)
  1. 将对象传给reactive函数,而reactive函数会返回一个proxy对象,这个proxy对象自带拦截GET(访问对象属性)与SET(改变对象属性)
  2. 拦截到GET后,proxy会调用track(对象,属性名)函数 — 保存
  • 在targetMap中,它会给对象创建一个新的映射,值(value)是一个新的deps图,即映射对象属性到一个新的deps
  • deps就是一个effects集(Set)
  1. 拦截到SET后