Vue2 source code learning notes-1

Posted by kevinjo on Tue, 04 Jan 2022 06:20:21 +0100

reference resources:

Vue technology decryption

Interviewer: Vue instance mounting process

The difference between runtime compiler and runtime only in vue

Explanation of different builds

If there are any errors, please point them out~

For more study notes, please stamp: https://github.com/6fa/WebKno...

Contents of this document:

1. Source code core directory
2. Source code construction
3. Runtime only and runtime compiler
4. Entry (runtime+compiler mode)

1. Source code core directory

For the Vue2 source code, you can view the official github: https://github.com/vuejs/vue

The source code is mainly placed in the src Directory:

src
├── compiler        # Compile related 
├── core            # Core code 
├── platforms       # Support of different platforms
├── server          # Server side rendering
├── sfc             # . vue file parsing
├── shared          # Shared tool code
  • compiler

    • Store compilation related code, including
    • Parse the template into ast tree (abstract syntax tree), AST tree optimization and code generation
  • core

    • Core code, including
    • Built in components, global API encapsulation, tool functions
    • Vue instantiation, responsive, virtual DOM
  • platforms

    • vue.js entry
    • The two directories represent two main entries, which are packaged into vue.com running on the web and weex respectively js
  • server

    • Server side rendering related
  • sfc

    • Handle The vue file is parsed into a js object
  • shared

    • Some tools will be used by browser Vue JS and server Vue JS sharing

2. Source code construction

In the package of the source code As you can see from the JSON file, the entry to build the code is scripts / build js:

{
  //...
  "scripts": {
     //...
     "build": "node scripts/build.js",
     "build:ssr": "npm run build -- web-runtime-cjs,web-server-renderer",
     "build:weex": "npm run build -- weex",
     //...
   }
}

scripts/build.js key code:

  • First read the configuration from the configuration file (config), and then filter the build configurations (builds) through the command-line parameters to build Vue. Net for different purposes js
  • build() is built by rollup (packaging tool)
//scripts/build.js

let builds = require('./config').getAllBuilds()

// filter builds via command line arg
// Filter builds by comparing command line parameters
if (process.argv[2]) {
  const filters = process.argv[2].split(',')
  builds = builds.filter(b => {
    return filters.some(f => b.output.file.indexOf(f) > -1 || b._name.indexOf(f) > -1)
  })
} else {
  // filter out weex builds by default
  // weex builds are filtered out by default
  builds = builds.filter(b => {
    return b.output.file.indexOf('weex') === -1
  })
}

build(builds)

Take another look at the config configuration file:

// script/config.js
//Configuration follows Rollup's build rules

const builds = {
  // Runtime only (CommonJS). Used by bundlers e.g. Webpack & Browserify
  'web-runtime-cjs': {
    entry: resolve('web/entry-runtime.js'), //Build entry
    dest: resolve('dist/vue.runtime.common.js'), //Built file location
    format: 'cjs', //Construction format, cjs means to follow CommonJS, ES means ES Module, and UMD means UMD specification
    banner
  },
  // Runtime+compiler CommonJS build (CommonJS)
  'web-full-cjs': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.common.js'),
    format: 'cjs',
    alias: { he: './entity-decoder' },
    banner
  },
  //...
}

3. Runtime only and runtime compiler

compiler:

Code used to compile template strings into JavaScript rendering functions

runtime:

Code used to create Vue instances, render and process virtual DOM, etc. It's basically removing everything else from the compiler

runtime + compiler (full version):

If you write a template, you need to compile the template into a render function (generate a virtual DOM) at run time. Because in Vue2, the final rendering is through the render function. If you write the template attribute, you need to compile it into the render function, and you need the compiler.

// Compiler required
new Vue({
  template: '<div>{{ hi }}</div>'
})

// No compiler required
new Vue({
  render (h) {
    return h('div', this.hi)
  }
})
runtime only:

You need to use the vue loader of webpack vue files are compiled into JS files: templates inside vue files are precompiled into JavaScript at build time. (at this time, the new Vue option in main.js still cannot write the template attribute, only the. vue file can be written.)

Since the compiler is no longer required, the volume is reduced by about 30%, and the runtime only version should be used as much as possible.

4. Entry (runtime+compiler mode)

runtime+compiler mode entry file location: Src / platforms / Web / entry runtime with compiler js

The code focuses on:

  • Vue is introduced (the entry of Vue is. / runtime/idex)
  • The $mount method is defined on Vue's prototype
  • Export Vue
import config from 'core/config'
import { warn, cached } from 'core/util/index'
import { mark, measure } from 'core/util/perf'

import Vue from './runtime/index'
import { query } from './util/index'
import { compileToFunctions } from './compiler/index'
import { shouldDecodeNewlines, shouldDecodeNewlinesForHref } from './util/compat'

//...

//Define $mount method
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (){......}


//...

