What does reading this article help you?
- When learning vue source code found the componentization process is very round?
- In a reactive process
Observer
,Dep
,Watcher
Three objects silly silly not clear? - Confused about the process of collecting objects, arrays, and distributing updates?
dep
,watcher
Intermodulation chaos? - Want to go directly to codeology source code is very boring, boring, and lack of general flow concept?
- Like me, haven’t seen the vue source code for a while and seem a bit forgotten? But don’t know where to start in a quick review?
This article focuses on componentized and responsive core processes. Maybe you can find the answer to all of these questions here
1. Componentization process
1. The wholenew Vue
What does the phase do?
- performinitOperation. Include and not limit
initLifecycle
,initState
Etc. - Perform the mount. Mount the element
- compilerThe step is not available in the Runtime-only version.
- The Compiler step compiles the template property to generate the Render function.
- Generally in the project is in
.vue
File development, through vue-loader processing to generate render function.
- performrender. Generate vnode
- Render example, as shown below
<div id="app">{{ message }}</div> Copy the code
- Corresponding to the handwritten render function
render (h) { return h('div', { attrs: { id: 'app'}},this.message) } Copy the code
- Patch. The old and new VNodes are diff rendered to the real DOM
2. How do normal DOM elements render to the page?
- perform
$mount
.- Actual execution
mountComponent
- This is going to instantiate a Watcher
- Watcher does that
get
Method, triggerupdateComponent
- Actual execution
- perform
updateComponent
. performvm._update(vm._render(), hydrating)
- perform
vm.render()
.- Render actually calls
createElment
(h
Function) - Generate components, native VNodes, and return, depending on the tag
- Render actually calls
- perform
vm.update()
.createElm()
到createChildren()
Recursive calls - Convert the VNode to a real DOM and render it to the page
3. How do components render to pages?
-
Here with the following code case to explain more clearly ~ yes, is so familiar! Is an initialized Vue project
// mian.js import Vue from 'vue' import App from './App.vue' new Vue({ render: h= > h(App), }).$mount('#app') Copy the code
// App.vue <div id="app"> <p>{{ msg }}</p> </div> <script> export default { name: 'App', data () { return { msg: 'hello world'}}}</script> Copy the code
-
The differences between components and ordinary elements are mainly explained in two points:
- How to generate VNodeCreate a component VNode
createComponent
- (Note: this corresponds to the expansion of the purple block of the Render flow above!!)
- Distinguish between common vNodes
- Common VNode: Tags are reserved HTML tags, such as
tag: 'div'
- Component VNode: Tag is used
vue-component
At the beginning, such astag: 'vue-component-1-App'
- Common VNode: Tags are reserved HTML tags, such as
-
How to patch — component New Vue to patch process createComponent
- (Note: this step corresponds to the expansion of the purple block in the patch process above!!)
- $vnode: placeholder vnode. The final render is where the VNode is mounted. All components are recursively called createComponent until there is no longer a component VNode, which is eventually converted to a normal DOM.
{ tag: 'vue-component-1-App'.componentInstance: {component instance},componentOptions: {Ctor, ... ,}}Copy the code
- _vnode: renders vnodes.
{ tag: 'div', { "attrs": { "id": "app"}},// The corresponding placeholder vnode is $vnode parent: { tag: 'vue-component-1-App'.componentInstance: {component instance},componentOptions: {Ctor, ... ,}},children: [ // Corresponds to the p tag { tag: 'p'.{{MSG}} children: [{ text: 'hello world'}]}, {VNode is the same if there are components tag: 'vue-component-2-xxx'}}]Copy the code
- How to generate VNodeCreate a component VNode
4. Vue componentization simplifies the process
- You may have lost your head after watching the fine-grained Vue componentization process, but I will review it with a simplified version of the flow chart to improve your understanding
2. Responsive process
- Case code
/ / case
export default {
name: 'App',
data () {
return {
msg: 'hello world',
arr = [1.2.3]}}}Copy the code
1. Rely on collection
-
This section introduces three objects: Observer, Dep, and Watcher. There are two collection methods: Object and array
- Three core objects:
Observer
(blue),Dep
(green),Watcher
(purple)
- Dependency collection preparation phase — Instantiation of the Observer, Dep
- Notice how objects and root arrays are handled differently. Here is the core code + diagram to explain
// The following is a description of the methods used in initData calls, in the order in which they are called function observe (value, asRootData) { if(! isObject(value))return // Non-objects are not processed // Instantiate the Observer object var ob; ob = new Observer(value); return ob } function Observer (value) { this.value = value; // Save the current data this.dep = new Dep(); // Instantiate the deP, array to rely on the collected DEP (corresponding to the case arR) def(value, '__ob__'.this); if (Array.isArray(value)) { if (hasProto) { // This will rewrite the array prototype. __proto__ points to an object that overrides the array method protoAugment(value, arrayMethods); } else { copyAugment(value, arrayMethods, arrayKeys); } this.observeArray(value); } else { this.walk(value); }}// Go through the array of elements, and observe each entry. That is, an object in the array is converted to a responsive object Observer.prototype.observeArray = function observeArray (items) { for (var i = 0, l = items.length; i < l; i++) { observe(items[i]); }}// Call defineReactive to traverse all attributes of the object Observer.prototype.walk = function walk (obj) { var keys = Object.keys(obj); Keys = [' MSG ', 'arr'] for (var i = 0; i < keys.length; i++) { defineReactive(obj, keys[i]); }}function defineReactive (obj, key, val) { // Generate a closure dep var dep = new Dep(); // If val is of type object, recursively call observe. Arr in the case code follows this logic varchildOb = ! shallow && observe(val);Object.defineProperty(obj, key, { get: function reactiveGetter () { // find the value of value var value = getter ? getter.call(obj) : val; if (Dep.target) { // dep. target is the current Watcher // Here is the closure dep dep.depend(); if (childOb) { // The case code arR will go to this logic childOb.dep.depend(); // Here is the deP in the Observer, where the array ARR depends on collection if (Array.isArray(value)) { dependArray(value); }}}return value }, set: function reactiveSetter (newVal) { // This is explained in an update distributed below}}); }Copy the code
- Dependency collection trigger phase — Wather instantiates, accesses data, and triggers dependency collection
- Three core objects:
2. Send updates
-
Distribute update distinguish object attribute, array method to explain
-
So let’s just think about, what happens when I do this?
this.msg = 'new val'
this.arr.push(4)
-
Yes, there is no doubt that they trigger the get first, and then what? Let’s look at
- Object property changes trigger set and send updates.
this.msg = 'new val'
.Object.defineProperty (obj, key, { get () {... },set: function reactiveSetter (newVal) { var value = getter ? getter.call(obj) : val; // Determine whether the new value has changed from the old value if(newVal === value || (newVal ! == newVal && value ! == value)) {return } // If the new value is a reference type, convert it to reactivechildOb = ! shallow && observe(newVal);// All watchers in deP are notified to updatedep.notify(); }}...Copy the code
- Array call method.
this.arr.push(4)
// Array method overrides are in Observer methods function Observer () { if (hasProto) { This.arr.__proto__ = arrayMethodsprotoAugment(value, arrayMethods); }}// The following is an implementation of the array method override var arrayProto = Array.prototype; // Save the prototype of the real array var arrayMethods = Object.create(arrayProto); // Create an object based on a real array Arraymethods.__proto__ = array.prototype var methodsToPatch = [ 'push'.'pop'.'shift'.'unshift'.'splice'.'sort'.'reverse' ]; // A decorator model that overrides 7 array methods methodsToPatch.forEach(function (method) { // Save the native array method var original = arrayProto[method]; // Hijack an array method in an arrayMethods object def(arrayMethods, method, function mutator () { var args = [], len = arguments.length; while ( len-- ) args[ len ] = arguments[ len ]; var result = original.apply(this, args); var ob = this.__ob__; // When we call this.arr.push(), we get an ob instance of the array object var inserted; switch (method) { case 'push': case 'unshift': inserted = args; break case 'splice': inserted = args.slice(2); break } if (inserted) { ob.observeArray(inserted); } // Since the array object instantiates a DEP in the New Observer, we can get the DEP attribute in the OB instance ob.dep.notify(); return result }); }) Copy the code
- Object property changes trigger set and send updates.
-
This is the end of the whole New Vue phase, all the way to dependency collection and distribution of updates. As can be seen from the flow chart, Vue application is composed of Vue components. Although the whole componentized and responsive process is many, the core path once it is through, you will suddenly understand.
-
In general, Vue source code is actually relatively good to use. The overall code process is very clear, if you want to go into a certain logic can be combined with the flow chart to carefully study, I believe you will gain.
Writing this article is also a knowledge of their own precipitation, after all, long before the study of Vue source code, but has not taken notes. Looking back now, I find that many of them are a little forgotten, but lack a quick memorization, review notes. If you want to directly hard source re memory, or more time-consuming and laborious ~ as knowledge sharing, I hope to help to want to learn source code, want to advance you, we encourage each other, progress together!