As we all know, the responsivity principle of VUe2 is implemented by object.defineProperty. Vue3 is implemented through a Proxy. So, what’s the difference? This article will be through the hand tear simple principle to understand its realization principle.

vue2

On the first source

    function trackArray(arr) {
      const prototype = Array.prototype
      const newProto = Object.create(prototype)
      const methods = ['push'.'pop'.'shift'.'unshift'.'splice'.'sort'.'reverse']
      methods.forEach(item= > {
        newProto[item] = function (. args) { prototype[item].call(arr, ... args)console.log('call ', item)
        }
      })
      arr.__proto__ = newProto
    }

    function track(obj, key, value) {
      observe(value) // Deep listening is required here
      Object.defineProperty(obj, key, {
        get() {
          console.log('get', key)
          return value
        },
        set(newValue) {
          if (value === newValue) {
            return
          }
          console.log('set', key)
          value = observe(newValue) // When changing value to an object, further listening is required}})}function observe(obj) {
      if (typeofobj ! = ='object' || obj === null) { // Only listen for reference type data
        return obj
      }
      
      if (Array.isArray(obj)) {
        trackArray(obj)
        return obj
      }

      for (let key in obj) {
        track(obj, key, obj[key])
      }

      return obj
    }
Copy the code

Because Object.defineProperty is an attribute level API. Only one property of an object can be intercepted. So you can only traverse the properties of the objects you want to listen to in order to trace. So you can’t listen for new properties. The Object.definePropertyAPI itself cannot track Object deletion properties. So in VUe2, $set and $delete are officially provided. It is worth mentioning that implementing deep listening is recursive at all levels once, even if it is not yet used. And you need extra processing for arrays. Otherwise you can’t track the array API on its prototype chain. The idea is to create an object that points to Array.prototype and create the seven API properties in vue2. Each of its properties calls the API on Array.Prototype. At the same time can achieve tracking. Notice that this points to. Finally, the listening array is prototyped to point to the created object.

Array.prototype = array. prototype = array. prototype = array. prototype Why is that? Obj [key] = obj[key] = obj[key] = obj[key] 3. In the source code, can the code shown below be exchanged?

      / / the old
      methods.forEach(item= > {
        newProto[item] = function (. args) { prototype[item].call(arr, ... args)console.log('call ', item)
        }
      })
      / / new
      methods.forEach(item= > {
        newProto[item] = (. args) = > {
          prototype[item].call(this. args)// change arr to this
          console.log('call ', item)
        }
      })
Copy the code

Observe (value) on the return value of get(). Anyway, there’s deep recursive listening in set(value), so I don’t have to start with it, okay? A: If placed in a return value, the reference type is recursively accessed to the end, resulting in redundant deep access tracing. So why isn’t this the case with vue3? A proxy simply wraps a layer of proxies and does not directly access properties

. observe(value)// Deep listening is required here
      Object.defineProperty(obj, key, {
        get() {
          console.log('get', key)
          return value // return observe(value)?},...Copy the code

vue3

Old rules, first on the source code

    function reactive(obj) {
      if (typeofobj ! = ='object' || obj === null) {
        return obj
      }

      const proxyHandle = {
        get(target, key, receiver) {
          const result = Reflect.get(target, key, receiver)
          if (Reflect.ownKeys(target).includes(key)) {
            console.log('get props: ', key)
          }
          const obervedRes = reactive(result)
          return obervedRes
        },
        
        set(target, key, value, receiver) {
          if (value === target[key]) {
            return true
          }
          const oldKeys = Reflect.ownKeys(target)
          if (oldKeys.includes(key)) {
            console.log('set: has props: ', key)
          } else {
            console.log('set: new Props: ', key)
          }     
          
          // const observedVal = reactive(value)
          const result = Reflect.set(target, key, value, receiver)
          return result
        },
        
        deleteProperty(target, key) {
          const res = Reflect.deleteProperty(target, key)
          console.log('delete: props')
          return res
        }
      }

      return new Proxy(obj, proxyHandle)
    }
Copy the code

Proxy Is an OBJECT – level API. The implementation is relatively simple… A few questions: Why don’t you use deep listening on set values?

conclusion

Object in the vue2. DefineProperty:

  1. Deep listening is done recursively once and for all during initialization
  2. Unable to trace object to add new value, delete property
  3. Arrays require extra processing

The proxy vue3:

  1. Deep listening listens on values as they are fetched (on-demand listening)
  2. You can listen for property additions and deletions perfectly,
  3. Arrays can use apis natively, without having to deal with the APIS that need to be listened on