This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.

Only special effort, can look effortless!

preface

This is a Vue source series of articles, recommended from the first article Vue source series (a) : Vue source interpretation of the correct posture to start reading. Article is my personal learning source code of a process, here to share the hope to help you.

This article continues to update, expect to like and follow 🙏

Find the Vue constructor

A test case was written in the previous article (Vue Source Code Series 1: Correct Posture for Interpreting Vue source code) and associated with the source files.

There are two ways to find the vue constructor:

The debugger to find

Debugger search this method is relatively simple, but very not programmers, do not have to step by step to find, logic is not rigorous. How to find the debugger, from the last article in the breakpoint (debugger) start. This is the code that executes to a breakpoint

Next, for those of you who don’t know how to break a point, the ICONS in the upper right corner of the image are used for debugging breakpoints

  • Icon 1: Resume script execution – To resume script execution, i.e. to skip the debugger and continue executing the script
  • Chart 2: step over next function call – When it encounters a function, it executes the function directly and proceeds to the next step without displaying the details of the function being executed.
  • Icon 3: step into next function call – When it encounters a function, it does not run the function directly, but instead goes inside the function and executes it step by step, showing the details of the specific function being executed.
  • Icon 4: Step out of current functionIcon 3Inside the function, we can use this icon function to execute the rest of the code inside the function.
  • Icon 5: step – Comes step by step and encounters the function entry, can be understood asChart 2Icon 3The combination of
  • Icon 6: deactivate breakpoints – Deactivate breakpoints 😉
  • Icon 7: Don’t pause on exceptions – To cancel Google Chrome exceptions, check ☑️ Pasue on Caught exceptions

Click icon 5 twice to find the Vue constructor.

How do I find the Vue constructor in the source code? Just right-click Reveal in sidebar (shown in the sidebar) to see the location of the Vue constructor in the source code.

The source code is in vue – > SRC – > core – > instance – > index.js.

.function Vue (options) {
    // In non-production environments (development environments), a warning is ignored if the constructor is not called with new
    // if (process.env.NODE_ENV ! == 'production' &&
    / /! (this instanceof Vue)
    // ) {
    // warn('Vue is a constructor and should be called with the `new` keyword')
    // }
    this._init(options)
}
...
Copy the code

Then click on icon 5 twice and right-click Reveal in sidebar (shown in the sidebar) to see where this._init(options) is defined in the source code.

This is an easy and simple way to find our Vue constructor.

Start layer by layer with the packaged entry file

First of all, let’s have a look at the command related code of our running project and find the packaged entry file:

// package.json
"scripts": {
    "dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev --sourcemap". }Copy the code

Scripts /config.js. The value of TARGET in the environment variable is web-full-dev

Then go to web-full-dev in config.js and we’ll find the packaged entry file:

Do a code analysis:

// Run time + a development version of the compiler (large code, complete information, suitable for learning).'web-full-dev': {
    // Import file
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.js'),
    format: 'umd'.env: 'development'.alias: { he: './entity-decoder' },
    banner
},
...

Copy the code

Do the same for the resolve method above

const aliases = require('./alias')
// The entire resolve method relies on path.resolve() underneath
const resolve = p= > {
  const base = p.split('/') [0] // Base = web
  if (aliases[base]) {
    / / map "aliases (base)" need to "the require ('. / alias')" view, specific mapping address is: "SRC/platforms/web"
    // "p.ice (base. Length + 1)
    return path.resolve(aliases[base], p.slice(base.length + 1))}else {
    return path.resolve(__dirname, '.. / ', p)
  }
}
Copy the code

So the real package entry address is: SRC /platforms/web/entry-runtime-with-compiler.js

Istanbul ignore if: not following Istanbul ignore if: not following Istanbul ignore if: not following Istanbul ignore if: not following Istanbul ignore if: not following Istanbul ignore if: not following Istanbul ignore if: not following Istanbul ignore if: not following Istanbul ignore if: not following Istanbul ignore if: not following Istanbul ignore if

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

import config from 'core/config'
import { warn, cached } from 'core/util/index'
import { mark, measure } from 'core/util/perf'
// The file itself does not define the Vue, but relies on the Vue of./runtime/index
import Vue from './runtime/index'
import { query } from './util/index'
import { compileToFunctions } from './compiler/index'
import { shouldDecodeNewlines, shouldDecodeNewlinesForHref } from './util/compat'

