Ins and Outs of Vue 3 Reactivity

Vue.js 3.0.0 alpha was released on January 2020, and nine months later, on September 2020, vue-next 3.0 was released officially with the code name "One Piece" (yes, the anime). There was a lot of changes with the Vue 3, but I will discuss mainly on the new reactivity system for which I've been waiting.

In order to facilitate your understanding, this article contains simple codes that highlight the flow of the reactivity systems of vue@2.6.12 and vue-next@3.0.4.

Preface

Here’s how the code works, step-by-step.

  1. Vue walks through each and every property of data object with the walk() and adds get/set traps.
  2. The getter that was passed as parameter is executed with the new Watcher(). Then, Vue accesses data.a and data.b and activates the get trap.
    2.1. dep.depend is called, and when data.a changes, dep.depend() executes Dep.target that uses data.a and makes it reactive.
    2.2. The steps for the data.b's get trap is the same as 2.1.
  3. sum is evaluated to be 3.
  4. data.a is changed to 7.
  5. data.a's set trap is called and calls the dep.notify().
  6. The subs array found in dep has a getter, so the getter is executed to reevaluate the value of sum to be 9.

Vue 2 reactivity system uses the Object.defineProperty, and therefore, cannot detect if a new property is added to the object or if an element of an array changes. Vue 2 had to use vm.$set() method to induce notify() by force. For arrays, if you take a look at observer/array.js, internal methods like push, pop, and shift support reactivity.

Vue 3 Reactivity

  1. track the code when you access a value
  2. trigger the tracked code when the value changes.

Let’s take it step by step.

track: the code that will be executed reactively

If you map out the collection structure above, you get the following.

  1. targetMap<WeakMap> tracks the target that will be a reactive object.
  2. depsMap<Map> becomes the values for each reactive object and stores the key for each target.
  3. dep<Set> collection tracks the codes that are executed every time the key is changed.

trigger: executes the tracked code

reactive: creating the reactive object

activeEffect

Vue addresses this issue by the activeEffect variable. The code that will be executed reactively, the response expression, is only added to the deps when the activeEffect exists in the get trap.

Putting it all together

Here’s how the code works, step-by-step.

  1. Create a reactive proxy with reactive({a: 1, b: 2}).
  2. Pass and execute the sumFn to the effect function.
    2.1. sumFn is assigned to the activeEffect.
    2.2. sumFn is executed.
    2.2.1. Access numbers a and b, and since activeEffect exists, set the sumFn to be executed later in deps<Set>.
    2.2.2. 3 is assigned to the sum as the function executes.
  3. Pass and execute the multiplyFn to the effect function, and the steps 2.1 and 2.2 are repeated.
  4. When the numbers.a changes, Vue finds all functions tracked in a's deps<Set> and executes every function.

Conclusion

In 2021, I look forward to more tools and companion libraries to gain compatibility with Vue.js 3, thereby extending the realm of Vue.js.

JavaScript UI Library Open Source by http://ui.toast.com