手动实现一个Vue插件

在web开发过程中,实现一个全局的定制化组件是很常见的需求,下面会以message组件为例子,来自己手动实现一个全局的message组件,分别用vue和react两种方式来实现,vue的message对照element-ui,而react的message对照antd,本篇文章会首先介绍vue的实现方法,下一篇会介绍react实现方法

我们看antd或者element不难发现,其实message这样的全局组件和常规组件不同,他暴露的不是一个组件而是一些调用API

// element-ui的Message调用
this.$message({
  message: '恭喜你,这是一条成功消息',
  type: 'success'
});
// antd的Message调用
import { message } from 'antd'
message.success({ content: 'Loaded!', key, duration: 2 })

这其实就是意味着,在调用API之前,先把装这些全局组件的容器渲染到页面上,在正式调用API的时候再把,内容渲染到容器中

实现这个vue版本的messsage其实分为三个部分

  • message组件的template部分
  • 如何把message组件实例放到vue原型上(方便调用,不用每个文件都引入)
  • 考虑一些边界问题
  • 如何使用这个message
  1. message组件的template部分(模板和样式)
<template>
    <div class="message-wrap">
      <transition-group name="msg-fade">
        <div class="message" :class="item.type" v-for="item in list" :key="item.tag">
            <span class="icon"></span>
            <p class="content">{{item.content}}</p>
        </div>
      </transition-group>
    </div>
</template>

<style scoped lang='less'>
.message-wrap{
    position: fixed;
    left: 50%;
    top: 60px;
    transform: translate(-50%);
    z-index: 1000;
    .message{
      height: 34px;
      min-width: 180px;
      padding: 0px 10px;
      font-size: 12px;
      margin-top: 30px;
      display: flex;
      align-items: center;
      border-radius: 4px;
      box-shadow: 0 0 8px #ddd;
      border: 1px solid #eee;
      .icon {
        width: 10px;
        height: 10px;
        border-radius: 50%;
        margin-right: 6px;
      }
    }
    .error {
      color: #ff4d4f;
      >.icon{
        background: #ff4d4f;
      }
    }
     .warning {
      color: #faad14;
      >.icon{
        background: #faad14;
      }
    }
     .success {
      color: #52c41a;
      >.icon{
        background: #52c41a;
      }
    }
}
.msg-fade-enter-active {
  animation: alert-fade-in .3s;
}

.msg-fade-leave-active {
  animation: alert-fade-out .3s;
}

@keyframes alert-fade-in {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}

@keyframes alert-fade-out {
  0% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}
</style>
  1. 如何把message组件实例以及instance上暴露的API放到vue原型上(方便调用,不用每个文件都引入)

    要达到这个效果,需要用到Vue.extend()和Vue.use()

    • 在Vue.use()时候会去,执行组件里面的install方法,我们可以在这里把message组件实例以及instance上暴露的API放到vue原型上
<script>
let tag= 0;
let instance = null;
const Msg = {
  name:'message',
  data(){
    return {
      type:'',
      content:'',
      list:[]
    }
  },
  install(Vue){
    Vue.prototype.$msg = (config)=>{
      // 创建构造器
      let MessageConstructor = Vue.extend(Msg);
      if(!instance){
        instance = new MessageConstructor();
        // 创建 MessageConstructor 实例,并挂载到一个元素上。
        instance.$mount();
        document.body.appendChild(instance.$el); 
      }
      instance.add(config)
    }
  },
}

export default Msg
</script>
  1. 考虑一些边界问题
    • 考虑到页面长度,限制最多容纳10条数据
    • 考虑到超时之后超时的message消失,需要给每一条数据添加唯一tag
<script>
let tag= 0;
let instance = null;
const Msg = {
  methods:{
    add(eachList){
      // 超过10条删除第一条
      if(this.list.length > 10){
        this.list.shift()
      }
      // 给每一条message加一个唯一tag
      const obj = Object.assign({tag: tag++},eachList)
      this.list.push(obj);
      setTimeout(()=>{
        this.removeList(obj.tag)
      },eachList.duration)
    },
    removeList(tag){
      const newList = this.list.filter(each=>each.tag !== tag);
      this.list = newList;
    }
  },
}

export default Msg
</script>
  1. 如何使用这个message
// main.js
import Vue from 'vue';
import App from './App.vue';
import msg from './index.vue';

Vue.use(msg)
new Vue({
  render: h => h(App),
}).$mount('#app')

// xxx.vue
this.$message(
  {
    type:'error',
    content:'自定义message',
    duration: 5000,
  }
)

效果如下:
在这里插入图片描述

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