1. Prepare

1.1 installation

  1. Install a rollup

     npm i -g rollup
    Copy the code
  2. Pull the source code

     git clone https://github.com/vuejs/vue.git
    Copy the code
  3. Modify the startup file package.json and add –sourcemap to generate the new DIST

     "dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:webfull-dev",
    Copy the code
  4. Dist /vue.js debugging introduced

    <script src=".. /.. /dist/vue.js"></script>Copy the code
  5. Through Chrome, debugger, by right clicking Revael in sidebar in the left debug box to display the corresponding tree structure.

Package version 1.2

  • Full version: Contains both compiler and runtime versions.

  • Compiler: Code used to compile template strings into JavaScript rendering functions.

  • Runtime: Code for creating Vue instances, rendering and processing virtual DOM, and so on. Basically everything else is stripped out of the compiler.

    Note: runtime runtime.js is not able to do <template> parsing renderingCopy the code

  1. UMD: The UMD version can be used directly in the browser with the <script> tag. Vue.umd. js is equivalent to vue.js and is the complete version, including all compilers + runtimes. Suitable for asynchronous loading

  2. CommonJS: The CommonJS version works with older packaging tools like Browserify or WebPack 1. Suitable for Node.js server rendering, using the NODEJS format referenced by require(“”)

  3. ES Module: (supports import from the latest standard) Starting in 2.6, Vue provides two ES Modules (ESM) buildfiles:

    1. ESM for packaging tools: Modern packaging tools such as WebPack 2 or Rollup. 2. ESM for browsers (2.6+) : Used for direct import in modern browsers via <script type="module">.Copy the code
  4. Runtime runtime cannot do <template> parsing render

2. The analysis

2.1 Source code Structure

2.2 Package compilation analysis

2.2.1 package. Json entrance

“dev”: Rollup -w -c scripts/config.js –sourcemap –environment TARGET:web-full-dev Find the file scripts/config.js and match it

'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
  },
  
 / / the entry
  entry: resolve('web/entry-runtime-with-compiler.js'),
//resolve method resolution
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)
  }
}
  
 / / scripts/alias. Js
const path = require('path')
const resolve = p= > path.resolve(__dirname, '.. / ', p)
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')}// Find the corresponding code entry for web
web: resolve('src/platforms/web'), general entry address is: SRC /platforms/web/ + entry-Runtime -with-compiler
Copy the code

2.2.2 Start with entry-Runtime-with-compiler

Analysis principles: Ignore printing, optimize performance, debug code.

// Check if there is a template, or $el if there is no template
import Vue from './runtime/index' // Here is an example of vue

const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
) :Component {
    el = el && query(el)
    ...
    
  const options = this.$options
  // resolve template/el and convert to render function
  if(! options.render) {// Check if there is no render function
       let template = options.template
        if (template) { // Check if there is no template
        
        }else if (el) { $el = $el; $el = $el
          template = getOuterHTML(el)
        }
        if(template) {if the template is not empty, the template generates the render functionconst { render, staticRenderFns } = compileToFunctions(template, {
        outputSourceRange: process.env.NODE_ENV ! = ='production',
        shouldDecodeNewlines,
        shouldDecodeNewlinesForHref,
        delimiters: options.delimiters,
        comments: options.comments
      }, this)
      options.render = render / / set the render
      
  } 
  
  return mount.call(this, el, hydrating)// Perform the mount
Copy the code

2.2.3 / SRC/platforms/web/runtime/index, js

Instantiate the Vue object

import { mountComponent } from 'core/instance/lifecycle'

// This is the diff algorithm. Install a __patch__ method. __patch__ is underlined to indicate the internal method and is not intended for external use
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
) :Component {
  el = el && inBrowser ? query(el) : undefined
  return mountComponent(this, el, hydrating)// Mount the component
}

Vue.prototype.__patch__ = inBrowser ? patch : noop

Copy the code

2.3 Core source library 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'

// Initialize the global method
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 Vue
Copy the code

2.3.1 SRC/core/instance/index. Js

Constructor of Vue

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')}// Execute the initialization method
  this._init(options)
}

initMixin(Vue) // The _init called above is mixed in here
stateMixin(Vue) $data,$props,$set,$delete,$watch
eventsMixin(Vue)$on,$once,$off,$emit
lifecycleMixin(Vue) // Lifecycle API _update,$forceUpdate,$destroy
renderMixin(Vue)// render API _render,$nextTick

export default Vue 

Copy the code

2.3.2 core/instance/init. Js

Creates a component instance, initializes its data, properties, events, and so on


initLifecycle(vm) // $parent,$root,$children,$refs
initEvents(vm) // Handle events and callbacks passed by the parent component
initRender(vm) // $slots,$scopedSlots,_c,$createElement
callHook(vm, 'beforeCreate')
initInjections(vm) // Get the injected data
initState(vm) // Initialize props, methods, data, computed, watch
initProvide(vm) // Provide data injection
callHook(vm, 'created')

Copy the code

2.3.3 / core/instance/state. Js

Initialize data, including props, Methods, data, computed, and Watch Web full-stack Architect

// Data reactivity
function initData (vm: Component) {
// Perform data reactivity
observe(data, true /* asRootData */)}Copy the code

2.4 Vue loading process

2.5 Wathcer asynchronous queue refresh process

3 virtual DOM learning

3.1 snabbdom implementation

<! DOCTYPE html><html lang="en">
<head></head>
<body>
<! Snabbdom -->
<script src=".. /.. /node_modules/snabbdom/dist/snabbdom.js"></script>
<script>
// The reactive function written earlier
function defineReactive(obj, key, val) {
    Object.defineProperty(obj, key, {
        get() {
            return val
        },
        set(newVal) {
            val = newVal
            // Notification update
            update()
        }
    })
}

// Import patch factory init, h is the factory where vnodes are generated
const { init, h } = snabbdom
// Get the patch function
const patch = init([])
// Last vnode, returned by patch()
let vnode;
// Update function to convert data operation to DOM operation and return new vNode
function update() {
    if(! vnode) {// Initialize without the last vnode, passing in the host element and vnode
        vnode = patch(app, render())
    } 
    else {
    // Update, pass in the old and new vNodes to compare and update
    vnode = patch(vnode, render())
    }
}
// Render function that returns a vNode describing the DOM structure

function render() {
    return h('div', obj.foo)
}
/ / data
const obj = {}
// Define the response
defineReactive(obj, 'foo'.' ')
// Assign a date as the initial value
obj.foo = new Date().toLocaleTimeString()
// Periodically change data, update function will be re-executed
setInterval(() = > {
    obj.foo = new Date().toLocaleTimeString()
}, 1000);
</script>
</body>
</html>
Copy the code

To be continued…