随手记录,虚心求教。
版本
- 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.js
vc-select/Select.js
vc-select/BaseSelect.js
vc-select/SelectTrigger.js
vc-trigger/Trigger.js
,
其中,
vc-trigger/Trigger.js
里面的
红框部分为啥没执行,整个函数作用是啥。