vue3.0封装dropdown下拉菜单

8 篇文章 0 订阅
4 篇文章 0 订阅

vue3.0出来已经有段时间的了,也与必要开始研究它了!
先看下我们要实现的效果
在这里插入图片描述
很常见的展开显示菜单项的内容,在vue3.0里面怎么开发,这里样式我们用的是bootstrap的默认样式

思路一:

	<DropDown :title="'退出'" :list="menuLists" />

思路二:

	<drop-down :title="'退出'">
	   <drop-dowm-item>新建文章</drop-down-item>
	   <drop-dowm-item>编辑文章</drop-down-item>
	   <drop-dowm-item>个人信息</drop-down-item>
	</drop-down>

两种思路都行,相比较而言,第二种思路比较清晰,使用的时候知道具体的层次,也是elementUI组件开发的模式.
现在就第二种组件开发思路进行分析

DropDown.ts

<template>
  <div class="dropdown" ref="dropDownRef">
    <a
      @click.prevent="toggleOpen"
      class="btn btn-secondary dropdown-toggle"
      href="#"
    >
      {{ title }}
    </a>
    <div class="dropdown-menu" :style="{ display: 'block' }" v-show="isOpen">
      <slot></slot>
    </div>
  </div>
</template>

js部分

<script lang="ts">
import { defineComponent, ref, onMounted, onUnmounted, watch } from "vue";
import useClickOutside from "../hooks/useClickOutside";
export default defineComponent({
  name: "DropDown",
  props: {
    title: {
      type: String,
      required: true,
    },
  },

  setup(context) {
    const isOpen = ref(false);
    //vue3.0获取dom对象的引用
    const dropDownRef = ref<null | HTMLElement>(null);
    const toggleOpen = () => {
      isOpen.value = !isOpen.value;
    };
    const handleClick = (e: MouseEvent) => {
      console.log(e.target, "e");
      if (dropDownRef.value) {
        console.log(dropDownRef.value);
        if (
        //contains判断节点是否包含节点
          !dropDownRef.value.contains(e.target as HTMLElement) &&
          isOpen.value
        ) {
          isOpen.value = false;
        }
      }
    };
    onMounted(() => {
    //注册全局的点击事件
      document.addEventListener("click", handleClick);
    });
    onUnmounted(() => {
    //解绑
      document.removeEventListener("click", handleClick);
    }); 
    return {
      isOpen,
      toggleOpen,
      dropDownRef,
    };
  },
});
</script>

DropDownItem.ts

<template>
  <li class="dropdowm-option" :class="{ 'is-disabled': disabled }">
    <slot></slot>
  </li>
</template>
<style scoped>

/* 此处是插槽需要穿透 */
.dropdowm-option.is-disabled >>> * {
  color: #6c757d;
  pointer-events: none;
  background-color: transparent;
}
</style>
<script lang="ts">
import { defineComponent } from "vue";

export default defineComponent({
  props: {
    disabled: {
      type: Boolean,
      default: false,
    },
  },
  setup() {
    return {};
  },
});
</script>

到这里这个组件就完成了。但是…我们可以看到点击整个document隐藏这个事件与整个组件的关联不大,因此我们可以抽取成一个hooks

useClickOutside.ts

import { ref, onMounted, onUnmounted,Ref } from 'vue'
const useClickOutside = (elementRef:Ref<null | HTMLElement>) => {
    const isClickOutside = ref(false)
    const handler = (e: MouseEvent) => {
        console.log(elementRef.value);
        if (elementRef.value) {
            if (elementRef.value.contains(e.target as HTMLElement)) {
                isClickOutside.value = false
            } else {
                isClickOutside.value = true
            }
        }
    }
    onMounted(() => {
      document.addEventListener("click", handler);
    });
    onUnmounted(() => {
      document.removeEventListener("click", handler);
    });
    return isClickOutside
}

export default useClickOutside

然后再改写我们的DropDown.ts组件

//删掉之前已有的事件逻辑
<script lang="ts">
... 
	const isClickOutside = useClickOutside(dropDownRef);
    /* console.log(isClickOutside.value, "isClickOutside"); */
    //引入监听方法,数据变化时我们改变isOpen的值为false
    watch(isClickOutside, (newValue) => {
      if (isOpen.value && isClickOutside.value) {
        isOpen.value = false;
      }
    });
 ...
 </script>

实现了同样的效果,整个组件的代码也精简了不少!打完收工~~

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
好的,以下是一个简单的Vue3 Select组件的封装示例: ```vue <template> <div class="select-wrapper" @click="toggleDropdown"> <div class="selected-item">{{ selectedOption.label }}</div> <div class="dropdown" v-show="isDropdownOpen"> <div class="option" v-for="option in options" :key="option.value" @click="selectOption(option)"> {{ option.label }} </div> </div> </div> </template> <script> import { ref } from 'vue'; export default { props: { options: { type: Array, required: true }, value: { type: Object, required: true } }, emits: ['update:value'], setup(props, { emit }) { const isDropdownOpen = ref(false); const selectedOption = ref(props.value); const toggleDropdown = () => { isDropdownOpen.value = !isDropdownOpen.value; }; const selectOption = (option) => { selectedOption.value = option; isDropdownOpen.value = false; emit('update:value', option); }; return { isDropdownOpen, selectedOption, toggleDropdown, selectOption }; } }; </script> <style scoped> .select-wrapper { position: relative; display: inline-block; width: 150px; } .selected-item { padding: 8px; border: 1px solid #ccc; cursor: pointer; } .dropdown { position: absolute; top: 100%; left: 0; z-index: 1; width: 100%; max-height: 200px; overflow-y: auto; border: 1px solid #ccc; border-top: none; background-color: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); } .option { padding: 8px; cursor: pointer; } .option:hover { background-color: #f2f2f2; } </style> ``` 使用方法: ```vue <template> <div> <Select :options="options" v-model="selectedOption" /> </div> </template> <script> import Select from './Select.vue'; export default { components: { Select }, data() { return { options: [ { label: 'Option 1', value: 1 }, { label: 'Option 2', value: 2 }, { label: 'Option 3', value: 3 }, { label: 'Option 4', value: 4 } ], selectedOption: { label: 'Option 1', value: 1 } }; } }; </script> ``` 注意,上述代码中使用了Vue3的Composition API,如`ref`、`setup`等,需要使用Vue3.0及以上版本。同时,由于该组件使用了`v-model`,需要在`emit`中添加`'update:value'`事件以支持双向绑定。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值