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
- Global design: including global interface, default options, etc
- 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:
- The data is monitored by the observer, and the ability to subscribe to the changes of a data item is provided
-
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 example
v-text="message"
After being parsed (for illustration only, the actual program logic will be more rigorous and complex)- Dependent data items
this.$data.message
, and - Corresponding view update method
node.textContent = this.$data.message
- Dependent data items
- 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 thesrc
In the table of contents, we mainly focus on this directory (in facttest/unit/specs
It’s also worth looking at the directory, which is the test case for each source file.
src
There 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 all
api/*
Directory, which is almost the “top” interface encapsulation, the actual implementation is buried in other folders -
And then there was
instance/init.js
If 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.js
、batcher.js
, andobserver/dep.js
It is also the key to the relationship between data observation and view dependence -
instance/compile.js
: View initialization, related subroutines (directories) arecompiler/*
、directive.js
、parsers/*
-
- Other core elements:
directives/*
、element-directives/*
、filters/*
、transition/*
- And of course
util/*
In fact, there is a similar directorycache.js
- Finally
config.js
Default 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._eventsCount
What is it?
At the beginninginstance/init.js
I 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.js
Medium$broadcast
Method to avoid unnecessary depth traversal: when there is a broadcast event coming, if the current VM’s_eventsCount
For0
, you do not have to continue propagating the event to its child VMS. And this document will be available later_eventsCount
The 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.js
For 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_reused
I started to look at this fieldinit.js
It’s also confusing when If there is no corresponding VM, create a new one.
Step 2: traverse every item in the old list if_reused
If 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_reused
Standard. 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
Component’s[keep-alive]
Characteristics
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.defineProperty
This 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
- Unable to listen to data
length
, leading toarr.length
Such data changes cannot be monitored - Change the data through the corner, i.e. similar
arr[2] = 1
Such 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$set
And$remove
To 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 allparsers
There 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.js
The 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 yespath
String 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 codeinIdent
This state is ambiguous and corresponds to three places in the graph, namelyin ident
And 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.
summary
Vue.js The code details in are more than that, such as:
-
cache.js
Cache mechanism design and scenario application inparsers/path.js
(2) -
parsers/template.js
InsidecloneNode
Method 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!