组件结构
dropdown 组件对参数做了一些处理,然后直接调用了vc-trigger组件来进行渲染,先看一下整体的组件调用结构。

极简实现
这个组件需要满足以下几个基本的功能:
- 可以传入两个插槽 default和popup,default是默认展示的节点,点击后可以弹出popup。
- popup可以指定在文档中的位置,默认是插入到- body节点下面。
- popup可以相对于- default来进行定位。
效果如下:

同时,popup也插入到了body节点下面。

简易代码
先看一下整体的数据流转过程,用户传入的两个插槽是如何渲染到页面中的。这两个查擦的传递过程需要仔细看一下。

接下来逐步解释一下从底层到上层的代码。
Align.tsx
- 用户传入的信息就是default插槽的内容,给它套一个div节点,用来获取引用,方便定位。
return () => {
  const child = slots.default?.();
  if (child) {
    return <div ref={nodeRef}>{child}</div>;
  }
  return null;
};
- 当组件挂载或者挂载位置变化的时候,调用对齐的方法。
onMounted(() => {
  align();
});
watch(
  () => [props.align, props.target],
  () => {
    align();
  },
  { immediate: true, deep: true, flush: 'post' }
);
- 上面用到了一个方法align,这个方法的主要作用就是让用户传入的default节点和target对齐。target是props传入的节点;default被手动加的一层div包裹,通过ref来进行代理。
import { alignElement } from 'dom-align';
const nodeRef = ref<HTMLElement | null>(null);
const align = () => {
  if (!nodeRef.value) return;
  const { align: latestAlign, target: latestTarget } = props;
  let result: any;
  let targetElement: HTMLElement | null = null;
  if (typeof latestTarget === 'function') {
    targetElement = latestTarget();
  }
  if (targetElement && targetElement.nodeType === Node.ELEMENT_NODE) {
    result = alignElement(nodeRef.value, targetElement, latestAlign);
  }
};
🥇 定位是通过dom-align实现的,地址如下:https://yiminghe.me/dom-align/ 。具体用法请见后选源码章节。
PopupInner.tsx
主要是增加一个动画效果,用户传入的popup直接传递到Align组件。同时也要通过props 把定位的target传递下去。
return () => (
  <Transition>
    {props.visible ? (
    <Align align={props.align} target={props.target}>
      {slots.default?.()}
    </Align>
    ) : null}
  </Transition>
)
Popup.tsx
主要作用是增加一个蒙层。但是dropdown这个组件不使用蒙层,所以先注释掉。
return () => {
  if (!props.visible) return null;
  return (
    <div class="v-popup">
      {/* <Mask visible /> */}
      <PopupInner target={props.target} align={props.align} visible>
        {slots.default?.()}
      </PopupInner>
    </div>
  );
};
Trigger.tsx
- 需要接受用户传入的default和popup,default需要拿到引用,传递到Align组件进行定位;popup传递到Popup组件中。
- 用户的定位传递的是"top"、"topRight"等字符串,需要转换成dom-align需要的配置对象。
const align: any = computed(() => {
	const { placement } = props;
	return placements[placement];
});
const getComponent = () => {
	return (
		<Popup
			style={{ position: 'absolute' }}
			target={() => triggerRef.value!}
			align={align.value}
			visible={props.visible}
		>
			{slots.popup?.()}
		</Popup>
	);
};
const triggerRef = ref<HTMLElement>();
return () => {
	const portal = <Portal>{getComponent()}</Portal>;
	const trigger = (
		<div style={{ display: 'inline-block' }} ref={triggerRef}>
			{slots.default?.()}
		</div>
	);
	return (
		<>
			{portal}
			{trigger}
		</>
	);
};
源码分析
🎯 代码较多,后续会分开每个组件写一篇源码解析文档。










![[数据集][目标检测]乱堆物料检测数据集VOC+YOLO格式1143张1类别](https://i-blog.csdnimg.cn/direct/c592ccb2e92647559a56f6991390a931.png)







