6
Please indicate the source for reprinting: Grape City official website, Grape City provides developers with professional development tools, solutions and services, and empowers developers.
Original reference: https://www.sitepoint.com/vue-3-reactivity-system/

Reactivity systems are one of the key parts of modern front-end frameworks. The high interactivity, dynamics and responsiveness of the application system all rely on it to support. Every web developer should understand the function and practical operation of this system.

principle

The response system is a mechanism that automatically synchronizes the data source (model) with the data presentation (view) layer. Every time the model changes, the view is re-rendered.

Take a simple Markdown editor as an example. Usually the editor has two panes: one pane is used to write Markdown code (used to modify the base model), and the other pane is used to preview the compiled HTML (display the updated view). When we write something in the writing pane, it will be automatically previewed in the preview pane immediately. This example is relatively simple, but in actual situations it will be much more complicated.

In many cases, the data we want to display depends on other data. In this case, it is necessary to track the relevant data and update the data according to the tracking situation. For example, we have a fullName, which consists of firstName and lastName attributes. After modifying any of its dependencies, fullName will automatically re-evaluate and display the results in the view.

After understanding what a responsive system is, and before understanding how the responsive system in Vue 3 works and how to use it in practice, let us quickly review the content of the responsive system in Vue 2 and its precautions.

Introduction to Vue 2's responsive system

The response in Vue 2 is more or less "hidden". No matter what we put in the data object, Vue will make it reactive implicitly. Although this can make the developer's work easier, but the flexibility will inevitably decrease.
Behind the scenes, Vue 2 uses ES5 Object.defineProperty to convert all the properties of the data object into getters and setters. For each component instance, Vue creates a dependency observer program instance, and the observer records any attributes that depend on collection/tracking during component rendering. When the attribute triggers the dependency's setter, the observer is notified, the component is re-rendered and the view is updated. But there will be some problems.

Change detection warning

Due to the limitations of the Object.defineProperty method, Vue cannot detect certain data changes. include:

  • Add attributes to the object or remove attributes from the object (for example, obj.newKey = value)
  • Set array items by index (eg arr[index] = newValue)
  • Modify the length of the array (for example, arr.length = newLength)

However, in order to solve these problems, Vue provides the Vue.set API method, which adds a property to the response object to ensure that the new property is also responsive, which triggers the view update.

Use the following example to discuss this situation:

  <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>
  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 
    }
  }
});

In the above example, we will find that none of these three methods work. We cannot add new attributes to the person object, we cannot use the index of activities to edit the items in the array, and we cannot modify 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)
    }
  }
});

In this example, we use the Vue.setAPI method to add the 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 approach is completely feasible but slightly clumsy, and it will lead to inconsistencies in the code. Vue 3 solves this problem.
Let's continue to see 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')

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

In Vue 2.6, the introduced Vue.observable API method exposes the responsive system to a certain extent, so that developers can experience the content of the responsive system. In fact, this is exactly the same method used by Vue to package data objects, and is useful for creating small cross-component state storage in simple scenarios. But there is still no way to compare with Vue3's responsive system, so I will introduce it in detail next.

Note: Since the Object.defineProperty method is only ES5 and not adjustable, Vue 2 does not support IE8 and below.

How does the Vue 3 responsive system work?

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

The Proxy API allows developers to intercept and modify lower-level object operations on the target object. A proxy is a clone/wrapper of an object and provides special functions (called targets) that respond to specific operations and override the built-in behaviors of JavaScript objects (called traps). If you still need to use the default behavior, you can use the corresponding Reflection API, whose name, as the name suggests, reflects the method of the Proxy API. Here is an example to understand 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) 

To create a new proxy, use the new Proxy(target, handler) constructor. It takes two parameters: the target object (person object) and the handler object, which defines which operations (get and set operations) will be intercepted. In the handler object, get and set traps are used to track when properties are read and when properties are modified/added. Set the console statement to ensure that it runs correctly.

The following parameters are used in get and set traps:

  • target: the target object of the proxy package
  • property: property name
  • value: attribute value (this parameter is only used for setting operations)
  • receiver: the object of the operation (usually a proxy)

The Reflect API method and its corresponding proxy method accept the same parameters

The track function and trigger function in the comments are specifically for Vue, used to track when to read attributes and when to modify/add attributes.

In the last part of the example, the original person object is output using a console statement. Then use another statement to read the proxy object with the attribute name. Next, modify the age attribute and create a new hobby attribute. Finally, output the object again to see if it is updated correctly.

The above is the complete workflow of the Vue3 responsive system, but it will be much more complicated in actual work.

There are some considerations for using the Vue 3 responsive system:

  • Only available for browsers that support ES6+
  • The response proxy is not equal to the original object

to sum up

Above we have compared the reactive system part of Vue2 and Vue3, and explained the working principle of the reactive system. In the following articles, we will further introduce the API of the reactive system in Vue3, so stay tuned .

Further reading

If you are already proficient in Vue3, let us learn more deeply form editing system based on Vue 3 components


葡萄城技术团队
2.7k 声望28.6k 粉丝

葡萄城创建于1980年,是专业的软件开发技术和低代码平台提供商。以“赋能开发者”为使命,葡萄城致力于通过各类软件开发工具和服务,创新开发模式,提升开发效率,推动软件产业发展,为“数字中国”建设提速。