nutUI的toast提示框组件源码,教你一步一步,封装公共组件

话不多说,这个篇博客只为了,将toast组件原滋原味的放到自己的项目,看源码,理解大致意思,后续会去封装属于自己的组件

首先:在components文件夹中创建如下文件:

 

 下面我将按照逻辑顺序将各个文件的源码贴出来:

index.js: (可以看到,给toast对象添加了自定义方法install,用来在vue实例上挂载组件)

import Toast from './_toast';
import './toast.scss';

Toast.install = function (Vue) {
    Vue.prototype['$toast'] = Toast;
}

export default Toast;

_toast.js:

这个js文件主要用Vue的extend方法来把自己写的vue文件传进去,创建一个构造器,然后再渲染,最后挂载在body上;主要代码在红色的代码上;最后抛出了一个toast对象。

import Vue from "vue";
import settings from "./toast.vue";

let ToastConstructor = Vue.extend(settings);
let instance;//实例
let instanceArr = [];//实例列表
let defaultOptionsMap = {};//默认配置参数地图
const defaultOptions = {//默认配置参数
  msg: "",//提示语
  visible: false,//是否显示
  duration: 2000, //显示时间(毫秒)
  timer: null,//计时器
  center: true,//位置
  type: "",//类型
  customClass: "",//自定义类名
  bottom: 300,//距离底部多少距离
  size: "base",//大小
  icon: null,//icon
  textAlignCenter: true,
  loadingRotate: true,
  bgColor: "rgba(36, 36, 36, 0.8)",//北京颜色
  onClose: null,
  textTimer: null,
  cover: false, //透明遮罩层
  coverColor: "rgba(0, 0, 0, 0)",
  timeStamp: null,//时间戳
  closeOnClickOverlay: false,
};
let currentOptions = {
  ...defaultOptions
};

function _showToast() {
    instance.vm = instance.$mount();
    document.body.appendChild(instance.$el);
    Vue.nextTick(() => {
        instance.visible = true;
    });
}

function _getInstance(obj) {
  //获取实例
  let opt = {
    id: new Date().getTime(),
    ...currentOptions,//默认配置
    ...defaultOptionsMap[obj.type],
    ...obj,//传入的配置
  };

  //有相同id者共用一个实例,否则新增实例
  if (opt["id"] && instanceArr[opt["id"]]) {
    instance = instanceArr[opt["id"]];
    instance.hide(true);
    instance = Object.assign(instance, opt);
  } else {
    instance = new ToastConstructor({
      //混入
      data: Object.assign(opt, obj)
    });
    opt["id"] && (instanceArr[opt["id"]] = instance);
  }

    _showToast();
    return instance;
}

function errorMsg(msg) {
  if (!msg) {
    console.warn("[NutUI Toast]: msg不能为空");
    return;
  }
}

let Toast = {
  text(msg, obj = {}) {
    errorMsg(msg);
    return _getInstance({ ...obj, msg, type: "text" });
  },
  success(msg, obj = {}) {
    errorMsg(msg);
    return _getInstance({ ...obj, msg, type: "success" });
  },
  fail(msg, obj = {}) {
    errorMsg(msg);
    return _getInstance({ ...obj, msg, type: "fail" });
  },
  warn(msg, obj = {}) {
    errorMsg(msg);
    return _getInstance({ ...obj, msg, type: "warn" });
  },
  loading(msg, obj = {}) {
    obj = { ...obj, id: obj.id || "loading", msg, type: "loading" };
    obj.cover = typeof obj.cover !== "undefined" ? obj.cover : true; //loading类型默认打开遮罩层
    obj.duration = obj.duration || 0; //loading类型默认不自动关闭
    return _getInstance(obj);
  },
  setDefaultOptions(type, options) {
    if (typeof type === "string") {
      defaultOptionsMap[type] = options;
    } else {
      Object.assign(currentOptions, type);
    }
  },
  resetDefaultOptions(type) {
    if (typeof type === "string") {
      defaultOptionsMap[type] = null;
    } else {
      currentOptions = { ...defaultOptions };
      defaultOptionsMap = {};
    }
  }
};

export default Toast;

再来看一下toast.vue文件:这是模板文件

<template>
  <transition name="toastfade">
    <div
      :id="id"
      :class="toastClass"
      v-if="visible"
      :style="{
        bottom: center ? 'auto' : bottom + 'px',
        'background-color': coverColor
      }"
      @click="clickCover"
    >
      <div
        class="nut-toast-inner"
        :style="{
          'text-align': textAlignCenter ? 'center' : 'left',
          'background-color': bgColor
        }"
      >
        <span v-if="hasIcon" class="nut-toast-icon-wrapper">
          <i
            :class="[
              'nut-toast-icon',
              type,
              { 'nut-toast-icon-rotate': type === 'loading' && loadingRotate }
            ]"
            :style="{ 'background-image': cusIcon }"
          ></i>
        </span>
        <span class="nut-toast-text" v-html="msg"></span>
      </div>
    </div>
  </transition>
