Proxy 与 Vue
You Yuxi delivered a keynote speech titled “Vue 3.0 Updates”, elaborating on the update plan and direction of “Vue 3.0” (interested partners can take a look at the complete PPT), indicating that the use of’Object.defineProperty 'has been abandoned in favor of the faster native ’ Proxy '!!
This removes many of the limitations of the previous Vue2.x implementation based on Object.defineProperty: the inability to listen for property additions and deletions, array index and length changes, and support for Map, Set, WeakMap, and WeakSet!
Proxy Introduction
Overview
Proxy is used to modify the default behavior of certain operations, which is equivalent to making modifications at the language level, so it belongs to a kind of “meta programming”, that is, programming in programming languages.
Proxy can be understood as setting up a layer of “interception” before the target object. Access to the object from the outside world must first pass through this layer of interception, so it provides a mechanism to filter and rewrite the access from the outside world. The original meaning of the word proxy is proxy, which is used here to mean that it “proxies” certain operations, which can be translated as “proxy”.
1 | var obj = new Proxy ({}, { |
The above code sets up a layer of interception on an empty object, redefining the behavior of reading (‘get’) and setting (‘set’) properties. The specific syntax will not be explained here for the time being, just look at the running result. Read and write the properties of the object’obj 'with the interception behavior, and you will get the following result.
1 | obj.count = 1 |
The above code shows that the proxy actually overloads the dot operator, that is, overwrites the original definition of the language with its own definition.
ES6 natively provides the Proxy constructor function to generate Proxy instances.
1 | var proxy = new Proxy(target, handler); |
All uses of Proxy objects are in the above form, the only difference is the writing of the’handler ‘parameter. Among them,’ new Proxy () ‘means to generate a’Proxy’ instance, ‘target’ parameter means the target object to be intercepted, and’handler 'parameter is also an object used to customize the interception behavior.
Here is another example of intercepting read property behavior.
1 | var proxy = new Proxy({}, { |
In the above code, as a constructor function, ‘Proxy’ accepts two parameters. The first parameter is the target object to be proxied (the above example is an empty object), that is, if there is no intervention of’Proxy ‘, the object that the operation originally wants to access; the second parameter is a configuration object. For each proxied operation, a corresponding processing function needs to be provided, which will intercept the corresponding operation. For example, in the above code, the configuration object has a’get’ method to intercept access requests to target object properties. The two arguments to the’get ‘method are the target object and the property to be accessed. As you can see, since the intercepting function always returns’ 35 ‘, accessing any property will result in’ 35 '.
Note that for’Proxy ‘to work, you must operate on the’Proxy’ instance (the’proxy 'object in the above example), not on the target object (the empty object in the above example).
If the’handler 'does not set any interceptions, it is equivalent to going directly to the original object.
1 | var target = {}; |
In the above code, ‘handler’ is an empty object, without any interception effect, accessing’proxy ‘is equivalent to accessing’target’.
One trick is to set the Proxy object to the’object.proxy ‘property, so that it can be called on the’object’ object.
1 | var object = { proxy: new Proxy(target, handler) }; |
Proxy instances can also serve as prototype objects for other objects.
1 | var proxy = new Proxy({}, { |
In the above code, the’proxy ‘object is the prototype of the’obj’ object. The’obj ‘object itself does not have the’time’ property, so according to the prototype chain, the property will be read on the’proxy 'object, resulting in interception.
The same interceptor function can be set to intercept multiple operations.
1 | var handler = { |
For operations that can be set, but do not set interception, they fall directly on the target object and produce results in the original way.
Below is a list of interception operations supported by Proxy, a total of 13.
- ** get (target, propKey, receiver) **: Intercepts the reading of object properties, such as’ proxy.foo ‘and’proxy [’ foo ‘]’.
- ** set (target, propKey, value, receiver) **: Intercepts the setting of object properties, such as’ proxy.foo = v ‘or’proxy [’ foo ‘] = v’, and returns a boolean value.
- ** has (target, propKey) **: Intercepts the operation of’propKey in proxy 'and returns a boolean value.
- ** deleteProperty (target, propKey) **: Intercepts the operation of’delete proxy [propKey] 'and returns a boolean value.
- ** ownKeys (target) **: Intercept’Object.getOwnPropertyNames (proxy) ‘,’ Object.getOwnPropertySymbols (proxy) ‘,’ Object.keys (proxy) ‘,’ for… in 'loops, returning an array. This method returns the property names of all the properties of the target object itself, while the return result of’Object.keys () ’ only includes the traversable properties of the target object itself.
- ** getOwnPropertyDescriptor (target, propKey) **: Intercept Object.getOwnPropertyDescriptor (proxy, propKey) and return the description object of the property.
- ** defineProperty (target, propKey, propDesc) **: Intercept’Object.defineProperty (proxy, propKey, propDesc) ‘,’ Object.defineProperties (proxy, propDescs) ', return a boolean value.
- ** preventExtensions (target) **: Intercept’Object.preventExtensions (proxy) 'and return a boolean value.
- ** getPrototypeOf (target) **: Intercept Object.getPrototypeOf (proxy) and return an object.
- ** isExtensible (target) **: Intercept’Object.isExtensible (proxy) 'and return a boolean value.
- ** setPrototypeOf (target, proto) **: Intercepts’Object.setPrototypeOf (proxy, proto) ', returns a boolean value. If the target object is a function, there are two additional operations that can be intercepted.
- ** apply (target, object, args) **: Intercepts the operation of the proxy instance as a function call, such as’proxy (… args) ‘,’ proxy.call (object,… args) ‘,’ proxy.apply (…) '.
- ** construct (target, args) **: Intercepts operations called by Proxy instances as constructor functions, such as’new proxy (… args) '.
Advanced usage
get
By using Proxy, the operation of reading properties (‘get’) can be transformed into executing a function, thereby realizing the chain operation of properties.
1 | var pipe = function (value) { |
After the above code sets Proxy, the effect of chaining the function name is achieved.
set
Sometimes, we will set internal properties on the object. The first character of the property name starts with an underscore, indicating that these properties should not be used externally. Combining the’get ‘and’set’ methods can prevent these internal properties from being read and written externally.
1 | const handler = { |
In the above code, as long as the first character of the property name read and write is an underscore, it will be thrown wrong, so as to achieve the purpose of prohibiting reading and writing internal properties.
has
The’has’ method is used to intercept the’HasProperty ‘operation, that is, it will take effect when determining whether an object has a certain property. A typical operation is the’in’ operator.
The’has’ method can accept two parameters, namely the target object and the property name to be queried.
The following example uses the has method to hide certain properties from the in operator.
1 | var handler = { |
In the above code, if the first character of the property name of the original object is an underscore, ‘proxy.has’ will return’false ‘, so it will not be detected by the’in’ operator.
If the original object is not configurable or prohibits expansion, ‘has’ interception will report an error.
1 | var obj = {a: 10}; |
In the above code, the’obj ‘object prohibits expansion, and as a result, using the’has’ interception will report an error. That is, if a property is not configurable (or the target object is not extensible), the’has’ method must not “hide” (i.e. return’false ') the property of the target object.
It is worth noting that the “has” method intercepts the “HasProperty” operation, not the “HasOwnProperty” operation, that is, the “has” method does not determine whether a property is a property of the object itself or an inherited property.
Use of Proxy in Vue
vue2.x
Recursion traverses the data in the data, using Object.defineProperty()Hijack getters and setters, do data dependency collection processing in getters, and in setters, listen for changes in data and notify the place that subscribes to the current data. 部分源码 src/core/observer/index.js#L156-L193, the version is 2.6.11 as follows
1 | let childOb = !shallow && observe(val) |
What’s wrong with doing this?
- Undetectable addition and deletion of object properties: When you add a new property’newProperty ‘to an object, the newly added property does not have a mechanism for vue to detect data updates (because it is added after initialization).’ vue. $set ‘lets vue know that you have added a property, and it will handle it for you.’ $set 'is also handled internally by calling’Object.defineProperty () ’
- Unable to monitor the change of the index of the array, resulting in setting the value of the array directly through the index of the array, and cannot respond in real time.
- When there is a lot of data in the data and the hierarchy is very deep, there will be performance issues, because it is necessary to traverse all the data in the data and set it to be responsive.
vue3.0
Vue3.0 has not been officially released yet, butvue-next The relevant code has been open sourced and is currently in an alpha version.
Why use Proxy to solve the above problems? Mainly because Proxy is an interception object, an “interception” is performed on the “object”, and external access to the object must first pass this layer of interception. No matter what properties of the access object, previously defined or newly added, it will go to interception,
Example
Here is a simple data response using Object.defineProperty () and Proxy respectively
Use Object.defineProperty () to implement:
1 | class Observer { |
The execution result of the above code is
1 | //Modify the output of the original attribute age of obj |
As you can see, adding a property to an object is not monitored internally. The newly added property needs to be manually monitored again using’Object.defineProperty () ‘. This is why the addition and deletion of object properties cannot be detected in’vue 2.x’. The ‘$set’ provided internally is handled by calling’Object.defineProperty () '.
Next we use’Proxy 'instead of’Object.defineProperty () ’ implementation
1 | const obj = { |
You can see the output below
1 | //Modify the age attribute of the original object |
Reference article: