@TOC

First, pre-work

1. Obtain the Vue source code

Git Clone github.com/vuejs/vue.g… The current version is 2.6.11

2. Vue source project file structure

2.1 Project root Directory Structure description

2.2 Description of the core code directory

3. Set up the debugging environment

1) Install dependency: NPM I 2) Terminate when installing phantom. Js if slow 3) install rollup: NPM i-g rollup 4) Modify dev script in package.json configuration file to add sourcemap

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

5) Run the NPM run dev command

6) According to the configuration in Step 4, a mapping file will be generated in the dist directory after successful operation, which is convenient for debugging in the browser when writing test cases.

Second, look for the project operation entry file

To get a clearer understanding of how Vue works and the initialization process, we need to find the entry file of the application. Json configuration file. Here is a mind map I drew myself: A.

  1. Find the web-full-dev configuration item in the scripts/config.js file by following the dev configuration item in package.json:
"dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web- full-dev".Copy the code
'web-full-dev': {
    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
  1. Web /entry-runtime-with-compiler.js is the path of the entry file packaged for the project, where web is the alias, and we need to find the real path corresponding to web. Enter the resolve() method:
const aliases = require('./alias')
const resolve = p= > {
  const base = p.split('/') [0]
  if (aliases[base]) {
    return path.resolve(aliases[base], p.slice(base.length + 1))}else {
    return path.resolve(__dirname, '.. / ', p)
  }
}
Copy the code
  1. Entering the scripts/alias.js file, we find that the corresponding path to web is SRC /platforms/web
module.exports = {
  vue: resolve('src/platforms/web/entry-runtime-with-compiler'),
  compiler: resolve('src/compiler'),
  core: resolve('src/core'),
  shared: resolve('src/shared'),
  web: resolve('src/platforms/web'),
  weex: resolve('src/platforms/weex'),
  server: resolve('src/server'),
  sfc: resolve('src/sfc')}Copy the code
  1. So, the path to our entry file is SRC /platforms/web/entry-runtime-with-compiler.js.

Note; This file is the version with the compiler, in order to facilitate us to have a clearer understanding of the whole Vue working mechanism, the webpack we use in our daily work is not the version with the compiler, but through the additional injection vue-loader implementation.

3. Analysis of initialization process of new Vue(

1. Mind mapping

New Vue() initialization: new Vue() => _init() => $mount() => mountComponent() => new Watcher() => updateComponent() => render() => _update()

Here is a mind map I drew from the source code:In the mind map, the Vue initialization process is listed step by stepEach core function methodWhat has been done, and each core function method source file path. Let’s analyze the source code.

2. Source code analysis

2.1 Extend the $mount() method

In the entry file SRC /platforms/web/entry-runtime-with-compiler.js, an extension to $mount() is implemented.

Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
) :Component {
  el = el && query(el)

  /* istanbul ignore if */
  if (el === document.body || el === document.documentElement) { process.env.NODE_ENV ! = ='production' && warn(
      `Do not mount Vue to <html> or <body> - mount to normal elements instead.`
    )
    return this
  }

  const options = this.$options
  // resolve template/el and convert to render function
  if(! options.render) {let template = options.template
    if (template) {
      if (typeof template === 'string') {
        if (template.charAt(0) = = =The '#') {
          template = idToTemplate(template)
          /* istanbul ignore if */
          if(process.env.NODE_ENV ! = ='production' && !template) {
            warn(
              `Template element not found or is empty: ${options.template}`.this)}}}else if (template.nodeType) {
        template = template.innerHTML
      } else {
        if(process.env.NODE_ENV ! = ='production') {
          warn('invalid template option:' + template, this)}return this}}else if (el) {
      template = getOuterHTML(el)
    }
    if (template) {
      /* istanbul ignore if */
      if(process.env.NODE_ENV ! = ='production' && config.performance && mark) {
        mark('compile')}const { render, staticRenderFns } = compileToFunctions(template, {
        outputSourceRange: process.env.NODE_ENV ! = ='production',
        shouldDecodeNewlines,
        shouldDecodeNewlinesForHref,
        delimiters: options.delimiters,
        comments: options.comments
      }, this)
      options.render = render
      options.staticRenderFns = staticRenderFns

      /* istanbul ignore if */
      if(process.env.NODE_ENV ! = ='production' && config.performance && mark) {
        mark('compile end')
        measure(`vue The ${this._name} compile`.'compile'.'compile end')}}}return mount.call(this, el, hydrating)
}
Copy the code

The new Vue() extension method is used to create an instance of our new Vue(), and handle the template or el option that may exist in the options passed in.

  1. Reader () -> template -> el
  2. If reader() does not exist, template or EL is converted to an HTML template string, which is then converted to reader()

Example:

// render > template > el
// Create an instance
const app = new Vue({
    el: '#demo'.// template: '<div>template</div>',
    // template: '#app',
    // render(h){return h('div','render')},
    data: {foo:'foo'}})Copy the code

However, we did not find the constructor method for new Vue(), so where is the constructor for Vue? Through the file header

import Vue from ‘./runtime/index’

Seeing that Vue is imported from here, let’s go into the file and have a look.

2.2 the SRC/platforms/web/runtime/index. Js

A review of the source shows that the file does two main things:

2.2.1 Define the __Patch__ method.

Vue.prototype.__patch__ = inBrowser ? patch : noop
Copy the code

Remember, this method is how the virtual DOM (vNode) is generated from the real DOM in Vue.

Vnode will be covered in a future article.

2.2.2 Implement $mount().

Vue.prototype.$mount = function (.)
Copy the code

In $mount () will call mountComponent () method, this method is in the SRC/core/instance/lifecycle. Js defined).

