一、要做什么?
我们在引入Element-ui的时候有看到这种使用Vue.use()的用法,然后element-ui里所有的组件我们都可以全局使用,这到底是怎么做到的呢,下面我以一个公共弹框组件为例,说一说我的写法,仅供参考。
呈现的最后效果:
(1)简单的标题
(2)提示内容可交互(v-html中的点击事件)
二、实践之前可以了解一下Vue的api
(1)vue-extend的用法,和compont创建组件的区别
https://cn.vuejs.org/v2/api/#Vue-extend
我觉得比较大的区别是 vue-extend创建的实例更加灵活,因为挂载的节点可以是任意指定的。
(2)使用Vue.use() 创建插件的方式
https://cn.vuejs.org/v2/api/#Vue-use
https://cn.vuejs.org/v2/guide/plugins.html
三、实践
(一)首先使用Vue.extend(options)创建弹框的模板实例
(1)创建message_box.vue 文件
<template>
<transition name="fade">
<div class="message-box" v-if="ifShow">
<div class="message-box-mask" @click.stop="closeMask"></div>
<transition name="msgbox-bounce">
<div class="message-box-line" :class="[ifShow ? 'v-modal-enter' : 'v-modal-leave']" v-if="ifShow">
<div class="message-box-header" v-if="showTitle">
{{title}}
</div>
<!-- 在父级标签定义点击事件(事件委托) -->
<div class="message-box-content" @click.stop="clickItem($event)" v-html='content'>
</div>
<div class="message-box-footer">
<span v-if="showLeftBtn" class="btn-cancel" @click.stop="left">
{{leftBtnText}}
</span>
<span @click.stop="right">
{{rightBtnText}}
</span>
</div>
</div>
</transition>
</div>
</transition>
</template>
<script>
export default {
computed: {
showTitle() {
if(this.title){
return true
}
return false
},
showLeftBtn() {
if(this.leftBtnText){
return true
}
return false
},
},
data() {
return {
ifShow: false,
title: '',
content: '',
leftBtnText: '', // 左按钮文本为空时,左按钮不显示
rightBtnText: '',
resolve: '',
reject: '',
isClickCloseMask: true, // 是否可以点击mask蒙版关闭弹框(除弹框以外的区域)
promise: '' // promise实例
};
},
mounted(){
},
methods: {
clickItem(event){
// content内容为v-html形式传入,事件如 click会被当成html 字符串,点击事件无效
// 如果当前内容文本有很多地方需要点击或其他事件,可以给每个需要点击的文本加上一个id作区分
switch (event.target.id) {
case 'baidu':
// 当前点击到的具体标签是,对应的id是 baidu
window.location.href='https://www.baidu.com/';
break;
case 'blog':
// 当前点击到的具体标签是,对应的id是 blog
window.location.href='https://blog.csdn.net/qq_35430000';
break;
default:
break;
}
},
closeMask(){
if (!this.isClickCloseMask) {
return
}
this.ifShow = false;
this.remove();
},
right() {
this.ifShow = false;
this.resolve('yes');
this.remove();
},
left() {
this.ifShow = false;
this.reject('no');
this.remove();
},
showMsgBox(data) {
this.title = data.title;
this.content = data.content;
this.leftBtnText = data.leftBtnText;
this.rightBtnText = data.rightBtnText;
if (data.isClickCloseMask === false) {
this.isClickCloseMask = false ;
}
this.ifShow = true;
this.promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
return this.promise;
},
remove() {
setTimeout(() => {
this.destroy();
}, 300);
},
destroy() {
// 销毁vm实例与DOM之间的关联
this.$destroy();
// document.body.removeChild(this.$el);
}
}
};
</script>
<style lang="less" scoped>
.fade-leave-active{
transition: opacity 0.5s;
}
.fade-leave-to{
opacity: 0;
}
.message-box {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 2000;
}
.message-box-mask {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.5);
}
@keyframes scaleIn {
0%{
opacity: 1;
transform: translate3d(-50%, -50%, 0) scale(0);
}
50%{
opacity: 1;
transform: translate3d(-50%, -50%, 0) scale(0.6);
}
70%{
opacity: 1;
transform: translate3d(-50%, -50%, 0) scale(0.9)
}
100%{
opacity: 1;
transform: translate3d(-50%, -50%, 0) scale(1)
}
}
.model-in{
animation: scaleIn 0.3s ease-in;
}
.message-box-line {
margin-top: -36px;
padding-top: 0.4rem;
position: absolute;
top: 50%;
left: 50%;
transform: translate3d(-50%, -50%, 0) scale(1);
width: 8rem;
border-radius: 6px;
background-color: #fff;
transition: all 0.2s;
}
.message-box-header {
margin: 0 auto;
padding-top: 0.2rem;
padding-bottom: 0;
font-size: 0.48rem;
color: #333;
text-align: center;
}
.message-box-content {
margin: 0 auto;
padding: 0.45rem 0.8rem 0.7rem 0.8rem;
line-height: 0.6rem;
font-size: 0.4rem;
color: #666;
text-align: left;
border-bottom: .01rem solid #DFDFDF;
}
.message-box-footer {
display: flex;
height: 1.3rem;
line-height: 1.3rem;
text-align: center;
font-size: 0.48rem;
color: #F8931B;
span{
flex: 1;
text-align: center;
}
}
.btn-cancel{
color: #666;
border-right: .01rem solid #DFDFDF;
}
.msgbox-bounce-enter {
opacity: 0;
-webkit-transform: translate3d(-50%, -50%, 0) scale(0.7);
transform: translate3d(-50%, -50%, 0) scale(0.7);
}
.msgbox-bounce-leave-active {
opacity: 0;
-webkit-transform: translate3d(-50%, -50%, 0) scale(0.9);
transform: translate3d(-50%, -50%, 0) scale(0.9);
}
.v-modal-enter {
-webkit-animation: v-modal-in 0.2s ease;
animation: v-modal-in 0.2s ease;
}
.v-modal-leave {
-webkit-animation: v-modal-out 0.2s ease forwards;
animation: v-modal-out 0.2s ease forwards;
}
@-webkit-keyframes v-modal-in {
0% {
opacity: 0;
}
100% {
}
}
@keyframes v-modal-in {
0% {
opacity: 0;
}
100% {
}
}
@-webkit-keyframes v-modal-out {
0% {
}
100% {
opacity: 0;
}
}
@keyframes v-modal-out {
0% {
}
100% {
opacity: 0;
}
}
</style>
(2)把实例挂载到document.body 上(或者挂载到你指定的某个节点上)。
创建 index.js文件
import Vue from 'vue'
import msgboxVue from './message_box.vue';
const MessageBox = function (options) {
const MessageBoxInstance = Vue.extend(msgboxVue);
// 实例化vue实例
let currentMsg = new MessageBoxInstance().$mount();
// let currentMsg = new MessageBoxInstance().$mount('#app');
// $mount()中参数可以为空,也可以任意指定一个节点,但注意挂载后这个节点将会被覆盖
// $el记录了new实例的dom,可以把这个dom任意插入到哪个节点
// 此时不指定节点,我们把弹框的实例挂载到 body上
document.body.appendChild(currentMsg.$el);
return currentMsg.showMsgBox(options);
};
export default MessageBox;
值得注意的是,上面的$mount('#app')中的参数可以为空也可以指定某个具体节点。
return currentMsg.showMsgBox(options); return的是一个promise实例
(二)使用Vue.use的方式构建一个全局可访问的插件
(1)了解api
https://cn.vuejs.org/v2/api/#Vue-use
https://cn.vuejs.org/v2/guide/plugins.html
import MessageBox from '../src/components/message_box';
const utils = {
install:function (Vue,options) {
// 1. 添加全局方法或属性
Vue.myGlobalMethod = function () {
// 逻辑...
}
// 2. 添加全局指令
Vue.directive('my-directive', {
bind (el, binding, vnode, oldVnode) {
// 逻辑...
}
})
// 3. 注入组件选项
Vue.mixin({
created: function () {
// 逻辑...
}
})
// 4. 添加实例方法
Vue.prototype.$myMethod = function (methodOptions) {
// 逻辑...
}
// 5.按照4的方式,添加一个公共弹框组件方法,这样全局可通过 this.confirm 访问
/*
* @param data {object}
* title {string} 提示框title
* message {string} 提示框内容
* leftBtnText {string} 左侧按钮文字
* rightBtnText {string} 右侧按钮文字
**/
Vue.prototype.confirm = function (data) {
return MessageBox(data)
.then(sucess => {
return Promise.resolve(sucess);
})
.catch(err => {
return Promise.reject(err);
});
};
}
}
export default utils
(2)main.js中引入并使用插件
(三)、插件调用以及配置(样式等也可以根据需求,自行改造)
mounted(){
let name1 = '百度';
let name2 = '我的博客';
let template = `<p>我是一行文字:</p>
<p>如果你点击<span id='baidu' style='color:#EE3B3B'>《${name1}》</span>可以跳转。</p>
<p>如果你点击<span id='blog' style='color:#EE3B3B'>《${name2}》</span>可以跳转。</p>
`
// 弹框配置
this.confirm({
title: "标题", // 标题
content: template, // 内容
leftBtnText: "取消", // 左按钮文本为空时,左按钮不显示
rightBtnText: "确定",
isClickCloseMask: true // 是否可以点击mask蒙版关闭弹框(除弹框以外的区域)
})
.then(res => {
// 点击了确定按钮,要做的操作
console.log('点击了确定按钮');
})
.catch(err => {
// 点击了取消按钮,要做的操作
console.log('点击了取消按钮');
});
}