從0開(kāi)始手寫(xiě)一個(gè)Vue消息彈窗組件

前言

近期閑來(lái)無(wú)事爱致,就想折騰一點(diǎn)東西哥放,無(wú)奈技術(shù)水平太低牍鞠,做不了什么高深復(fù)雜的項(xiàng)目咖摹,所以還是先從簡(jiǎn)單的做起吧。平時(shí)我們做項(xiàng)目的時(shí)候或多或少都會(huì)用到一些組件庫(kù)难述,比如element萤晴、muse之類的吐句,所以這次我想自己手動(dòng)寫(xiě)一個(gè)組件,封裝成一個(gè)包店读,可以暴露到外部去調(diào)用嗦枢。

效果預(yù)覽

廢話不多說(shuō),這次寫(xiě)的是一個(gè)Vue的消息彈窗組建屯断,借鑒了element的message組件文虏,大家可以去看一看element的源碼,里面一些組件的實(shí)現(xiàn)方式很巧妙殖演,非常值得去學(xué)習(xí)氧秘。話不多說(shuō),先來(lái)看看最終的實(shí)現(xiàn)效果:

不同主題

關(guān)閉按鈕
不同位置

代碼

第一步應(yīng)該是寫(xiě)一個(gè)模板:

<template>
  <transition name="msg-fade" @after-leave="afterLeave">
    <div :class="classes" class="msg-container" v-show="visible" role="alert" @mouseenter="clearTimer" @mouseleave="startTimer">
      <span>{{ message }}</span>
      <i class="msg-icon-wrapper" v-if="showCloseButton" @click="close">
        <img class="msg-icon" src="./close.svg" alt="close" />
      </i>
    </div>
  </transition>
</template>

可以看到趴久,html內(nèi)容是很簡(jiǎn)單的丸相,主要是幾個(gè)標(biāo)簽和一些props,還有幾個(gè)事件彼棍,然后我們?cè)賮?lái)看js:

<script>
export default {
  name: 'Message',
  props: {
    // 要顯示的消息
    message: {
      type: String,
      require: true
    },
    // 主題
    type: {
      type: String,
      default: 'normal'
      // normal success error warning
    },
    // 是否顯示關(guān)閉按鈕
    showCloseButton: {
      type: Boolean,
      default: false
    },
    // 組件出現(xiàn)的位置
    location: {
      type: String,
      default: 'top-center'
      // top-center top-right top-left bottom-center bottom-right bottom-left
    },
    // 自定義樣式名
    className: {
      type: String,
      default: ''
    },
    // 持續(xù)時(shí)間
    duration: {
      type: Number,
      default: 2000
    }
  },
  data () {
    return {
      // 動(dòng)態(tài)修改樣式
      classes: [
        {
          'msg-icon-padding': this.showCloseButton
        }, 
        `msg-${this.type}`,
        `msg-${this.location}`,
        this.className
      ],
       // 控制組件顯示
      visible: false,
       // 是否關(guān)閉
      closed: false,
       // 定時(shí)器
      timer: null
   }
  },
  watch: {
    closed (newVal) {
      if (newVal) {
        this.visible = false
      }
    }
  },
  methods: {
    close () {
      this.closed = true
    },
    afterLeave () {
      this.$destroy(true)
      this.$el.parentNode.removeChild(this.$el)
    },
    clearTimer () {
      clearTimeout(this.timer)
    },
    startTimer () {
      const timeout = this.duration
      if (timeout > 0) {
        this.timer = setTimeout(() => {
          if (!this.closed) {
            this.close()
          }
        }, timeout);
      }
    }
  },
  mounted () {
    this.startTimer()
    this.visible = true
  },
  beforeDestroy () {
    this.clearTimer()
  }
}
</script>

可以看到組件的代碼邏輯也是非常簡(jiǎn)單的灭忠,props的處理我就不多細(xì)說(shuō)了,可能每一個(gè)人的需求都一樣座硕。重點(diǎn)要講一講的是組件的創(chuàng)建和銷毀過(guò)程更舞。在組件掛載后,組件會(huì)調(diào)用startTimer方法生成一個(gè)定時(shí)器坎吻,按照duration指定的時(shí)間后調(diào)用close方法缆蝉,close方法會(huì)設(shè)定data的closed為true,visible監(jiān)聽(tīng)到closed變?yōu)閠rue后就變?yōu)閒alse瘦真,所以根據(jù)v-show這個(gè)指令刊头,組件的display樣式屬性就會(huì)變成none。最后Vue官方內(nèi)置的過(guò)渡組件transition會(huì)調(diào)用一系列鉤子函數(shù)诸尽,我們直接在afterLeave這個(gè)鉤子上銷毀組件和清除dom原杂。看到這里您机,可能有同學(xué)會(huì)有疑問(wèn)了穿肄?為啥要這樣銷毀組件啊,直接用一個(gè)prop控制組件顯示不就可以了嗎际看?其實(shí)對(duì)于一般的組件我們是沒(méi)必要弄成這樣咸产,但是我們得思考一下消息彈窗的使用場(chǎng)景啊。一般情況下仲闽,消息彈窗都是在完成了某種操作后脑溢,彈出來(lái)告訴用戶操作成功與否,所以我們通常用js控制消息彈窗顯示赖欣。因此屑彻,我們的消息彈窗組件不僅僅是聲明式的验庙,更應(yīng)該是命令式的,直接用js控制顯示與否社牲。

