將真實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>

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM