vue3对组件、子组件、DOM元素的渲染
我们先思考一下这两个问题:
- 在menuOptions发生改变的情况下,a-layout组件及其内部的hrp-g组件会不会重新渲染?
- 在去掉外层 上的key的情况下,内层的getPageKey()发生改变的情况下,内部的hrp-g会不会重新渲染?
<a-layout class="layout" v-if="layoutType === layoutTypeMap.vertical" style="height: 100%;" :key="new Date().getTime()">
<a-layout-content style="padding: 20px 50px">
<a-breadcrumb style="padding-bottom: 20px" v-if="config.showBreadcrumb">
<a-breadcrumb-item>Home</a-breadcrumb-item>
<a-breadcrumb-item>List</a-breadcrumb-item>
<a-breadcrumb-item>App</a-breadcrumb-item>
</a-breadcrumb>
<div class="hrp-layout-content-vertical" :key="getPageKey()">
<hrp-g
v-if="init"
:key="contents.pageId"
:settings="{
config:contents,
data:config.name ? data[config.name] : data,
topData: topData,
parentData:data,
parentConfig:config
}"
/>
</div>
</a-layout-content>
</a-layout>
<div
class="hrp-context-menu"
v-show="menuOptions.length > 0 && menuVisible"
ref="contextMenuWrapper"
:style="{
top: menuTop + 'px',
left: menuLeft + 'px',
zIndex: zIndex
}">
<hrp-g v-for="(item,index) in menuOptions" :key="index" :settings="{
config:item,
data:data,
topData:topData,
parentData:parentData,
parentConfig:parentConfig
}" />
</div>
上面两个问题的答案是:
- 问题1:会重新渲染。
- 问题2:不会重新渲染。
为什么会是这样的结果呢?下面我们分析一下。
首先上面绑定了一个值时间戳的key属性,在Vue中,当你给一个组件添加:key="new Date().getTime()" 时,每次重新渲染时都会生成一个新的时间戳值,这会导致Vue认为这是一个全新的组件实例,从而触发整个组件树的重新创建,包括其所有子组件。基于这个,我们很容易就可以分析得到以下结论:
- 当 menuOptions 发生变化时,组件会重新渲染
- 如果 有一个动态变化的key (如时间戳),Vue会认为这是一个新组件
- 这会导致整个 及其所有子组件(包括 hrp-g )被销毁并重新创建
- 而当没有这个动态key时,Vue只会更新变化的部分,不会重新创建整个组件树
那么有人可能会问,根据上面的解释,问题2应该是会重新渲染才对,为什么是答案是不会渲染呢。
原因是因为: 1、当去掉外层的动态key之后,当menuOptions变化是,Vue会尝试只更新变化的部分,此时内层的:key="getPageKey()"只会影响
这个DOM元素本身,而不会强制其子组件 hrp-g 完全重建。
2、没有外层动态key时, Vue会尽量复用组件实例,hrp-g组件实例会被保留,只要其props变化时或者它自身的key发生变化(:key="contents.pageId")以及条件渲染的条件发生变化时才会更新。
按照上面的解释,似乎还存在一个问题,什么问题呢?
那就是a-layout上面的动态key应该只影响该组件本身才对,而不应该导致内部的hrp-g组件重新渲染,但实际情况却不是这样,这是为什么呢? 这里存在一个关键区别:
- Vue对DOM元素和组件的不同处理方式。 当一个普通的DOM元素(如div)的key发生变化时,Vue只会重新创建该DOM元素,但会尝试保留其子组件的状态 当一个组件(如)的key发生变化时,Vue会将其视为一个全选的组件实例,导致整个组件树销毁并重建
- 组件实例的生命周期 组件是有自己的生命周期和内部状态的,它是一个独立的功能单元。相反,DOM元素没有生命周期和内部状态。 当组件因key变化被视为新组件时,会触发完整的销毁和重建过程,包括其所有子组件。
那么内层的 :key="getPageKey()" 只会影响
这个DOM元素本身,而不会强制其子组件 hrp-g 完全重建。这是怎么做到的呢?
这个主要是Vue的渲染机制决定的。
Vue的渲染过程分为几个关键步骤: 1、虚拟DOM的创建:Vue先创建虚拟DOM树 2、差异比较(Diff算法):比较新旧虚拟DOM树的差异 3、DOM更新:只更新变化的部分,为了提高性能,Vue在更新DOM时会尽可能地保留和复用组件实例。
因为本文重点不在解析diff算法就不展开详细讲diff算法了,后续有需求可以针对Vue3的diff算法出一篇分析文章。