Grape city website, grape city for developers to provide professional development tools, solutions and services, enabling developers.

Reactivity Systems are one of the key parts of modern front-end frameworks. Application systems are highly interactive, dynamic, and responsive. The capabilities and practices of this system should be understood by every Web developer.

The principle of

A response system is a mechanism that automatically synchronizes the data source (model) with the data presentation (view) layer. Each time the model changes, the view is rerendered.

Take a simple Markdown editor for example. Typically, the editor has two panes: one for writing Markdown code, which modifies the base model, and the other for previewing compiled HTML, which shows updated views. When we write something in the Write pane, it is immediately previewed automatically in the Preview pane. This is a simple example, but in reality it’s a lot more complicated.

In many cases, the data we want to display depends on other data. In this case, the relevant data needs to be tracked and updated based on the tracking. For example, we have a fullName, which consists of the firstName and lastName attributes. After any of its dependencies are modified, fullName is automatically reevaluated and the results are displayed in the view.

Now that we know what a responsive system is, let’s have a quick review of what a responsive system is in Vue 2 and what to look for before looking at how it works in Vue 3 and how it can be used in practice.

An introduction to the responsive system of Vue 2

The responses in Vue 2 are more or less “hidden”. Vue makes reactive IMPLICITLY of whatever we place in our data objects. This makes the developer’s job easier, but it inevitably reduces flexibility. Behind the scenes, Vue 2 uses ES5 object.DefineProperty to transform all properties of a data Object into getters and setters. For each component instance, Vue creates an instance of a dependency observer that records any properties that are collected/tracked during the component’s rendering. When a property triggers a dependency setter, the observer is notified and the component is rerendered and the view is updated. But there are also some problems.

Change detection warning

Vue was unable to detect some data changes due to the limitations of the object.defineProperty method. Include:

  • Add properties to or remove properties from an object (e.g. Obj.newkey = value)
  • Set an array entry by index (for example, arr[index] = newValue)
  • Modify the length of the array (for example, arr.length = newLength)

To address these issues, however, Vue provides the VUe.set API method, which adds a property to the response object, ensuring that the new property is also responsive, triggering view updates.

Discuss the situation with the following example:

  <h1>Hello! My name is {{ person.name }}. I'm {{ person.age }} years old.</h1>
  <button @click="addAgeProperty">Add "age" property</button>
  <p>Here are my favorite activities:</p>
  <ul>
    <li v-for="item, index in activities" :key="index">
      {{ item }}
      <button @click="editActivity(index)">Edit</button>
    </li>
  </ul>
  <button @click="clearActivities">Clear the activities list</button>
</div>

Copy the code
  el: '#app',
  data: {
    person: {
      name: "David"
    },
    activities: [
      "Reading books",
      "Listening music",
      "Watching TV"
    ]
  },
  methods: { 
    // 1. Add a new property to an object
    addAgeProperty() {
      this.person.age = 30
    },
    // 2. Setting an array item by index
    editActivity(index) {
      const newValue = prompt('Input a new value')
      if (newValue) {
        this.activities[index] = newValue
      }
    },
    // 3. Modifying the length of the array
    clearActivities() { 
      this.activities.length = 0 
    }
  }
});

Copy the code

In the example above, we see that none of these methods work. We can’t add new properties to the Person object, we can’t use the index of Activities to edit the items in the array, and we can’t change the length of the Activities array.

The optimization is as follows:

  el: '#app',
  data: {
    person: {
      name: "David"
    },
    activities: [
      "Reading books",
      "Listening music",
      "Watching TV"
    ]
  },
  methods: { 
    // 1. Adding a new property to the object
    addAgeProperty() {
      Vue.set(this.person, 'age', 30)
    },
    // 2. Setting an array item by index
    editActivity(index) {
      const newValue = prompt('Input a new value')
      if (newValue) {
        Vue.set(this.activities, index, newValue)
      }
    },
    // 3. Modifying the length of the array
    clearActivities() { 
      this.activities.splice(0)
    }
  }
});

Copy the code