</template>
<script>
export default {
  name: "nut-toast",
  props: {},
  data() {
    return {
      id: "",
      msg: "",
      visible: false,
      duration: 2000, //显示时间(毫秒)
      timer: null,
      center: true,
      type: "",
      customClass: "",
      bottom: 30,
      size: "base",
      icon: null,
      textAlignCenter: true,
      loadingRotate: true,
      bgColor: "rgba(46, 46, 46, 0.7)",
      onClose: null,
      textTimer: null,
      cover: false,
      coverColor: "rgba(0, 0, 0, 0)",
      timeStamp: null,
      closeOnClickOverlay: false
    };
  },
  watch: {
    visible(val) {
      if (val) {
        this.show();
      }
    }
  },
  computed: {
    cusIcon() {
      return this.icon ? `url("${this.icon}")` : "";
    },
    toastClass() {
      return [
        "nut-toast",
        { "nut-toast-center": this.center },
        { "nut-toast-has-icon": this.hasIcon },
        { "nut-toast-cover": this.cover },
        { "nut-loading": this.type == "loading" },
        this.customClass,
        "nut-toast-" + this.size
      ];
    },
    hasIcon() {
      if (this.type !== "text") {
        return true;
      } else {
        return this.cusIcon;
      }
    }
  },
  methods: {
    show(force) {
      this.clearTimer();
      clearTimeout(this.textTimer);
      if (this.duration) {
        this.timer = setTimeout(() => {
          this.hide(force);
        }, this.duration);
      }
    },
    hide(force) {
      this.clearTimer();
      this.visible = false;
      if (force) {
        clearTimeout(this.textTimer);
      } else {
        this.textTimer = setTimeout(() => {
          clearTimeout(this.textTimer);
          this.msg = "";
        }, 300);
      }
      typeof this.onClose === "function" && this.onClose();
    },
    clearTimer() {
      if (this.timer) {
        clearTimeout(this.timer);
        this.timer = null;
      }
    },
    clickCover() {
      if (this.closeOnClickOverlay) {
          this.hide();
      }
    }
  },
  destroyed() {
    this.textTimer = null;
    this.timer = null;
  }
};
</script>

以上是主要的js文件,下面把两个样式文件也贴上,这个组件样式和逻辑代码使用的都很巧妙,不能说哪个重要哪个不重要:

toast.scss

