如何写Vue Plugin 以 Toast 为例

Params

name type default description
title string 标题
content string 内容
type string 'default' 样式:success, error, warning, default
onShow function 显示时的回调
onHide function 隐藏时的回调

示例

组件中使用

this.$toast({
        title: '消息提示',
        content: '您有一条新消息',
        type: 'warning',
        onShow: () => {
          console.log('on toast show')
        },
        onHide: () => {
          console.log('on toast hide')
        }
      })

this.$toast({title: '错误', content: '无法连接到服务器', type: 'error'})

this.$toast({title: '成功', content: '已设置为推荐', type: 'success'})

this.$toast({title: '消息', content: '这是一条普通消息'})

1. 写一个Toast组件

src/components/toast/index.vue

<template>
  <transition name="toast-fade">
    <div class="toast"
      :class="objClass" 
      v-show="isActive"
      @mouseenter="onMouseenter"
      @mouseleave="onMouseleave"
      >
      <button class="toast-close-button" @click="hide">×</button>
      <div class="toast-container">
        <div class="toast-title">{{title}}</div>
        <div class="toast-content">{{content}}</div>
      </div>
    </div>
  </transition>
</template>

<script>
export default {
  data: () => ({
    list: [],
    title: null,
    content: null,
    type: null,
    isActive: false,
    timer: null,
    onShow: () => {},
    onHide: () => {}
  }),
  computed: {
    objClass () {
      // 样式'success, error, warning, default'
      return this.type ? 'toast-' + this.type : null
    }
  },
  methods: {
    // 显示
    show (params) {
      let {content, title, onShow, onHide, type} = params
      this.type = type
      this.content = content
      this.title = title
      this.onShow = onShow
      this.onHide = onHide

      this.isActive = true
      this.setTimer()
    },

    // 隐藏
    hide () {
      this.isActive = false
    },

    // 计时器
    setTimer () {
      clearTimeout(this.timer)
      this.timer = setTimeout(() => {
        this.isActive = false
      }, 4000)
    },

    // 鼠标移至组件时保持显示状态
    onMouseenter () {
      clearTimeout(this.timer)
    },

    // 鼠标移开组件时重新定时
    onMouseleave () {
      if (this.isActive) this.setTimer()
    }
  },
  watch: {
    isActive (val) {
      if (val && typeof this.onShow === 'function') {
        this.onShow()
      } else if (!val && typeof this.onHide === 'function') {
        this.onHide()
      }
    }
  }
}
</script>

<style>
.toast {
  position: fixed;
  top: 10px;
  right: 10px ;
  display: block;
  width: 300px;
  overflow: hidden;
  box-shadow: 0 0 6px #999;
  opacity: .8;
  border-radius: 3px 3px;
  padding: 15px 15px 15px 15px;
  background-position: 15px center;
  background-repeat: no-repeat;
  color: #333;
  background-color: #f0f3f4;
}
.toast-default {

}
.toast-success {
  color: #fff;
  background-color: #51a351;
  padding: 15px 15px 15px 50px;
  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==") !important;
}
.toast-error {
  color: #fff;
  background-color: #bd362f;
  padding: 15px 15px 15px 50px;
  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=") !important;
}
.toast-warning {
  color: #fff;
  background-color: #f89406;
  padding: 15px 15px 15px 50px;
  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=") !important;
}
.toast:hover {
  opacity: 1;
  box-shadow: 0 0 18px #888;
  transition: all 200ms ease;
}
.toast-container {
  vertical-align: middle;
}

.toast-fade-enter, .toast-fade-leave-active {
  opacity: 0;
  transform: translateX(100%);
}
.toast-fade-leave-active,
.toast-fade-enter-active {
  transition: all 400ms cubic-bezier(.36,.66,.04,1);
}
.toast-title {
  font-size: 14px;
  font-weight: bold;
}
.toast-close-button {
    padding: 2px 2px;
    border: none;
    background: transparent;
    position: relative;
    right: -10px;
    top: -15px;
    float: right;
    font-size: 20px;
    font-weight: bold;
    color: #ffffff;
    -webkit-text-shadow: 0 1px 0 #ffffff;
    text-shadow: 0 1px 0 #ffffff;
    -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80);
    filter: alpha(opacity=80);
}

</style>

2 将组件注册成为plugin

src/components/toast/plugin.js

import Toast from './index'

export default {
  install (Vue, options = {}) {
    const VueToast = Vue.extend(Toast)
    let toast = null

    function $toast (params) {
      return new Promise(resolve => {
        if (!toast) {
          toast = new VueToast()

          toast.$mount()

          document.querySelector(options.container || 'body').appendChild(toast.$el)
        }
        console.log('plugin done')
        toast.show(params)
        resolve()
      })
    }

    Vue.prototype.$toast = $toast
  }
}

3 在main中加载

main.js

import toast from './components/toast/plugin'
Vue.use(toast)

4 在页面中使用

src/demo/toast.vue

<template>
  <div>
    <button class="btn btn-default" @click="showToast">show toast</button>
  </div>
</template>

<script>
export default {
  methods: {
    showToast () {
      this.$toast({
        title: '消息提示',
        content: '您有一条新消息',
        type: 'warning',
        onShow: () => {
          console.log('on toast show')
        },
        onHide: () => {
          console.log('on toast hide')
        }
      })
    }
  }
}
</script>

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351