最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

手寫實現(xiàn)vue2下拉菜單dropdown組件實例

 更新時間:2022年08月24日 15:07:09   作者:陌年微涼_  
這篇文章主要為大家介紹了手寫vue2下拉菜單dropdown組件實例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

概述

一般后臺項目結(jié)合分頁組件用到這個dropdown組件,用來做顯示分頁條數(shù),其他用到這個組件的地方倒不是很多,其實現(xiàn)思路和select組件有那么些相似,現(xiàn)記錄下這個組件的實現(xiàn)。

最終效果(動圖沒顯示出來,請稍定會兒,可以先看后面)

實現(xiàn)原理

這個組件和select組件記起相似,可以參考我之前的文章【手寫vue2select下拉組件】,要做這個組件,需要注意以下幾點:

組件分為兩部分:

  • 供我們點擊的文字,按鈕,超鏈接等等(當成插槽供用戶提供)
  • 下拉菜單項(支持邊框,禁用等)

使用該組件應當提供的事件應該是點擊item項,然后將對應的item的對應value暴露出來,供用戶使用。

組件菜單項的顯示隱藏需要過渡動畫。

默認菜單項方向向下,當下方可視區(qū)的高度不足以容納下拉菜單的高度的時候,自動讓菜單方向向上。

具體實現(xiàn)

目錄結(jié)構(gòu)

emitter.js

這個在之前的組件實現(xiàn)過程中介紹過這個文件,主要是為了解決跨多層級父子組件之前數(shù)據(jù)通信的,本質(zhì)上實現(xiàn)原理為發(fā)布訂閱模式。

/**
 * @Description 由于涉及到跨組件之間通信,因此我們只有自己實現(xiàn)發(fā)布訂閱的模式,來實現(xiàn)組件之間通信,靈感主要來源于element-ui組件庫源碼中跨層級父子組件通信方案,本質(zhì)上也是發(fā)布訂閱和$emit和$on
 * @param { String } componentName 組件名
 * @param { String } eventName 事件名
 * @param { argument } params 參數(shù)
 **/
// 廣播通知事件
function _broadcast(componentName, eventName, params) {
  // 遍歷當前組件的子組件
  this.$children.forEach(function (child) {
    // 取出componentName,組件options上面可以自己配置
    var name = child.$options.componentName;
    // 如果找到了需要通知的組件名,觸發(fā)組件上面的$eimit方法,觸發(fā)自定義事件
    if (name === componentName) {
      child.$emit.apply(child, [eventName].concat(params));
    } else {
      // 沒找到,遞歸往下找
      _broadcast.apply(child, [componentName, eventName].concat([params]));
    }
  });
}
const emiiter = {
  methods: {
    // 派發(fā)事件(通知父組件)
    dispatch(componentName, eventName, params) {
      var parent = this.$parent || this.$root;
      var name = parent.$options.componentName;
      // 循環(huán)往上層父組件,知道知道組件名和需要觸發(fā)的組件名相同即可,然后觸發(fā)對應組件的事件
      while (parent && (!name || name !== componentName)) {
        parent = parent.$parent;
        if (parent) {
          name = parent.$options.componentName;
        }
      }
      if (parent) {
        parent.$emit.apply(parent, [eventName].concat(params));
      }
    },
    // 廣播事件(通知子組件)
    broadcast(componentName, eventName, params) {
      _broadcast.call(this, componentName, eventName, params);
    },
  },
};
export default emiiter;

MyDropdown.vue

主要暴露給用戶使用的組件

<template>
  <div
    class="my-dropdown"
    @click.stop="trigger == 'click' ? (showMenu = !showMenu) : ''"
    @mouseenter="trigger == 'hover' ? (showMenu = true) : ''"
    @mouseleave="trigger == 'hover' ? (showMenu = false) : ''"
    ref="myDropDdown"
  >
    <div class="tip-text" ref="tipText">
      <slot></slot>
    </div>
    <slot name="list"></slot>
  </div>
