The constructor

When we use VUE, we use

var app = new Vue({ el: '#app', data: { message: 'Hello Vue! '}})Copy the code

Here an instance of Vue is generated, and it is clear that the Vue after the new keyword is an ES6 class or constructor. View the source code to see a constructor.

// New Vue produces an object, which is a Vue instance object that actually works in memory. Function Vue (options) {// When user new Vue, _init is the method added to initMixin this._init(options)} // The advantages of using constructors instead of class syntax are as follows: // Through the function call of xxxMixin, the function of Vue is split into multiple modules to achieve. // They all extend methods on Vue's Prototype. // This approach is difficult to implement with Class. initMixin(Vue) stateMixin(Vue) eventsMixin(Vue) lifecycleMixin(Vue) renderMixin(Vue) export default VueCopy the code

In Vue source code, is the use of ES6 syntax, but this did not use the class to declare, because if written class declaration class, class will be very large, and Vue contains much content, need to do split, so the use of constructor syntax, adopted in different modules to add functions to the Vue constructor.

Option object, Vue constructor, Vue instance object

In the official Vue documentation, you can see this sentence.

In daily development, writing a component looks like this:

<template> ... </template> <script> } </script>Copy the code

The first thing to be clear about is that all we write in our daily development is option objects. This is different from the React class (or the React component constructor) that defines React directly.

Notice that the option object passed in by the root component, the data property, is an object. In addition to the root component, the option object we write, the data property, is a method. The difference is that the root component is new Vue, which creates Vue instance objects directly. The other option objects we write are first extended by vue.extend into child Vue constructors inherited from the Vue constructor when the code runs. The component instance (the actual working Vue component object) is instantiated with the child Vue constructor. So the data property of the option object we write is common to all component instances, so the data property needs to be a method that returns unique data data for different component instances.

In conclusion, the distinction between option objects, Vue constructors, and Vue instance objects helps us to better understand how Vue works. From the above analysis we can see that the author has a good idea of object-oriented programming.

closure

Vue source code also some closure applications.

Cache variable

// The deP variable exists in the scope chain of the get/set function under Object.defineProperty. Export function defineReactive (const dep = new dep () object.defineProperty (obj, key, {get: function reactiveGetter () { ... Dep.depend ()} set: function reactiveSetter (newVal) {... // Dep.notify ()}}}Copy the code

Because of the closure, the DEP object is not garbage collected after the defineReactive method is executed, but exists in the scope chain of the GET/SET method.

The function is currified

The patch method is used to convert vNodes into DOM. The PATCH method is platform-dependent, and DOM and related operations are different in Web and Weex environments. Therefore, the different parts of the platform are abstracted as parameters, and a method called createPatchFunction is passed to generate patch methods for different platforms.

// nodeOps, modules is platform-specific logic passed to createPatchFunction. The return value is patch. Patch can use nodeOps, modules to perform some real DOM operations. export const patch: Function = createPatchFunction({ nodeOps, modules })Copy the code

Caulization is an application of closure. Here, the patch method polymorphism is realized by using Caulization.

In this way, the patch method is called during the process of VNode conversion into a real DOM node, which only considers the logic of the old and new VNode trees of diff, not the logic of manipulating the real DOM.

The this pointer to the lifecycle hook function

New Vue({data: {a: 1}, created: function () {// 'this' points to vm instance console.log('a is:' + this.a)}}) // => "A is: 1"Copy the code

The orientation of this is also where we feel a little strange when we first learn Vue. Why does this refer to data?

Because we write the option object, which is then assembled into the component’s constructor. Instantiate the component with the constructor to get the VM instance. When a VM instance calls a lifecycle hook function

Handlers [I]. Call (vm)Copy the code

The next question is how the VM instance accesses data.

Vue first turns data into a responsive object and places it on the vm’s _data property. Each value vm._data. XXX is then delegated to vm. XXX by proxy. The proxy here is also implemented via Object.defineProperty

// The principle is the following code, such as data a attribute. DefineProperty (vm, 'a', {get: getGet._data.object.defineProperty (vm, 'a', {get: function proxyGetter () { return this._data.a }, set: function proxySetter (val) { this._data.a = val } })Copy the code

The life cycle

BeforeCreate:

You cannot get the values defined in props, data, or call functions defined in methods.

The vue-Router source code looks like this:

// We can write this.$router. The following code logic works. Vue.mixin({ beforeCreate () { this._routerRoot = this this._router = this.$options.router } }) Object.defineProperty(Vue.prototype, '$router', { get () { return this._routerRoot._router } })Copy the code

BeforeCreate is a good way to hang variable objects for the VM instance that you want to use when writing code.

Similarly, in the Vuex source code, beforeCreate is used to add the $store variable to the VM instance.

Created:

If you need to access props, data, etc., you need to use the Created hook function.

In the component tree of the application we wrote, the order of execution for Created is parent to child.

Mounted:

After the actual DOM is mounted, execute Mounted.

In the component tree of our application, unlike Created, Mounted, the order of execution is child to parent.

Destroy:

Component’s destruction hook function.

In our application’s component tree, the same order of execution as Mounted and destroy is from child to parent.

activated & deactivated

The activated and deactivated hook functions are custom hooks for keep-alive components.

The details of the nextTick

NextTick is a microtask Promise implementation by default on the Web, using macro tasks if not supported.

The confusion when studying the diff algorithm

When LEARNING the Vue diff algorithm, I was confused by the comparison between the old and new DOM trees. What happens if the node is a VNode of component type?

The first thing to be clear about is that most articles on diff algorithms do not consider component type VNodes. So when looking at these articles, just think of all the nodes as VNodes that can be mapped to real DOM nodes and consider the algorithm described.

This is conducive to a more focused study of the logic of the DIFF algorithm itself. The DIFF algorithm of Vue is based on the Snabbdom library, that is, a DIFF algorithm without component nodes.

Diff logic with component type Vnode nodes

When there is no component type VNode in the old and new VNode trees, patch is only responsible for comparing the differences between the two trees and then operating the real DOM. However, when there are vNodes of component type in the VNode tree, the responsibility of patch method becomes more, such as producing subtrees of VNode of component type.

Consider a scenario where the new diff tree has one more Vnode than the old one. Simply add a real DOM mapped by this Vnode to the real DOM tree. However, when the old and new VNode trees contain component type VNodes, the new tree has one more component type VNode than the old tree. Component type VNodes cannot be mapped directly to the real DOM, but instead form a child Vnode tree. This process of forming a child tree produces the same recursive effect as that of forming a parent tree.

So a page of Vnode tree, should be a big tree, there are also nested many trees, their diff logic is not interfering with each other in some cases, in addition to the tree to the tree in the Vnode node to add or delete.

As can be seen from the above hypothetical scenario, it takes some work to figure out the overall Vue diff logic, and the specific logic should be studied in the source code.

algorithm

When studying the logic of diff algorithm, we can also see some algorithm ideas of the author.

The first is that the virtual DOM tree uses a tree structure. Diff is the traversal of the tree. Although the comparison is layer by layer, the traversal is DFS (depth-first traversal).

In Vue3. X, the longest recursive subsequence algorithm (the topic on Leecode, which can be solved by greedy + dichotomy) is also used to ensure the minimum movement of the real DOM when comparing the Children changes of two VNodes.