How do you stand out from the crowd? Kill a large number of candidates who are at least deep in one area. Here and in the following articles, give all candidates a source code analysis of the VUE stack that they can understand, whether you are in an interview or on a project

Start with a simple vUE component example

     <html>
         <head></head>
         <body>
             <div id="app">hello {{msg}}</div>
          <script>
            export default {
                el:'app',
                data () {
                    return {
                      msg:"Vue"
                    }
                  }
            }
    </script>
         </body>
     </html>
Copy the code

The final text on the page is hello Vue

And when we want to modify the text in the view, we need to modify the properties in data to dynamically modify the text in the view

We all know that if you do the same thing with regular JS without vue you need to manipulate the DOM element to get the ID node and then add text

So what does Vue do for us that makes it so easy to manipulate views without writing JS code

With this in mind, let’s begin our tour of vue source code by first looking at what does vue initialization do

View the source code considerations

The VUE version here is the 2.6 official release because the 3.0 official release is backward compatible with 2.6 developer learning and its ecology has a while to go

Crossing the stage

The source code structure of VUE is complex. In the process of viewing the source code, we only look at the main line process and skip the branch steps to figure out the main work of Vue

Vue will have a lot of built version packaging and will execute the corresponding version file packaging according to the user’s command. In this case, the full version of the packaging entry file entry-running-with-Compiler.js on the Web platform

Vue initialization

Two main things are done in the entry file:

  • – Rewrote the $mount method and added new functionality to first check if there is render if template is not compiled into the render function, if there is a mount render to the DOM
  • Register the vue static method complier to compile the HTML string into Render
src/platforms/web/entry-running-with-compiler.js
/* @flow */

import config from 'core/config'
import { warn, cached } from 'core/util/index'
import { mark, measure } from 'core/util/perf'
import Vue from './runtime/index' /* Vue constructor file */
import { query } from './util/index'
import { compileToFunctions } from './compiler/index'
import { shouldDecodeNewlines, shouldDecodeNewlinesForHref } from './util/compat'