// The method to get the host element
const idToTemplate = cached(id= > {
  const el = query(id)
  return el && el.innerHTML
})

// Extend the $mount method
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
) :Component {
  el = el && query(el)
  // Get the option $options
  const options = this.$options
  // resolve template/el and convert to render function
  // If the render option does not exist
  if(! options.render) {// find template
    let template = options.template
    // If template exists,
    if (template) {
      // We can write template
      if (typeof template === 'string') { // If it is a string template such as "
      
template
"
if (template.charAt(0) = = =The '#') { // If it is a selector of the host element, for example: "#app" // call the idToTemplate() method above to find template = idToTemplate(template) } // If it is a DOM element } else if (template.nodeType) { // use its innerHTML template = template.innerHTML } else { return this } // If el is set } else if (el) { // Use el's outerHTML as the template template = getOuterHTML(el) } // If the template option exists, compile it to get the render function if (template) { // Change template to render function const { render, staticRenderFns } = compileToFunctions(template, { outputSourceRange: process.env.NODE_ENV ! = ='production', shouldDecodeNewlines, shouldDecodeNewlinesForHref, delimiters: options.delimiters, comments: options.comments }, this) // The resulting render function will be assigned to options options.render = render options.staticRenderFns = staticRenderFns } } // Perform the default mount return mount.call(this, el, hydrating) } * new Vue({* el: "#app", * template: "
template
", * template: "#app", * render(h){ return h("div", "render")}, * data: {} *}) {render > template > el */; render > template > el */
// Get the outerHTML method function getOuterHTML (el: Element) :string { if (el.outerHTML) { return el.outerHTML } else { const container = document.createElement('div') container.appendChild(el.cloneNode(true)) return container.innerHTML } } Vue.compile = compileToFunctions export default Vue Copy the code

/runtime/index’./runtime/index’./runtime/index

// Vue is not defined here. It is also imported. What does this file do?

import Vue from 'core/index'
import config from 'core/config'
import { extend, noop } from 'shared/util'
import { mountComponent } from 'core/instance/lifecycle'
import { devtools, inBrowser } from 'core/util/index'

import {
  query,
  mustUseProp,
  isReservedTag,
  isReservedAttr,
  getTagNamespace,
  isUnknownElement
} from 'web/util/index'

import { patch } from './patch'
import platformDirectives from './directives/index'
import platformComponents from './components/index'

// install platform specific utils
// This is some configuration, not important, skip directlyVue.config.mustUseProp = mustUseProp Vue.config.isReservedTag = isReservedTag Vue.config.isReservedAttr = isReservedAttr  Vue.config.getTagNamespace = getTagNamespace Vue.config.isUnknownElement = isUnknownElement// install platform runtime directives & components
// This is some extension, not important, skip directly
extend(Vue.options.directives, platformDirectives)
extend(Vue.options.components, platformComponents)

// install platform patch function
// Install a patch function, also called patch function or update function. The main function is to convert the virtual DOM to the real DOM (vDOM => DOM).
Vue.prototype.__patch__ = inBrowser ? patch : noop

// public mount method
// Implements the $mount method: only one mountComponent() method is called
// $mount converts the virtual DOM to a real DOM and appends it to the host element (vdom => dom => append)
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
) :Component {
  el = el && inBrowser ? query(el) : undefined
  return mountComponent(this, el, hydrating)
}

Copy the code

Continue to look at the Vue dependency file “core/index” in the file.

// src/core/index.js 

// The same Vue is not defined in a file. It is also imported.
import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
import { isServerRendering } from 'core/util/env'
import { FunctionalRenderContext } from 'core/vdom/create-functional-component'

/ / the entire file is mainly do one thing: initialize static API: global Vue. Use/set/component/delete... Methods such as
initGlobalAPI(Vue)
...

Copy the code

Continue to look at the Vue dependency file “./instance/index” in the file.

// src/core/instance/index.js

import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '.. /util/index'

// There is no import of Vue, so we can determine that this file is the definition of Vue
// Declaration of the Vue constructor
function Vue (options) {
  // initialize method,
  this._init(options)
}

_init() = _init()

// Initialize mixin
initMixin(Vue)
// State mixin
stateMixin(Vue)
// events mixin
eventsMixin(Vue)
// Life cycle mixin
lifecycleMixin(Vue)
// Blending of render functions
renderMixin(Vue)

// These are the methods and properties that initialize instances
// The _init() method must be mixed with the initialization: initMixin ()

export default Vue

Copy the code

The _init() method must be mixed in with the initialization: initMixin(), so keep looking at the file where initMixin() is located.

Source code analysis

The vue.prototype. _init method in the following code, which is the subject of today’s talk: what does Vue initialization do? What to say.

// src/core/instance/init.js.export function initMixin (Vue: Class<Component>) {

  // Accept the options passed in by the user: options
  Vue.prototype._init = function (options? :Object) {
  
    // Vue instance
    const vm: Component = this
    
    // Vue instance identifier _uid flag
    vm._uid = uid++

    // VUE flag to avoid being observed by Observe
    vm._isVue = true
    
    // Option merge: user options and system default options need to be merged
    // Process the configuration content of the component, merging the options passed in with the options of the constructor itself (the plug-in policy is to merge the default configuration with the incoming configuration)
    if (options && options._isComponent) {
      // Subcomponents: Optimized internal component (subcomponent) instantiation, and dynamic options merging is quite slow, only some special parameter properties need to be handled here. Reduce the dynamic search of prototype chain and improve the execution efficiency
      initInternalComponent(vm, options)
    } else {
      // Root component: Merging global configuration options into the root component's configuration is an option merge
      vm.$options = mergeOptions(
        // Get the basic options for the current constructor
        resolveConstructorOptions(vm.constructor), 
        options || {},
        vm
      )
    }
    
    // expose real self
    vm._self = vm
    
    // The following method is the most important core code for initialization
    initLifecycle(vm) // Initialize vue instance lifecycle properties, component relationship properties, such as $root, $parent, $children, $refs
    initEvents(vm) // Initializes listening for custom component events. If there are parent listener events, add them to the instance
    initRender(vm) // Initialize slots, render functions, and so on for render. $createElm (h); $createElm (h)
    
    callHook(vm, 'beforeCreate') // Call the lifecycle hook function, where you can see what initialization a component did before and after creation
    
    // provide/inject
    // provide: Provide a data directly in the ancestor
    // Inject: Can be directly used after inject in the descendant
    initInjections(vm) // Inject capital. As a component, it needs to inject data that has been passed down from its ancestors before it can provide data to its descendants
    initState(vm) / / on the props, the methods, data and computed, watch is initialized, including reactive processing
    initProvide(vm) // Provide the data after injecting it
    // In a nutshell, the above three initializations are simply: initialization of the component's data and state
    
    callHook(vm, 'created') // created is initialized and ready to mount
    
    // Example binding to the corresponding DOM element
    // The component constructor sets the el option, which will be mounted automatically, so there is no need to mount it at $mount
    // The component constructor does not set el, and the $mount procedure is not here
    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
  }
}
...
Copy the code


What does Vue initialization do?

At this point, the entire Vue initialization is complete and detailed code will be summarized later in the article. Here is a summary of what is done for Vue initialization:

1. Merge options. Process component configuration by merging the options passed in with the options of the constructor itself (merge user options with the default options).

2. Initialize vue instance lifecycle attributes, such as root, root, root, parent, children, children, children, refs

3. Initialize listening for custom component events. If there is a parent listening event, add it to the instance

4, Initialize the slots, render functions, etc. There are only two things: slot processing and the declaration of $createElm, the h declaration in the render function

5. Call the beforeCreate hook function to show the initialization of a component before and after creation

6. Initialize injected data, inject data first. As a component, it needs to inject data that has been passed down from its ancestors before it can provide data to its descendants

7, to the props, the methods, data, computed, watch is initialized, including reactive processing

Initialize provide after infusing data passed down from ancestors

Create hook (); create hook ()

10. Mount to the corresponding DOM element. If the component constructor sets the EL option, it will be mounted automatically, so there is no need to manually call $mount to mount it


conclusion

This completes the initialization process, and the next chapter is an analysis of the reactive principle

The link will be posted when the article is finished. I hope you can support me a lot. Please click “like” and follow 🙏