zoukankan      html  css  js  c++  java
  • Vue | 虚拟DOM

    一、真实DOM和其解析流程

    浏览器渲染引擎工作流程,大致可分为5步:

    创建DOM树——创建StyleRules——创建Render树——布局Layout——绘制Painting

    第一步,用HTML分析器,分析HTML元素,构建一颗DOM树(标记化和树构建)。

    第二步,用CSS分析器,分析CSS文件和元素上的inline样式,生成页面的样式表。

    第三步,将DOM树和样式表关联起来,构建一颗Render树(这一过程又称为Attachment)。
    每个DOM节点都有attach方法,接受样式信息,返回一个render对象(又名renderer)。
    这些render对象最终会被构建成一颗Render树。

    第四步,有了Render树,浏览器开始布局,为每个Render树上的节点确定一个在显示屏上出现的精确坐标。

    第五步,Render树和节点显示坐标都有了,就调用每个节点paint方法,把它们绘制出来。

    DOM树的构建过程:

    构建DOM树是一个渐进过程,为达到更好用户体验,渲染引擎会尽快将内容显示在屏幕上。它不必等到整个HTML文档解析完毕之后才开始构建render数和布局。

    这三个过程在实际进行的时候又不是完全独立,而是会有交叉。会造成一边加载,一遍解析,一遍渲染的工作现象。

    CSS的解析是从右往左逆向解析的(从DOM树的下-上解析比上-下解析效率高),嵌套标签越多,解析越慢。

    webkit渲染引擎工作流程

    二、操作真实DOM

    用我们传统的开发模式,原生JS或JQ操作DOM时,浏览器会从构建DOM树开始从头到尾执行一遍流程。

    在一次操作中,我需要更新10个DOM节点,浏览器收到第一个DOM请求后并不知道还有9次更新操作,因此会马上执行流程,最终执行10次。

    例如,第一次计算完,紧接着下一个DOM更新请求,这个节点的坐标值就变了,前一次计算为无用功。

    计算DOM节点坐标值等都是白白浪费的性能。频繁操作还是会出现页面卡顿,影响用户体验

    三、虚拟DOM的好处

    虚拟DOM就是为了解决浏览器性能问题而被设计出来的。如前,若一次操作中有10次更新DOM的动作,虚拟DOM不会立即操作DOM

    而是将这10次更新的diff内容保存到本地一个JS对象中,最终将这个JS对象一次性attch到DOM树上,再进行后续操作,避免大量无谓的计算量。

    所以,用JS对象模拟DOM节点的好处是:

    页面的更新可以先全部反映在JS对象(虚拟DOM)上,操作内存中的JS对象的速度显然要更快,等更新完成后,再将最终的JS对象映射成真实的DOM,交由浏览器去绘制

    四、实现虚拟DOM

    真是的DOM节点

    真实DOM

    JS来模拟DOM节点实现虚拟DOM

    虚拟DOM

    Element方法具体实现

    Element方法具体实现

    第一个参数是节点名(如div),第二个参数是节点的属性(如class),第三个参数是子节点(如ul的li)。

    除了这三个参数会被保存在对象上外,还保存了key和count。其相当于形成了虚拟DOM树。

    虚拟DOM树

    将其映射成真实DOM

    虚拟DOM对象映射成真实DOM

    五、diff操作

    old tree

    new tree

    平层Diff,只有以下4种情况:

    1、节点类型变了,例如下图中的P变成了H3。我们将这个过程称之为REPLACE。直接将旧节点卸载并装载新节点。这样做效率不高。

    2、节点类型一样,仅仅属性或属性值变了。我们将这个过程称之为PROPS。此时不会触发节点卸载和装载,而是节点更新。

    查找不同属性方法

    3、文本变了,文本对也是一个Text Node,也比较简单,直接修改文字内容就行了,我们将这个过程称之为TEXT。

    4、移动/增加/删除 子节点,我们将这个过程称之为REORDER。看一个例子,在A、B、C、D、E五个节点的B和C中的BC两个节点中间加入一个F节点。

    我们简单粗暴的做法是遍历每一个新虚拟DOM的节点,与旧虚拟DOM对比相应节点对比,在旧DOM中是否存在,不同就卸载原来的按上新的。
    这样会对F后边每一个节点进行操作。卸载C,装载F,卸载D,装载C,卸载E,装载D,装载E。效率太低。

    粗暴方法

    如果我们在JSX里为数组或枚举型元素增加上key后,它能够根据key,直接找到具体位置进行操作,效率比较高,可以用Levenshtein Distance算法来实现

    最终Diff出来的结果

    映射成真实DOM

    虚拟DOM有了,Diff也有了,现在就可以将Diff应用到真实DOM上了。深度遍历DOM将Diff的内容更新进去。

    根据Diff更新DOM

    根据Diff更新DOM

    我们会有两个虚拟DOM(js对象,new/old进行比较diff),用户交互我们操作数据变化new虚拟DOM,old虚拟DOM会映射成实际DOM(js对象生成的DOM文档)通过DOM fragment操作给浏览器渲染。

    当修改new虚拟DOM,会把newDOM和oldDOM通过diff算法比较,得出diff结果数据表(用4种变换情况表示)。

    再把diff结果表通过DOM fragment更新到浏览器DOM中。

    注:

    虚拟DOM的存在的意义:vdom 的真正意义是为了实现跨平台,服务端渲染,以及提供一个性能还算不错 Dom 更新策略。vdom 让整个 mvvm 框架灵活了起来

    Diff算法:只是为了虚拟DOM比较替换效率更高,通过Diff算法得到diff算法结果数据表(需要进行哪些操作记录表)。

    原本要操作的DOM在vue这边还是要操作的,只不过用到了js的DOM fragment来操作dom(统一计算出所有变化后统一更新一次DOM)进行浏览器DOM一次性更新。

    参考:https://www.jianshu.com/p/af0b398602bc

  • 相关阅读:
    POJ 2236 Wireless Network(并查集)
    POJ 2010 Moo University
    POJ 3614 Sunscreen(贪心,区间单点匹配)
    POJ 2184 Cow Exhibition(背包)
    POJ 1631 Bridging signals(LIS的等价表述)
    POJ 3181 Dollar Dayz(递推,两个long long)
    POJ 3046 Ant Counting(递推,和号优化)
    POJ 3280 Cheapest Palindrome(区间dp)
    POJ 3616 Milking Time(dp)
    POJ 2385 Apple Catching(01背包)
  • 原文地址:https://www.cnblogs.com/pp-yang/p/12571810.html
Copyright © 2011-2022 走看看