In this example, we use the vue.setAPI method to add a new Age attribute to the Person object and select/modify specific items from the active array. In the last case, use the JavaScript built-in splice method.

This is perfectly feasible but slightly unwieldy and leads to code inconsistencies. Vue 3 solves this problem. Let’s continue with the following example:

  data() {
    return {
      person: {
        name: "David"
      },
      activities: [
        "Reading books",
        "Listening music",
        "Watching TV"
      ]
    }
  },
  methods: { 
    // 1. Adding a new property to the object
    addAgeProperty() {
      this.person.age = 30
    },
    // 2. Setting an array item by index
    editActivity(index) {
      const newValue = prompt('Input a new value')
      if (newValue) {
        this.activities[index] = newValue
      }
    },
    // 3. Modifying the length of the array
    clearActivities() { 
      this.activities.length = 0 
    }
  }
}

Vue.createApp(App).mount('#app')

Copy the code

You can see that in Vue 3, all the methods work.

In Vue 2.6, the Vue. Observable API method was introduced to expose the responsive system to a certain extent, allowing developers to experience the content of the responsive system. In fact, this is exactly the same approach that Vue uses internally to wrap data objects and is useful for creating small cross-component state stores in simple scenarios. But it still doesn’t compare to Vue3’s responsive system, so I’ll give you more details.

Note: Because the object.defineProperty method is an ES5-only and non-tunable feature, Vue 2 does not support IE8 and later.

How does the Vue 3 responsive system work

The reactive system in Vue 3 has been completely rewritten to take full advantage of the ES6 Proxy and Reflect API. The new version adds a responsive API that makes the system more flexible and powerful than before.

The Proxy API allows developers to intercept and modify lower-level object operations on target objects. Proxies are clones/wrappers of objects that provide special functions (called targets) that respond to specific actions and override built-in behavior of JavaScript objects (called traps). If you still need to use the default behavior, you can use the corresponding Reflection API, which, as the name implies, reflects the methods of the Proxy API. Here is an example of how to use these apis in Vue 3:

  name: "David",
  age: 27
};

const handler = {
  get(target, property, receiver) {
    // track(target, property)
    console.log(property) // output: name
    return Reflect.get(target, property, receiver)
  },
  set(target, property, value, receiver) {
    // trigger(target, property)
    console.log(`${property}: ${value}`) // output: "age: 30" and "hobby: Programming"
    return Reflect.set(target, property, value, receiver)
  }
}

let proxy = new Proxy(person, handler);   

console.log(person)

// get (reading a property value)
console.log(proxy.name)  // output: David

// set (writing to a property)
proxy.age = 30;

// set (creating a new property)
proxy.hobby = "Programming";

console.log(person) 

Copy the code

To create a new Proxy, use the new Proxy(target, handler) constructor. It takes two arguments: a target object (a Person object) and a handler object that defines which operations (get and set operations) will be intercepted. In handler objects, get and set traps track when properties are read and when properties are modified/added. Set the console statements to ensure that they work correctly.

Take the following parameters in the GET and set traps:

  • Target: The target object of the proxy wrapper
  • Property: property name
  • Value: attribute value (this parameter is used for set operations only)
  • Receiver: The object (usually a proxy) that operates on

The Reflect API method takes the same arguments as its corresponding proxy method

The track and trigger functions in the comments are specific to Vue to keep track of when properties are read and when they are modified/added.

In the last part of the example, you output the original Person object with console statements. Then read the proxy object with the property name in another declaration. Next, modify the Age property and create a new Hobby property. Finally, output the object again to see if it was updated correctly.

That’s the complete workflow for a Vue3 responsive system, but it’s much more complicated in practice.

With the Vue 3 responsive system, there are a few other considerations:

  • Applicable only to browsers that support ES6 +
  • The response proxy is not equal to the original object

conclusion

We’ve compared the responsive systems sections of Vue2 and Vue3, and explained how responsive systems work. In a later article, we’ll introduce the RESPONSIVE systems API of Vue3. Stay tuned.