A brief discussion on the whole process of Vue's first rendering

A brief discussion on the whole process of Vue's first rendering

Yesterday, a friend asked me what Vue did when the page was first loaded. It seems that this question may be vague in many friends' minds. Today, let's take a detailed look at the first rendering process of Vue.

To understand the whole process of Vue's first rendering, where should we start? Obviously, we should start with the entry file, that is, main.js

1. Vue initialization

First, let's look at main.js. The first and most important thing is to introduce vue.

import vue from 'vue'

In fact, after vue is packaged, there are multiple versions in the dist folder, namely
Universal version (UMD) : full version vue.js and runtime version vue.runtime.js in
CommonJs version : full version in vue.common.js and runtime version in vue.runtime.common.js
ES Module versions: full version in vue.esm.js and runtime version in vue.runtime.esm.js
Generally, after vue 2.6, the projects we create with vue/cli use the vue.runtime.esm.js runtime version, that is, when introducing vue, the vue.esm.js version will be introduced.

So, after vue is introduced, will the relevant code in vue be executed? So which code in the Vue source code is executed most recently (the introduced Vue is the packaged Vue in the Vue source code), we first need to know where the entry file is

vue entry file

The entry file of Vue is mainly under src/platforms/web of the Vue source code structure

insert image description here

When packaging Vue, you can choose different Vue entry files for packaging. Different entry files will package different Vue versions.
Here we mainly talk about the complete version of entry-runtime-with-compiler.js
Let's first understand the difference between the full version and the runtime version

Differences between full version and runtime version

The full version is a combination of runtime version + compiler. The runtime version does not have a compiler, that is, it has no template compilation function. It is mainly used to create Vue instances and render virtual DOM. Smaller and lighter (the compiler has more than 3,000 lines of code)
What does it mean?

<body>
	<div id="app">
		<p>I am the content in index.html</p>
	</div>
</body>
new Vue({
	template: '<div>I am the content rendered by template</div>'
}).$mount('#app')

The above situation,
If it is the full version of vue, there is a compiler compiler, which will compile the template passed in when new Vue into a render function and assign it to the render attribute of options. Then after $mount, it will render the render function into a virtual dom, and then convert the virtual dom into a real dom, so the final page will show the sentence " I am the content rendered by the template template ." The original sentence will be overwritten.

If it is a runtime version, there is no compiler and the content in the template will not be compiled, so the page will only have the original DOM

Now let's continue to look down and find the entry file. Let's start to see what will be executed

insert image description here

It can be seen that the entry file first imports vue, then undergoes some processing, and finally exports vue
We first find out where the Vue constructor is created step by step by importing the path of Vue. As shown above, vue is imported from runtime/index, so let's go to runtime/index

insert image description here

This file is the same. It imports vue, does some processing, and then exports vue. Let's go up and find core/index

insert image description here

The same is true for this file. Let's continue looking up and find ./instance/index

insert image description here

Here, we find the creation of our vue constructor, which is in the src/core/instance/index.js file of the source code.

Then, from the reference relationship just above, we can find that after Vue is introduced into the project, the order of the files that will be executed first is

src/core/instace/index.js ===> 1
src/core/index.js ===> 2
src/platforms/web/runtime/index.js ===> 3
src/platforms/web/entry-runtime-with-compiler.js 4

So, let's take a look at what each file executes.
First src/core/instace/index.js

1.1、src/core/instace/index.js

First, this file defines the vue constructor and initializes some vue instance properties and instance methods, that is, various methods and properties are added under the vue.prototype prototype

insert image description here

Next, let's take a closer look at which instance properties or methods of Vue are initialized by each method.

1.1.1、initMixin(Vue)

insert image description here

1.1.2、stateMixin(Vue)

insert image description here

1.1.3、eventsMixin(Vue)

insert image description here

1.1.4、lifecycleMixin(Vue)

insert image description here

1.1.5、renderMixin(Vue)

insert image description here

After src/core/instace/index.js is executed, it will continue to execute the next file

