從零開始徒手擼一個vue的toast彈窗元件

noahlam發表於2018-05-11

相信普通的vue元件大家都會寫,定義 -> 引入 -> 註冊 -> 使用,行雲流水,一氣呵成,但是如果我們今天是要自定義一個彈窗元件呢?

首先,我們來分析一下彈窗元件的特性(需求): 0. 輕量 --一個元件小於 1Kib (實際打包完不到0.8k)

  1. 一般都是多處使用 --需要解決每個頁面重複引用+註冊
  2. 一般都是跟js互動的 --無需 在<template>裡面寫 <toast :show="true" text="彈窗訊息"></toast>

今天,我們就抱著上面2個需求點,來實現一個基於vue的toast彈窗元件,下圖是最終完成的效果圖.

step_3

一. 先寫一個普通的vue元件

檔案位置 /src/toast/toast.vue

<template>
  <div class="wrap">我是彈窗</div>
</template>

<style scoped>
  .wrap{
    position: fixed;
    left: 50%;
    top:50%;
    background: rgba(0,0,0,.35);
    padding: 10px;
    border-radius: 5px;
    transform: translate(-50%,-50%);
    color:#fff;
  }
</style>
複製程式碼

二. 在我們需要使用的頁面引入元件,方便看效果和錯誤

<template>
  <div id="app">
    <toast></toast>
  </div>
</template>

<script>
  import toast from './toast/toast'
  export default {
    components: {toast},
  }
</script>
複製程式碼

step_1

三. 實現動態載入元件

可以看到,已經顯示出一個靜態的彈出層了,接下來我們就來看看如何實現動態彈出.

我們先在 /src/toast/ 目錄下面,新建一個index.js, 然後在index.js裡面,敲入以下程式碼(由於該程式碼耦合比較嚴重,所以就不拆開一行一行講解了,改成行內註釋)

檔案位置 /src/toast/index.js

import vue from 'vue'

// 這裡就是我們剛剛建立的那個靜態元件
import toastComponent from './toast.vue'

// 返回一個 擴充套件例項構造器
const ToastConstructor = vue.extend(toastComponent)

// 定義彈出元件的函式 接收2個引數, 要顯示的文字 和 顯示時間
function showToast(text, duration = 2000) {

  // 例項化一個 toast.vue
  const toastDom = new ToastConstructor({
    el: document.createElement('div'),
    data() {
      return {
        text:text,
        show:true
      }
    }
  })

  // 把 例項化的 toast.vue 新增到 body 裡
  document.body.appendChild(toastDom.$el)

  // 過了 duration 時間後隱藏
  setTimeout(() => {toastDom.show = false} ,duration)
}

// 註冊為全域性元件的函式
function registryToast() {
  // 將元件註冊到 vue 的 原型鏈裡去,
  // 這樣就可以在所有 vue 的例項裡面使用 this.$toast()
  vue.prototype.$toast = showToast
}

export default registryToast
複製程式碼

附一個傳送門 vue.extend 官方文件

四. 試用

到這裡,我們已經初步完成了一個可以全域性註冊和動態載入的toast元件,接下來我們來試用一下看看

  1. 在vue的入口檔案(腳手架生成的話是./src/main.js) 註冊一下元件

檔案位置 /src/main.js

import toastRegistry from './toast/index'

// 這裡也可以直接執行 toastRegistry()
Vue.use(toastRegistry)
複製程式碼
  1. 我們稍微修改一下使用方式,把第二步 的引用靜態元件的程式碼,改成如下
<template>
  <div id="app">
    <input type="button" value="顯示彈窗" @click="showToast">
  </div>
</template>

<script>
  export default {
    methods: {
      showToast () {
        this.$toast('我是彈出訊息')
      }
    }
  }
</script>
複製程式碼

step_2

可以看到,我們已經不需要在頁面裡面引入註冊元件,就可以直接使用this.$toast()了.

五. 優化

現在我們已經初步實現了一個彈窗.不過離成功還差一點點,缺少一個動畫,現在的彈出和隱藏都很生硬.

我們再對 toast/index.js 裡的showToast函式稍微做一下修改(有註釋的地方是有改動的)

檔案位置 /src/toast/index.js

function showToast(text, duration = 2000) {
  const toastDom = new ToastConstructor({
    el: document.createElement('div'),
    data() {
      return {
        text:text,
        showWrap:true,    // 是否顯示元件
        showContent:true  // 作用:在隱藏元件之前,顯示隱藏動畫
      }
    }
  })
  document.body.appendChild(toastDom.$el)

  // 提前 250ms 執行淡出動畫(因為我們再css裡面設定的隱藏動畫持續是250ms)
  setTimeout(() => {toastDom.showContent = false} ,duration - 1250)
  // 過了 duration 時間後隱藏整個元件
  setTimeout(() => {toastDom.showWrap = false} ,duration)
}

複製程式碼

然後,再修改一下toast.vue的樣式

檔案位置 /src/toast/toast.vue

<template>
  <div class="wrap" v-if="showWrap" :class="showContent ?'fadein':'fadeout'">{{text}}</div>
</template>

<style scoped>
  .wrap{
    position: fixed;
    left: 50%;
    top:50%;
    background: rgba(0,0,0,.35);
    padding: 10px;
    border-radius: 5px;
    transform: translate(-50%,-50%);
    color:#fff;
  }
  .fadein {
    animation: animate_in 0.25s;
  }
  .fadeout {
    animation: animate_out 0.25s;
    opacity: 0;
  }
  @keyframes animate_in {
    0% {
      opacity: 0;
    }
    100%{
      opacity: 1;
    }
  }
  @keyframes animate_out {
    0% {
      opacity: 1;
    }
    100%{
      opacity: 0;
    }
  }
</style>
複製程式碼

大功告成,一個toast元件初步完成

step_3

總結

  1. vue.extend 函式可以生成一個 元件構造器 可以用這個函式構造出一個 vue元件例項
  2. 可以用 document.body.appendChild() 動態的把元件加到 body裡面去
  3. vue.prototype.$toast = showToast 可以在全域性註冊元件
  4. 顯示動畫比較簡單,隱藏動畫必須要在隱藏之前預留足夠的動畫執行時間
  5. 本文原始碼地址 在這裡
  6. 以上都不重要,重要的是 給本文來個star

相關文章