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

基于Vue3+ts封裝一個(gè)簡(jiǎn)單版的Message組件

 更新時(shí)間:2023年09月01日 10:03:58   作者:實(shí)在想不出什么名字  
近日項(xiàng)目中需要使用信息提示框的功能,ui組件庫使用的是字節(jié)的arco-design-vue,看了一下,現(xiàn)有的Message不滿足要是需求,直接使用message組件的話,改樣式太麻煩,所以本文就本就介紹了基于Vue3+ts封裝一個(gè)簡(jiǎn)單版的Message組件,需要的朋友可以參考下

Vue3+ts封裝一個(gè)Message組件

項(xiàng)目中需要使用信息提示框的功能,ui組件庫使用的是字節(jié)的arco-design-vue??戳艘幌?,現(xiàn)有的Message不滿足要是需求,直接使用message組件的話,改樣式太麻煩。Notification組件樣式倒是符合了,但是彈出的位置不符合,查看了一下相關(guān)api,這位置不支持"top"。既然如此,那就去查看它倆的源碼,找到我想要的,修修改改,自己也寫一個(gè),嘻嘻。

源碼分析

源碼(以Message組件為例)主要分為三個(gè)模塊:message-item.vue、message-list.vue、message.ts。下面簡(jiǎn)單介紹一下這三個(gè)模塊

  • message-item.vue: 主要是編寫Message信息彈框的樣式和彈框按時(shí)(定時(shí))自動(dòng)消失(關(guān)閉)的邏輯
  • message-list.vue:主要就是添加動(dòng)畫
  • message.ts:這個(gè)最主要的。這里面要實(shí)現(xiàn)創(chuàng)建message、刷新message、關(guān)閉message、銷毀message以及message分類等一系類邏輯,同時(shí)還要把這些邏輯封裝(暫時(shí)找不到更好的詞來表達(dá))成一個(gè)對(duì)象拋出去。沒錯(cuò),這是一個(gè)單例設(shè)計(jì)模式(全局永遠(yuǎn)只有一個(gè)Message對(duì)象)

代碼實(shí)現(xiàn)

在完全實(shí)現(xiàn)之前,我看 message-list.vue 組件中的邏輯那么簡(jiǎn)單,想也不想就直接把它跟 message-item.vue 組成了一個(gè)組件 message-ul.vue,等到要實(shí)現(xiàn)邏輯的時(shí)候,才發(fā)現(xiàn)自己這個(gè)寫不行,因?yàn)槊恳粋€(gè)Message都可以設(shè)置自己的持續(xù)時(shí)長(zhǎng),時(shí)間一到就自己關(guān)閉了,不會(huì)影響到其他的。但是在 message-ul.vue 組件中,處理的是一個(gè)列表,要保證每個(gè)列表項(xiàng)的定時(shí)關(guān)閉不影響其他的,我沒想到解決辦法(嗚嗚嗚,我太笨了 ),所以最后還是老老實(shí)實(shí)跟 arco-design-vue 一樣(其實(shí)就是直接copy它)把 message-ul 組件拆分。
ps:后來我想了想,那可是字節(jié)哎,里面的大佬這么寫肯定是有原因的,我老老實(shí)實(shí)照著抄就行了,居然妄想組合操作。不過實(shí)踐檢驗(yàn)真理,我也算是.....好吧,我就是瞎搞了。
不嗶嗶了,看代碼吧!

message-item.vue

<template>
  <li class="message-item message">
    <p class="title">
      <img v-if="type === 'success'" src="@/assets/icons/task-sucess.png" alt="" />
      <img v-if="type === 'error'" src="@/assets/icons/task-error.png" alt="" />
      <img v-if="type === 'warn'" src="@/assets/icons/task-cancel.png" alt="" />
      <span>{{ title }}</span>
    </p>
    <p v-if="prompt" class="name">
      {{ prompt.length >= 20 ? `${prompt.slice(0, 15)}...` : prompt }}
    </p>
    <span class="close" @click="handleClose">
      <IconClose />
    </span>
  </li>
