五分鐘一百行程式碼,手寫一個vue專案全域性通用的toast提示元件

山里看瓜發表於2024-10-18

前言:

我們已經分享過如何快速實現自己需要的全域性彈框元件;

在開發 Vue 專案時,特別是H5頁面的專案,還有一個元件是我們非常常用的,它相對彈框來說沒有那麼大,並且不需要手動關閉在需要更簡潔的提示使用者一些資訊時非常常用,它就是 toast 提示元件;

接下來我們會帶著大家手寫一個全域性的 toast 提示元件,當你在專案任何地方需要使用時,都可直接呼叫。

檢視往期文章:

十五分鐘兩百行程式碼,手寫一個vue專案全域性通用的彈框

第一步:新建資料夾及主要檔案

Vue專案中,一般來說我們公用元件是放在 src/components,所以我們直接在src/components/toast下新建如下兩個檔案:

  1. index.vue:該檔案是 toast 元件的內容,跟我們寫普通 vue 元件一樣,包含 toast 的結構、樣式以及基礎邏輯;
  2. index.js:註冊 index.vue 元件為全域性元件。因為該元件我們不需要手動關閉,並且涉及到新增元素和自動刪除元素,所以該檔案會有一些元素層面上的操作和邏輯,相對上期的彈框元件的 index.js 的檔案來說會相對複雜些,我們下邊會逐行講解,當然也會提供完整程式碼,請往下看。

第二步:書寫元件內容

index.vue元件內容如下:

  1. 結構 + js 程式碼
<template>
  <transition name="appear">
    <section class="toast" v-if="show">
      <div v-html="msg" class="toast-con"></div>
    </section>
  </transition>
</template>

<script type="text/ecmascript-6">
export default {
  name: "toast",
  data() {
    return {
      show: false,
      msg: '',
      time: 1000
    }
  },
  methods: {
    async open() {
      if (this.show) {
        return;
      }
      this.show = true;
      let result = await this.close();
      return result;
    },
    close() {
      return new Promise((resolve) => {
        setTimeout(() => {
          this.show = false;
          resolve(true);
        }, this.time);
      });
    }
  }
}
</script>

程式碼說明:

  • 我們這裡用到了 Vuetransition 元件,用於包裹需要動畫效果的元素。name="appear" 指定了使用名為 "appear" 的過渡效果;
    • 詳情參考 Vue 官網:https://cn.vuejs.org/guide/built-ins/transition.html
  • <div v-html="msg" class="toast-con"></div> 這句程式碼是我們toast的主要內容,透過 v-html + msg 來動態屬性來插入,msg在index.js 中修改;
  • methods:定義元件的方法。
    • open():非同步方法,用於顯示 Toast。如果已顯示,則直接返回;否則設定 show 為 true 並呼叫 close() 方法等待其完成。
    • close():返回一個 Promise,該 Promise 在 time 毫秒後解決,同時將 show 設定為 false,從而隱藏 Toast。
  1. 樣式
<style lang="less" scoped>
.default-message {
  position: fixed;
  right: 0;
  top: 0;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 1000;
  background: rgba(0, 0, 0, 0.7);

  .default-message-title {
    color: #333;
    margin: 0;
    line-height: 1.5;
    font-size: 18px;
    min-height: 18px;
    padding-top: 20px;
    text-overflow: ellipsis;
    font-weight: bold;
    cursor: move;
    text-align: center;
  }

  .default-message-content {
    width: 85%;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate3d(-50%, -50%, 0);
    background-color: #fff;
    border-radius: 6px;
    transition: all 0.2s ease-in;
    color: #999;
    font-size: 18px;
  }

  .default-message-value {
    padding: 28px 18px;
    text-align: center;
    position: relative;
    color: #999;
    text-align: center;
    font-size: 14px;
    color: rgba(102, 102, 102, 1);
  }
  .default-message-btns {
    // border-top: 1px solid #ddd;
    display: flex;
    height: 60px;
    position: relative;
    &:after {
      position: absolute;
      content: "";
      display: inline-block;
      left: 0;
      right: 0;
      top: 0;
      height: 1px;
      transform: scaleY(0.5);
      background: #ddd;
    }
    .default-message-btn {
      flex: 1;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 16px;
      padding: 0 3px;
    }
    .default-message-submit {
      color: #26a2ff;
    }
    .default-message-cancle {
      color: #999;
      position: relative;
      &:after {
        position: absolute;
        content: "";
        display: inline-block;
        top: 0;
        right: 0;
        bottom: 0;
        width: 1px;
        transform: scaleX(0.5);
        background: #ddd;
      }
    }
  }
  @keyframes fadeIn {
    from {
      opacity: 0;
    }
    to {
      opacity: 1;
    }
  }
}
</style>

第三步:註冊成全域性元件

import { createApp } from 'vue';
import ToastComponents from './index.vue';

const LayerToastId = 'layer-Toast-wrapper';