Vue.compile = compileToFunctions

export default Vue

4.1 Vue entrance

The location where Vue is introduced above is Src / platforms / Web / Runtime / index js

Code focus:

  • Importing Vue from core/index
  • Some extensions are made to Vue objects, such as defining the basic $mount method on the Vue prototype
  • From core / index JS can see again

    • Vue objects are imported from core/instance/index, so the real Vue instance is defined in core/instance/index
    • Initialize global API: initGlobalAPI(Vue)
// src/platforms/web/runtime/index.js

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,
  //......
} from 'web/util/index'

//......

// install platform specific utils
Vue.config.mustUseProp = mustUseProp
//......

// public mount method
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && inBrowser ? query(el) : undefined
  return mountComponent(this, el, hydrating)
}

//......

export default Vue
// core/index

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'

initGlobalAPI(Vue)

//......

export default Vue

4.1.1 definition of Vue

Location: core/instance/index, key points:

  • Vue constructor is defined (why not use class? Vue is passed as a parameter later, mainly to the prototype expansion methods of Vue. These expansion methods are scattered in various modules, and it is difficult to implement if it is class)
  • Xxxmixin expands some methods on Vue's prototype, such as_ The init method is added in initMixin
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'

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)

export default Vue

4.1.2 initGlobalAPI

The above is for Vue If the prototype extends the method, the initglobal API adds static methods to Vue itself.

Location: Src / core / global API / index js

//......

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.'
      )
    }
  }
  Object.defineProperty(Vue, 'config', configDef)

  // Vue. It is better not to rely on the method of util exposure, because it may change frequently and is unstable
  Vue.util = {
    warn,
    extend,
    mergeOptions,
    defineReactive
  }

  Vue.set = set
  Vue.delete = del
  Vue.nextTick = nextTick

  // 2.6 explicit observable API
  Vue.observable = <T>(obj: T): T => {
    observe(obj)
    return obj
  }

  Vue.options = Object.create(null)
  ASSET_TYPES.forEach(type => {
    Vue.options[type + 's'] = Object.create(null)
  })

  // this is used to identify the "base" constructor to extend all plain-object
  // components with in Weex's multi-instance scenarios.
  Vue.options._base = Vue

  extend(Vue.options.components, builtInComponents)

  initUse(Vue)
  initMixin(Vue)
  initExtend(Vue)
  initAssetRegisters(Vue)
}

4.2 mount

$mount method focus:

  • If template or el is defined, the innerHTML of template or el is converted to the render function
  • The transformation is implemented through compileToFunctions
  • Finally, the shared $mount method is mounted on the Vue prototype. This is done in order to reuse. The runtime mode is used directly, and it saves the steps to convert render functions. The location is in src/platform/web/runtime/index.js.
  • The $mount method actually calls the mountComponent method (defined in src/core/instance/lifecycle.js)
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && query(el)

    //...

  const options = this.$options
  
  // Convert the outerHTML of template or el to the render function
  if (!options.render) {                  
    let template = options.template
    if (template) {
      if (typeof template === 'string') { //template is a string
        if (template.charAt(0) === '#') {
          template = idToTemplate(template)  //idToTemplate is the innerHTML of the element obtained by id
                    //...
        }
      } else if (template.nodeType) {    //template is a node
        template = template.innerHTML
      } else {     //Invalid template
        if (process.env.NODE_ENV !== 'production') {
          warn('invalid template option:' + template, this)
        }
        return this
      }
    } else if (el) {        //If template does not exist but el exists
      template = getOuterHTML(el)
    }
    if (template) {
      //...
            // compileToFunctions converts template to render function
      const { render, staticRenderFns } = compileToFunctions(template, {
        //... Options passed
      }, this)
      options.render = render
      options.staticRenderFns = staticRenderFns

      //...
    }
  }
  return mount.call(this, el, hydrating)
}
// src/platform/web/runtime/index.js
// Reusable share $mount

Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && inBrowser ? query(el) : undefined
  return mountComponent(this, el, hydrating)
}

4.3 summary

runtime+compiler mode entry (SRC / platforms / Web / entry runtime with compiler. JS)

  • Introduce vue from runtime/index (src/platforms/web/runtime/index.js)

    • Importing Vue from core/index

      • Vue objects are imported from core/instance/index, so the real Vue instance is defined in core/instance/index

        • Vue constructor defined
        • Some methods to expand Vue's prototype
      • Initialize global API: initGlobalAPI(Vue)
    • Expand Vue objects, such as defining the basic $mount method on Vue prototype
  • Define $mount on the vue prototype (note that the shared $mount method is different)

    • Convert template to render function, which is implemented through compileToFunctions
    • Call the base $mount method (src/platform/web/runtime/index.js)
    • The base $mount method actually calls the mountComponent method (src/core/instance/lifecycle.js)

Topics: Javascript Front-end Vue.js