1 message 实现
效果
实现思路
因为message弹出盒子内容是通过this.$message()的方式实现的,所以我们需要将实现方法绑定到vue实例原型上才行(全局访问)
在用户调用服务时根据ui组件创建一个虚拟Dom, 在虚拟Dom上添加所需的props,然后转为真实Dom插入到目标元素上即可
重点在于获取,实例化一个虚拟dom,转换真实dom
我们先创建好服务文件
// 弹窗方法
export default function ({
type, message, showClose, close
}) {}
然后将写好的弹窗页面导入
import AlertComm from './index.vue'
import Vue from 'vue'
// 创建弹窗组件继承AlertComm原型
let Alert = Vue.extend(AlertComm);
// 弹窗方法
export default function ({
// 配置参数
type, message, showClose, close
}) {}
因为是将弹窗组件挂载到body下的,所以需要获取body
const body = document.body;
实例一个弹窗组件
import AlertComm from './index.vue'
import Vue from 'vue'
let Alert = Vue.extend(AlertComm);
// 弹窗方法
export default function ({
type, message, showClose, close
}) {
const body = document.body;
// 实例化一个message(此处是一个虚拟Dom,之后我们要想其中更改需要props)
const alertVnode = new Alert({
//绑定到一个创建的div(避免绑定到body上而破坏body结构)
el: document.createElement('div')
})
}
向body节点对象添加真实Dom和虚拟Dom方便后面调用
import AlertComm from './index.vue'
import Vue from 'vue'
let Alert = Vue.extend(AlertComm);
// 弹窗方法
export default function ({
type, message, showClose, close
}) {
const body = document.body;
// 实例化一个message
const alertVnode = new Alert({
el: document.createElement('div')
})
// 弹窗组件实例(虚拟)
body.intsance = alertVnode
// 添加实例props(相当于给ui组件添加props)
body.instance.$props.type = type
body.instance.$props.isShow = true
body.instance.$props.title = message
body.instance.$props.myClose = showClose
body.instance.$props.myWithClose = close
// 绑定一个真实DOM
body.alert = alertVnode.$el
// 真实Dom绑定到body
body.appendChild(body.alert)
}
在页面中引用
this.$tyMessage({
type,
message: msg,
showClose,
close: () => {
this.text = '手动关闭了'
}
});
弹窗组件
<template>
<transition>
<div ref="ani" class='message' v-if="show">
<Alert :type='myType' showIcon :closable='isclose' :title='myTitle' @close="withclose" />
</div>
</transition>
</template>
<script>
import Alert from '../../alert/src/main.vue'
export default {
name: 'message',
props: {
isShow: {
type: Boolean,
default: false
},
type: {
type: String,
default: 'info'
},
title: {
type: String,
default: '文案'
},
myClose: {
type: Boolean,
default: false
},
myWithClose: {
type: Function,
}
},
mounted() {
this.show = true
setTimeout(() => {
this.show = false
}, 5000);
},
data() {
return {
myType: 'info',
myTitle: '文案',
show: false,
isclose: false
}
},
methods: {
withclose() {
this.$props.myWithClose()
}
},
watch: {
isShow: {
handler(newV) {
if (typeof newV == 'boolean') {
this.show = newV;
}
},
immediate: true,
},
type: {
handler(newV) {
this.myType = newV;
},
immediate: true,
},
title: {
handler(newV) {
this.myTitle = newV;
},
immediate: true,
},
myClose: {
handler(newV) {
if (typeof newV == 'boolean') {
this.isclose = newV;
}
},
immediate: true,
}
},
components: {
Alert
}
}
</script>
<style lang='less' scoped>
@import '../../css/message.less';
</style>
loading实现
效果
服务方式实现
实现
和message差不多
需要注意调用时返回的是一个对象,需要借此对象完成关闭loading的功能
代码
/*
全屏loading方法
*/
import Vue from 'vue'
import mark from '../../mark/src/main.vue'
// 创建一个组件继承mark
let Mark = Vue.extend(mark)
let fatherDom = ''
export default {
start({
target = '',
body = false,
text = '',
spinner = '',
background = ''
}) {
// 实例化mark
let markVNode = new Mark({
el: document.createElement('div')
})
// 创建挂载的父节点
fatherDom = target.$el
// 判断是否挂载body
if (body && typeof body == 'boolean') {
fatherDom = document.body
}
// 获取绑定元素的样式用于获取定位 -- 判断是否该为绑定元素添加定位
let style = document.defaultView.getComputedStyle(fatherDom)
// 待dom渲染完成后再获取
Vue.nextTick(() => {
if (style.position == 'static') {
fatherDom.style.position = 'relative';
}
})
// 父级上挂载虚拟dom
fatherDom.instance = markVNode;
// 父级上挂载真实dom
fatherDom.loading = markVNode.$el;
// 再loading组件实例上添加属性
fatherDom.instance.$props.show = true;
fatherDom.instance.$props.text = text;
fatherDom.instance.$props.icon = spinner;
fatherDom.instance.$props.bjColor = background;
// 父级上添加loading
fatherDom.appendChild(fatherDom.loading);
return this
},
close() {
fatherDom.instance.$props.show = false;
}
}
指令方式实现
实现
使用自定义指令,需要使用两个生命周期钩子, 还有新提到的获取样式的方式---同专栏有文章提到。 其他并无难点
代码
/*
loading加载
*/
import Vue from 'vue';
import Mark from '../../mark/src/main.vue'
let markVdom = Vue.extend(Mark);
export default {
name: 'tyLoading',
directive: {
// 获取目标元素 ,在目标元素下插入一个遮罩元素来控制展示与显示
bind: (el, bind) => {
// 实例化一个mark组件挂载到创建的div上
let markVnode = new markVdom({
el: document.createElement('div')
});
// 获取绑定元素的样式用于获取定位 -- 判断是否该为绑定元素添加定位
let style = document.defaultView.getComputedStyle(el)
// 待dom渲染完成后再获取
Vue.nextTick(() => {
if (style.position == 'static') {
el.style.position = 'relative';
}
})
// el.instance就是mask实例就是我们的loading组件
el.instance = markVnode;
// 将真实Dom给到el的mark
el.mark = markVnode.$el;
if (bind.modifiers.fullscreen) {
//挂载到body下
document.body.appendChild(el.mark)
} else {
// 将loading的真实dom添加到绑定指令元素下
el.appendChild(el.mark);
}
// 控制loaing显示隐藏 (有value则去判断)
bind.value && toggleLoading(el, bind);
},
update(el, bind) {
if (bind.oldValue !== bind.value) {
toggleLoading(el, bind);
}
}
}
}
const toggleLoading = (el, binding) => {
// 获取到文本信息
let text = el.getAttribute('loading-text');
// 获取图标信息
const icon = el.getAttribute('loading-spinner');
// 获取背景色彩信息
const bjColor = el.getAttribute('loading-background')
if (binding.value) {
// 再loading组件实例上添加属性
el.instance.$props.show = true;
el.instance.$props.text = text;
el.instance.$props.icon = icon;
el.instance.$props.bjColor = bjColor;
} else {
el.instance.$props.show = false;
}
}