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

相信普通的vue組件你們都會寫,定義 -> 引入 -> 註冊 -> 使用,行雲流水,一鼓作氣,可是若是咱們今天是要自定義一個彈窗組件呢?css

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

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

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

step_3

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

文件位置 /src/toast/toast.vuegithub

<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

三. 實現動態加載組件

能夠看到,已經顯示出一個靜態的彈出層了,接下來咱們就來看看如何實現動態彈出.api

咱們先在 /src/toast/ 目錄下面,新建一個index.js, 而後在index.js裏面,敲入如下代碼(因爲該代碼耦合比較嚴重,因此就不拆開一行一行講解了,改爲行內註釋)bash

文件位置 /src/toast/index.jsapp

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
相關文章
相關標籤/搜索