</template>
<script lang="ts" setup>
  import { onMounted, onUnmounted } from 'vue';
  import { IconClose } from '@arco-design/web-vue/es/icon';
  const emits = defineEmits(['close']);
  let timer = 0; // 每個(gè)實(shí)例?(item)有自己的定時(shí)器,這樣就不會(huì)影響其他的item了
  const props = defineProps({
    title: String, // 標(biāo)題
    prompt: {
      type: String,
      default: undefined,
    },
    id: {
      type: [String, Number],
      required: true,
    },
    type: {
      type: String,
      required: true,
    },
    duration: {
      type: Number,
      default: 3000,
    },
  });
  const clearTimer = () => {
    if (timer) {
      window.clearTimeout(timer);
      timer = 0;
    }
  };
  function handleClose() {
    emits('close', props.id);
  }
  const startTimer = () => {
    if (props.duration > 0) {
      timer = window.setTimeout(handleClose, props.duration);
    }
  };
  onMounted(() => {
    startTimer();
  });
  onUnmounted(() => {
    clearTimer();
  });
</script>

message-list.vue

<template>
  <TransitionGroup tag="ul" class="message-box" name="list" :theme="appStore.theme">
    <Message v-for="item in messages" :key="item.id" v-bind="item" @close="handleClose" />
  </TransitionGroup>
</template>
<script lang="ts" setup>
  import type { PropType } from 'vue';
  import { useAppStore } from '@/store';
  import { MessageItem } from './types';
  import Message from './message-item.vue';
  const appStore = useAppStore();
  const emits = defineEmits(['close']);
  defineProps({
    messages: {
      type: Array as PropType<MessageItem[]>,
      default: () => [],
    },
  });
  function handleClose(id: string | number) {
    emits('close', id);
  }
</script>
<style lang="less">
  // 樣式這里就放個(gè)動(dòng)畫的,其他的就不放了
  .list-enter-active,
  .list-leave-active {
    transition: all 0.5s ease;
  }
  .list-enter-from {
    opacity: 0;
    transform: translateY(30px);
  }
  .list-leave-to {
    opacity: 0;
    transform: translateY(-30px);
  }
</style>

message.ts

