vlambda博客
学习文章列表

【总结】理解虚拟DOM与真实DOM


一、真实DOM

【总结】理解虚拟DOM与真实DOM

    1、WebKit的WebCore层包含了HTML引擎,负责将HTML文档解析成DOM树,由标签节点构成的文档树。最终解析出来的DOM树交由DOM模块负责管理,这个DOM就是我们俗称的真实DOM。


    2、JavaScript Core是webKit默认的javascript引擎(现在被替换为了V8引擎),负责执行javascript代码。


    3、为了给JavaScript提供操作DOM树的能力,浏览器在全局对象window上为JavaScript封装了一个document对象,然后在该对象上提供了大量的DOM操作接口。以及jq的操作$()DOM的方法。

【总结】理解虚拟DOM与真实DOM

webkit内核

        

    4、真实DOM查询节点的几种方法

    • getElementById()

    • getElementsByClassName()

    • getElementsByName()

    • getElementsByTagName()

    • getElementsByTagNameNS()

    • document.querySelector()

    • ParentNode.querySelector()

    • document.querySelectorAll()

    • ParentNode.querySelectorAll()

        在浏览器控制台查看document.getElementById,可以看到它的函数体是{ [native code] },这表示它是一个用C++编写的函数,因此无法查看具体实现。当我们在调用这个函数时,JavaScript引擎并没有直接与DOM模块交互,而是由浏览器来操作DOM模块,随后再把操作结果返回给JavaScript引擎。这种借助父级模块实现两个同级模块交互的通信方式非常常见。

        正是由于JavaScript需要借助浏览器提供的DOM接口才能操作真实DOM,所以操作真实DOM的代价往往是比较大的(这其中还涉及C++与JavaScript数据结构的转换问题)。再加上修改DOM经常导致页面重绘,所以一般来说,DOM操作越多,网页的性能就越差

【总结】理解虚拟DOM与真实DOM

        截止到目前,如何有效地减少对真实DOM的操作,仍然是前端性能优化的一个关键点。虚拟DOM就是目前较为流行的一个解决方案。


二、虚拟DOM(保存在JS的内存中)

    JavaScript无法直接操作DOM是带来上述性能问题的根源之一(其他原因包括,真实DOM树的体积非常庞大,而且操作它会导致页面重绘)。那么能不能在JavaScript内存中,以js对象的形式也描述一棵DOM树呢?

    Vue采用了响应式系统和虚拟DOM结合的方式。

【总结】理解虚拟DOM与真实DOM

        对于开发者而言,虚拟DOM的实现是透明的,它只是框架自动高效更新DOM的一种内部解决方案。开发者需要按照框架给定的语法定义数据和视图的绑定关系,随后就只需要关心数据变化(即业务逻辑)即可。

        虚拟DOM本质上是一个js对象,通过对象来表示真实的DOM结构。tag用来描述标签,props用来描述属性,children用来表示嵌套的层级关系。

const vnode = { tag: 'div', props: { id: 'container', }, children: [{ tag: 'div', props: { class: 'content', }, text: 'This is a container' }]}
//对应的真实DOM结构<div id="container"> <div class="content"> This is a container </div></div>

虚拟DOM的更新不会立即操作DOM,而是会通过diff算法,找出需要更新的节点,按需更新,并将更新的内容保存为一个js对象,更新完成后再挂载到真实dom上,实现真实的dom更新。

通过虚拟DOM,解决了操作真实DOM的三个问题。

  • 无差别频繁更新导致DOM频繁更新,造成性能问题

  • 频繁回流与重绘

  • 开发体验

另外由于虚拟DOM保存的是js对象,天然的具有跨平台的能力,而不仅仅局限于浏览器。

虚拟DOM实现原理主要分三部分:

  • 通过js建立节点描述对象

  • diff算法比较分析新旧两个虚拟DOM差异

  • 将差异patch到真实dom上实现更新

三、虚拟DOM与真实DOM的区别(dom操作量越大越明显)

  1. 虚拟DOM不会进行排版与重绘操作;

  2. 虚拟DOM进行频繁修改,然后一次性比较并修改真实DOM中需要改的部分,最后并在真实DOM中进行排版与重绘,减少过多DOM节点排版与重绘损耗;

  3. 真实DOM频繁排版与重绘的效率是相当低的;

  4. 虚拟DOM有效降低大面积(真实DOM节点)的重绘与排版,因为最终与真实DOM比较差异,可以只渲染局部;

    虚拟DOM的损耗计算:

【总结】理解虚拟DOM与真实DOM

// badlet ul = document.querySelector('#mylist');ul.append(li)ul.append(li)ul.append(li)
// good,两次重排重绘let ul = document.querySelector('#mylist');ul.style.display = 'none'; // 减少重绘ul.append(li)ul.append(li)ul.append(li)
// better,一次重排重绘let fragment = document.createDocumentFragment();fragment .append(li)fragment .append(li)fragment .append(li)ul.appendChild(fragment);

四、Diff算法

        为了避免不必要的渲染,按需更新,虚拟DOM会采用Diff算法进行虚拟DOM节点比较,比较节点差异,从而确定需要更新的节点,再进行渲染。vue采用的是深度优先,同层比较的策略。

【总结】理解虚拟DOM与真实DOM

新节点与旧节点的比较主要是围绕三件事来达到渲染目的

  • 创建新节点

  • 删除废节点

  • 更新已有节点

如何比较新旧节点是否一致呢?

vue-diff流程图

        当数据发生改变时,set方法会让调用Dep.notify通知所有订阅者Watcher,订阅者就会调用patch给真实的DOM打补丁,更新相应的视图。


具体参考vue源码

总结:

        要理解为什么需要虚拟DOM,必须弄清楚JavaScript引擎和DOM模块之间的关系,并体会由这种关系导致的DOM操作的性能问题。虚拟DOM设计的核心就是用高效的js操作,来减少低性能的DOM操作,以此来提升网页性能。


        从一定程度上来说,是浏览器的架构问题催生了虚拟DOM,而这个架构问题几乎需要重构浏览器内核才能解决,所以目前虚拟DOM仍广为流行。但是,框架给你的保证是,你在不需要手动优化的情况下,我依然可以给你提供过得去的性能,性能比较也要区分场景,diff不是免费的。


参考:https://www.cnblogs.com/wind-lanyan/p/9061684.html

Vue.js
#Vue #编程 #程序员 #前端‮发开‬ #前端 Vue面试‮之题‬Vue中观察者模式和发布订阅模式的区别和场景