«Изучение исходного кода Vue (3)» вы не знаете — принцип начального рендеринга

исходный код
«Изучение исходного кода Vue (3)» вы не знаете — принцип начального рендеринга

предисловие

В предыдущем разделе мы говорили о"Изучение исходного кода Vue (2)" вы не знаете - принцип компиляции шаблона, то есть шаблон преобразуется вrender函数所需格式, то сегодня я расскажу вам, как Vue использует эту штуку для создания настоящего DOM и отображения его на странице.

код

Шаг

$mount
mountComponent
_render выполняется для получения виртуального DOM
_update выполняет виртуальный DOM для реального DOM и отображает

2. Инициализируйте 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)

3. Функция mountComponent (функция входа рендеринга)

// lifecycle.js


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

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

    // 上一节讲到把模板编译成render函数渲染所需格式
    // 那么这一节就需要执行_render函数,来调用render函数生成虚拟DOM
    // 然后接收返回值虚拟DOM,调用_update函数把虚拟DOM转为真实DOM并渲染
    vm._update(vm._render())

    return vm
}

function lifecycleMixin(Vue) {
    // 将_update挂在Vue原型上
    Vue.prototype._update = function (vnode) {
        const vm = this

        // 执行patch函数,patch函数在下面有讲
        vm.$el = patch(vm.$el, vnode) || vm.$el
    }
}

module.exports = {
    mountComponent,
    lifecycleMixin
}

4. Функция _render (выполните функцию рендеринга, чтобы получить виртуальный DOM)

// render.js


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

function renderMixin(Vue) {
    // 将_render函数挂在Vue原型上
    Vue.prototype._render = function() {
        const vm = this

        // 把上一节生成的render函数取出来
        const { render } = vm.$options

        // 执行render函数并获得虚拟DOM
        const vnode = render.call(vm)

        return vnode 
    }

    // 创建元素节点虚拟DOM
    Vue.prototype._c = function(...args) {
        return createElement(...args)
    }

    // 创建文本节点虚拟DOM
    Vue.prototype._v = function (text) {
        return createTextNode(text)
    }

    // 对象的情况,把对象转成字符串
    Vue.prototype._s = function (val) {
        return val === null ? '' : typeof val === 'object' ? JSON.stringify(val) : val
    }
}

module.exports = {
    renderMixin
}

Ниже приведены конкретные функции и классы, необходимые для создания виртуального DOM.

// vdom/index.js


// 创建某一个节点的虚拟DOM
class Vnode {
    constructor(tag, data, key, children, text) {
        this.tag = tag
        this.data = data
        this.key = key
        this.children = children
        this.text = text
    }
}

// 创建元素节点虚拟DOM
function createElement(tag, data= {}, ...children) {
    const key = data.key
    return new Vnode(tag, data, key, children)
}

// 创建文本节点虚拟DOM
function createTextNode(text) {
    return new Vnode(undefined, undefined, undefined, undefined, text)
}

module.exports = {
    createElement,
    createTextNode
}

5. Функция исправления (преобразование виртуального DOM в реальный DOM и визуализация)

// vdom/patch.js


function patch(oldVnode, vnode) {
    // 本节只讲初次渲染
    // 初次渲染时oldVnode就是el节点,以后非初次渲染时,oldVnode就是上一次的虚拟DOM

    // 判断oldVnode的类型
    const isRealElement = oldVnode.nodeType
    if (isRealElement) {
        // 初次渲染
        const oldElm = oldVnode
        const parentElm = oldElm.parentNode

        // 生成真实DOM对象
        const el = createElm(vnode)

        // 将生成的真实DOM。插入到el的下一个节点的前面
        // 也就是插到el的后面
        // 不直接appendChild是因为可能页面中有其他el同级节点,不能破坏顺序
        parentElm.insertBefore(el, oldElm.nextSibling)

        // 删除老el节点
        parentElm.removeChild(oldVnode)

        return el
    }
}

// 虚拟DOM生成真实DOM
function createElm(vnode) {
    const { tag, data, key, children, text } = vnode

    // 判断是元素节点还是文本节点
    if (typeof tag === 'string') {
        // 创建标签
        vnode.el = document.createElement(tag)

        // 解析虚拟DOM属性
        updateProperties(vnode)

        // 递归,将子节点也生成真实DOM
        children.forEach(child => {
            return vnode.el.appendChild(createElm(child))
        })
    } else {
        // 文本节点直接创建
        vnode.el = document.createTextNode(text)
    }

    return vnode.el
}

// 解析虚拟DOM的属性
function updateProperties(vnode) {
    const newProps = vnode.data || {}
    const el = vnode.el
    for(let key in newProps) {
        if (key === 'style') {
            // style的处理
            for (let styleName in newProps.style) {
                el.style[styleName] = newProps.style[styleName]
            }
        } else if (key === 'class') {
            // class的处理
            el.className = newProps.class
        } else {
            // 调用dom的setAttribute进行属性设置
            el.setAttribute(key, newProps[key])
        }
    }
}

module.exports = {
    patch
}

6. Конкретная блок-схема

image.png

Эпилог

Я не знаю, будет ли это кто-нибудь читать, в любом случае, я закончил писать! ! ! Ну давай же! ! !

Изучите группу, прикоснитесь к группе рыб, заходите, болтайте и смейтесь, хе-хе

пожалуйста, нажмите здесьСвязь