export function initGlobalAPI (Vue: GlobalAPI) {
  // config
  const configDef = {}
  configDef.get = () => config
  if (process.env.NODE_ENV !== 'production') {
    configDef.set = () => {
      warn(
        'Do not replace the Vue.config object, set individual fields instead.'
      )
    }
  }
  // Added a new config property Object.defineProperty(Vue, 'config', configDef)

  // Added a new static member util
  Vue.util = {
    warn,
    extend,
    mergeOptions,
    defineReactive
  }

  // Added 3 static members set delete nextTick
  Vue.set = set
  Vue.delete = del
  Vue.nextTick = nextTick

  // Added a new static member observable
  Vue.observable = <T>(obj: T): T => {
    observe(obj)
    return obj
  }

  // Initialize options. Options is an empty object.</T>
  Vue.options = Object.create(null)
  ASSET_TYPES.forEach(type => {
    Vue.options[type + 's'] = Object.create(null)
  })

  Vue.options._base = Vue

  // Register a global component keep-alive builtInComponents inside which is the keep-alive component export extend(Vue.options.components, builtInComponents)

  // The following are initialized Vue.use() Vue.mixin() Vue.extend() 
  initUse(Vue)
  initMixin(Vue)
  initExtend(Vue)
  // Initialize Vue.directive(), Vue.component(), vue.filter()
  initAssetRegisters(Vue)
}

1.2, src/core/index.js

insert image description here

It can be seen that this file mainly adds a lot of static instance methods and properties to Vue. What are the specific ones added?
Let's continue to look at the method initGlobalAPI(Vue) that is executed

1.2.1 initGlobalAPI(Vue)

export function initGlobalAPI (Vue: GlobalAPI) {
  // config
  const configDef = {}
  configDef.get = () => config
  if (process.env.NODE_ENV !== 'production') {
    configDef.set = () => {
      warn(
        'Do not replace the Vue.config object, set individual fields instead.'
      )
    }
  }
  // Added a new config property Object.defineProperty(Vue, 'config', configDef)

  // Added a new static member util
  Vue.util = {
    warn,
    extend,
    mergeOptions,
    defineReactive
  }

  // Added 3 static members set delete nextTick
  Vue.set = set
  Vue.delete = del
  Vue.nextTick = nextTick

  // Added a new static member observable
  Vue.observable = <T>(obj: T): T => {
    observe(obj)
    return obj
  }

  // Initialize options. Options is an empty object.</T>
  Vue.options = Object.create(null)
  ASSET_TYPES.forEach(type => {
    Vue.options[type + 's'] = Object.create(null)
  })

  Vue.options._base = Vue

  // Register a global component keep-alive builtInComponents inside which is the keep-alive component export extend(Vue.options.components, builtInComponents)

  // The following are initialized Vue.use() Vue.mixin() Vue.extend() 
  initUse(Vue)
  initMixin(Vue)
  initExtend(Vue)
  // Initialize Vue.directive(), Vue.component(), vue.filter()
  initAssetRegisters(Vue)
}

1.3, src/platforms/web/runtime/index.js

insert image description here

1.4, src/platforms/web/entry-runtime-with-compiler.js

insert image description here

The main function of this file is to rewrite the $mount method under the vue prototype. We will talk about what is done in the $mount method later.

1.5. Summary of Vue initialization

The entire process written above is something that will be executed immediately after the user imports the vue file when using vue

After these are executed, will they continue to execute the main.js file in our project?
This will execute

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

At this time, our vue constructor will start to be called

2. Vue constructor execution

At this point, the vue constructor will be executed first.

insert image description here

It can be seen that the _init method is mainly executed. From here, the life cycle of Vue begins to execute

insert image description here

The above is just the most important part of the code in the _init() method (there is too much code, so I won’t take screenshots of all of it. You can look at the source code yourself). It can be seen that:

2.1. beforeCreate hook

Before the life cycle beforeCreate hook, the main thing vue does is to add various properties and methods to the vue prototype, various static properties and methods to vue, and various properties and methods to the vm instance.

2.2, created hook