</template>
<script>
import emitter from "./emitter";
export default {
  name: "MyDropdown",
  componentName: "MyDropdown",
  mixins: [emitter],
  props: {
    // 觸發(fā)顯示方式
    trigger: {
      type: String,
      default: "click",
    },
    // 下來菜單的出現(xiàn)位置(上方,下方)
    placement: {
      type: String,
      default: "bottom",
      validator: function (value) {
        // 這個值必須匹配下列字符串中的一個
        return ["bottom", "top"].includes(value);
      },
    },
  },
  data() {
    return {
    //控制菜單是否顯示
      showMenu: false,
    };
  },
  mounted() {
      //初始化自定義事件
    this.initEvent();
  },
  methods: {
    // 初始化
    initEvent() {
    //訂閱當item點擊的時候,發(fā)布on-click事件,告知外部
      this.$on("item-click", (params) => {
        this.$emit("on-click", params);
        this.showMenu = false;
      });
      //空白點擊要隱藏菜單,需要執(zhí)行的函數(shù)需要綁定this指向
      this.handleEmptyDomElementClickBindThis =
        this.handleEmptyDomElementClick.bind(this);
      window.addEventListener("click", this.handleEmptyDomElementClickBindThis);
    },
    // 處理空白區(qū)域點擊,隱藏菜單列表
    handleEmptyDomElementClick(e) {
      if (!Array.from(this.$refs.myDropDdown.childNodes).includes(e.target)) {
        this.showMenu = false;
      }
    },
  },
  beforeDestroy() {
    // 移除window上面的事件
    window.removeEventListener(this.handleEmptyDomElementClickBindThis);
  },
  watch: {
  //變化的時候,通知子組件隱藏菜單列表
    showMenu() {
      this.broadcast("MyDropdownMenu", "set-menu-status", this.showMenu);
    },
  },
};
</script>
<style lang="less">
.my-dropdown {
  position: relative;
}
</style>

MyDropdownMenu.vue

主要暴露給用戶使用的組件,菜單列表組件

<template>
<!-- 涉及到高度,位移,過渡使用js鉤子函數(shù)的方式比較好處理些 -->
  <transition
    @before-enter="beforeEnter"
    @enter="enter"
    @leave="leave"
    v-bind:css="false"
  >
    <div class="my-dropdown-menu" v-if="showMeune" ref="myDroupdownMenu">
      <slot></slot>
    </div>
  </transition>
</template>
<script>
import emitter from "./emitter";
export default {
  name: "MyDropdownMenu",
  componentName: "MyDropdownMenu",
  mixins: [emitter],
  data() {
    return {
      showMeune: false,
      timer: null,
    };
  },
  mounted() {
    this.$on("set-menu-status", (status) => {
      this.showMeune = status;
    });
  },
  methods: {
  //進入前,初始化需要過渡的屬性
    beforeEnter: function (el) {
      // 初始化
      el.style.opacity = 0;
      el.style.transform = "scaleY(0)";
      el.style.transformOrigin = "top center";
    },
  //dom進入
    enter: function (el, done) {
    //獲取文檔可視區(qū)高度
      const htmlClientHeight = document.documentElement.clientHeight;
      //菜單列表相對于父元素的top偏移量
      const offsetTop = el.offsetTop;
      const scrollHeight = el.scrollHeight;
      //獲取當前元素和可視區(qū)的一些長度(top,left,bottom等)
      const { bottom } = el.getBoundingClientRect();
      // 說明底部高度不夠顯示菜單了,這時候我們需要調(diào)整菜單朝上面顯示
      if (htmlClientHeight - bottom < scrollHeight) {
        el.style.transformOrigin = "bottom center";
        el.style.top = -(scrollHeight + 20) + "px";
      } else {
      //查看是否placement屬性,是的話我們主動處理
        if (this.$parent.placement == "top") {
          el.style.transformOrigin = "bottom center";
          el.style.top = -(scrollHeight + 20) + "px";
        } else {
          el.style.top = offsetTop + "px";
        }
      }
      el.style.transform = "scaleY(1)";
      el.style.opacity = 1;
    //根據(jù)官網(wǎng)事例,必須在enter和leave里面調(diào)用done函數(shù),不然過渡動畫不生效(切記)
      done();
    },
    //dom元素離開 
    leave: function (el, done) {
      el.style.transform = "scaleY(0)";
      el.style.opacity = 0;
      clearTimeout(this.timer);
      this.timer = setTimeout(() => {
      //根據(jù)官網(wǎng)事例,必須在enter和leave里面調(diào)用done函數(shù),不然過渡動畫不生效(切記)
        done();
      }, 250);
    },
  },
};
</script>
<style lang="less">
.my-dropdown-menu {
  min-width: 100px;
  max-height: 200px;
  overflow: auto;
  margin: 5px 0;
  padding: 5px 0;
  background-color: #fff;
  box-sizing: border-box;
  border-radius: 4px;
  box-shadow: 0 1px 6px rgb(0 0 0 / 20%);
  z-index: 900;
  transform-origin: top center;
  position: absolute;
  transition: transform 0.25s ease, opacity 0.25s ease;
}
</style>

