WEB面试题_虚拟DOM与DOM Diff 的原理 - Go语言中文社区

WEB面试题_虚拟DOM与DOM Diff 的原理


虚拟DOM与DOM Diff 的原理

虚拟DOM

DOM 操作慢是对比于JS原生API,如数组操作,任何基于DOM的库(VUE/React)都不可能在操作DOM时比DOM快。

他是什么?

一个能代表DOM树的对象,同窗含有标签名、标签上的属性、事件监听和子元素们,以及其他属性。

(React中)虚拟DOM的样子

const vNode = {
  key:null,
  props:{
   children:[ //子元素们
     {type:'span',...},
     {type:'span',...}
   ],
   className:"red"//标签上的属性
   onClick:() => {} //事件
  },
  ref:null,
  type:"div", //标签名 or 组件名
 ...
 //有个div里面有两个子元素span,div有class和事件监听,onClick事件是一个函数,的虚拟dom。
}

(Vue中)虚拟DOM的样子

const cNode = {
  tag:"div",//标签名 or 组件名
  data:{
    class:"red",//标签上的属性
    on:{
      click:() => {//事件
        
      }
    },
    children:[//子元素们
      {tag:"span",...},
      {tag:"span",...}
    ],
    ...
  }
}
//vue里面表示一个div,class为“red”,onclick

如何创建虚拟DOM

React.creatElement:

creatElement(
  'div',//标签名
  {classNmae.'red',onClick:() => {}},
  [
    creatElement('span',{//属性},'span1内容'),
    creatElement('sapn',{},'sapn2内容')
  ]
)
//创建一个div里边有两个span

现在创建虚拟DOM方法—通过Babel 转为creatElement 形式

<div className="red" onClaick={fn}>
  <sapn> span1内容 </span>
  <sapn> span2内容 </span>
</div>

Vue(只能在render函数里得到h)

h('div',{
  class:'red',
  on:{
    click: () => {}
  },
},[h('span',{},'span1内容'),h('span',{},'span2内容')])

现在创建虚拟DOM方法—通过vue-loader 转为 h 形式

<div className="red" @claick="fn">
  <sapn> span1内容 </span>
  <sapn> span2内容 </span>
</div>

他有什么优点?

  1. 减少DOM操作
    (1)虚拟DOM可以将多次操作并为一次操作,比如你添加1000个节点,却是一个接一个操作的。(减少DOM操作的次数)
    (2)虚拟DOM借助 DOM diff算法 可以把多余的操作省掉,比如你添加1000个节点,其实只有10个是新增的。(减少DOM操作的范围)
  2. 跨平台渲染
    虚拟DOM不仅可以变成DOM,还可以变成小程序、iOS应用、安卓应用,因为虚拟DOM本质上只是一个JS对象。

他有什么缺点?

需要额外的创建函数,如createElement 或 h,但可以通过JSX来简化成XML写法。

DOM diff 虚拟DOM的对比算法

把虚拟DOM想象成树形

<div :class="x">
  <sapn v-if="y"> {string1} </span>
  <sapn> {string2} </span>
</div>

在这里插入图片描述

当数据变化时:

x从red变成green
在这里插入图片描述
DOM diff 发现

  1. 标签类型没变,只需要更新div对应的DOM的属性;
  2. 子元素没变,不更行;
    y 从 true 变成 false:
    在这里插入图片描述
    DOM diff 发现
  3. div 没变,不用更新;
  4. 子元素1标签没变,但是children变了,更新DOM内容;
  5. 子元素2不见了,删除对应的DOM

他是什么?

就是一个函数,我们称之为patch;
patches= patch(oldVNode,newVNode)
paatches 就是要运行的DOM操作,可能长这样:

[
  {type: 'INSERT' , vNode:...},
  {type: 'TEXT' , vNode:...},
  {type: 'PROPS' , propsPatch:[...] //属性}
]

DOM diff 可能的大概逻辑:

Tree diff

  1. 将新旧两棵树逐层对比,找出哪些节点需要更新;
  2. 如果节点是组件就看Component diff;
  3. 如果节点是标签就看Element diff;

Component diff

  1. 如果节点是组件,就先看组件类型;
  2. 类型不同直接替换(删除旧的);
  3. 类型相同则只更新属性;
  4. 然后深入组件做Tree diff(递归);

Element diff

  1. 如果节点是原生标签,则看标签名;
  2. 标签名不同直接替换,相同则只更新属性;
  3. 然后进入标签后代做Tree diff (递归)

他有什么优缺点?

同级比较存在bug,会出现识别错误的问题,用:key="item.id"解决。

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_40041509/article/details/109474158
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