As can be seen from the figure above, after the beforeCreate hook is executed, three methods are mainly executed: initInjections, initState, initProvide

// Inject inject into the vm instance callHook(vm, 'beforeCreate')

// Inject inject into the vm instance initInjections(vm)

// Initialize vm's $props, $methods, $data, computed, watch
initState(vm)

// Inject provide into vm instance initProvide(vm)

//Execute the created lifecycle callHook(vm, 'created')

In fact, the focus is on the initState(vm) method, in which the $props, $data, $methods, computed, watch, etc. of the vm instance are initialized. At the same time, an initData() method is called inside it, which calls the observer() method to convert all the data in data into responsive data. That is, add a data interceptor.

So it can be seen that before the create life cycle, the $props, $data, $methods, computed, and watch properties of the vm will be initialized.
Therefore, this is why we can call various data in our data in create and call various methods such as props or methods.

After the created life cycle is completed, continue to look down

insert image description here

It can be seen that here it is judged whether vm.$options.el exists and what vm.$options.el is.
When new Vue ({}) is called, all properties of the object passed in will be mounted under options.

new Vue({
  el: '#app'
  router,
  store,
  render: h => h(App)
})

Therefore, vm.$options.el is the el passed in above.
Here we check whether el exists. If it exists, we will continue to execute $mount.

Then everyone may be curious, if it does not exist, then it will be stuck and will not be able to move forward. Yes, if not, it will not go on. If you want the code to continue, you must execute the $mount method.
At this point, let's look at the common use of Vue.

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

There is no el passed in here, so the source code

insert image description here

I definitely won’t leave. However, when creating a new Vue, users can use the newly created Vue instance to call $mount. In this way, it may be easier for you to understand the life cycle diagram on our official website.

insert image description here

Okay, let's move on. The next step is to execute $mount. Let's look at the $mount method

2.3, $mount function

Remember we overwrote $mount when we initialized it before? So when we execute $mount now, we execute the overwritten mount.

insert image description here

Here, the mount method before the rewrite is stored before the rewrite, and then the mount method before the rewrite is called at the end.
After rewriting, the most critical code is to determine whether there is a render function

insert image description here

The main function of this step is to determine whether there is a render function.
If yes, directly execute the render function before the rewritten $mount method.