MyDropdownItem.vue

主要暴露給用戶使用的組件,菜單列表項組件,組件內(nèi)容很簡單,主要就是展示item數(shù)據(jù)和綁定點擊事件。

<template>
  <div
    :class="[
      'my-dropdownItem',
      divided ? 'my-dropdownItem-divided' : '',
      disabled ? 'my-dropdownItem-disabled' : '',
    ]"
    @click.stop="handleItemClick"
  >
    <slot></slot>
  </div>
</template>
<script>
import emitter from "./emitter";
export default {
  name: "MyDropdownItem",
  componentName: "MyDropdownItem",
  mixins: [emitter],
  props: {
    divided: {
      type: Boolean,
      default: false,
    },
    disabled: {
      type: Boolean,
      default: false,
    },
    name: {
      type: String,
      default: "",
    },
  },
  data() {
    return {};
  },
  methods: {
    handleItemClick() {
      if (this.disabled) return;
      // item項點擊通知dropdown組件派發(fā)到外部的自定義事件
      this.dispatch("MyDropdown", "item-click", this.name);
    },
  },
};
</script>
<style lang="less">
.my-dropdownItem {
  margin: 0;
  line-height: normal;
  padding: 7px 16px;
  clear: both;
  color: #515a6e;
  font-size: 14px !important;
  white-space: nowrap;
  list-style: none;
  cursor: pointer;
  transition: background 0.2s ease-in-out;
  &:hover {
    background: #f3f3f3;
  }
}
.my-dropdownItem-divided {
  border-bottom: 1px solid #eee;
}
.my-dropdownItem-disabled {
  color: #cacdd2;
  cursor: not-allowed;
  &:hover {
    background: #fff;
  }
}
</style>

總結(jié)

類似組件庫中的這種經(jīng)常出現(xiàn)跨多層級組件通信,需要特殊處理,一般emitter.js文件里面的封裝我們在開發(fā)中一般是用不到的,我們寫組件經(jīng)常也就父子組件之間,很少會跨祖孫級別,但是在組件庫中,這種關(guān)系就很多,因此需要單獨利用發(fā)布訂閱來處理,這種模式用到實際項目里面也是很管用的。