const idToTemplate = cached(id= > {
  const el = query(id)
  return el && el.innerHTML
})
// Retain the $mount method for Vue instances
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (el? : string | Element,// False for non-SSR and true for SSRhydrating? : boolean) :Component {
  // Get the el object
  el = el && query(el)

  /* istanbul ignore if */
  // EL cannot be body or HTML
  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
  // Convert template/el to render function
  if(! options.render) {let template = options.template
    // If the template exists
    if (template) {
      if (typeof template === 'string') {
        // If the template is an ID selector
        if (template.charAt(0) = = =The '#') {
          // Get the innerHTML of the corresponding DOM object
          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) {
        // If the template is an element, return the innerHTML of the element
        template = template.innerHTML
      } else {
        if(process.env.NODE_ENV ! = ='production') {
          warn('invalid template option:' + template, this)}// Otherwise return the current instance
        return this}}else if (el) {
      // If there is no template, get el's outerHTML as the template
      template = getOuterHTML(el)
    }
    if (template) {
      /* istanbul ignore if */
      if(process.env.NODE_ENV ! = ='production' && config.performance && mark) {
        mark('compile')}// Convert template to render function
      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')}}}// Call the mount method to render the DOM
  return mount.call(this, el, hydrating)
}

/** * Get outerHTML of elements, taking care * of SVG elements in IE as well. */
 /* is not related to the main line
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

After making a series of judgments in this file, vue ends up rendering the DOM to the view, but here we don’t see what vue’s constructor is doing./ Runtime /index.js

src/platforms/web/runtime/index.js
/* @flow */

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
/ / judgment is a key attribute (form element input/checked/selected/muted)
// If this is the case, set the el.props property (the property is not set to the tag)Vue.config.mustUseProp = mustUseProp Vue.config.isReservedTag = isReservedTag Vue.config.isReservedAttr = isReservedAttr  Vue.config.getTagNamespace = getTagNamespace Vue.config.isUnknownElement = isUnknownElement// install platform runtime directives & components
// Properties in vue. Options are available globally
extend(Vue.options.directives, platformDirectives)
extend(Vue.options.components, platformComponents)

// install platform patch function
Vue.prototype.__patch__ = inBrowser ? patch : noop

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

// devtools global hook
// Here are the debugging related methods
/* istanbul ignore next */
if (inBrowser) {
  setTimeout(() = > {
    if (config.devtools) {
      if (devtools) {
        devtools.emit('init', Vue)
      } else if( process.env.NODE_ENV ! = ='production'&& process.env.NODE_ENV ! = ='test'
      ) {
        console[console.info ? 'info' : 'log'] ('Download the Vue Devtools extension for a better development experience:\n' +
          'https://github.com/vuejs/vue-devtools')}}if(process.env.NODE_ENV ! = ='production'&& process.env.NODE_ENV ! = ='test'&& config.productionTip ! = =false &&
      typeof console! = ='undefined'
    ) {
      console[console.info ? 'info' : 'log'] (`You are running Vue in development mode.\n` +
        `Make sure to turn on production mode when deploying for production.\n` +
        `See more tips at https://vuejs.org/guide/deployment.html`)}},0)}export default Vue


Copy the code

It does the following things

  • Registered properties for internal use only

    • It registers attributes in the vue constructor for internal use only and has little external effect on whether the registered tags are reserved or reserved attributes and whether the passed parameters are HTML-specific tags and attributes
  • Directives and components are registered

    • Adding the registration of platform-specific global components to the constructor shows that the directive and components are web platform-specific and truly global. The extend method assigns the following arguments to the preceding arguments and registers the global directives v-show V-model and V-Transition components And all can be accessed globally
  • The path function is registered

    • If you’re adding a path method, what it does is it converts the virtual DOM into the formal DOM
  • Registered $mount

    • Here we register $mount on the vue instance and call the mountComponent method to render the DOM to the interface

As you can see, the vue constructor is still missing at the end and you can continue to look at the imported core/index.js

src/core/index.js
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' // registers static methods for the vue constructor InitGlobalAPI (Vue) // 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}) // Vue. Version = '__VERSION__' export default vueCopy the code

Focusing on initGlobalAPI here, we hold down Ctrl + left mouse button in vscode to jump to this file

src/core/global-api/index



/* @flow */

import config from '.. /config'
import { initUse } from './use'
import { initMixin } from './mixin'
import { initExtend } from './extend'
import { initAssetRegisters } from './assets'
import { set, del } from '.. /observer/index'
import { ASSET_TYPES } from 'shared/constants'
import builtInComponents from '.. /components/index'
import { observe } from 'core/observer/index'

import {
  warn,
  extend,
  nextTick,
  mergeOptions,
  defineReactive
} from '.. /util/index'

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.')}}// Initialize the vue.config object
  Object.defineProperty(Vue, 'config', configDef)

  // exposed util methods.
  // NOTE: these are not considered part of the public API - avoid relying on
  // them unless you are aware of the risk.
  // These tools are not considered part of the global API, so don't rely on them unless you are aware of some of the risks
  Vue.util = {
    warn,
    extend,
    mergeOptions,
    defineReactive
  }
  // Static method set/delete/nextTick
  Vue.set = set
  Vue.delete = del
  Vue.nextTick = nextTick

  // 2.6 explicit observable API
  // make an object responsive
  Vue.observable = <T>(obj: T): T= > {
    observe(obj)
    return obj
  }
  // Initialize the vue. options object and extend it
  // components/directives/filters
  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

  // Set the keep-alive component
  extend(Vue.options.components, builtInComponents)

  Vue. Use () is used to register plug-ins
  initUse(Vue)
  // Register Vue. Mixin () to implement mixin
  initMixin(Vue)
  // Register vue.extend () to return a component constructor based on the passed options
  initExtend(Vue)
  // Register vue.directive (), Vue.component(), vue.filter ()
  initAssetRegisters(Vue)
}


Copy the code

This file registers static members vue.config registers util methods set/delete/nextTick registers an object as responsive data… Static method of

/instance/index’./instance/index’./instance/index

src/core/instance/index
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' // the class is not used here because it is easy to mix the instance members with the Vue instance. // This is not a production environment $data/$props/$set/$delete/$watch event $on/$once/$off/$emit lifecycle method 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')} // Call _init() This._init (options)} // Register the _init() method of the VM, Initialize vm initMixin(Vue) // register VM $data/$props/$set/$delete/$watch stateMixin(Vue) // initialize event-related methods // $on/$once/$off/$emit $forceUpdate/$destroy lifecycleMixin(Vue) // Add render // $nextTick/_render renderMixin(Vue) export default VueCopy the code

This is where you finally see the vue constructor, which does two things: creates the constructor and sets up the vue instance members

Finally, we use mind maps to comb through the process

conclusion

At the end of the day, VUE is about dividing each function into different modules that we can look at and maintain in the future