import type { AppContext, Ref } from 'vue';
import { createVNode, render, ref, reactive } from 'vue';
import { MessageItem, MessageConfig, MessageMethod } from './types';
import messageVue from './message-list.vue';
type _MessageConfig = MessageConfig & {
  type: 'success' | 'error' | 'warn';
};
class MessageManger {
  // 定義一個(gè)集合,用于存儲(chǔ)所有message的id,這里使用Set是為了自動(dòng)去重
  private readonly messageIds: Set<number | string>;
  // 定義一個(gè)響應(yīng)式集合用于存儲(chǔ)所有message對(duì)象,用響應(yīng)式的好處,數(shù)據(jù)變?cè)噲D變,試圖變數(shù)據(jù)變
  private readonly messages: Ref<MessageItem[]>;
  private container: HTMLElement | null;
  private messageCount = 0;
  constructor(config: _MessageConfig, appContext?: AppContext) {
    this.messageIds = new Set();
    this.messages = ref([]);
    this.container = document.createElement('div');
    this.container.setAttribute('class', 'my-message');
    // 創(chuàng)建一個(gè)虛擬DOM:同時(shí)messages傳遞給 messageVue;處理(實(shí)現(xiàn)) messageVue 向外拋出的close、afterClose等方法,
    const vm = createVNode(messageVue, {
      messages: this.messages.value,
      onClose: this.remove,
      onAfterClose: this.destroy,
    });
    // eslint-disable-next-line no-use-before-define
    if (appContext ?? Message.myContext) {
      // eslint-disable-next-line no-use-before-define
      vm.appContext = appContext ?? Message.myContext;
    }
    // 將虛擬DOM渲染到指定容器中,并將容器添加到body標(biāo)簽里
    render(vm, this.container);
    document.body.appendChild(this.container);
  }
  add = (config: _MessageConfig) => {
    // 添加message時(shí),如果沒有傳遞id就使用 messageCount 創(chuàng)建一個(gè)
    this.messageCount += 1;
    const id = config.id ?? `message_${this.messageCount}`;
    if (this.messageIds.has(id)) {
      return this.update(id, config);
    }
    const message: MessageItem = reactive({ id, ...config });
    this.messages.value.push(message);
    this.messageIds.add(id);
    return {
      close: () => this.remove(id),
    };
  };
  update = (id: number | string, config: _MessageConfig) => {
    for (let i = 0; i < this.messages.value.length; i += 1) {
      if (this.messages.value[i].id === id) {
        const resetOnUpdate = config.duration !== undefined;
        Object.assign(this.messages.value[i], { ...config, id, resetOnUpdate });
        break;
      }
    }
    return {
      close: () => this.remove(id),
    };
  };
  remove = (id: number | string) => {
    for (let i = 0; i < this.messages.value.length; i += 1) {
      const item = this.messages.value[i];
      if (item.id === id) {
        if (typeof item.onClose === 'function') {
          item.onClose(id);
        }
        this.messages.value.splice(i, 1);
        this.messageIds.delete(id);
        break;
      }
    }
    this.destroy();
  };
  clear = () => {
    this.messages.value.splice(0);
  };
  destroy = () => {
    // 如果所有message都關(guān)閉了,那就銷毀整個(gè)實(shí)例對(duì)象,并從body中移除容器
    if (this.messages.value.length === 0 && this.container) {
      render(null, this.container);
      document.body.removeChild(this.container);
      this.container = null;
      // eslint-disable-next-line no-use-before-define
      messageInstance = null;
    }
  };
}
let messageInstance: MessageManger | null = null;
const types = ['success', 'error', 'warn'] as const;
const message = types.reduce((pre, value) => {
  pre[value] = (config: MessageConfig, appContext?: AppContext) => {
    const newConfig: _MessageConfig = { ...config, type: value };
    if (!messageInstance) {
      messageInstance = new MessageManger(newConfig, appContext);
    }
    return messageInstance.add(newConfig);
  };
  return pre;
}, {} as MessageMethod);
message.clear = () => {
  if (messageInstance) {
    messageInstance?.clear();
  }
};
const Message = {
  ...message,
  myContext: null as AppContext | null,
};
// 上面這段代碼等同于
/* const message = {
  success: (config: MessageConfig, appContext?: AppContext) => {
    const newConfig: _MessageConfig = { ...config, type: 'success' };
    if (!messageInstance) {
      messageInstance = new MessageManger(newConfig, appContext);
    }
    return messageInstance.add(newConfig);
  },
  error: (config: MessageConfig, appContext?: AppContext) => {
    const newConfig: _MessageConfig = { ...config, type: 'error' };
    if (!messageInstance) {
      messageInstance = new MessageManger(newConfig, appContext);
    }
    return messageInstance.add(newConfig);
  },
  warn: (config: MessageConfig, appContext?: AppContext) => {
    const newConfig: _MessageConfig = { ...config, type: 'warn' };
    if (!messageInstance) {
      messageInstance = new MessageManger(newConfig, appContext);
    }
    return messageInstance.add(newConfig);
  },
  clear: () => {
    if (messageInstance) {
      messageInstance?.clear();
    }
  },
}; */
// 拋出去的是一個(gè)對(duì)象,每次調(diào)用 message.xxx() 都是調(diào)用的同一個(gè)對(duì)象,所以這是一個(gè)單例模式
export default message;

types.ts

import type { AppContext } from 'vue';
export interface MessageItem {
  id: number | string;
  title: string;
  prompt?: string;
  type: 'success' | 'error' | 'warn';
  duration?: number;
  closable?: boolean;
  onClose?: (id: number | string) => void;
}
export interface MessageConfig {
  prompt?: string;
  title: string;
  id?: string;
  closable?: boolean;
  duration?: number;
  onClose?: (id: number | string) => void;
  type?: 'success' | 'error' | 'warn';
}
export interface MessageReturn {
  close: () => void;
}
export interface MessageMethod {
  success: (config: MessageConfig, appContext?: AppContext) => MessageReturn;
  error: (config: MessageConfig, appContext?: AppContext) => MessageReturn;
  warn: (config: MessageConfig, appContext?: AppContext) => MessageReturn;
  remove: (id: string) => void;
  clear: () => void;
}

到此,一個(gè)使用Vue3+TS實(shí)現(xiàn)的簡(jiǎn)單版的Message組件就完成了。如果有更好的實(shí)現(xiàn)方法,歡迎在評(píng)論區(qū)討論。同時(shí)也歡迎各位大佬指出我的不足

