6
头图

Hi everyone, this is Kasong.

I'm fine on the weekend, so I'm ready to find an excellent library with a small amount of code to learn from. Finally, I chose the recently released petite-vue for the following reasons:

  • The amount of code is small (only 5.8kb), and the source code is highly modular (compared to React ), easy to read
  • Build based on Vite , execute yarn dev to start debugging the source code
  • There is no virtual DOM and compile-time solution, which can be used as a basis for reading the source code of Vue
  • The underlying responsive update principle is also applicable to Mobx , SolidJS etc., reading multiple copies at a time

It was decided that it was him.

But weekend time is so precious, and I haven't used Vue in 4 years, how can I learn the source code efficiently?

Best in without looking at the source code of the source learned .

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

How come how soon

It can petite-vue understood as: The real DOM replace Vue template simple Vue .

For example, Demo as follows:

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

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

Then execute the following code to complete the initialization of petite-vue

createApp({count: 0}).mount()

At this time page:

When reading the source code of the framework, you should not debug it all the way from the entry function as soon as you get started. It is easy to get confused. The correct way is to peel it layer by layer like an onion:

这好像是大蒜?

So, let us take a look at the call stack of first screen rendering Performance

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

Judging by the createContext and reactive probably to create a responsive context. As for response, we are still unclear.

Then look at the red box on the right:

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

  • Traverse DOM
  • Complete the two-way binding of data and view
  • Initial rendering

Next, let's verify the conjecture.

Note that so far, we haven't read a line of source code yet

Verify traversal of the DOM

In the call stack, walk and walkChildren been called many times. There is a high probability that they are the specific traversal work execution method, let us confirm.

In the source walk marked method log :

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

Excluding the text node corresponding to the line break "\n " , the printing order is as follows:

walk div
walk <button>add 1</button>
walk "add 1"
walk <p>0</p>
walk "0"

From the printed results, this is a depth-first traversal (if there are child nodes, traverse the child nodes, and if there are no child nodes, traverse the sibling nodes)

Obviously, petite-vue mount When using depth-first traversal each associated with, and traverse to the context state associated DOM node for processing.

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

createApp({count: 0}).mount()

After traversing, <p>{{count}}</p> becomes <p>0</p> .

Determine the granularity of two-way binding

Next we need to confirm the scope of two-way binding, namely:

After the update is triggered, what range of DOM will be traversed again and the corresponding DOM operation will be executed?

After opening Performance , click <button>add 1</button> trigger the update:

As you can see, there is no walk , walkChildren (or similar traversal process), and only reactiveEffect is called to update DOM .

This means mount depth-first traversal of the establishment of state one relationship between the method of updating the DOM.

Because the corresponding relationship is determined, no additional traversal process is needed to determine the DOM that needs to be changed.

After updating the status, you only need to find the update the DOM method and execute it.

For example: link the count state with the following function:

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

Whenever count changes, call setCount(count) to update p corresponding to DOM .

Therefore, petite-vue mainly includes two points:

  1. mount depth-first traversal DOM , for stateful DOM (such as <p>{{count}}</p> ) to establish a one-to-one correspondence between the state and the method of updating the DOM
  2. update found that when state corresponding Method and performs DOM

It can be seen that even if you don't go deep into the source code, you can get a general understanding of the workflow.

If you want to go further, such as understanding how the relationship is established (involving responsive update ), then you need to go deep into the source code.

It is recommended Vue Mastery of Vue 3 Reactivity course, can be filled responsive update this knowledge.

to sum up

This article introduces how to read the source code of a complex framework-from abstract to concrete.

  1. From mount when the update call stack when derived overall workflow
  2. Discover the core knowledge from the overall work process

After mastering the overall work flow and responsive update, you can read the part you are interested in so that you will not fall into the huge amount of code.

Have you lost your studies?


卡颂
3.1k 声望16.7k 粉丝