Vue虚拟Dom和真实Dom互相转化的简单实现

103 阅读1分钟

参考链接 www.bilibili.com/video/BV1LE…

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
<div id="root">
    <div class="my">
        <div title="tt1" id="id">hello1</div>
        <div title="tt2">hello2</div>
        <div title="tt3">hello3</div>
        <ul>
            <li>1</li>
            <li>2</li>
            <li>3</li>
        </ul>
    </div>
</div>

<script>


    /**
     * 文本节点 => { tag: undefined, value: '文本节点内容' }
     * 元素节点<div title="1" class="c"> <span>123 </span> </div> => {tag: 'div', data: { title: '1', class: 'c' }, children: [{ tag: 'span', value:123}]}
     */
    class VNode {
        constructor(tag, data, value, type) {
            this.tag = tag && tag.toLowerCase();
            this.data = data;
            this.value = value;
            this.type = type;
            this.children = [];
        }

        appendChild(vnode) {
            this.children.push(vnode);
        }
    }

    /**
     * 使用递归 来遍历 DOM 元素, 生成 虚拟 DOM
     * @param node  真实的Dom节点
     * @returns 虚拟 DOM
     */
    function getVNode(node) {
        let nodeType = node.nodeType;
        let _vnode = null;
        if (nodeType === 1) {
            let nodeName = node.nodeName;
            let attrs = node.attributes;
            let _attrObj = {};
            for (let i = 0; i < attrs.length; i++) {
                _attrObj[attrs[i].nodeName] = attrs[i].nodeValue;
            }
            _vnode = new VNode(nodeName, _attrObj, undefined, nodeType);
            // 考虑 node 的子元素
            let childNodes = node.childNodes;
            for (let i = 0; i < childNodes.length; i++) {
                _vnode.appendChild(getVNode(childNodes[i])); // 递归
            }

        } else if (nodeType === 3) {
            _vnode = new VNode(undefined, undefined, node.nodeValue, nodeType);
        }
        return _vnode;
    }

    let root = document.querySelector('#root');
    let vroot = getVNode(root);
    // console.log('vroot', vroot);


    /**
     * 将 vNode 转换为真正的 DOM
     * @param vnode 虚拟Dom
     * @returns 真实的Dom
     */
    function parseVNode(vnode) {
        // 创建 真实的 DOM
        let type = vnode.type;
        let _node = null;
        if (type === 3) {
            return document.createTextNode(vnode.value); // 创建文本节点
        } else if (type === 1) {
            _node = document.createElement(vnode.tag);
            // 属性
            let data = vnode.data;
            Object.keys(data).forEach((key) => {
                let attrName = key;
                let attrValue = data[key];
                _node.setAttribute(attrName, attrValue);
            });
            // 子元素
            let children = vnode.children;
            children.forEach(subvnode => {
                _node.appendChild(parseVNode(subvnode)); // 递归转换子元素 ( 虚拟 DOM )
            });
            return _node;
        }
    }
    let dom2 = parseVNode(vroot);
    console.log('dom2', dom2);

</script>
</body>

</html>