@import "./animation/rotate";
.nut-toast {
    position: fixed;
    left: 0;
    bottom: 150px;
    width: 100%;
    text-align: center;
    pointer-events: none;
    z-index: 9999;
    font-family: $font-family;
    &.nut-toast-small {
        .nut-toast-inner {
            font-size: $font-size-small;
        }
    }
    &.nut-toast-large {
        .nut-toast-inner {
            font-size: $font-size-large;
        }
    }
    &.nut-toast-cover {
        display: flex;
        align-items: center;
        justify-content: center;
        pointer-events: auto;
        height: 100%;
    }
    .nut-toast-inner {
        position: relative;
        display: inline-block;
        font-size: $font-size-base;
        max-width: 65%;
        text-align: center;
        line-height: 1.5;
        padding: 10px 30px;
        word-break: break-all;
        background: rgba(46, 46, 46, .8);
        border-radius: 7px;
        color: #fff;
    }
    &.nut-toast-has-icon {
        .nut-toast-inner {
            padding: 70px 50px 30px;
        }
        .nut-toast-icon-wrapper {
            position: absolute;
            left: 0;
            top: 20px;
            width: 100%;
            height: 50px;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .nut-toast-icon {
            display: inline-block;
            width: 30px;
            height: 30px;
            background-repeat: no-repeat;
            background-size: 100%;
            &.success {
                background-image: url("data:image/svg+xml,%3Csvg width='48' height='46' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd' %3E%3Cpath d='M8 0h18c11.046 0 20 8.954 20 20v18a8 8 0 0 1-8 8H20C8.954 46 0 37.046 0 26V8a8 8 0 0 1 8-8z' fill='rgb(255,255,255)'/%3E%3Cpath d='M43.562 3L22.01 23.803l-4.855-4.557a2.934 2.934 0 0 0-4.147.132l-1.324 1.41a1 1 0 0 0 .045 1.414l9.047 8.49a2 2 0 0 0 2.763-.025L47.741 7.12 43.562 3z' fill='rgb(44,42,53)'/%3E%3C/g%3E%3C/svg%3E");
            }
            &.fail {
                background-image: url("data:image/svg+xml,%3Csvg width='46' height='46' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cpath d='M8 0h18c11.046 0 20 8.954 20 20v18a8 8 0 0 1-8 8H20C8.954 46 0 37.046 0 26V8a8 8 0 0 1 8-8z' fill='rgb(255,255,255)'/%3E%3Cg fill='rgb(44,42,53)'%3E%3Cpath d='M13.6 15.722l1.415-1.414a2 2 0 0 1 2.828 0L33.4 29.864a1 1 0 0 1 0 1.414l-1.414 1.414a2 2 0 0 1-2.828 0L13.6 17.136a1 1 0 0 1 0-1.414z'/%3E%3Cpath d='M33.4 15.722l-1.415-1.414a2 2 0 0 0-2.828 0L13.6 29.864a1 1 0 0 0 0 1.414l1.414 1.414a2 2 0 0 0 2.828 0L33.4 17.136a1 1 0 0 0 0-1.414z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
            }
            &.warn {
                background-image: url("data:image/svg+xml,%3Csvg width='46' height='46' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cpath d='M8 0h18c11.046 0 20 8.954 20 20v18a8 8 0 0 1-8 8H20C8.954 46 0 37.046 0 26V8a8 8 0 0 1 8-8z' fill='rgb(255,255,255)'/%3E%3Cpath d='M23 23V12' stroke='rgb(44,42,53)' stroke-width='6' stroke-linecap='round'/%3E%3Cpath d='M21 30h3a2 2 0 0 1 2 2v3a1 1 0 0 1-1 1h-3a2 2 0 0 1-2-2v-3a1 1 0 0 1 1-1z' fill='rgb(44,42,53)'/%3E%3C/g%3E%3C/svg%3E");
            }
            &.loading {
                background: url("data:image/svg+xml, %3Csvg class='icon' viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='rgb(230,230,230)' d='M874.667 533.333h-192c-12.8 0-21.334-8.533-21.334-21.333 0-12.8 8.534-21.333 21.334-21.333h192c12.8 0 21.333 8.533 21.333 21.333 0 12.8-8.533 21.333-21.333 21.333zM648.533 407.467C640 416 627.2 416 618.667 407.467c-8.534-8.534-8.534-21.334 0-29.867L755.2 241.067c8.533-8.534 21.333-8.534 29.867 0 8.533 8.533 8.533 21.333 0 29.866L648.533 407.467zM512 896c-12.8 0-21.333-8.533-21.333-21.333v-192c0-12.8 8.533-21.334 21.333-21.334s21.333 8.534 21.333 21.334v192c0 12.8-8.533 21.333-21.333 21.333zm0-533.333c-12.8 0-21.333-8.534-21.333-21.334v-192c0-12.8 8.533-21.333 21.333-21.333s21.333 8.533 21.333 21.333v192c0 12.8-8.533 21.334-21.333 21.334zM270.933 782.933c-8.533 8.534-21.333 8.534-29.866 0s-8.534-21.333 0-29.866L377.6 616.533c8.533-8.533 21.333-8.533 29.867 0 8.533 8.534 8.533 21.334 0 29.867L270.933 782.933zm104.534-375.466L238.933 270.933c-8.533-8.533-8.533-21.333 0-29.866s21.334-8.534 29.867 0L405.333 377.6c8.534 8.533 8.534 21.333 0 29.867-6.4 6.4-21.333 6.4-29.866 0zM362.667 512c0 12.8-8.534 21.333-21.334 21.333h-192C136.533 533.333 128 524.8 128 512c0-12.8 8.533-21.333 21.333-21.333h192c12.8 0 21.334 8.533 21.334 21.333zm285.866 104.533l136.534 136.534c8.533 8.533 8.533 21.333 0 29.866-8.534 8.534-21.334 8.534-29.867 0L618.667 646.4c-8.534-8.533-8.534-21.333 0-29.867 6.4-6.4 21.333-6.4 29.866 0z'/%3E%3C/svg%3E") no-repeat;
                background-size: cover;
            }
        }
    }
    &.nut-toast-center {
        top: 50%;
        transform: translateY(-50%);
    }
    &.nut-loading {
        .nut-toast-inner {
            padding: 25px;
            display: inline-flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
        }
        .nut-toast-icon-wrapper {
            position: static;
            height: 30px;
        }
        .nut-toast-text:not(:empty) {
            margin-top: 10px;
            margin-bottom: -10px;
        }
        .nut-toast-icon {
            width: 40px;
            height: 40px;
            &.nut-toast-icon-rotate {
                animation: rotation 2s linear infinite;
            }
        }
    }
}

.toastfade-enter-active {
    transition: opacity 0.1s;
}

.toastfade-leave-active {
    transition: opacity 0.3s;
}

.toastfade-enter,
.toastfade-leave-active {
    opacity: 0;
}

animation/rotate.scss

@keyframes rotation {
    0% {
        -webkit-transform: rotate(0deg);
    }
    100% {
        -webkit-transform: rotate(360deg);
    }
}

@include make-animation(nutRotate);

 

 

将toast文件放到项目中后如何使用呢???

首先,在main.js中:注册一下

import toast from '@/components/toast';

toast.install(Vue);

 

在项目中使用:

this.$toast.success("123")

以上就是nutUI的toast组件的源码,主要是熟悉一下组件的封装,后续就可以封装属于自己的全局组件了!

 

 

posted @ 2020-06-13 18:54  古墩古墩  Views(1282)  Comments(0Edit  收藏  举报