《关于把虚拟dom转换成真实dom原来是这么一回事》






真实dom结构

在学习虚拟DOM之前,先来了解一下真实的DOM结构,这里不得不提的是关于浏览器渲染方面的知识。

当浏览器拿到一个HTML文件,首先会根据HTML文件构建出一个DOM树来,并行加载CSS文件,图片,JS脚本,值得注意的是DOM树的渲染和CSSOM渲染是并行执行的,而不是串行进行的。(JS脚本需要在html尾部加载,或是写入window.onload方法里,让DOM加载完成后再去加载JS脚本,防止进程阻塞,JS脚本是同步加载的)。

在构建完DOM树和CSSOM树之后,即可开始Render Tree即渲染树的构建,进行布局,绘制。

关于真实dom结构的详情,可以看我的另一篇博文,相信看完之后,对virtualdom会有一个更清晰的认知。

《关于实现一个函数把真实dom转换成虚拟dom原来是这么一回事》

创建虚拟dom的思路

vue中,虚拟dom创建时,使用的相关操作如下,这里用的是网上的图。

在这里插入图片描述

将虚拟DOM映射为真实DOM




确定VNode的结构
class Element {
    constructor(tagName, props, children) {
        this.tagName = tagName
        this.props = props
        this.children = children
    }
}



创建一个结点的方法
/**
 * 创建一个结点
 * @param {*} type 
 * @param {*} props 
 * @param {*} children 
 */
function createElement(type, props, children) {
    return new Element(type, props, children);
}
给结点设置properties的方法
/**
 * 给结点设置properties
 * @param {*} node 
 * @param {*} prop 
 * @param {*} value 
 */
function setAttrs(node, prop, value) {
    switch (prop) {
        case 'value':
            if (node.tagName == 'INPUT' || node.tagName === 'TEXTAREA') {
                node.value = value;
            }
            else {
                node.setAttribute(prop, value);
            }
            break;
        case 'style':
            node.style.cssText = value;
            break;
        default:
            node.setAttribute(prop, value);
            break;
    }
}



将一个virtualdom转换成真实dom对象
/**
 *  将一个virtualdom转换成真实dom对象      这里使用了深度优先DFS算法进行节点的遍历。
 * @param {*} vDom 
 */
function render(vDom) {
    const { tagName, props, children } = vDom

    const el = document.createElement(tagName);

    for (let key in props) {
        setAttrs(el, key, props[key]);
    }
    children.map((c)=>{
        if(c instanceof Element){
            c = render(c);
        }else{
            c = document.createTextNode(c);
        }

        el.appendChild(c)
    })

    return el;
}




将一个节点添加到root元素上
/**
 * 将一个节点添加到root元素上
 * @param {*} rDom 
 * @param {*} rootEl 
 */
function renderDom(rDom, rootEl){
    rootEl.appendChild(rDom);
}




测试

完整代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div id="app">

    </div>

    <script>
        class Element {
            constructor(tagName, props, children) {
                this.tagName = tagName
                this.props = props
                this.children = children
            }
        }

        /**
         * 创建一个结点
         * @param {*} type 
         * @param {*} props 
         * @param {*} children 
         */
        function createElement(type, props, children) {
            return new Element(type, props, children);
        }


        /**
         * 给结点设置properties
         * @param {*} node 
         * @param {*} prop 
         * @param {*} value 
         */
        function setAttrs(node, prop, value) {
            switch (prop) {
                case 'value':
                    if (node.tagName == 'INPUT' || node.tagName === 'TEXTAREA') {
                        node.value = value;
                    } else {
                        node.setAttribute(prop, value);
                    }
                    break;
                case 'style':
                    node.style.cssText = value;
                    break;
                default:
                    node.setAttribute(prop, value);
                    break;
            }
        }


        /**
         *  将一个virtualdom转换成真实dom对象
         * @param {*} vDom 
         */
        function render(vDom) {
            const {
                tagName,
                props,
                children
            } = vDom

            const el = document.createElement(tagName);

            for (let key in props) {
                setAttrs(el, key, props[key]);
            }
            children.map((c) => {
                if (c instanceof Element) {
                    c = render(c);
                } else {
                    c = document.createTextNode(c);
                }

                el.appendChild(c)
            })

            return el;
        }


        /**
         * 将一个节点添加到root元素上
         * @param {*} rDom 
         * @param {*} rootEl 
         */
        function renderDom(rDom, rootEl) {
            rootEl.appendChild(rDom);
        }


        // 开始测试

        const rootEl = document.getElementById("app")

        const vDom = createElement(
            'ul', {
                class: 'list',
                style: 'width:300px;height:300px;background-color:orange'
            },
            [
                createElement(
                    'li', {
                        class: 'item',
                        data_id: 0,
                    },
                    [
                        createElement(
                            'p', {
                                class: 'text',
                            },
                            ["第一个列表项"]
                        )
                    ]
                ),
                createElement(
                    'li', {
                        class: 'item',
                        data_id: 1,
                    },
                    [
                        createElement(
                            'p', {
                                class: 'text',
                            },
                            [
                                createElement(
                                    'span', {
                                        class: "title"
                                    },
                                    [
                                        '第二个列表项'
                                    ]
                                )
                            ]
                        )
                    ]
                ),
                createElement(
                    'li', {
                        class: "item",
                        data_id: 2
                    },
                    [
                        '第三个列表项'
                    ]
                )
            ]
        )
    
    

        trueDom = render(vDom)
        renderDom(trueDom, rootEl)


    </script>
</body>

</html>

成功渲染

在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值