zoukankan      html  css  js  c++  java
  • 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组件的源码,主要是熟悉一下组件的封装,后续就可以封装属于自己的全局组件了!

  • 相关阅读:
    大二第二学期周学习进度总结(十三)
    java课程之团队开发冲刺阶段2.5
    java课程之团队开发冲刺阶段2.4
    java课程课后作业190530之用户体验评价
    java课程课后作业190530之找水王
    java课程之团队开发冲刺阶段2.3
    python运算学习之Numpy ------ 认识数组、数组的创建
    安装 jdk
    公司想辞退的你六种表现,你get到了么?
    性能测试基础知识系统学习之相关术语
  • 原文地址:https://www.cnblogs.com/fqh123/p/13121402.html
Copyright © 2011-2022 走看看