If you have any thoughts on my blog or articles and you want to let me know, you can either post a comment below(public) or tell me via this i_kkkp@163.com

Vue Shallow Reactivity vs Deep Reactivity

Introduction

Explore the differences between reactive and shallowReactive, delving into the concepts of deep reactivity and shallow reactivity in Vue.js.

Shallow Reactivity vs Deep Reactivity

const obj = reactive({ foo: { bar: 1 } })
effect(() =>{ 
console.log(obj.foo.bar)
})
// Modifying obj.foo.bar value does not trigger reactivity
obj.foo.bar = 2

Initially, an object obj is created with a property foo containing another object { bar: 1 }. When accessing obj.foo.bar inside an effect function, it is noticed that modifying obj.foo.bar does not trigger the effect function again. Why does this happen? Let’s take a look at the current implementation:

function reactive(obj) {
  return new Proxy(obj ,{
    get(target, key, receiver) {
      if (key === 'raw') 
        return target;
    track(target, key);
// When reading the property value, return it directly
    return Reflect.get(target, key, receiver)
}
// Other trapping functions are omitted
})
}

In the given code, when accessing obj.foo.bar, it first reads the value of obj.foo. Here, Reflect.get is used to directly return the result of obj.foo. Since the result obtained through Reflect.get is a plain object, namely { bar: 1 }, it is not a reactive object. Therefore, when accessing obj.foo.bar inside the effect function, no reactivity is established. To address this, the result returned by Reflect.get needs to be wrapped:

function reactive(obj) {
  return new Proxy(obj, {
    get(target, key, receiver) {
      const result = Reflect.get(target, key, receiver);
      // If the result is an object, make it reactive
      if (typeof result === 'object') {
        return reactive(result);
      }
      return result;
    },
    // Other traps...
  });
}

In this code snippet, the reactive function is defined. It takes an object as a parameter and returns a proxy of that object. The proxy uses a get trap function that triggers when accessing a property of the object. In the get trap, Reflect.get is used to retrieve the property value. If the result is an object, it is made reactive by calling the reactive function recursively. This ensures that nested objects also possess reactive properties, allowing modifications to trigger the reactivity system.

Shallow Reactivity

However, there are scenarios where deep reactivity is not desired, leading to the concept of shallowReactive or shallow reactivity. Shallow reactivity means that only the top-level properties of an object are reactive. For example:

Suppose we have an object with a nested object as its property:

let obj = {
  innerObj: {
    key: 'value'
  }
}

If we apply deep reactivity to obj:

let reactiveObj = reactive(obj);

Any modifications to obj or innerObj properties will trigger the reactivity system:

reactiveObj.innerObj.key = 'new value'; // Triggers reactivity

However, if we want only the top-level properties of obj to be reactive, meaning modifications to obj trigger reactivity but modifications to innerObj do not, we use the shallowReactive function:

let shallowReactiveObj = shallowReactive(obj);

With shallowReactive, only modifications to obj will trigger reactivity:

shallowReactiveObj.innerObj = {}; // Triggers reactivity
shallowReactiveObj.innerObj.key = 'new value'; // Does not trigger reactivity

Vue.js and reactive vs shallowReactive

In Vue.js, both reactive and shallowReactive functions are used to create reactive objects. Let’s explore their differences.

The reactive function creates deeply reactive objects. This means that both the object itself and all its nested objects become reactive. Any modifications to the object or its nested objects’ properties will trigger the reactivity system.

On the other hand, the shallowReactive function creates shallowly reactive objects. This means that only the top-level properties of the object are reactive. If the object contains nested objects, modifications to those nested objects’ properties will not trigger the reactivity system.

let obj = {
  innerObj: {
    key: 'value'
  }
}

let reactiveObj = Vue.reactive(obj);
reactiveObj.innerObj.key = ‘new value’; // Triggers reactivity

let shallowReactiveObj = Vue.shallowReactive(obj);
shallowReactiveObj.innerObj.key = ‘new value’; // Does not trigger reactivity

Readonly and Shallow Readonly

After discussing reactivity and shallow reactivity, let’s talk about readonly and shallow readonly:

Vue.js provides readonly and shallowReadonly functions to create readonly reactive objects.

The readonly function creates deeply readonly reactive objects. This means that both the object itself and all its nested objects are readonly. Any attempts to modify the object or its nested objects’ properties will fail.

The shallowReadonly function creates shallow readonly reactive objects. This means that only the top-level properties of the object are readonly. If the object contains nested objects, properties of these nested objects can be modified.

let obj = {
  innerObj: {
    key: 'value'
  }
}

let readonlyObj = Vue.readonly(obj);
readonlyObj.innerObj.key = 'new value'; // This will fail because the object is readonly

let shallowReadonlyObj = Vue.shallowReadonly(obj);
shallowReadonlyObj.innerObj.key = 'new value'; // This will succeed because only top-level properties are readonly

Note: This article is a translated version of the original post. For the most accurate and up-to-date information, please refer to the original source.
```

A Brief Discussion on WebAssembly Vue Proxy and Reflect

Comments