vue实现全局消息提醒功能(vue-extend)

1.需求背景

(1)在一般的管理系统或者H5应用中,需要交互反馈提醒。这种交互反馈,往往需要在多个组件中使用到,那么是否可以将其抽离出来,封装一个组件呢?答案是肯定的,我们可以根据日常的业务,对消息提醒功能进行封装,那么问题来了,如何实现一次注册,多次使用呢,关键时刻,vue.extend API就派上用场了

2. vue.extend({组件选项})的用法

(1)官方的解释是:使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象。
按照我的理解是extend方法可以把一个组件选项作为参数,使用此方法,可以获得该组件的构造函数,然后通过new 方法创建一个组件出来。最后通过amount 方法挂载到对应的结点上。废话不多说,直接上代码。

// 创建构造器
var Profile = Vue.extend({
  template: '

{{firstName}} {{lastName}} aka {{alias}}

'
, data: function () { return { firstName: 'Walter', lastName: 'White', alias: 'Heisenberg' } } }) // 创建 Profile 实例,并挂载到一个元素上。 new Profile().$mount('#mount-point')

(2)疑惑:这么方法何种场景适用?
解释一下,对比正常的组件挂载流程,需要我们在 .vue 文件中的component进行注册,然后在template文件中,编写相应的代码。
但很多时候,我们需要在组件之外的地方,使用到这个组件的一些api,比如在axios 拦截响应请求时,对异常消息提示,做出反馈,这个时候无法使用普通的组件注册方式调用消息提醒组件的api。因为这个是一个js文件,因此,通过这个extend API,提供了我们一个可以在.vue 文件之外,创建组件,并挂载,调用其api实现相应功能的能力。

3 封装消息提醒组件

<template>
    <div class="toast" v-show="showToast">
        <div class="info" v-if="type == 'info'">{{ message }}</div>
        <div class="loading" v-if="type == 'loading'">
            <div class="circle">
                <div class="line" style="--line:1"></div>
                <div class="line" style="--line:2"></div>
                <div class="line" style="--line:3"></div>
            </div>
            <div class="message">{{ message }}</div>
        </div>
    </div>
</template>

<script>
export default {
    data() {
        return {
            message: "",
            duration: 3000,
            timer: null,
            type: "info",
            showToast: false,
            forbidClick: true,//加载时禁止点击
            lock: false,
        }
    },
    watch: {
        showToast(val) {
            if (val) {
                if (this.type == "info") {
                    this.startTime()
                } else {
                    clearTimeout(this.timer)
                    this.loading()
                }
            }
        }
    },
    methods: {
        startTime() {
            clearTimeout(this.timer)//之前的未关闭,需要清除,保障全局唯一性
            this.timer = setTimeout(() => {
                this.clear()
            }, this.duration)
        },
        clear() {
            this.showToast = false
            this.lock = false;
            this.lockClick()
        },
        loading() {
            this.lock = true
            this.lockClick();
        },
        lockClick() {
            if (this.lock && this.forbidClick) {
                document.body.classList.add('unclickable');
            } else {
                document.body.classList.remove('unclickable');
            }

        }
    }
}
</script>

<style lang="less">
:root {
  --dw: min(calc(100vw/750), 750px/750);
}
.px(@ww, @att) {
    @{att}: calc(@ww * var(--dw));
}

.padding(@top, @left, @att) {
    @{att}-top: calc(@top * var(--dw));
    @{att}-bottom: calc(@top * var(--dw));
    @{att}-left: calc(@left * var(--dw));
    @{att}-right: calc(@left * var(--dw));
}

.toast {
    position: fixed;
    left: 50%;
    top: 45%;
    transition: all .5s;
    transform: translateX(-50%) translateY(-50%);
    color: #FFF;
    text-align: center;
    background: rgba(17, 17, 17, 0.6);
    max-width: 80%;
    z-index: 9999;
    .px(200, min-width);
    .padding(16, 32, padding);
    .px(24, border-radius);
    .px(24, font-size);
}

.loading {
    min-height: calc(200*var(--dw));
    display: flex;
    flex-direction: column;
    justify-content: center;

    .circle {
        .line {
            display: inline-block;
            .px(15, width);
            .px(15, height);
            .px(15, border-radius);
            background-color: #FE0033;
            animation: loadingA 0.6s linear infinite;
            animation-delay: calc(0.1s*var(--line));
        }

    }

    .message {
        margin-top: calc(32*var(--dw));
        font-weight: 400;
        color: white;
    }

    @keyframes loadingA {
        0% {
            transform: translate(0, 0);
        }

        50% {
            transform: translate(0, calc(15*var(--dw)));
        }

        100% {
            transform: translate(0, 0);
        }
    }

}

.unclickable {
    overflow: hidden;

    * {
        pointer-events: none;
    }

}
</style>

(1)组件提供两种类型消息,一种是普通的消息提醒 info,另外一种是loading加载提示。默认是info类型。
(2) 在消息提醒没有消失的情况下,默认是不可点击页面的任何按钮。通过pointer-events: none; 控制
(3)info消息提示默认显示是三秒,通过duration字段控制时长,loading加载是没有消失时间,如果没有手动关闭,则会一直显示。

4全局注册

toast 组件为上面封装的消息提醒组件。

import toast from "./index.vue"

function install(Vue){
    const ToastConstructor = Vue.extend(toast);//使用基础 Vue 构造器,创建一个“子类 ,可以通过js语法实例化组件并挂载到对应地方
    const instance = new ToastConstructor();
    instance.$mount(document.createElement("div"));
    document.body.appendChild(instance.$el);
    Vue.prototype.$Toast = {
        info(message,duration=3000){
            instance.showToast = false;
            Vue.nextTick(()=>{
                instance.message = message;
                instance.duration = duration;
                instance.type = "info"
                instance.showToast = true
               
            })
        },
        loading(message){
            instance.showToast = false;
            Vue.nextTick(()=>{
                instance.message = message;
                instance.type = "loading"
                instance.showToast = true
               
            })
        },
        clear(){
            instance.clear()
        },
        
       
    }
}

export default install

(1)全局注册,主要通过把对应的方法挂载到vue的原型上,这样便可以实现任何组件可以使用到了。
接下来便是在main.js 中,进行操作。
initShowToast 为上面代码对外暴露的index脚本

import initShowToast from "./views/components/toast/index"
initShowToast(Vue)

5 使用方式

(1)组件使用
直接通过this.$toast 访问对应的api,this.$toast 需要根据实际在vue原型挂载的命名来确定。我上面是Toast,因此需要对应。

  getPop(id) {
            this.$Toast.loading("正在领取...")
            getCoupop({ stockId: id }).then(res => {
                console.log(res)
                //领取成功后,需要刷新
                this.refreshCoupon()
            }).catch(e => {
                console.log(e)
                this.$Toast.clear()
            })
        },

(2)axios拦截器使用
需要注意的是 js 文件无法通过this 访问到vue,因此需要以下方法才能实现调用。

import Vue from 'vue'
import initShowToast from "../views/components/toast/index"
initShowToast(Vue)
const Toast = Vue.prototype.$Toast
//下面是使用示例

//loading加载与消息提醒关闭
if(config.showLoading==false){
        Toast.clear()
       }else{
        Toast.loading('加载中...');
    }
//普通消息提示
Toast.info(response.data.err_msg)

你可能感兴趣的:(vue,vue.js,javascript,前端)