什么是虚拟DOM?什么又是DOM diff?

141 阅读2分钟

什么是虚拟DOM?

  虚拟 DOM (Virtual DOM )这个概念相信大家都不陌生,简单地说,就是一个普通的 JavaScript 对象,包含了 tagpropschildren 三个属性,以这三个属性来描述一个DOM节点,每组描述就是一个VNode,整个VNode的集合就是一个虚拟DOM树。

虚拟DOM有什么优点?

减少DOM操作

 虚拟DOM可以将多次操作合并为一次操作,譬如往网页中用添加1000个节点,使用JS是一个接一个操作的,DOM操作需要1000次,而虚拟DOM是一次性操作的,

 虚拟DOM借助DOM diff可以把多余的操作省掉,比如添加1000个节点,其中990都没有更改,其实只有10个是需要更改的,那么操作这10个节点就可以了。

跨平台

 虚拟DOM不仅可以变成DOM,还可以变成小程序、IOS应用,安卓应用,因为虚拟DOM本质上是一个JS对象。

虚拟DOM长什么样子

React

const vNode = {
  key: null,
  props: {
    children: [   //子元素们
        {type:'span',...},
        {type:'span',...}
    ],
    className:'red'    //标签上的属性
    onClick:()=>{}      //事件
  },
  ref:null,
  type:'div',       //标签名或者组名
  ...
};

Vue

const vNode = {
  tag: "div",     //标签名或者组件名
  data: {            //标签上的属性
    class: "red",
    on: {          //事件
      click: () => {},  
    },
  },
  children: [          //子元素们
    { tag: "span",... },      
    { tag: "span",... }
],
...
};

如何创建一个虚拟DOM

React.createElemernt

createElement("div", { className: "red", onClick: () => {} },
[ createElement("span", {}, "span1"),
  createElement("span", {}, "span2"),
]);
==========================================================================================
使用JSX                                 //通过babel转译 
<div className="red" onClick={() => {}}>
  <span>span1</span>
  <span>span2</span>
</div>;



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

h(
  "div",
  {
    class: "red",
    on: {
      click: () => {},
    },
  },
  [h("span", {}, "span1"), 
  h("span", {}, "span2")]
);
=======================================================================================
template                       //使用Vue-loader转译
<div class="red" :Click="fn">
  <span>span1</span>
  <span>span2</span>
</div>;

虚拟DOM有什么缺点?

创建虚拟DOM需要额外的创建函数,譬如createElement或者Vue的h函数,但是可以通过JSX来简化成XML写法,但是这样又会暴露出一个缺点,就是React和Vue会极度的依赖打包工具,浏览器只能读懂JS语言,所以需要通过babel和Vue-loader来转译。

DOM diff 是什么?

DOM diff就是一个函数,我们称之为patch,用作比较两个虚拟DOM的区别,其本质就是对比两个对象的区别。
patches=patch(oldVNode,newVNode)

我们可以把虚拟DOM 想象成一个树形。

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

image.png

数据发生变化时

class发生改变,从red变成green

image.png

DOM diff会从上到下,从左到右的去对比,发现:

  1. div的标签类型没有发生变化,但是class属性发生变化,所以更新DOM对应的属性
  2. 元素没有发生改变,不更新

当y从true变为了false

image.png

  1. div和其属性没有发生变化,所以不更新。
  2. 检查子元素1和2,发现子元素2的span标签删除。
  3. 然后子元素1的span内的hello 变成了world,然后进行更新。