本身完成一个简朴的假造 DOM

本身完成假造 DOM

从 HTML 中提炼数据结构

先来看下我们的 HTML

<div class='root'>
    <h1><span>傅雷家信</span></h1>
    <span>读家信,想付雷</span>
</div>

从 HTML 中我们能够抽离出它的数据结构:

  1. 起首页面中只需要一个根节点root,定义为:nodesDate数组
  2. root内有两个子元素h1span,数组有两项,每项为内容为tagchildren
  3. 接下来内部一切元素都是云云定义,直到碰到文本元素,将他定义为text
nodesDate = {
    tag:"div",
    children:[{
        tag:'h1',
        children:[{
            tag:'span',
            children:[{
                tag:'#text',
                text:'傅雷家信'
            }]
        }]
    },{
        tag:'span',
        children:[{
            tag:'#text',
            text:'读家信,想傅雷'
        }]
    }]
}

用这类视野在看 HTML 的话,就不是纯真的 HTML 了,而是一堆hash

从上面数据结构中我们能够提炼出3个有效的属性,分别是tagchildrentext,那我们是否是能够定义一个要领,通报这三个参数,就可以满足我们的需求呢?

组织 HTML 要领

想一下我们拿到这三个参数后,要干什么呢?

当然是在页面中天生 DOM 元素啊!

对,这三个参数是我们各自私有属性,经由过程这三个属机能天生各自的 DOM,天生 DOM 的要领是否是公用的呢?

所以能够用用组织函数形式建立我们要的要领,(PS:之前讲过new操作符的背地的逻辑,不理解的可移步:运用 new 操作符内部到底在做什么

function vNode(tag,children,text){
    this.tag = tag
    this.children = children
    this.text = text
}

vNode.prototype.render = function(){
    //如 tag 为文本的话,建立一个文本节点
    if(this.tag === '#text'){
        return document.createTextNode(this.text)    // 返回文本
    }
    
    //tag 不是文本的话,建立一个 DOM 节点,而且遍历 children,每次遍历都是挪用本身的 render 要领
    let element = document.createElement(this.tag)
    this.children.forEach((vChild)=> {
        element.appendChild(vChild.render())    //在遍历 h1 时,没有直接跳出,而是在其内部不停轮回。直到 h1 内部的 children 悉数轮回完毕为止,才进入下一个元素 span,当 h1 轮回完毕时,h1 内部的节点都已天生好了。
    })
    return element        //返回节点
}

function v(tag,children,text){
    //假如 chilren 为字符串,那末就把 children 赋值给 text,并把 children 初始化为 [],不然背面会报错
    if(typeof children === 'string'){
        text = children
        children = []
    }
    return new vNode(tag,children,text)
}

//花样拜见 nodesData,vNode 的实例化
let vNodes = v('div',[
    v('h1',[
        v('span',[
            v('#text','傅雷家信')])
    ]),
    v('span',[
        v('#text','——傅敏')])
])

const root = document.querySelector('.root')    //猎取 root 节点
root.appendChild(vNodes.render())    //这里只运转一次,把终究的 DOM 添加进页面中

完成增编削

假如此时一个数据更改比方,根据之前的逻辑

root.innerText = ''
root.appendChild(vNodes.render())

假如数据非常大,用这类要领基础没啥意义,每一次修改 DOM 树都要从新衬着一遍,形成机能低下,有什么好的要领能够完成呢?

function patchElement(parent, newVNodes, oldVNodes, index = 0) {
    //假如没有通报老的 VNodes,默许就是新的
    if(!oldVNodes) {
        parent.appendChild(newVNodes.render())
      } else if(!newVNodes) {
        parent.removeChild(parent.childNodes[index])
      } else if(newVNodes.tag !== oldVNodes.tag || newVNodes.text !== oldVNodse.text) {    
          //假如元素不一样或许文本不一样,走这边
          //当有走这边时,newVNodes 是和 oldVNodes 差别的谁人值,这里的 parent 是当前元素或文本的 parent
          //replaceChild(sp1,sp2),是将 sp2 换成 sp1
        parent.replaceChild(newVNodes.render(), parent.childNodes[index])
      } else {
        for(let i = 0; i < newVNodes.children.length || i < oldVNodes.children.length; i++) {
        //取值永远是 newVNodes.length,除非不传 newVNodes
        //这里 index 只有当 i 变化时,下一次才是 index 才即是 i 的值
        //当 i = 0 时,此次的 parent.childNode[index],是下一次的 parent,所以这里要用 index
        //当轮回走完,发明元素或许文本不一样时,才走第三个逻辑
              patchElement(parent.childNodes[index], newVNodes.children[i], oldVNodes.children[i], i)
        }
      }
}
    原文作者:UCCs
    原文地址: https://segmentfault.com/a/1190000016132497
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