If not, it will first determine whether there is a template (whether options.template exists, options.template may be an id selector or a dom). If a template exists, the content in the template will be obtained and assigned to the template. If options.template does not exist, the dom specified by el will be used as the template (i.e. #app), the dom under el will be obtained, and assigned to the template

After the template gets the DOM, it continues to compile the template into a render function and mount the compiled render function under the options.render attribute

insert image description here

Then it will continue to execute the $mount before the rewrite. Understanding this, we can understand the other part of the life cycle diagram.

insert image description here

2.4, beforeMount

Next, let's continue to look at the execution of the $mount function before rewriting

insert image description here

It can be seen that \$mount mainly executes the function mountComponent. Let's continue to look at the mountComponent function

insert image description here

It can be seen that this function mainly does the following 4 things. Let's look at them one by one.
1. The beforeMount hook is executed, so we can conclude that before beforeMount, we mainly initialize and get the render function. After beforeMount, the render function is rendered into a virtual DOM, and then the real DOM is updated.
There are three ways to get the render function <br /> First: the user passes it in to render

insert image description here

Second: .vue file is compiled into render

insert image description here

In this way, a render function is passed in, and the App.vue file is executed before the h function is used in the function.
The .vue file is finally converted into a render function with the help of vue-loader

Third, compile the template into a render function

insert image description here
2. Define an updateComponent function, which calls the vm_update method and executes the vm._render() method.

The result after the operation is passed as a parameter to the _update method. As we said before, the _render method renders the render function internally into a virtual dom, so the return value of _render() is a vnode.

Let's first look at how the render function is converted into a virtual DOM inside the _render() function.

insert image description here

Then let's look at what is done inside the _update function

insert image description here

It can be seen that in the _update function, the __patch__ method is executed to compare the two new and old DOMs, so as to find out the differences and update the real DOM. If it is the first rendering, the current vnode is directly used to generate the real dom.

Therefore, it is concluded that the main function of the entire updateComponent method is to render the render function and update the DOM.
The key to updating the DOM lies in when to call the updateComponent function.

3. Create a new watcher instance

insert image description here

It can be seen that when a new watcher instance is created, the updateComponent function is passed in as a parameter.
At this point, when we look at the new Watcher, the Watcher constructor will be executed. Let's see what is done in the Watcher constructor.

insert image description here

There are three types of watchers: rendering watchers, $watch function watchers, and computed watchers. The page we render here is the rendering watcher

Above, we pass the function we passed in to the getter

insert image description here

Going down, get() is called

insert image description here

It can be seen that get() calls the function we passed in, and the function we passed in is the render function, which triggers the virtual dom to update the real dom, and the returned value is the real dom after rendering, and finally assigned to this.value, which will finally be used to update the dependent. Our current weather instance is the watcher of the main vue instance, so it can be understood as the watcher of the entire page. When we call this.$fouceUpdate(), we are calling the update method of this instance to update the entire page.
So, when a new Wacher is created, updateComponent will be automatically called once, which is our first rendering.

Now, let’s continue looking down

insert image description here

Internally, a judgment is made if vm._isMounted is true (that is, the Mounted hook has been executed), and vm._isDestroyed is false (that is, the current component has not been destroyed). At this point, if an update occurs, it means that it is not the first rendering, so the beforeUpdate hook is executed, and updated will definitely be executed later. We won't talk about updates here.

After new Watcher, the code continues to go down

insert image description here

If the current vnode is null, it means that the virtual dom has not been generated before, which means that this is definitely the first rendering. At this time, vm._isMounted is set to true. And execute the mounted hook function, at this time, the first rendering is completed.

2.5, mounted

It can be seen that the main work done in the whole process from beforeMount to mounted is
1. Rendering render function becomes virtual dom vnode
2. Execute the vm._update function to convert the virtual dom into the real dom
If it is between beforeUpdate and updated hooks, it means it is not the first rendering, then the virtual DOM will have two old and new ones. At this time, the role of the vm._update function is to compare the old and new vnodes, find the differences, and update what needs to be updated

This is the entire process of the first render. This is the end of this article about the whole process of Vue's first rendering. For more relevant Vue's first rendering content, please search for previous articles on 123WORDPRESS.COM or continue to browse the following related articles. I hope everyone will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • Detailed description of the server-side rendering process of Vue components
  • A brief analysis of Vue rendering process
  • Detailed explanation of the first rendering process of Vue source code

<<:  Linux system to view CPU, machine model, memory and other information

>>:  A brief discussion on Nginx10m+ high concurrency kernel optimization

Recommend

JS canvas realizes the functions of drawing board and signature board

This article shares the specific code of JS canva...

A small question about the execution order of SQL in MySQL

I encountered a sql problem at work today, about ...

CentOS 6.5 configuration ssh key-free login to execute pssh command explanation

1. Check and install pssh, yum list pssh 2. Becau...

Summary of MySQL data migration

Table of contents Preface: 1. About data migratio...

Vue achieves seamless carousel effect

This article shares the specific code of Vue to a...

Solution to the garbled problem of web pages when the encoding is set to utf-8

Recently, when I was writing web pages with PHP, I...

JavaScript realizes magnifying glass special effects

The effect to be achieved: When the mouse is plac...

JavaScript+HTML to implement student information management system

Table of contents 1. Introduction 2. Rendering 3....

Using MySQL database in docker to achieve LAN access

1. Get the mysql image docker pull mysql:5.6 Note...

Web designers should optimize web pages from three aspects

<br />With the increase of bandwidth, there ...

How to use http and WebSocket in CocosCreator

Table of contents 1. HttpGET 2. HTTP POST WebSock...

Detailed explanation of the role of static variables in MySQL

Detailed explanation of the role of static variab...

How to change $ to # in Linux

In this system, the # sign represents the root us...

JS implementation of Apple calculator

This article example shares the specific code of ...