1.需求背景
(1)在一般的管理系统或者 H5 应用中,需要交互反馈提醒。这种交互反馈,往往需要在多个组件中使用到,那么是否可以将其抽离出来,封装一个组件呢?答案是肯定的,可以根据日常的业务,对消息提醒功能进行封装,那么问题来了,如何实现一次注册,多次使用呢,关键时刻,vue.extend API 就派上用场了
2. vue.extend({组件选项})的用法
(1)官方的解释是:使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象。 按照理解是extend方法可以把一个组件选项作为参数,使用此方法,可以获得该组件的构造函数,然后通过 new 方法创建一个组件出来。最后通过 amount 方法挂载到对应的结点上。废话不多说,直接上代码。
// 创建构造器
var Profile = Vue.extend({
template: "<p>{{firstName}} {{lastName}} aka {{alias}}</p>",
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 0.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,extend,--,instance,Vue,组件,全局,message
From: https://www.cnblogs.com/wp-leonard/p/17888605.html