欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-06-09 17:02:53
...

虚拟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>

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

当数据变化时:

x从red变成green
WEB面试题_虚拟DOM与DOM Diff 的原理
DOM diff 发现

  1. 标签类型没变,只需要更新div对应的DOM的属性;
  2. 子元素没变,不更行;
    y 从 true 变成 false:
    WEB面试题_虚拟DOM与DOM Diff 的原理
    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"解决。

相关标签: WEB面试题 vue