Before we get to that, we need to understand the core concept of Vue, mutable

No matter vue2 or VUe3, in the implementation process, the core concept has remained stable, the concept of variable data source as the core, to achieve the whole UI changes and updates

The simplest way to put it is: initialize data to generate a page, directly modify the source data to trigger the update, the page re-render

Anyone who follows VUE knows that vue3 uses proxy instead of defineProperty,

When using vue2, we often run into the problem that adding a new object attribute obj. A = 1 cannot be hijacked by vue2 and must be updated using the $set method provided by vue2

The reason for this is pretty clear, because defineProperty can only hijack one of the attributes of the current object

const a = {
    b: 1};Object.defineProperty(a, 'b', {
    set: function() {},
    get: function() {}});Copy the code

When we added a attribute to object A, the newly added attribute was not hijacked by defineProperty. Although a new attribute was still successfully generated on the corresponding object, we know that vue2 was hijacked by setter and getter of defineProperty. Since the new data is not hijacked, the page will not be re-rendered no matter how updated it is

In VUE3, using a proxy for data proxying eliminates this concern

const p = new Proxy({
    a: 1.b: 2}, {get: function(obj, value) {
        console.log('get', obj, value);
        return Reflect.get(obj, value);
    },
    set: function(obj, prop, value) {
        console.log('set', obj, prop, value);
        return Reflect.set(obj, prop, value); }})Copy the code

Proxy for the data proxy, it can respond to the newly added attribute. When a new attribute is added, it can respond to the GET to proxy the current object

How is VUE3 proxied by proxy

First of all, we can see several new apiref, Reactive, effect and computed in VUE3

Ref and reactive

const normal = ref(0);
const state = reactive({
    a: 1.b: 2,})Copy the code

In vue3, the compatible processing of VUe2 also uses reactive, that is, instance.data = reactive(data). The entire data attribute is deputized by Reactive

We know that data in VUe2 is hijacked using Object.defineperproty, so how does it use proxy to proxy data in Reactive, which is in accordance with the old writing method and the new compositionApi

Ps: Reactive only checks and proxies the incoming data through proxy, and the most important things are SET and GET. Therefore, we should go directly to the base. After all, the hot sauce is served in a hot sauce

get

Can analyze, vuE2 or VUE3, for the acquisition of data do not do the main content of what difference

  1. Get the data for the currently needed key
  2. Depend on the acquisition

However, in view of the feature of vuE3 using proxy, some additional compatibility has been made here

  1. If the data retrieved is an object, the object is reusedreactivePerform a data proxy
  2. If it is a shallow data broker, the currently retrieved data is returned directly

Effect dependent acquisition

In addition to the normal data proxy, VUE also has computed and Watch, which can be directly generated by using watchEffect and computed methods in VUe3

Their data update and dependency processing rely on get on the current data for dependency collection

Let’s look at the core of the code


// targetMap A Map collection of data for all current agents
// depsMap Specifies the Map set for each Key of the current agent's data
// dep specifies the corresponding dependency for the key in the current agent's data
ActiveEffect Data currently generated by Effect or computed

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()))}if(! dep.has(activeEffect)) { dep.add(activeEffect) activeEffect.deps.push(dep) }Copy the code

It may be difficult to interpret this code purely from this point of view, but from the perspective of the overall use of VUE3, go back to interpret this code

setup() {
    const b = reactive({
        c: 1.b: 2});// effect is a method returned directly from the reactivity package in vue
    const a = effect(() = > {
        returnb.c; })}Copy the code

First of all, B defined by Reactive is used in effect. Based on the superficial phenomenon, we can know that when B.C changes, A also changes synchronously

The reason for this change is the activeEffect in the above source code. When the created effect is called, the activeEffect will be set to itself, and the corresponding callback function will be executed. The function call will trigger the getter of the data used by each. Inject the corresponding Effect dependency into each used data

As for why we set such a complex attribute dependency fetch, it is because we use proxy to proxy the whole object, so we can’t directly bind the current field in the getter like vue2 does with obect. defineProperty. In VUe3, the entire object is directly treated as a Map. The key of each Map is the corresponding attribute, and the value is all the objects that depend on the current attribute

set

With GET, the original thinking and mode are still maintained

  1. Setting current Data
  2. Publish subscribed data (trigger dependency updates)

In VUE3, there is a partial difference, after all, it is a separate library

  1. If you call effect directly, it will be modified directly when the detected data changes
  2. If you call Watch or watchEffect, it follows vue’s own scheduling scheme

Therefore, if you want to update the current data directly, you can use Effect first, which is faster than watchEffect. The disadvantage is that you may have to write a lot of things by yourself

As for how to achieve it is actually very simple, right

  1. Gets the dependencies for the currently updated data
  2. The group enters the Set waiting to run
  3. perform

However, there is a special processing for the length property of the array, which is somewhat different. Let’s talk more about the array operation in VUe3

An array of

In VUe2, there is an extra layer of processing for arrays, proxying the basic methods of arrays, because using Object.defineProperty has a natural disadvantage over arrays

The specific reasons are very clear in the vUE documentation and will not be described in detail here

The document address

Using proxy in VUe3 solves this problem perfectly, just because proxy can listen for array changes and do a test

const a = new Proxy([1.2] and {get: function(obj, prop) {
        console.log('get', obj, prop);
        return Reflect.get(obj, prop);
    },
    set: function(obj, prop, value) {
        console.log('set', obj, prop, value);
        return Reflect.set(obj, prop, value); }}); a.push(1);

get [1.2] push
get [1.2] length
set [1.2] 2 1
set [1.2.1] length 3
Copy the code

When we proxy an array, we directly call push to insert a new data, and we can obviously see that both the getter and setter are called twice, once for the push method and once for the length of the array. That is to say, the proxy will not only detect the method we are currently calling, We can also tell if the length of our data has changed

Push is an operation on the current array, but there are some methods in the array that return a new array. Will the proxy also proxy the newly generated array? Here we take splice as an example

/ / a = [1, 2]
a.splice(0.1)

get [1.2] push
get [1.2] length
get [1.2] constructor
get0 [1, 2]get[1, 2] 1set0 2 [1, 2]set [2,empty] length 1
Copy the code

In terms of representation, the array after the proxy will only listen for the contents of the current array, that is, the changes generated after the splice call will not be propped

Now let’s go back to vue3’s trigger method. This is a dependency update that vue triggers after a set is complete. Again, we’ll look at the optimization for arrays, in addition to normal execution

The // add method adds the current dependency to an array waiting for an update
else if (key === 'length' && isArray(target)) {
    depsMap.forEach((dep, key) = > {
      if (key === 'length' || key >= (newValue as number)) {
        add(dep)
      }
    })
} 
Copy the code

Vue3 calls add only when the set length is set, and all updates are performed at the same time. Vue3 calls Add only when the set length is set, and all updates are performed

conclusion

It has to be said that Proxy is much more powerful than defineProperty. It not only solves the historical problems of VUE, makes the vue experience to a higher level, but also removes a lot of methods that are necessary because of defineProperty and simplifies the package size of Vue

Although proxy compatibility is much worse than defineProperty, IE has been largely abandoned in VUE, so if you need to run your project under IE, ditch Vue and use a lower version of React. Hahahahahahaha

In the mobile terminal, there is basically no such version limitation. If the version is too low to use proxy, I believe polyfill can find it