Before we get to that, we need to understand the core concept of Vue, mutable
No matter vue2 or VUe3, in the implementation process, the core concept has remained stable, the concept of variable data source as the core, to achieve the whole UI changes and updates
The simplest way to put it is: initialize data to generate a page, directly modify the source data to trigger the update, the page re-render
Anyone who follows VUE knows that vue3 uses proxy instead of defineProperty,
When using vue2, we often run into the problem that adding a new object attribute obj. A = 1 cannot be hijacked by vue2 and must be updated using the $set method provided by vue2
The reason for this is pretty clear, because defineProperty can only hijack one of the attributes of the current object
const a = {
b: 1};Object.defineProperty(a, 'b', {
set: function() {},
get: function() {}});Copy the code
When we added a attribute to object A, the newly added attribute was not hijacked by defineProperty. Although a new attribute was still successfully generated on the corresponding object, we know that vue2 was hijacked by setter and getter of defineProperty. Since the new data is not hijacked, the page will not be re-rendered no matter how updated it is
In VUE3, using a proxy for data proxying eliminates this concern
const p = new Proxy({
a: 1.b: 2}, {get: function(obj, value) {
console.log('get', obj, value);
return Reflect.get(obj, value);
},
set: function(obj, prop, value) {
console.log('set', obj, prop, value);
return Reflect.set(obj, prop, value); }})Copy the code
Proxy for the data proxy, it can respond to the newly added attribute. When a new attribute is added, it can respond to the GET to proxy the current object
How is VUE3 proxied by proxy
First of all, we can see several new apiref, Reactive, effect and computed in VUE3
Ref and reactive
const normal = ref(0);
const state = reactive({
a: 1.b: 2,})Copy the code
In vue3, the compatible processing of VUe2 also uses reactive, that is, instance.data = reactive(data). The entire data attribute is deputized by Reactive
We know that data in VUe2 is hijacked using Object.defineperproty, so how does it use proxy to proxy data in Reactive, which is in accordance with the old writing method and the new compositionApi
Ps: Reactive only checks and proxies the incoming data through proxy, and the most important things are SET and GET. Therefore, we should go directly to the base. After all, the hot sauce is served in a hot sauce
get
Can analyze, vuE2 or VUE3, for the acquisition of data do not do the main content of what difference
- Get the data for the currently needed key
- Depend on the acquisition
However, in view of the feature of vuE3 using proxy, some additional compatibility has been made here
- If the data retrieved is an object, the object is reused
reactive
Perform a data proxy - If it is a shallow data broker, the currently retrieved data is returned directly
Effect dependent acquisition
In addition to the normal data proxy, VUE also has computed and Watch, which can be directly generated by using watchEffect and computed methods in VUe3
Their data update and dependency processing rely on get on the current data for dependency collection
Let’s look at the core of the code
// targetMap A Map collection of data for all current agents
// depsMap Specifies the Map set for each Key of the current agent's data
// dep specifies the corresponding dependency for the key in the current agent's data
ActiveEffect Data currently generated by Effect or computed
let depsMap = targetMap.get(target)
if(! depsMap) { targetMap.set(target, (depsMap =new Map()))}let dep = depsMap.get(key)
if(! dep) { depsMap.set(key, (dep =new Set()))}if(! dep.has(activeEffect)) { dep.add(activeEffect) activeEffect.deps.push(dep) }Copy the code
It may be difficult to interpret this code purely from this point of view, but from the perspective of the overall use of VUE3, go back to interpret this code
setup() {
const b = reactive({
c: 1.b: 2});// effect is a method returned directly from the reactivity package in vue
const a = effect(() = > {
returnb.c; })}Copy the code
First of all, B defined by Reactive is used in effect. Based on the superficial phenomenon, we can know that when B.C changes, A also changes synchronously
The reason for this change is the activeEffect in the above source code. When the created effect is called, the activeEffect will be set to itself, and the corresponding callback function will be executed. The function call will trigger the getter of the data used by each. Inject the corresponding Effect dependency into each used data
As for why we set such a complex attribute dependency fetch, it is because we use proxy to proxy the whole object, so we can’t directly bind the current field in the getter like vue2 does with obect. defineProperty. In VUe3, the entire object is directly treated as a Map. The key of each Map is the corresponding attribute, and the value is all the objects that depend on the current attribute
set
With GET, the original thinking and mode are still maintained
- Setting current Data
- Publish subscribed data (trigger dependency updates)
In VUE3, there is a partial difference, after all, it is a separate library
- If you call effect directly, it will be modified directly when the detected data changes
- If you call Watch or watchEffect, it follows vue’s own scheduling scheme
Therefore, if you want to update the current data directly, you can use Effect first, which is faster than watchEffect. The disadvantage is that you may have to write a lot of things by yourself
As for how to achieve it is actually very simple, right
- Gets the dependencies for the currently updated data
- The group enters the Set waiting to run
- perform
However, there is a special processing for the length property of the array, which is somewhat different. Let’s talk more about the array operation in VUe3
An array of
In VUe2, there is an extra layer of processing for arrays, proxying the basic methods of arrays, because using Object.defineProperty has a natural disadvantage over arrays
The specific reasons are very clear in the vUE documentation and will not be described in detail here
The document address
Using proxy in VUe3 solves this problem perfectly, just because proxy can listen for array changes and do a test
const a = new Proxy([1.2] and {get: function(obj, prop) {
console.log('get', obj, prop);
return Reflect.get(obj, prop);
},
set: function(obj, prop, value) {
console.log('set', obj, prop, value);
return Reflect.set(obj, prop, value); }}); a.push(1);
get [1.2] push
get [1.2] length
set [1.2] 2 1
set [1.2.1] length 3
Copy the code
When we proxy an array, we directly call push to insert a new data, and we can obviously see that both the getter and setter are called twice, once for the push method and once for the length of the array. That is to say, the proxy will not only detect the method we are currently calling, We can also tell if the length of our data has changed
Push is an operation on the current array, but there are some methods in the array that return a new array. Will the proxy also proxy the newly generated array? Here we take splice as an example
/ / a = [1, 2]
a.splice(0.1)
get [1.2] push
get [1.2] length
get [1.2] constructor
get0 [1, 2]get[1, 2] 1set0 2 [1, 2]set [2,empty] length 1
Copy the code
In terms of representation, the array after the proxy will only listen for the contents of the current array, that is, the changes generated after the splice call will not be propped
Now let’s go back to vue3’s trigger method. This is a dependency update that vue triggers after a set is complete. Again, we’ll look at the optimization for arrays, in addition to normal execution
The // add method adds the current dependency to an array waiting for an update
else if (key === 'length' && isArray(target)) {
depsMap.forEach((dep, key) = > {
if (key === 'length' || key >= (newValue as number)) {
add(dep)
}
})
}
Copy the code
Vue3 calls add only when the set length is set, and all updates are performed at the same time. Vue3 calls Add only when the set length is set, and all updates are performed
conclusion
It has to be said that Proxy is much more powerful than defineProperty. It not only solves the historical problems of VUE, makes the vue experience to a higher level, but also removes a lot of methods that are necessary because of defineProperty and simplifies the package size of Vue
Although proxy compatibility is much worse than defineProperty, IE has been largely abandoned in VUE, so if you need to run your project under IE, ditch Vue and use a lower version of React. Hahahahahahaha
In the mobile terminal, there is basically no such version limitation. If the version is too low to use proxy, I believe polyfill can find it