虚拟DOM

虚拟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)。
文档编译者:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,284评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,115评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,614评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,671评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,699评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,562评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,309评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,223评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,668评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,859评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,981评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,705评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,310评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,904评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,023评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,146评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,933评论 2 355

推荐阅读更多精彩内容