很多同學(xué)應(yīng)該都使用過(guò)element粪薛,element在全局引入的時(shí)候會(huì)向vue上掛載一個(gè)message的方法,一般我們都是this.$message這樣來(lái)操作的搏恤,我的目標(biāo)也是做成這樣子违寿。

import Vue from 'vue'
import Message from './Message.vue'

const messageConstructor = Vue.extend(Message)
let count = 1

const notify = (options = {}) => {
  // 如果Vue運(yùn)行在服務(wù)端,放棄調(diào)用
  if (Vue.prototype.$isServer) {
    return
  }
  
  if (typeof options === 'string') {
    options = {
      message: options
    }
  }

  // 創(chuàng)造實(shí)例
  const instance = new messageConstructor({
    propsData: options
  })

  // 渲染并掛載到body上
  const vm = instance.$mount()
  document.body.appendChild(vm.$el)
  vm.$el.style.zIndex = count

  // 返回一個(gè)主動(dòng)關(guān)閉的方法
  const close = () => {
    vm.closed = true
  }
  return close
}

export default notify

這里我們用了Vue的一個(gè)方法挑社,vue.extend,這個(gè)方法接受一些配置參數(shù)巡揍,返回一個(gè)可以生產(chǎn)vue實(shí)例的構(gòu)造函數(shù)痛阻,其實(shí)我們平時(shí)使用的組件一般都是這些構(gòu)造函數(shù)實(shí)例化的對(duì)象。代碼的邏輯很簡(jiǎn)單腮敌,當(dāng)notify方法被調(diào)用的時(shí)候會(huì)創(chuàng)造一個(gè)message組件的實(shí)例對(duì)象阱当,同時(shí)傳入props,最后渲染成dom并掛載到document.body上糜工。最后我們提供一個(gè)install方法將notify和message組件全局安裝到Vue中:

class Plugin {
  static install (Vue) {
    Vue.prototype.notify = notify
    Vue.mixin({
      components: {
        'oce-message': Message
      }
    })
  }
}
export default Plugin


// 全局注冊(cè)
import oce-message from 'oce-message'
import Vue from 'vue'

Vue.use(oce-message)

// 使用
<oce-message ... />
this.notify(...)

好了弊添,一個(gè)簡(jiǎn)單的消息彈窗組件就完成了,具體的代碼和使用方法在這里oce-message可以找到捌木,歡迎star和提issue油坝。

后續(xù)

這個(gè)組件有點(diǎn)簡(jiǎn)陋,其實(shí)有一些可以優(yōu)化的地方:

  1. 樣式
  2. 過(guò)渡動(dòng)畫(huà)
  3. 性能(實(shí)例是否可以做成一個(gè)單例刨裆?)

??????
我是naecoo澈圈,前端打雜工程師,偶爾寫(xiě)寫(xiě)灌水文章...
Github
博客

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末帆啃,一起剝皮案震驚了整個(gè)濱河市瞬女,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌努潘,老刑警劉巖诽偷,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異疯坤,居然都是意外死亡报慕,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)压怠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)卖子,“玉大人,你說(shuō)我怎么就攤上這事刑峡⊙竺觯” “怎么了玄柠?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)诫舅。 經(jīng)常有香客問(wèn)我羽利,道長(zhǎng),這世上最難降的妖魔是什么刊懈? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任这弧,我火速辦了婚禮,結(jié)果婚禮上虚汛,老公的妹妹穿的比我還像新娘匾浪。我一直安慰自己,他們只是感情好卷哩,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布蛋辈。 她就那樣靜靜地躺著,像睡著了一般将谊。 火紅的嫁衣襯著肌膚如雪冷溶。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,036評(píng)論 1 285
  • 那天尊浓,我揣著相機(jī)與錄音逞频,去河邊找鬼。 笑死栋齿,一個(gè)胖子當(dāng)著我的面吹牛苗胀,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瓦堵,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼柒巫,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了谷丸?” 一聲冷哼從身側(cè)響起堡掏,我...
    開(kāi)封第一講書(shū)人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎刨疼,沒(méi)想到半個(gè)月后泉唁,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡揩慕,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年亭畜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片迎卤。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拴鸵,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情劲藐,我是刑警寧澤八堡,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站聘芜,受9級(jí)特大地震影響兄渺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜汰现,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一挂谍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧瞎饲,春花似錦口叙、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至仗哨,卻和暖如春形庭,著一層夾襖步出監(jiān)牢的瞬間铅辞,已是汗流浹背厌漂。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留斟珊,地道東北人苇倡。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像囤踩,于是被迫代替她去往敵國(guó)和親旨椒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容