let Toast = async function (msg, time) {
  time = time || 2000;

  let ToastEl = document.getElementById(LayerToastId);
  // 如果DOM中含有這個元素 不執行
  if (ToastEl) {
    return;
  }
  const div = document.createElement('div');
  div.setAttribute('id', LayerToastId)
  document.body.appendChild(div);
  let layerToastEl = createApp(ToastComponents).mount('#' + LayerToastId);
  // 修改元件中的data的值
  layerToastEl.msg = msg;
  layerToastEl.time = time;
  // 執行元件中的方法 等待關閉後返回promise
  let hasClosed = await layerToastEl.open();
  // 當Toast提示關閉後再刪除外層元素 時間最好與css動畫一致
  if (hasClosed) {
    setTimeout(() => {
      document.body.removeChild(div);
    }, 400);
  }
};
export default {
  install (app) {
    // 透過this.$toast訪問
    app.config.globalProperties.$toast = Toast;
  }
}

到這裡,我們的彈框元件就完成了。下邊我們對一些比較重要的程式碼做個解釋:

  • let Toast = async function 這裡表示Toast訊息,我們把它註冊成一個非同步函式,因為內部需要使用到定時器控制定時移除訊息容器;

  • let layerToastEl = createApp(ToastComponents).mount('#' + LayerToastId);

    • 這句程式碼的意思是:把我們引入的 index.vue 檔案建立成一個 Vue 的應用例項,並掛載到新建立的 div 上。
  • 以下是 index.js 檔案的逐行解釋:

// 引入 Vue 的 createApp 函式,用於建立 Vue 應用例項
import { createApp } from 'vue';
// 引入 Toast 元件
import ToastComponents from './index.vue';

// 定義一個常量,用於儲存 Toast 元件的容器元素的 ID
const LayerToastId = 'layer-Toast-wrapper';

// 定義一個非同步函式 Toast,用於顯示 Toast 訊息
let Toast = async function (msg, time) {
  // 如果未指定顯示時間,預設為 2000 毫秒
  time = time || 2000;

  // 獲取頁面上是否已存在 Toast 容器元素
  let ToastEl = document.getElementById(LayerToastId);
  // 如果已存在,不執行後續程式碼,直接返回
  if (ToastEl) {
    return;
  }
  // 建立一個 div 元素,用作 Toast 元件的容器
  const div = document.createElement('div');
  // 為該 div 設定 ID
  div.setAttribute('id', LayerToastId)
  // 將建立的 div 新增到 body 中
  document.body.appendChild(div);

  // 建立一個 Vue 應用例項,並掛載到新建立的 div 上
  let layerToastEl = createApp(ToastComponents).mount('#' + LayerToastId);
  // 設定 Toast 元件的訊息內容和顯示時間
  layerToastEl.msg = msg;
  layerToastEl.time = time;

  // 呼叫 Toast 元件的 open 方法,顯示 Toast 並等待其關閉
  let hasClosed = await layerToastEl.open();
  // 當 Toast 關閉後,延遲 400 毫秒後移除 Toast 容器元素
  // 這裡的 400 毫秒延時可以與 CSS 動畫的時間相匹配,確保動畫播放完成
  if (hasClosed) {
    setTimeout(() => {
      document.body.removeChild(div);
    }, 400);
  }
};

// 匯出一個物件,包含 install 方法,用於在 Vue 應用中安裝這個 Toast 功能
export default {
  install (app) {
    // 將 Toast 函式新增到 Vue 應用的全域性屬性中,使其可以透過 this.$toast 在任何元件中訪問
    app.config.globalProperties.$toast = Toast;
  }
}

專案中使用彈框

使用就非常簡單便利了,主要有以下幾種用法:

  1. Vue2 中使用:
// Vue2 中簡單使用
this.$toast("Toast提示在Vue2專案中的簡單使用"));

// Vue2中需要在提示後有進一步操作:可以任何你想的邏輯,包括髮介面、頁面處理等。
await this.$toast("Toast提示在Vue2專案中使用後有後續邏輯", 3000);
handleFunction(); // 這裡的函式代表提示後的邏輯程式碼
  1. Vue3 中使用:
// 在 Vue3 中使用時需要先引入app
import { app } from "@/main";

// Vue3 中簡單使用
app.config.globalProperties.$toast("Toast提示在Vue3專案中的簡單使用", 3000);

// Vue2中需要在提示後有進一步操作:可以任何你想的邏輯,包括髮介面、頁面處理等。
app.config.globalProperties.$toast("Toast提示在Vue3專案中使用後有後續邏輯");
handleFunction(); // 這裡的函式代表提示後的邏輯程式碼

說明:

  • 我們可以在使用時傳入合適的顯示停留時間,如果為傳入,則按預設的 2000 毫秒顯示;
  • 在 Vue3 中,你也可以把 $toast 重新儲存一下,後續不用每次都寫很長的 app.config....
import { app } from "@/main";

const toast = app.config.globalProperties.$toast

toast("簡便地使用toast提示");

toast 圖片示例

說明:在停留 2000 毫秒(或者我們設定的停留時間)之後會自動關閉。

寫在後面

這是一個比較基礎和通用的黑色半透明提示訊息,這邊示例的程式碼是比較全的,對細節要求不大的小夥伴可以直接抄作業;

背景顏色、字型、佈局等這些細節,因為每個業務場景不同,大家可以根據自己的需要適當調整;

透過修改結構和樣式程式碼,你可以讓訊息的樣式變得更加豐富或者更符合你的業務需求;

訊息元件我們一樣是使用固定單位的,如果小夥伴的專案需要使用響應式大小,直接對應替換大小單位(rem、vw)即可;

對你有幫助的話給作者點點關注吧,你的支援是我不斷更新的動力!Peace and love~~

相關文章