Location>code7788 >text

How Vue 3.5 Responsive Refactoring Reduced Memory Usage by 56%, Revealed!

Popularity:122 ℃/2024-11-13 08:52:02

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:
old

Dep dependency(e.g., ref responsive variables) can be passed through thedep attributeaccessedSub 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 thedepsThe attribute accesses theDep dependency(e.g. ref responsive variables), when theSub subscribersWhen 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 subscriberscollection, so that when this responsive variable changes it is not notified that it is no longer subscribed to hisSub subscribersUp.

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)flagThe property is calculated when the value ofdoubleCountActually, it only relies on responsive variablesflagcap (a poem)count1regard asflagvalue is switched to false, the computed attribute should become dependent on the variableflagcap (a poem)count2

Just the update above.Sub subscribersThe dependency logic, Vue has actually been refactored many times. In earlier versions of Vue3 it was straightforward to clear theSub subscribersresponsive variables on which it depends, and then re-execute the computation of the attributedoubleCountTime 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_trackIdcap (a poem)_depsLengthfields are marked, and dependency collection when re-executing the computed attribute can then utilize the_trackIdcap (a poem)_depsLengthfield determines whether the Dep dependency can be reused, and after executing the callback function that computes the property similarly utilizes the_trackIdcap (a poem)_depsLengthfield 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 listcap (a poem)version countThe 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:
reactive

In the new responsive modelSub subscriberscap (a poem)Dep dependencyThere is no longer a direct correlation between them, but through the intermediateLink nodeAs a bridge to correlation.

As we talked about in the previous section, before 3.5Sub Subscribersattribute will be de-depended on in theDep dependencyDep dependencyto store properties that depend on him in theSub subscribersSo it leads to whenSub subscribersDependent 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 dependencyThe Y-axis isSub subscribersLink nodeare used as points on top of the axes. Each set ofDep dependencycap (a poem)Sub subscriberswill all correspond to a Link node and can be accessed through thisLink nodeDirect access toDep dependencycap (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 subscribersAll 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)flagAfter switching the value ofSubscriber Sub1The responsive variables that it depends on are then removed from theflag+count1change intoflag+count2. What we need to do at this point is simple, create a newLink3node, which can be accessed directly to theSub1cap (a poem)Dep3. Then theLink1in the original pointing toLink2is changed to a pointer to theLink3The same time letting theLink3also points to a pointer to theLink1. And willLink2directionalLink1is changed to a pointer to theunoccupiedAs a result ofDep2It's not being relied on by any subscribers now, so it's a good idea to move theLink2original pointDep2was also changed to point to null, and similarly the pointer to theDep2directionalLink2pointer also points to null.

The above meal of operations, except for the necessary initialization of aLink3Outside 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)flagvalue is switched to false, the new responsive model diagram looks like the following:
reactive2

As you can see from the chart aboveLink2has been completely removed from the bi-directional chain table, and we've been manipulating the pointer's pointing the entire time, so theLink1It's always been reused, too.

V8 found that when doing garbage collectionLink2is no longer used by any variable, it can be assumed that theLink2is 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 listcap (a poem)version countThe 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 listcap (a poem)version countachieved, the optimization reduced the memory footprint by 56%. The main reason: in the new responsive system there is an additionalLink nodeFor linksSub subscriberscap (a poem)Dep dependencyThe update of a Sub subscriber dependency is just a pointer transformation, and can also reuse theLink nodeand those that will no longer be usedLink nodeThe 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.