随手记录,虚心求教。
版本
- node:18.14.0
- npm:9.6.0
- vue:3.3.4
- ant-design-vue: 4.0.2
问题复现
代码如下:
<script setup lang="ts">
const getPopupContainer = (triggerNode: Node) => triggerNode.parentElement?.parentElement || document.body
</script>
<template>
<div class="select-filter-wrapper">
<div class="filter-wrapper">
<a-select
style="width: 120px"
value="全部类型"
defaultOpen
class="type-select"
:getPopupContainer="getPopupContainer"
>
<a-select-option value="jack">Jack</a-select-option>
<a-select-option value="lucy">Lucy</a-select-option>
<a-select-option value="Yiminghe">yiminghe</a-select-option>
</a-select>
</div>
</div>
</template>
如果不加getPopupContainer属性,那么下拉框将会绑定到body元素下,而加了getPopupContainer属性(值为函数)后,将会绑定到返回值(类型是DOM)上。
getPopupContainer属性函数的参数是trggerNode,即触发下拉框显示的元素,即:
如果,我想将其挂载到
.ant-select元素上,就需要使用triggerNode.parentElement;如果我想挂载到和.ant-select同级的位置
那可能下意识写成了
triggerNode.parentElement.parentElement。
然而这里有一个问题
※ triggerNode.parentElement.parentElement在第一次执行时(注:执行,而非渲染到界面上)值为null。
其原因可能是,第一次执行时,vue在执行_createVNode的时候,整个Select组件虽然渲染了自己的DOM结构,还没有挂载到网页的Dom树上,这就是为什么triggerNode.parentElement取到了.ant-select(因为还在Select组件自己的DOM结构中),而triggerNode.parentElement.parentElement取到的是null(因为还没挂载到网页整体的DOM树上,.ant-select还没有父节点)。
影响
当像上述代码展示那样,我想让下拉框和Select组件同级,并且加个兜底选项document.body保证下拉框能够有地方挂载。
此时,如果我加了defaultOpen或者open属性,那么就会在第一次执行后就要渲染到界面上,可是由于triggerNode.parentElement.parentElement取到的是null,那么getPopupContainer的返回值就成了document.body了,即会直接渲染到body元素上。即使后续执行时Select组件已经挂载到网页的DOM树上了,再执行triggerNode.parentElement.parentElement可以顺利拿到.filter-wrapper元素,也无法改变挂载位置。
而如果不加defaultOpen或者open属性,那么只有在第一次触发下拉框时渲染,那此时Select组件已经挂载到网页的DOM树上了,此时再执行triggerNode.parentElement.parentElement时可以顺利拿到.filter-wrapper元素。就能顺利使得下拉框和Select组件同级
说白了,就是虚拟DOM的基本知识和小细节,执行和渲染
总结
其实也不是什么深入的问题,只是小细节加深一下对虚拟DOM和Vue的认知。getPopupContainer函数可以直接使用document.querySelector()便能规避这种问题。
延申内容
假设上述代码改为:
const getPopupContainer = (triggerNode: Node) => triggerNode.parentElement?.parentElement
就是去掉兜底选项,也能顺利使得下拉框和Select组件同级,当然这里并不是因为第一次执行triggerNode.parentElement.parentElement时可以顺利拿到.filter-wrapper元素,而是加了defaultOpen之后,执行了三次,第一次执行时因为未挂载而找不到.filter-wrapper,但是第二次执行时已经挂载好了,所以找到了.filter-wrapper。如果添加兜底,虽然也会执行三次,但是第一次由于已经得到了body,后续即使得到.filter-wrapper,也会被忽略。
预留问题
-
为什么加了
defaultOpen / open后会执行三次。 -
涉及到的文件:
select/index.jsvc-select/Select.jsvc-select/BaseSelect.jsvc-select/SelectTrigger.jsvc-trigger/Trigger.js,
其中,
vc-trigger/Trigger.js里面的
红框部分为啥没执行,整个函数作用是啥。