Vue.js Notes on learning source code


Recently, I’m very interested in the latest editionVue.jsThe source code of learning, feel really good, I think Vue.js The code is so elegant and incisive that the author himself may not have mentioned it. Well, let me do it:)

Sorting out the program structure

Vue.js Is a very typical MVVM program structure, the entire program from the top is roughly divided into

  1. Global design: including global interface, default options, etc
  2. VM instance design: including interface design (VM prototype), instance initialization process design (VM constructor)

Most of the contents can be followed directly Vue.js OfOfficial API reference documentsCorresponding, but the document does not have and is worth mentioning is the constructor design. The following is the most core work content of the constructor I picked out.

During the whole instance initialization process, the most important thing is to establish the relationship between the data (model) and the view (view). Vue.js Similar to many MVVM ideas, it mainly does three things:

  1. The data is monitored by the observer, and the ability to subscribe to the changes of a data item is provided
  2. The template is parsed into a document fragment, and then the directive is parsed to get the data items that each directive depends on and its update method. For examplev-text="message"After being parsed (for illustration only, the actual program logic will be more rigorous and complex)

    1. Dependent data itemsthis.$data.message, and
    2. Corresponding view update methodnode.textContent = this.$data.message
  3. The above two parts are combined by the watcher, that is, the data dependency in the directive is subscribed to the corresponding data observer. When the data changes, the observer will be triggered, and then the view update method corresponding to the dependency will be triggered. Finally, the original association effect of the template can be achieved.

So the core of the whole VM is how to implement the observer, the directive (parser) and the watcher

Document structure Sorting

