Vue核心虚拟Dom和 diff 算法

一、介绍虚拟DOM

什么是虚拟DOM?

之前我的理解:

虚拟DOM是一个真实DOM的映射,Vue是拿虚拟DOM描述真实DOM,虚拟DOM体积比真实DOM小,每次操作虚拟DOM都会触发重排,判断虚拟DOM发生的变动Vue从而渲染真实DOM。除了能提高性能上的好处之外虚拟DOM还可以与真实DOM解耦,使Vue可以不依赖真实DOM,操作更自由。

虚拟DOM就是通过JS来生成一个AST节点树

 

 

 为什么要有虚拟DOM?

let div = document.createElement('div')
let str = ''
for (const key in div) {
  str += key + ''
}
console.log(str)

发现一个dom上面的属性是非常多的

aligntitlelangtranslatedirhiddenaccessKeydraggablespellcheckautocapitalizecontentEditableisContentEditableinputModeoffsetParentoffsetTopoffsetLeftoffsetWidthoffsetHeightstyleinnerTextouterTextonbeforexrselectonabortonbluroncanceloncanplayoncanplaythroughonchangeonclickoncloseoncontextmenuoncuechangeondblclickondragondragendondragenterondragleaveondragoverondragstartondropondurationchangeonemptiedonendedonerroronfocusonformdataoninputoninvalidonkeydownonkeypressonkeyuponloadonloadeddataonloadedmetadataonloadstartonmousedownonmouseenteronmouseleaveonmousemoveonmouseoutonmouseoveronmouseuponmousewheelonpauseonplayonplayingonprogressonratechangeonresetonresizeonscrollonsecuritypolicyviolationonseekedonseekingonselectonslotchangeonstalledonsubmitonsuspendontimeupdateontoggleonvolumechangeonwaitingonwebkitanimationendonwebkitanimationiterationonwebkitanimationstartonwebkittransitionendonwheelonauxclickongotpointercaptureonlostpointercaptureonpointerdownonpointermoveonpointeruponpointercancelonpointeroveronpointeroutonpointerenteronpointerleaveonselectstartonselectionchangeonanimationendonanimationiterationonanimationstartontransitionrunontransitionstartontransitionendontransitioncanceloncopyoncutonpastedatasetnonceautofocustabIndexattachInternalsblurclickfocusenterKeyHintvirtualKeyboardPolicyonpointerrawupdatenamespaceURIprefixlocalNametagNameidclassNameclassListslotattributesshadowRootpartassignedSlotinnerHTMLouterHTMLscrollTopscrollLeftscrollWidthscrollHeightclientTopclientLeftclientWidthclientHeightattributeStyleMaponbeforecopyonbeforecutonbeforepasteonsearchelementTimingonfullscreenchangeonfullscreenerroronwebkitfullscreenchangeonwebkitfullscreenerrorchildrenfirstElementChildlastElementChildchildElementCountpreviousElementSiblingnextElementSiblingafteranimateappendattachShadowbeforeclosestcomputedStyleMapgetAttributegetAttributeNSgetAttributeNamesgetAttributeNodegetAttributeNodeNSgetBoundingClientRectgetClientRectsgetElementsByClassNamegetElementsByTagNamegetElementsByTagNameNSgetInnerHTMLhasAttributehasAttributeNShasAttributeshasPointerCaptureinsertAdjacentElementinsertAdjacentHTMLinsertAdjacentTextmatchesprependquerySelectorquerySelectorAllreleasePointerCaptureremoveremoveAttributeremoveAttributeNSremoveAttributeNodereplaceChildrenreplaceWithrequestFullscreenrequestPointerLockscrollscrollByscrollIntoViewscrollIntoViewIfNeededscrollTosetAttributesetAttributeNSsetAttributeNodesetAttributeNodeNSsetPointerCapturetoggleAttributewebkitMatchesSelectorwebkitRequestFullScreenwebkitRequestFullscreenariaAtomicariaAutoCompleteariaBusyariaCheckedariaColCountariaColIndexariaColSpanariaCurrentariaDescriptionariaDisabledariaExpandedariaHasPopupariaHiddenariaKeyShortcutsariaLabelariaLevelariaLiveariaModalariaMultiLineariaMultiSelectableariaOrientationariaPlaceholderariaPosInSetariaPressedariaReadOnlyariaRelevantariaRequiredariaRoleDescriptionariaRowCountariaRowIndexariaRowSpanariaSelectedariaSetSizeariaSortariaValueMaxariaValueMinariaValueNowariaValueTextgetAnimationsnodeTypenodeNamebaseURIisConnectedownerDocumentparentNodeparentElementchildNodesfirstChildlastChildpreviousSiblingnextSiblingnodeValuetextContentELEMENT_NODEATTRIBUTE_NODETEXT_NODECDATA_SECTION_NODEENTITY_REFERENCE_NODEENTITY_NODEPROCESSING_INSTRUCTION_NODECOMMENT_NODEDOCUMENT_NODEDOCUMENT_TYPE_NODEDOCUMENT_FRAGMENT_NODENOTATION_NODEDOCUMENT_POSITION_DISCONNECTEDDOCUMENT_POSITION_PRECEDINGDOCUMENT_POSITION_FOLLOWINGDOCUMENT_POSITION_CONTAINSDOCUMENT_POSITION_CONTAINED_BYDOCUMENT_POSITION_IMPLEMENTATION_SPECIFICappendChildcloneNodecompareDocumentPositioncontainsgetRootNodehasChildNodesinsertBeforeisDefaultNamespaceisEqualNodeisSameNodelookupNamespaceURIlookupPrefixnormalizeremoveChildreplaceChildaddEventListenerdispatchEventremoveEventListener

