虚拟DOM

作者: 芹菜斯_嘉丽 | 来源:发表于2017-02-24 14:54 被阅读0次

虚拟DOM

基础概念:

  1. virtual DOM是对真实DOM的描述和映射
  2. 当Virtual DOM改变后,我们得到一个新的virtual DOM。使用算法比较新旧两颗Virtual DOM,找到不同之处,仅仅将变化的部分在真实的DOM上进行修改。
    一. 抽象DOM树
    首先,使用js将DOM结构存储在内存中。
    例如:
    <ul class="list">
        <li>item 1</li>
        <li>item 2</li>
</ul>

对应的JS对象

{ type: ‘ul’, props: { ‘class’: ‘list’ }, children: [
  { type: ‘li’, props: {}, children: [‘item 1’] },
  { type: ‘li’, props: {}, children: [‘item 2’] }
] }

转换函数:
使用js 源码比较复杂,涉及树的深搜和广搜和递归等算法。

function h(str){
//..将DOM转换为DOM树后,再根据DOM树建立对应的js对象
return  JSobj;
}

可以使用babel jsx(后续学习和使用)

二.应用DOM表达式

根据已有的js表达式,创建真实的DOM

创建节点

创建节点:(接收Virtual DOM(js 对象)返回真实的DOM节点)

function createElement(node) {
  if (typeof node === 'string') {
    return document.createTextNode(node)
  }
  const $el = document.createElement(node.type);
  node.children         /*babel 使用递归,创建相应的子元素*/
    .map(createElement)
    .forEach($el.appendChild.bind($el));
  return $el;
}

三.处理节点变化

使用算法,比较新旧两颗Virtual DOM 树的变化,将变化的部分反映到真实的DOM节点
例如(1):

<ul>                            <ul>
   <li>item1</li>                      <li>item1</li>
<ul>                                    <li>item2</li>      
                                   </ul>
《old tree》                  《new tree》

旧Virtual DOM中不存在item2,新Virtual DOM中新增item2,需要使用append添加对应的<li>.

function updateElement($parent, newNode, oldNode) {
  if (!oldNode) {
    $parnet.appendChild(
      createElement(newNode)
    );
  }
}

例如(2):

<ul>                                    <ul>
    <li>item1</li>                              <li>item1</li>              
    <li>item2</li>                      </ul>
<ul>
 《old tree》                             《new tree》

新Virtual DOM中不存在item2,需要将对应的<li> 使用remove删除
算法描述:

function updateElement($parent, newNode, oldNode, index = 0) {
  if (!oldNode) {
    $parent.appendChild(
      createElement(newNode)
    );
  } else if (!newNode) {
    $parent.removeChild(
      $parnet.childNodes[index]
    )
  }
}

例如(3):

<ul>                                    <ul>
    <li>item1</li>                              <li>item1</li>              
    <li>item2</li>                              <button>submit</button> 
<ul>                                    <ul>
 《old tree》                             《new tree》

新旧Virtual DOM的同一位置,对应节点不同,使用replace替换。

算法描述:

function updateElement($arent, newNode, oldNode, index = 0) {
  if (!oldNode) {//旧节点中不存在,将新节点直接添加到父结点上
    $parent.appendChild(
      createElement(newNode)
    );
  } else if (!newNode) {
    $parent.removeChild(//新Virtual DOM中不存在,直接将该节点在DOM中删除
      $parnet.childNodes[index]
    );
  } else if (changed(newNode, oldNode)) {//同一位置,节点对比
    $parent.replaceChild(
      createElement(newNode), //节点发生改变,直接进行在DOM上进行替换
      $parnet.childNodes[index]
    );
  }
}

3.子节点的比较
对于节点的操作是基于对每个节点的比较,然后在每个节点上调用updateElement(),对于节点的比较需要用到递归算法。
比较思路:
(1)只有元素节点的子元素需要遍历比较(文本节点的子节点不需要遍历)
(2)把当前节点作为父节点传入
(3)所有的子节点需要遍历比较,即使有些时候可能是undefined,我们的函数可以处理这种情况。
(4)索引(index)也就是children数组的索引。
代码:

function updateElement($parent, newNode, oldNode, index = 0) {
  if (!oldNode) {
    $parent.appendChild(
        createElement(newNode)
    );
  } else if (!newNode) {
    $parent.removeChild(
      $parent.childNodes[index]
    );
  } else if (changed(newNode, oldNode)) {
    $parent.replaceChild(
      createElement(newNode),
      $parent.childNods[index]
    );
  } else if (newNode.type) {
    const newLength = newNode.children.length;
    const oldLength = oldNode.children.length;
    for (let i = 0; i < newLength || i < oldLength; i++) {
      updateElement(
        $parent.childNodes[index],
        newNode.children[i],
        oldNode.children[i],
      );
    }
  } 
}

节点对比时:
~新旧Virtual DOM 树对应位置节点比较。
~使用深度遍历方法,并使用index(索引)记录子元素在DOM树中的位置。

四.总结
Virtual DOM 算法
• 用 JavaScript 对象结构表示DOM树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中。
• 当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
• 把2所记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新了。

Virtual DOM 本质上就是在JS 和 DOM 之间做了一个缓存。可以类比 CPU 和硬盘,既然硬盘这么慢,我们就在它们之间加个缓存:既然 DOM 这么慢,我们就在它们 JS 和 DOM 之间加个缓存。CPU(JS)只操作内存(Virtual DOM),最后的时候再把变更写入硬盘(DOM)。
文档编译者:

相关文章

网友评论

      本文标题:虚拟DOM

      本文链接:https://www.haomeiwen.com/subject/yuqwwttx.html