可以使用渐进式的方式去理解如果使用vdom渲染dom

step1.使用js的原生方法去创建dom

创建div节点,创建text节点,然后挂载到根节点root上去

const dom = document.createElement('div')
dom.id = 'app'
document.getElementById('root').append(dom)

const textNode = document.createTextNode('')
textNode.nodeValue = 'hello world'
dom.append(textNode)

step2.使用vdom的方式去创建dom节点

因为react也好,vue也罢,都是基于vdom去渲染真实dom的,所以我们可以模拟使用Vdom的方式去创建dom节点

// 创建一个text的vdom
const textEl = {
    type: 'TEXT_ELEMENT',
    props: {
        nodeValue: 'hello world',
        children: []
    }   
}
// 创建一个div的vdom
const el = {
    type: 'div',
    props: {
        id: 'app',
        children: []
    }
}

// 执行挂载
const dom = document.createElement(el.type)
dom.id = el.props.id
document.getElementById('root').append(dom)

const textNode = document.createTextNode('')
textNode.nodeValue = textEl.props.nodeValue
dom.append(textNode)

step3.动态生成vdom

从step2中我们模拟了一个vdom去生成真实dom,但显然这样的vdom相当于是写死的

所以我们可以改成动态创建的方式去迭代一下

// 动态创建text vdom
const createTextEl = (text) => {
    return {
        type: 'TEXT_ELEMENT',
        props: {
            nodeValue: text,
            children: []
        }   
    }
}
// 动态创建其他的 vdom
const createElement = (type, props, ...children) => {
    return {
        type,
        props: {
            ...props,
            children
        }
    }
}

// 生成vdom
const el = createElement('div', {id: 'app'})
const textEl = createTextEl('hello world')

// 执行挂载操作
const dom = document.createElement(el.type)
dom.id = el.props.id
document.getElementById('root').append(dom)

const textNode = document.createTextNode('')
textNode.nodeValue = textEl.props.nodeValue
dom.append(textNode)

step4.使用render函数来动态渲染

vdom可以动态生成,但是挂载操作还是相当于写死的

所以我们可以通过写一个render函数来让他动态渲染

const createTextEl = (text) => {
    return {
        type: 'TEXT_ELEMENT',
        props: {
            nodeValue: text,
            children: []
        }   
    }
}
const createElement = (type, props, ...children) => {
    return {
        type,
        props: {
            ...props,
            children
        }
    }
}

/**
 * 动态渲染vdom
 * @param {*} app: vdom
 * @param {*} container: 父节点
 */
function render(app, container){
    // 1.创建dom节点
    const dom = app.type === 'TEXT_ELEMENT' 
								? document.createTextNode('') 
								: document.createElement(app.type)
    // 2.添加props
    Object.keys(app.props).forEach(key => {
        if(key !== 'children'){
            dom[key] = app.props[key]
        }
    });
    // 3.递归渲染子节点
    app.props.children.forEach(child => {
        render(child, dom)
    })
    // 4.添加到父节点中渲染
    container.append(dom)
}

// 使用
const textEl = createTextEl('hello world')
const app = createElement('div', {id: 'app'}, textEl)
render(app, document.getElementById('root'))