vue3.0实现下拉菜单的封装

Xylona ·
更新时间:2024-09-20
· 41 次阅读

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="#" rel="external nofollow" > {{ 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>

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



VUE 菜单 封装 下拉菜单

需要 登录 后方可回复, 如果你还没有账号请 注册新账号