将真实DOM转换为虚拟DOM/虚拟DOM转化为真实DOM

 

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
    
</style>
</head>
<body>

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


<script>
    // 用内存去表述DOM
    // 将真实DOM转化为虚拟DOM
    // <div />  => {tag:'div'}   元素转化
    // 文本节点 => {tag:undefined,value:'文本节点'}   文本节点转化
    // <div title="1" class="c"  />  => { tag:'div',data:{ title:'1',class:"c" } }   多属性转化
    // <div><div /></div> => {tag:'div',children:[{ tag:'div' }]} 

    // 用构造函数来 进行以上转换
    // 这一次我们用class语法

    class VNode {
        // 构造函数
        constructor( tag,data,value,type ){
            // tag:用来表述 标签  data:用来描述属性  value:用来描述文本 type:用来描述类型
            this.tag = tag && tag.toLowerCase();//文本节点时 tagName是undefined
            this.data = data;
            this.value = value;
            this.type = type;
            this.children = [];

        }
        appendChild( vnode ){
            this.children.push( vnode );
        }
    }
    /**
    利用递归 来遍历DOM元素 生成虚拟DOM
    Vue中的源码使用 栈结构  ,使用栈存储 父元素来实现递归生成
    */
    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++){//attrs[ i ] 属性节点(nodeType == 2) 是对象
                _attrObj[ attrs[ i ].nodeName ] = attrs[ i ].nodeValue;//attrs[ i ].nodeName:属性名 attrs[ i ].nodeValue:属性值
            }
            _vnode = new VNode( nodeName,_attrObj,undefined,nodeType);//标签名(DIV UI LI...)、所有属性对象、value值(只有文本标签有)、type类型(是元素还是文本)
            // 考虑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);//无标签名、无属性、有value、有type
        }
        return _vnode;
    }

    let root = document.querySelector("#root");

    let vroot = getVNode ( root );//虚拟DOM
    console.log(vroot);
    // 将vNode转化为真正的DOM
    function parseNode(vnode){
        // 在真正的vue中 也是使用递归+栈 数据类型
        // 创建真实的DOM
        let type = vnode.type;//拿到虚拟DOM的type,元素?文本?
        let _node = null;//用来放创建出来的元素  真实node
        if(type === 3){
            // 文本节点
            return document.createTextNode(vnode.value);//直接创建文本节点
        }else if(type === 1){
            // 元素节点
            _node = document.createElement(vnode.tag);//用tag名创建对应的标签

            // 属性
            let data = vnode.data;//键值对类型  真正的vue中药比这复杂的多(事件、指令等)
            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( parseNode(subvnode) );//将子元素放进去  递归转换子元素
            });
            return _node;
        };
    }

    let dom2 = parseNode(vroot);//虚拟dom转换成真实dom
    console.log(dom2);//打印出来的DOM和真实dom是一样的

</script>

    
</body>
</html>

 

 

 

 

posted @ 2020-09-06 20:02  古墩古墩  Views(1340)  Comments(0Edit  收藏  举报