以上就是基于Vue3+ts封裝一個(gè)簡(jiǎn)單版的Message組件的詳細(xì)內(nèi)容,更多關(guān)于Vue3+ts封裝Message組件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Vue 監(jiān)聽列表item渲染事件方法

    Vue 監(jiān)聽列表item渲染事件方法

    今天小編就為大家分享一篇Vue 監(jiān)聽列表item渲染事件方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-09-09
  • el-tooltip根據(jù)條件控制顯示的示例代碼

    el-tooltip根據(jù)條件控制顯示的示例代碼

    這篇文章主要介紹了el-tooltip根據(jù)條件控制顯示的示例代碼,包括列表型和樹狀圖型,本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2023-12-12
  • Vue shopCart 組件開發(fā)詳解

    Vue shopCart 組件開發(fā)詳解

    這篇文章主要介紹了Vue shopCart 組件開發(fā)詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-01-01
  • Vue項(xiàng)目中scrollIntoView導(dǎo)致的布局異常問題解決辦法

    Vue項(xiàng)目中scrollIntoView導(dǎo)致的布局異常問題解決辦法

    scrollIntoView的實(shí)現(xiàn)原理是通過調(diào)整滾動(dòng)容器的滾動(dòng)位置,使目標(biāo)元素在視口中可見,這篇文章主要介紹了Vue項(xiàng)目中scrollIntoView導(dǎo)致的布局異常問題解決辦法的相關(guān)資料,需要的朋友可以參考下
    2026-04-04
  • vue實(shí)現(xiàn)日歷組件

    vue實(shí)現(xiàn)日歷組件

    這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)日歷組件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • vue使用refs獲取嵌套組件中的值過程

    vue使用refs獲取嵌套組件中的值過程

    這篇文章主要介紹了vue使用refs獲取嵌套組件中的值過程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • vue?頁面刷新、重置、更新頁面所有數(shù)據(jù)的示例代碼

    vue?頁面刷新、重置、更新頁面所有數(shù)據(jù)的示例代碼

    Vue.js提供了多種方式來實(shí)現(xiàn)頁面刷新、重置和更新頁面所有數(shù)據(jù)的功能,下面通過示例代碼演示vue?頁面刷新、重置、更新頁面所有數(shù)據(jù),感興趣的朋友跟隨小編一起看看吧
    2024-01-01
  • 解決nuxt頁面中mounted、created、watch執(zhí)行兩遍的問題

    解決nuxt頁面中mounted、created、watch執(zhí)行兩遍的問題

    這篇文章主要介紹了解決nuxt頁面中mounted、created、watch執(zhí)行兩遍的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-11-11
  • vue前端常用的工具類總結(jié)

    vue前端常用的工具類總結(jié)

    這篇文章主要為大家詳細(xì)介紹了6個(gè)vue前端常用的工具類,可直接復(fù)用,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-01-01
  • Vue3進(jìn)行樣式Scoped和Global的設(shè)置方法

    Vue3進(jìn)行樣式Scoped和Global的設(shè)置方法

    在 Vue 3 中,組件化開發(fā)成為了我們提升前端開發(fā)效率的利器,其中,樣式的管理也是一個(gè)至關(guān)重要的部分,在 Vue 中,我們會(huì)經(jīng)常使用兩種樣式作用域:Scoped 樣式和 Global 樣式,本文將通過示例代碼來介紹Vue3如何進(jìn)行樣式Scoped和Global的設(shè)置,需要的朋友可以參考下
    2025-01-01

最新評(píng)論

渝北区| 永春县| 龙泉市| 平安县| 伊春市| 曲周县| 丹阳市| 大宁县| 墨竹工卡县| 师宗县| 大方县| 宜章县| 马山县| 宿松县| 长治县| 本溪市| 拉萨市| 夹江县| 南漳县| 新闻| 隆德县| 蓬溪县| 正镶白旗| 东丽区| 仁化县| 桦南县| 娄底市| 宜兴市| 北票市| 富平县| 卢氏县| 延庆县| 定远县| 巧家县| 洛川县| 永吉县| 江油市| 育儿| 嘉善县| 乐山市| 温泉县|