Hello, I’m Karsong.

I have nothing to do over the weekend. I’m going to find an excellent library with a small amount of code to study. The recently released Petite-Vue was chosen for the following reasons:

  • The code is small (only 5.8KB) and the source code is more modular (compared to React) and easier to read

  • Build based on Vite, execute YARN Dev to start debugging the source code

  • There is no virtual DOM, compile-time scheme, which can be used as a foundation for reading Vue source code

  • The underlying responsive update principle also applies to Mobx, SolidJS and other libraries. Read multiple copies at a time

I decided it was him.

But with weekends at a premium, and I haven’t used Vue in four years, how can I learn the source code efficiently?

It is best to learn the source code without looking at it.

Next, I use Petite-Vue as an example to demonstrate the correct posture for learning source code.

How fast? How fast

You can think of petite-VUE as a simple VUE that replaces the VUE template with the real DOM.

For example, Demo:

<script type="module">
  import { createApp } from '.. /src'
  createApp({count: 0}).mount()
</script>

<div v-scope>
  <button @click="count++">add 1</button>
  <p>{{count}}</p>
</div>  
Copy the code

Div and its descendants are real DOM tags, so the page is initialized as follows:

Then execute the following code to complete the petite-Vue initialization:

createApp({count: 0}).mount()
Copy the code

This page:

Read the framework source code to avoid a hand from the entry function debugging all the way, it is easy to meng forced. The correct way is to peel it layer by layer like an onion:

So, let’s take a look at the call stack for the first rendering from the Performance panel:

The call stack is roughly divided into blue boxes and red boxes. First, look at the blue box on the left:

The createContext and Reactive keywords presumably create a reactive context. We don’t know what reactive means.

Then look at the red box on the right:

From the depth of the call stack and the effect of the page rendering, we can guess that this part of the work includes:

  • Traversing the DOM

  • The bidirectional binding between data and view is complete

  • Initialize render

Next, let’s test the conjecture.

Note that we haven’t looked at any of the source code so far

Verify traversal of the DOM

In the call stack, walk and walkChildren have been called many times. It is highly likely that they are the specific methods of traversing work execution. Let’s confirm.

Type log in the walk method:

export const walk = (node: Node, ctx: Context): ChildNode | null | void= > {
  console.log('walk', node);
  // ...  
}
Copy the code

Excluding the text nodes corresponding to the newline character “\n “, the print order is as follows:

walk div
walk <button>add 1</button>
walk "add 1"
walk <p>0</p>
walk "0"
Copy the code

From the print, this is a depth-first traversal (traverses children if there are children, traverses siblings if there are no children)

Obviously, petite-Vue mount uses depth-first traversal and processes each DOM node traversed that is related to context state.

In Demo, the context contains the state {count: 0} :

createApp({count: 0}).mount()
Copy the code

After traversal

{{count}}

becomes

0

.

Determine the granularity of the bidirectional binding

Next we need to confirm the scope of the bidirectional binding, that is:

After the update is triggered, what range of DOM will be retraversed and DOM operations performed?

As you can see, the DOM is updated without any walk, walkChildren (or similar traversal), just by calling the reactiveEffect method.

This means that depth-first traversal at mount time establishes a one-to-one correspondence between the state and the methods used to update the DOM.

Because the correspondence is established, no additional traversal is required to determine which DOM to change.

When the state is updated, all you need to do is find the method to update the DOM that is related to it.

For example, associate the count state with the following function:

function setCount(value) {
  p.textContent = value;
}
Copy the code

Whenever count changes, setCount(count) updates the DOM corresponding to p.

Therefore, the working principle of Petite-Vue mainly includes two points:

  1. Depth first traverses the DOM during mount, establishing a one-to-one correspondence between the state of stateful DOM (such as

    {{count}}

    ) and the method used to update the DOM

  2. Update finds the corresponding DOM update method for that state and executes it

As you can see, you can get a general idea of the workflow without diving into the source code.

If you want to take things a step further, such as understanding how relationships are established (involving reactive updates), then you need to dig into the source code.

I recommend Vue 3 Reactivity for Vue Mastery to complement this knowledge with responsive updates.

conclusion

This article introduces a way to read the source code for complex frameworks – from the abstract to the concrete.

  1. Derive the overall workflow from the mount and update call stacks

  2. Discover the core knowledge from the overall workflow — responsive updates

Once you understand the overall workflow and responsive updates, read the parts that interest you so you don’t get bogged down in the code.

You, did you quit school?