When Vue Meets Proxy
Examine the Internal of vue.JS to Reveal How It Uses Proxy to Achieve Reactivity
Reactivity is an essential feature of every modern JavaScript framework, and Vue.js is no different. But what does reactivity really mean? There are lots of different answers. Many of them are overly complex and confusing.
Let me give a simple explanation.
Reactivity
Reactivity is the ability to make your program react to asynchronous data streams in a declarative way.
A typical example of reactivity is as below:
The above diagram illustrates the reactivity via Rxjs. The subscriber (i.e., UI component) subscribes to the publisher (i.e., Observable). When the state changes, the publisher will push the data stream using .next
, and the subscriber receives the data and reacts to the data change.
How does Vue.js implement reactivity? Let’s have a look at Vue 2.
Vue 2 data reactivity
In Vue 2, data reactivity is achieved by traversing the data and making use of Object.definedProperty()
to convert its properties to getter/setter. It collects data dependencies via a custom getter and monitors data change, and subscribes to events in a custom setter.
Image Source: https://vuejs.org/v2/guide/reactivity.html
Below is the gist of the Vue 2 source code on the reactivity implementation.
1 | Object.defineProperty(obj, key, { |
This code block is extracted from the defineReactive
function_._ The _defineReactive_
function is called for all data object properties when initialized. You can see the getter is defined to collect dependencies, and the setting will monitor the data change and send the notification when the change is detected.
While the above mechanism works, it does have a few issues. Two main ones are:
- Unable to detect the deletion or addition of a property. The reactivity is only applied when the app is initialized. When you add a new property at runtime, the new property won’t be reactive. In other words, a change in the property value won’t trigger reactive side effects. Vue 2 provides a workaround
.set
method allowing developers to add a property to be reactive manually. - Performance: For a large and/or nested data set, performance could be negatively affected by Vue 2’s requirement that the data transverse of all properties and the creation of getter/setters be done.
Proxy in Vue 3
In Evan You ’s article “The process: Making Vue 3 ”, he mentioned that one reason for the rewrite is to leverage new language features like ‘Proxy.”
The most noteworthy among them is Proxy , which allows the framework to intercept operations on objects. A core feature of Vue is the ability to listen to changes made to the user-defined state and reactively update the DOM. Vue 2 implements this reactivity by replacing the properties on state objects with getters and setters. Switching to Proxy would allow us to eliminate Vue’s existing limitations, such as the inability to detect new property additions, and provide better performance.
In Vue 3, using a Proxy is the key to resolving the reactivity-related issues in Vue 2.
Proxy in ES6
Introduced in ES6, Proxy enables defining custom behavior without direct access to an object. It works by intercepting as a Proxy in between.
The usage of a Proxy is straightforward.
1 | const proxy = new Proxy(target, handler); |
The above declaration includes:
- target: The object to proxy
- handler: It contains the “trap” methods. They are functions that override the original behaviors. For any available fundamental operation, there is a corresponding trap.
The below example shows how to create the name
property based on firstName
and surName
using Proxy.
1 | const handler = { |
Please note that using Proxy a new proxy instance is created instead of changing the original target object.
How Proxy Works in Vue 3
Let’s have a look at how Proxy is implemented in Vue 3. The core of the reactivity in Vue 3 is implemented in the createReactiveObject
function.
1 | function createReactiveObject( |
The gist of the above code:
- the data with primitive type will be directly returned as we only want to deal with objects here. Vue 3 uses ref to handle primitive type instead. Internally, it also makes use of reactive proxy objects.
- if the object already has a corresponding proxy, we return the cached proxy directly.
- Infer the target object type, vue3 will create a proxy only for Array, Object, Map, Set, WeakMap, and WeakSet. Objects outside the target type will be set as INVALID and returned.
- Create the new proxy object, and save it into the
proxyMap
cache before returning it.
How does Proxy resolve the Vue 2 reactivity issue?
Using a Proxy object will intercept any call to access or modify the object. Custom operation is defined in the getter and setter. For example, in the createSetter
function, the adding property is handled.
1 | const hadKey = |
If the property does not exist (hadKey
is false), the trigger method is used to notify the dependencies that a new property has been added. This solves the issue of being unable to detect the adding property in Vue 2. Similarly, the deleteProperty
handler operation solves the issue of being unable to detect deleting properties in Vue 2.
With the proxy implementation of reactivity API in Vue 3, there is no need to traverse all properties in data. This brings significant performance improvement when handling large data sets.
Conclusion
This article discusses how reactivity is implemented from Vue 2 to Vue 3.
Proxy is a powerful metaprogramming feature. Applying Proxy in Vue 3 is an elegant solution. Not only does it resolve the caveats from Vue 2, but it also opens the potential for further extension.
Understanding the internal mechanism will help you to write better code.
- Title: When Vue Meets Proxy
- Author: Sunny Sun
- Created at : 2021-12-12 00:00:00
- Updated at : 2024-08-16 19:46:32
- Link: http://coffeethinkcode.com/2021/12/12/when-vue-meets-proxy/
- License: This work is licensed under CC BY-NC-SA 4.0.