preface

In the last section we talk about “Vue source learning (2)” you don’t know – template compilation principle, which is to talk about the template to render function required format, so today, I will tell you, Vue is how to take this thing, to generate the real DOM and display to the page.

code

Step 1.

Graph TD $mount --> mountComponent mountComponent --> _render --> _render --> _render

2. Initialize Vue

const { initMixin } = require('./init')
const { lifecycleMixin } = require('./lifecycle')
const { renderMixin } = require("./render")

function Vue(options) {
    this._init(options)
}

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

3. MountComponent (rendering entry)

// lifecycle.js


const { patch } = require('./vdom/patch')

function mountComponent (vm, el) {
    vm.$el = el;

    // In the previous section, we talked about compiling the template to the render function
    // In this section, we need to execute the _render function to call the render function to generate the virtual DOM
    // Then receive the return value of the virtual DOM, and call the _update function to convert the virtual DOM into a real DOM and render it
    vm._update(vm._render())

    return vm
}

function lifecycleMixin(Vue) {
    // Hang _update on the Vue prototype
    Vue.prototype._update = function (vnode) {
        const vm = this

        // Execute the patch function, described below
        vm.$el = patch(vm.$el, vnode) || vm.$el
    }
}

module.exports = {
    mountComponent,
    lifecycleMixin
}

Copy the code

4. _render function (execute the render function to get the virtual DOM)

// render.js


const { createElement, createTextNode } = require('./vdom/index')

function renderMixin(Vue) {
    // Hang the _render function on the Vue prototype
    Vue.prototype._render = function() {
        const vm = this

        // Remove the render function generated in the previous section
        const { render } = vm.$options

        // Execute the render function and get the virtual DOM
        const vnode = render.call(vm)

        return vnode 
    }

    // Create the element node virtual DOM
    Vue.prototype._c = function(. args) {
        returncreateElement(... args) }// Create a text node virtual DOM
    Vue.prototype._v = function (text) {
        return createTextNode(text)
    }

    // In the case of an object, convert the object to a string
    Vue.prototype._s = function (val) {
        return val === null ? ' ' : typeof val === 'object' ? JSON.stringify(val) : val
    }
}

module.exports = {
    renderMixin
}
Copy the code

Below are the specific functions and classes required to create the virtual DOM

// vdom/index.js


// Create a virtual DOM for a node
class Vnode {
    constructor(tag, data, key, children, text) {
        this.tag = tag
        this.data = data
        this.key = key
        this.children = children
        this.text = text
    }
}

// Create the element node virtual DOM
function createElement(tag, data= {}, ... children) {
    const key = data.key
    return new Vnode(tag, data, key, children)
}

// Create a text node virtual DOM
function createTextNode(text) {
    return new Vnode(undefined.undefined.undefined.undefined, text)
}

module.exports = {
    createElement,
    createTextNode
}
Copy the code

5. Patch function (transform virtual DOM into real DOM and render)

// vdom/patch.js


function patch(oldVnode, vnode) {
    // This section covers only the first render
    // oldVnode is the EL node for the first rendering, and oldVnode is the last virtual DOM for all subsequent non-first rendering

    // Determine the type of oldVnode
    const isRealElement = oldVnode.nodeType
    if (isRealElement) {
        // First render
        const oldElm = oldVnode
        const parentElm = oldElm.parentNode

        // Generate real DOM objects
        const el = createElm(vnode)

        // The real DOM to be generated. Insert before the next node of the EL
        // Insert after el
        // Not directly appendChild because there may be other EL siblings on the page and the order cannot be broken
        parentElm.insertBefore(el, oldElm.nextSibling)

        // Delete the old EL node
        parentElm.removeChild(oldVnode)

        return el
    }
}

// The virtual DOM generates the real DOM
function createElm(vnode) {
    const { tag, data, key, children, text } = vnode

    // Determine whether it is an element node or a text node
    if (typeof tag === 'string') {
        // Create a label
        vnode.el = document.createElement(tag)

        // Parse virtual DOM properties
        updateProperties(vnode)

        // Recursively, the child node also generates the real DOM
        children.forEach(child= > {
            return vnode.el.appendChild(createElm(child))
        })
    } else {
        // The text node is created directly
        vnode.el = document.createTextNode(text)
    }

    return vnode.el
}

// Parse the properties of the virtual DOM
function updateProperties(vnode) {
    const newProps = vnode.data || {}
    const el = vnode.el
    for(let key in newProps) {
        if (key === 'style') {
            // Style of processing
            for (let styleName in newProps.style) {
                el.style[styleName] = newProps.style[styleName]
            }
        } else if (key === 'class') {
            // Class processing
            el.className = newProps.class
        } else {
            // Call dom setAttribute to set the attributes
            el.setAttribute(key, newProps[key])
        }
    }
}

module.exports = {
    patch
}
Copy the code

6. Specific flow chart

conclusion

I also do not know will not be someone to see, anyway, it is finished!! Come on!!

  • Do you want to know how Vuex works?
  • Do you really know how a Slot is “inserted”
  • “Vue source learning (a)” you don’t know – data responsive principle
  • “Vue source learning (2)” you do not know – template compilation principles
  • “Vue source learning (3)” you don’t know – first rendering principles

Study group, touch fish, come in to talk and laugh

Please click the link here