preamble
Vue 3.5 has refactored responsive again, and the refactored responsive system has two main components:bidirectional linked listcap (a poem)version count. In our first two posts we've talked about thebidirectional linked listcap (a poem)version countIn this post we'll talk about why this refactoring was able to reduce the memory footprint by 56%.
Ouyang is also graduating at the end of the year, join Ouyang's interview exchange group (to share information on internal promotion), high-quality vue source code exchange group
Why responsive has been reimagined 'again'
Because responsive was just refactored in the previous version of Vue 3.4, this responsive refactoring is the author of the vscode plugin Vue-Official (formerly known as Volar)Johnson ChuEngaged.
The refactoring in 3.4 optimized a lot of things, most intuitively: the value of the computed computed attribute didn't change, and another watch listened to the value of this computed. Before 3.4, the watch callback would still be triggered, but after the 3.4 optimization, it won't be triggered.
Prior to version 3.5, there were two roles in Vue's responsive system: the Sub subscriber and the Dep dependency.
Sub subscribers
: The main ones are watchEffect, watch, render function, computed, and so on.
Dep dependency
: The main responsive variables are ref, reactive and computed.
There is an interdependent relationship between the two of them, as shown below:
Dep dependency
(e.g., ref responsive variables) can be passed through thedep attribute
accessedSub subscribers
(e.g. the computed computed property), one knows exactly which subscribers depend on oneself and can go and notify subscribers when one's value changes.
equalSub subscribers
(e.g., the computed computation property) can be accessed via thedeps
The attribute accesses theDep dependency
(e.g. ref responsive variables), when theSub subscribers
When you no longer depend on a variable, you can access it through this relationship.Dep dependency
. Then remove yourself from the no-longer-dependent variable ofSub subscribers
collection, so that when this responsive variable changes it is not notified that it is no longer subscribed to hisSub subscribers
Up.
Let's look at an example with the following code:
<template>
<p>{{ doubleCount }}</p>
<button @click="flag = !flag">switch modes or data streamsflag</button>
</template>
<script setup>
import { computed, ref } from "vue";
const count1 = ref(1);
const count2 = ref(10);
const flag = ref(true);
const doubleCount = computed(() => {
("computed");
if () {
return * 2;
} else {
return * 2;
}
});
</script>
(coll.) fail (a student)flag
The property is calculated when the value ofdoubleCount
Actually, it only relies on responsive variablesflag
cap (a poem)count1
regard asflag
value is switched to false, the computed attribute should become dependent on the variableflag
cap (a poem)count2
。
Just the update above.Sub subscribers
The dependency logic, Vue has actually been refactored many times. In earlier versions of Vue3 it was straightforward to clear theSub subscribers
responsive variables on which it depends, and then re-execute the computation of the attributedoubleCount
Time to go back and collect the new responsive variables again.Obviously this version of memory use would be very wasteful.
The responsive system as refactored in the latest Vue 3.4 release will utilize the_trackId
cap (a poem)_depsLength
fields are marked, and dependency collection when re-executing the computed attribute can then utilize the_trackId
cap (a poem)_depsLength
field determines whether the Dep dependency can be reused, and after executing the callback function that computes the property similarly utilizes the_trackId
cap (a poem)_depsLength
field can then remove Dep dependencies that are no longer relied upon.
The above scheme looks perfect, but at its core, it relies on the order of the variables in the computed attribute to remain the same, and if the order changes, then it's still not reusable, and again, it's a waste of memory. (PS: It's okay if you can't read this paragraph of version 3.4 responsive, because it's a thing of the past)
Main reason for memory optimization: multiplexing Link nodes
In Vue 3.5 that man who knows Vue best stepped in and used thebidirectional linked list
cap (a poem)version count
The responsive system has been refactored again. To be honest, this refactoring has made reading responsive source code a much higher threshold, but the benefits are particularly noticeable, most notably by reusing Link nodes to achieve reduced memory usage.
Still the same example as above, corresponding to the new responsive model is shown below:
In the new responsive modelSub subscribers
cap (a poem)Dep dependency
There is no longer a direct correlation between them, but through the intermediateLink node
As a bridge to correlation.
As we talked about in the previous section, before 3.5Sub Subscribers
attribute will be de-depended on in theDep dependency
,Dep dependency
to store properties that depend on him in theSub subscribers
So it leads to whenSub subscribers
Dependent variables cannot be fully reused when they need to be updated, and memory is wasted.
If you can't read the following, it's not a problem of comprehension, the reason is that you are not familiar with bidirectional chain tables, you can first read my earlierbidirectional linked listArticle.
In the new 3.5 responsive model, the x-axis is theDep dependency
The Y-axis isSub subscribers
,Link node
are used as points on top of the axes. Each set ofDep dependency
cap (a poem)Sub subscribers
will all correspond to a Link node and can be accessed through thisLink node
Direct access toDep dependency
cap (a poem)Sub subscribers
。
Find a point on top of the Y-axis (e.g. Sub1 which is the calculation property)doubleCount
), starting horizontally you can findSub1 subscribers
All responsive variables on which it depends. Because horizontally these Link nodes are a bidirectional linked list and can be accessed directly through a particular Link node to his Dep dependencies.
(coll.) fail (a student)flag
After switching the value ofSubscriber Sub1
The responsive variables that it depends on are then removed from theflag+count1
change intoflag+count2
. What we need to do at this point is simple, create a newLink3
node, which can be accessed directly to theSub1
cap (a poem)Dep3
. Then theLink1
in the original pointing toLink2
is changed to a pointer to theLink3
The same time letting theLink3
also points to a pointer to theLink1
. And willLink2
directionalLink1
is changed to a pointer to theunoccupied
As a result ofDep2
It's not being relied on by any subscribers now, so it's a good idea to move theLink2
original pointDep2
was also changed to point to null, and similarly the pointer to theDep2
directionalLink2
pointer also points to null.
The above meal of operations, except for the necessary initialization of aLink3
Outside of that we've been doing pointers all along and not adding Sub subscriber dependencies or reducing dependencies like previous responsive, which is a very efficient way to do it.
(coll.) fail (a student)flag
value is switched to false, the new responsive model diagram looks like the following:
As you can see from the chart aboveLink2
has been completely removed from the bi-directional chain table, and we've been manipulating the pointer's pointing the entire time, so theLink1
It's always been reused, too.
V8 found that when doing garbage collectionLink2
is no longer used by any variable, it can be assumed that theLink2
is a variable that can be reclaimed, it will be reclaimed directly to free memory.
Link node reuse and freeing up memory by recycling Link nodes that are no longer in use as soon as possible are the main reasons for this responsive refactoring to reduce the memory footprint by 56%.
Other Optimizations
Dependency triggering is also much clearer with a bidirectional link table. When a responsive variable changes, it is only necessary to traverse the bidirectional link table consisting of the Link nodes of the Dep dependency (vertically), and then access the corresponding Sub subscriber directly through these Link nodes to trigger its dependency.
Based on this the triggering of Sub subscribers is a linear process, so it can be realized that the Sub subscribers that need to be triggered are strung together to form a queue composed of Sub subscribers. When the subscribers that need to be triggered have been collected, then go on to trigger the Sub subscribers to avoid the same subscriber being triggered multiple times.
Dependency triggering has also been made easier than before, and performance as well as memory has been improved.
In the end it's because of thebidirectional linked list
cap (a poem)version count
The computed compute property has gotten smarter after the addition of theinertia calculationThe computed property will only execute the callbacks in the computed property when someone uses it (e.g., using the computed property doubleCount in a template), and the watch that has been implemented since version 3.4 will not be triggered if the value of the computed property is unchanged and another watch listens for the value of the computed property. watch will not be triggered. For more information on this, see my previousversion countArticle.
summarize
Vue 3.5 responsive refactoring is primarily done through thebidirectional linked list
cap (a poem)version count
achieved, the optimization reduced the memory footprint by 56%. The main reason: in the new responsive system there is an additionalLink node
For linksSub subscribers
cap (a poem)Dep dependency
The update of a Sub subscriber dependency is just a pointer transformation, and can also reuse theLink node
and those that will no longer be usedLink node
The Link node is now isolated to make it easier for V8 to reclaim the Link node faster. In addition, Sub subscriber triggering has been made simpler, and the computed calculation property is nowinertia calculationThe optimizations also optimize memory usage.
Follow the public number: [Front-end Ouyang], give yourself a chance to advance vue
Also Ouyang wrote an open source ebookvue3 Compilation Principles Revealed, reading this book can give you a qualitative improvement in your knowledge of vue compilation. This book is accessible to beginner and intermediate front-ends and is completely free, just asking for a STAR.