A Watcher is created in mountComponent(), so whenever we create a Vue component instance with new Vue(), we create a new Watcher. In particular, components correspond to reader Watcher, and there is only one component; When users use computed, watch, and $watch, there are several user Watchers for every few attributes. Watcher’s relationship with Dep is many-to-many.

Here’s a question: when is $mount() called? We will find the answer to this question in the source code below.

However, we haven’t found the constructor method for new Vue() yet, so where is the constructor for Vue? Through the file header

import Vue from ‘core/index’

Seeing that Vue is imported from here, let’s go into the file and have a look.

2.3 the SRC/core/index. Js

A review of the source shows that the file does two main things:

2.3.1 Initializing the global API

initGlobalAPI(Vue)
Copy the code

2.3.2 Configuring SSR server rendering

Object.defineProperty(Vue.prototype, '$isServer', {
  get: isServerRendering
})

Object.defineProperty(Vue.prototype, '$ssrContext', {
  get () {
    /* istanbul ignore next */
    return this.$vnode && this.$vnode.ssrContext
  }
})

// expose FunctionalRenderContext for ssr runtime helper installation
Object.defineProperty(Vue, 'FunctionalRenderContext', {
  value: FunctionalRenderContext
})
Copy the code

That’s not what we’re going to do this time. Knowing that this file does both of those things is enough.

Through the file header

import Vue from ‘./instance/index’

Seeing that Vue is imported from here, let’s go into the file and have a look.

2.4 Vue initialization

2.4.1 Initialization constructor method of Vue

In the SRC/core/instance/index. Js here we finally found the Vue constructors to initialize method _init ().

function Vue (options) {
  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)
}

initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
Copy the code

In fact, the _init() method is defined in initMixin(Vue) below. Let’s take a look at each of the five methods to see what it does.

2.4.2 Initializing related Options

2.4.2.1.initmixin () defines the _init() method

_init()

  1. Merge options in new Vue()

Merge global components transition, transitionGroup, keepAlive, and custom components; Incorporate filters, custom directives, and so on. InitLifecycle (VM) initializes $parent, $root, $children, $refs, etc. 3. InitEvents (VM) initializes event listening. Events between components are distributed and listened to by themselves, so the child component receives the parent component’s event listener for processing. $scopedSolts; $scopedSolts; $scopedSolts; Define vm.$createElement(), which is the argument to reader(); Define the responses of $arrts and $Listeners. 5. CallHook (vm, ‘beforeCreate’) executes the hook function beforeCreate(). This hook can access only properties and methods, but not props, methods, data, computed, or watch. 6. InitState (VM) initializes props, methods, data, computed, and watch in options to perform responsive data processing. See the mind map above for a more detailed description of responsive processing. 7. Vm.$mount() is called when options for Vue() have el. 8. CallHook (VM, ‘created’) executes hook function created(). This hook can access props, methods, data, computed, and watch.

2.4.2.2 stateMixin ()

Main tasks:

  1. Respond to $data and $props
  2. Define global methods $set(), $delete(), $watch(). Concrete realization of the three method is in the SRC/core/observer/index, js.
2.4.2.3 eventMixin() defines event listening methods

$on(), $onco(), $off(), $emit().

2.4.2.4 lifecycleMixin() defines lifecycle hook functions

_update(), $forceUpdate() forceUpdate, $destory()

2.4.2.5 readerMixin ()

Define _reader(), $nextTick()

Four,

1. New Vue() initialization process

new Vue() => _init() => $mount() => mountComponent() => new Watcher() => updateComponent() => render() => _update()

2._reader () and __patch__

1) _reader() gets the VNode. 2) __Patch__ initializes and updates the VNode to the real DOM

3. Relationship between Dep and Watcher

1) Each responsive object and its key has a Dep; 2) Each component VM component instance has a Reader Watcher, and a user Watcher is created when users use computed, watch, or $watch to listen. 3) The relationship between Dep and Watcher is many-to-many, and their association operation is carried out in the addDep() method of Dep class.

4. Call $mount()

If $mount() is not called manually when new Vue(), it will be called automatically in _init() if there is el in options. The purpose of this method is to generate the real DOM and render the page.

5. Considerations for using lifecycle hook functions

1) beforeCreate() cannot access props, methods, data, computed, and watch.

2) Created () can access props, methods, data, computed, and watch.