Vue.js The source code is stored in thesrcIn the table of contents, we mainly focus on this directory (in facttest/unit/specsIt’s also worth looking at the directory, which is the test case for each source file.

srcThere are several parallel folders under the directory, and each folder is a part of independent and complete program design. However, in my opinion, these catalogues have a more three-dimensional relationship before them

  • First of allapi/*Directory, which is almost the “top” interface encapsulation, the actual implementation is buried in other folders
  • And then there wasinstance/init.jsIf you want to know everything from the top down Vue.js It is suggested to start with this document

    • instance/scope.js: data initialization, related subroutines (directory) areobserver/*watcher.jsbatcher.js, andobserver/dep.jsIt is also the key to the relationship between data observation and view dependence
    • instance/compile.js: View initialization, related subroutines (directories) arecompiler/*directive.jsparsers/*
  • Other core elements:directives/*element-directives/*filters/*transition/*
  • And of courseutil/*In fact, there is a similar directorycache.js
  • Finallyconfig.jsDefault configuration item

Space is limited, if you want to “read through” Vue.js I suggest reading and appreciating the whole introduction above.

Next are some code details that I think are worth mentioning

Some code / program details you can’t miss

this._eventsCountWhat is it?

At the beginninginstance/init.jsI immediately noticed a detail, that isthis._eventsCount = {}This sentence is followed by a note

for $broadcast optimization

Very curious, and then continued to look with doubt, until seeapi/events.jsMedium$broadcastMethod to avoid unnecessary depth traversal: when there is a broadcast event coming, if the current VM’s_eventsCountFor0, you do not have to continue propagating the event to its child VMS. And this document will be available later_eventsCountThe implementation of counting.

This is a very clever performance optimization method that can also be used in many places.

Diff mechanism of data update

Recently, there was a lot of discussion about the efficiency of view updating. I guess it is mainly caused by the concept of virtual dom. This time I looked at it in detail Vue.js The related implementation principle of.

In fact, the focus of view update efficiency mainly lies in the update of large list and the update of deep data, and the former is the focus of discussion (I don’t know whether the latter is due to small demand or no dispute). So here’s an introductiondirectives/repeat.jsFor the list update related code.

First of alldiff(data, oldVms)The annotation of this function gives a brief description of the whole comparison and update mechanism. It roughly means to compare the data status of VMS in the old and new lists, and then update the DOM by difference.

Step 1: traverse each item in the new list. If the VM of the item exists before it, type one_reusedI started to look at this fieldinit.jsIt’s also confusing when If there is no corresponding VM, create a new one.

Step 2: traverse every item in the old list if_reusedIf it is not marked, it means that it is no longer in the new list. Destroy the VM in place.

Step 3: sort out the order of the new VMS in the view, and restore the previously typed ones_reusedStandard. This list update is complete.

By the way Vue.js Element transition animation processing based on(v-transition)It’s also designed very cleverly. If you’re interested in it, you won’t introduce it


Vue.js For its components, a[keep-alive]If this feature exists, the cache mechanism will be used to quickly create components when components are repeatedly created to improve the performance of view update. Code indirectives/component.js

Data monitoring mechanism

How to monitor the changes of an object’s properties? It’s easy for us to think of itObject.definePropertyThis API designs a special getter / setter for this property, and then triggers a function in the setter to achieve the effect of listening.

But arrays can be a bit cumbersome, Vue.js The prototype change is adopted for almost every method that may change the data

However, this strategy faces two problems

  1. Unable to listen to datalength, leading toarr.lengthSuch data changes cannot be monitored
  2. Change the data through the corner, i.e. similararr[2] = 1Such assignment operations cannot be monitored

For this reason Vue.js It is clearly indicated in the document that it is not recommended to modify the data with the direct corner marker

At the same time Vue.js Two additional “sugar methods” are provided$setAnd$removeTo make up for the inconvenience. On the whole, it’s a design with a degree of trade-off. I personally used a similar design in the design of data binding library (a half done internal project will not show ugliness), so I quite agree with it and have resonance.

State machine design of path parser

First of allparsersThere are all kinds of “treasure” waiting for everyone to dig in the folder! Take a good look and you won’t regret it

parsers/path.jsThe main responsibility is to extract the data under a certain “path” in a JSON data, such as:

var path = 'a.b[1].v'
var obj = {
  a: {
    b: [
      {v: 1},
      {v: 2},
      {v: 3}
parse(obj, path) // 2

So yespathString parsing becomes the key. Vue.js Through state machine management, the path can be resolved

At first, it’s very big, but if you comb it a little bit:

Maybe we can see it more clearly. Of course, we can also find that there is a small problem in the source codeinIdentThis state is ambiguous and corresponds to three places in the graph, namelyin identAnd twoin (quoted) ident

In fact, I was looking at the codeSubmit the bug, the author was quick and quick, and fixed it on the same day. Now the latest code is not like this:

Moreover, the identification of state machine is changed from string to numeric constant, so the parsing is more accurate and the execution efficiency is higher.

A little bit of my own thinking

The first is the parsing process of the view, Vue.js The strategy is to convert element or template string into document fragment, and then decompose and parse its sub components and directives. I think there is some room for performance optimization. After all, DOM operation is still slower than pure JavaScript operation.

Then there is the thinking based on the mobile terminal, Vue.js Although it is really very small (about 22 KB after min + gzip), whether it can be smaller, continue to abstract the common core functions, and be faster is also a question worth considering.

Third, I love to pass Vue.js For the modular development mode, whether Vue can also use the form of web components + virtual DOM to bring such development mode to more fields is also a very meaningful thing.


Vue.js The code details in are more than that, such as:

  • cache.jsCache mechanism design and scenario application inparsers/path.js(2)
  • parsers/template.jsInsidecloneNodeMethod rewriting and compatibility with HTML auto completion mechanism
  • In the development and production environment, annotation nodes and invisible text nodes are used as “placeholders” of views

I’m also reading the code, understanding Vue.js At the same time, I learned a lot. At the same time, I think the code implementation is just Vue.js One of the outstanding elements, the overall program design, API design, details of the trade-off, project engineering considerations are very good!