• 作者:老汪软件技巧
  • 发表时间:2024-12-17 21:03
  • 浏览量:

在前端开发中,一个功能齐全、灵活易用的 Dropdown 组件是必不可少的。本文将带你从零搭建一个基于 Vue3 的 DropdownP 组件,展示从需求分析代码实现的完整流程。

需求分析确定方案

import type { VNode } from "vue";
import type { TooltipProps } from "../Tooltip/types";
// 需要在Tooltip组件的属性上进行继承和拓展
export interface DropdownProps extends TooltipProps {
  // 传入的菜单项,用一个数组保存
  menuOptions: MenuOption[];
  // 点击某一项之后关闭菜单展示
  afterClickItem?: boolean;
}
// 每一项列表具有的属性
export interface MenuOption {
   // 字符串或自定义节点
  label: string | VNode;
  key: string | number;
  disabled?: boolean;
  // 分割线 
  divided?: boolean;
}

DropdownP 支持以下事件:

export interface DropdownEmits {
   // 打开列表派发事件
  (e: "visible-change", value: boolean): void;
  // 选中选项派发事件
  (e: "select", value: MenuOption): void;
}

通过暴露实例方法,用户可以在外部手动控制菜单显隐状态:

export interface DropdownInstance {
  show: () => void;
  hide: () => void;
}

<template>
    <div class="yv-dropdown">
        <Tooltip>
        
            <slot>slot>
            
            <template #content>
                <ul>
                    <template >
                        
                        <hr >
                        
                        <li>
                            <RenderVnode  />
                        li>
                    template>
                ul>
            template>
        Tooltip>
    div>
template>

设计思路

<template>
  <div class="jd-dropdown">
    <Tooltip
      :trigger="trigger"
      :placement="placement"
      :popper-options="popperOptions"
      :open-delay="openDelay"
      :close-delay="closeDelay"
      @visible-change="visibleChange"
      :manual="manual"
      ref="tooltipRef"
    >
      <slot />
      <template #content>
        <ul class="jd-dropdown__menu">
          <template v-for="item in menuOptions" :key="item.key">
            
            <li
              v-if="item.divided"
              role="separator"
              class="divided-placeholder"
            />
            
            <li
              class="jd-dropdown__item"
              @click="itemClick(item)"
              :class="{
                'is-disabled': item.disabled,
                'is-divided': item.divided,
              }"
              :id="`dropdown-item-${item.key}`"
            >
              
              <RenderVnode :vNode="item.label" />
            li>
          template>
        ul>
      template>
    Tooltip>
  div>
template>
<script setup lang="ts">

写组件库_开发vue组件库_

import { ref } from "vue"; import type { Ref } from "vue"; import type { DropdownProps, DropdownInstance, DropdownEmits, MenuOption, } from "./types"; import type { TooltipInstance } from "../Tooltip/types"; import Tooltip from "../Tooltip/Tooltip.vue"; import RenderVnode from "../Common/RenderVnode"; defineOptions({ name: "JdDropdown", }); const props = withDefaults(defineProps<DropdownProps>(), { afterClickItem: true, }); const emits = defineEmits<DropdownEmits>(); const tooltipRef = ref() as Ref<TooltipInstance>; const visibleChange = (e: boolean) => { emits("visible-change", e); }; const itemClick = (e: MenuOption) => { if (e.disabled) return; emits("select", e); if (props.afterClickItem) { tooltipRef.value?.hide(); } }; defineExpose<DropdownInstance>({ // 通过闭包的形式,直接赋值会拿收不到对应的节点,因为是在setup函数中,此时实例还未被挂载 show: () => tooltipRef.value?.show(), hide: () => tooltipRef.value?.hide(), });
script>

//types.ts
import type { VNode } from "vue";
import type { TooltipProps } from "../Tooltip/types";
export interface DropdownProps extends TooltipProps {
  // 传入的菜单项,用一个数组保存
  menuOptions: MenuOption[];
  // 点击某一项之后关闭菜单展示
  afterClickItem?: boolean;
}
// 每一项列表具有的属性
export interface MenuOption {
  // 字符串或自定义节点
  label: string | VNode;
  key: string | number;// 每个选项的唯一标识
  disabled?: boolean;
  // 分割线
  divided?: boolean;
}
export interface DropdownEmits {
  // 打开列表派发事件
  (e: "visible-change", value: boolean): void;
  // 选中选项派发事件
  (e: "select", value: MenuOption): void;
}
export interface DropdownInstance {
  show: () => void;
  hide: () => void;
}

总结

通过上述步骤,我们成功地从零搭建了一个高度可定制的 Vue 3 Dropdown 组件。该组件不仅继承了 Tooltip 组件的弹出层管理功能,还增加了菜单列表、自定义选项、分割线等特性,能够满足大多数实际项目中的需求。此外,我们还通过 TypeScript 提供了严格的类型检查,确保组件的健壮性和可维护性。