以上就是手寫實現(xiàn)vue2下拉菜單dropdown組件實例的詳細內(nèi)容,更多關(guān)于vue 下拉菜單dropdown的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Vue.js中computed的基本使用方法

    Vue.js中computed的基本使用方法

    Vue.js中,computed屬性根據(jù)依賴進行緩存,只有依賴改變時才重新計算,這樣有效提高性能,computed屬性是響應式的,可以自動更新,并且默認是只讀的,它與methods的主要區(qū)別在于計算屬性具有緩存性,而方法每次調(diào)用都會執(zhí)行,使用computed可以使模板更加簡潔,提高應用性能
    2024-09-09
  • VUE使用echarts?5.0以上版本渲染器未導入錯誤問題

    VUE使用echarts?5.0以上版本渲染器未導入錯誤問題

    這篇文章主要介紹了VUE使用echarts?5.0以上版本渲染器未導入錯誤問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • 關(guān)于vue-lunar-full-calendar的使用說明

    關(guān)于vue-lunar-full-calendar的使用說明

    這篇文章主要介紹了關(guān)于vue-lunar-full-calendar的使用說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • Vue.js處理API請求失敗的最佳實踐和策略

    Vue.js處理API請求失敗的最佳實踐和策略

    在現(xiàn)代Web開發(fā)中,與后端API的交互是不可避免的,然而,網(wǎng)絡請求是不穩(wěn)定的,可能會因為各種原因失敗,因此,優(yōu)雅地處理API請求失敗的情況是提升用戶體驗和應用穩(wěn)定性的關(guān)鍵,本文將詳細介紹在Vue.js中處理API請求失敗的最佳實踐和策略,需要的朋友可以參考下
    2024-12-12
  • 基于Vue3+TypeScript的全局對象的注入和使用詳解

    基于Vue3+TypeScript的全局對象的注入和使用詳解

    這篇文章主要介紹了基于Vue3+TypeScript的全局對象的注入和使用,本篇隨筆主要介紹一下基于Vue3+TypeScript的全局對象的注入和使用,需要的朋友可以參考下
    2022-09-09
  • 使用element-ui +Vue 解決 table 里包含表單驗證的問題

    使用element-ui +Vue 解決 table 里包含表單驗證的問題

    這篇文章主要介紹了使用element-ui +Vue 解決 table 里包含表單驗證的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-07-07
  • vue?watch監(jiān)聽觸發(fā)優(yōu)化搜索框的性能防抖節(jié)流的比較

    vue?watch監(jiān)聽觸發(fā)優(yōu)化搜索框的性能防抖節(jié)流的比較

    這篇文章主要為大家介紹了vue?watch監(jiān)聽觸發(fā)優(yōu)化搜索框的性能防抖節(jié)流的比較,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-10-10
  • 使用vue-cli4.0快速搭建一個項目的方法步驟

    使用vue-cli4.0快速搭建一個項目的方法步驟

    這篇文章主要介紹了使用vue-cli4.0快速搭建一個項目的方法步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-12-12
  • vue通過定時器實現(xiàn)垂直滾動公告

    vue通過定時器實現(xiàn)垂直滾動公告

    這篇文章主要為大家詳細介紹了vue通過定時器實現(xiàn)垂直滾動公告,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • 解決axios發(fā)送post請求上傳文件到后端的問題(multipart/form-data)

    解決axios發(fā)送post請求上傳文件到后端的問題(multipart/form-data)

    這篇文章主要介紹了如何使用axios發(fā)送post請求上傳文件到后端(multipart/form-data),本文給大家?guī)砹藛栴}原因及解決方案,結(jié)合實例代碼給大家介紹的非常詳細,需要的朋友可以參考下
    2022-05-05

最新評論

财经| 璧山县| 略阳县| 偏关县| 崇礼县| 那曲县| 宁津县| 家居| 苍山县| 平遥县| 商都县| 同江市| 枞阳县| 惠水县| 镇远县| 轮台县| 石屏县| 宾阳县| 永年县| 临武县| 屯留县| 曲沃县| 邢台县| 天津市| 大石桥市| 达州市| 蒲城县| 灵宝市| 锦州市| 长顺县| 兴国县| 金昌市| 满城县| 体育| 舒城县| 麦盖提县| 巧家县| 茂名市| 溆浦县| 鄂尔多斯市| 平舆县|