所以直接操作DOM非常浪费性能

解决方案就是 我们可以用JS的计算性能来换取操作DOM所消耗的性能,既然我们逃不掉操作DOM这道坎,但是我们可以尽可能少的操作DOM

操作JS是非常快的,与真实DOM解耦,Vue可以不依赖真实DOM,操作更自由

二、介绍Diff算法

Vue3 源码地址 https://github.com/vuejs/core

<template>
  <div>
    <div :key="item" v-for="(item) in Arr">{{ item }}</div>
  </div>
</template>
 
 
 
<script setup lang="ts">
const Arr: Array<string> = ['a', 'B', 'C', 'D']
Arr.splice(2,0,'DDD')
</script>
 
 
<style>
</style>

 

 

 

 

 

 

 

总结:

1.没有Key的diff算法只有三步骤:

      首先进行新旧的VNode进行对比,新的会把旧的替换掉,如果发现有多的会进行第二步骤插入,如果发现少了会进性删除

2.有Key的diff算法分为五个步骤:
      首先进性前序算法如果对比过程中出现不一样会break跳出循环,然后进性尾序对比从后面开始对比发现不一样也会跳出循环与vue2的双端diff算法不同,vue2的diff算法是头和头尾和尾,然后头和尾尾和头,这样子交叉对比,vue3没有头尾交叉对比,vue3做了优化最长递增子序列算法。头尾比完之后发现多出来的会进行第三步patch新增,如若发现少了的会进行第四步骤卸载unmount。最难的是在第五步乱序有可能是做了位移有可能是做了新增,也可能删除新增位移同时发生很多一些不可控的情况。在第五步底层源码中是做了三个部分,构建新节点映射关系、纪录新节点在旧的节点中的位置数组,如果有多余的旧节点就给他删掉,如果新节点不包含的旧节点也给他删掉,如果节点出现交叉,说明是要移动了,要去求最长子序列算法。求出来之后如果当前遍历的这个节点不在子序列中要进行移动,如果在子序列中就跳过。

    

    

 

posted @ 2023-03-07 17:44  荣姐  阅读(66)  评论(0编辑  收藏  举报