前几天在开发时,心血来潮自己开发了一个v-loading指令,然后就应用到了页面中,我的页面有多处使用了该指令,但是令我EMO的时候也来了,
页面很简单,只有三块区域,基本相同,但是中间有个显示后台结果的div区,我就想着,用指令来整个遮盖效果,所以,三个区域的结果区都有v-loading。
v-loading指令代码如下,重点是update,也恰恰是它给我整emo了,因为,当我给v-loading重新赋值时,指令中的update居然执行了【3次】(发现问题是因为页面没有v-loading了,因为执行时,false,true,false,正好最后一次给我关闭了loading层)。一开始确实也没注意,以为每个指令都是个副本,各执行各的,但实际,如果你不在指令的update中加一层判断的话,那么,使用了多次相同的指令时,它的效果就会不一样了,也就是如下的判断。
指令的update方法中,要加入当前值与之前值的判断(很像watch)
if (binding.oldValue == binding.value) {
return
}
/** v-loading
* @desc 遮罩层loading,接受值类型:Boolean, Object
* @return 无
* @author vic
* @param true/false {Boolean} 打开、关闭loading
* @param { show: pageLoading, icon: true,appendToBody:true } {Object} loading配置,text:显示文字,icon:显示图标,appendToBody:是否为全屏,customClass:自定义样式
* @example
v-loading="true"
v-loading="false"
v-loading="{ show: pageLoading, icon: true }"
v-loading="{ show: pageLoading, text: '正在加载中...',appendToBody:true }"
* @tip text与icon只能二选一,样式可自定义
*/
// loading指令基本设置
const loadingID = 'vi_loading' //div id属性
const loadingClassName = 'vi-loading' //div class属性
function createElement(params) {
//创建元素
var div = document.createElement('div')
div.id = loadingID //id
div.className = `${loadingClassName} ${
params.customClass != undefined ? params.customClass : ''
}`
let innerHTML = `<div class='vi-loading_txt' style=''>加载中...</div>`
if (params != undefined) {
if (params.text != undefined) {
innerHTML = `<div class='vi-loading_txt'>${params.text}</div>`
} else if (params.icon != undefined) {
innerHTML = `<div class='vi-loading_txt'><span class='vi-loading_icon iconfont icon-jiazaizhong only-rotate-5'></span></div>`
}
}
div.innerHTML = innerHTML
div.style.zIndex = getDocumentMaxZIndex()
if (params.appendToBody) {
//文档中最后加入该div
document.body.insertBefore(div, document.body.lastChild)
} else {
// 往父级插入
params.insertDom.style.position = 'relative'
params.insertDom.insertBefore(div, params.insertDom.lastChild)
}
}
function removeElement(params) {
//清除div
var ele = null
if (params.id != undefined) {
ele = document.getElementById(loadingID)
if (ele != null) {
ele.remove()
}
ele = document.getElementsByClassName(loadingClassName)
if (ele.length != 0) {
ele.remove()
}
}
}
function getDocumentMaxZIndex() {
//获取文档元素的最大z-index
let arr = [...document.all].map(
(e) => +window.getComputedStyle(e).zIndex || 0
)
return arr.length ? Math.max(...arr) + 1 : 0
}
function getParams(el, binding, vnode, valueType) {
if (valueType == 'object') {
return {
insertDom: el,
appendToBody:
binding.value.appendToBody != undefined
? binding.value.appendToBody
: false,
text: binding.value.text,
icon: binding.value.icon,
customClass:
binding.value.customClass != undefined
? binding.value.customClass
: undefined, //自定义class
}
} else if (valueType == 'boolean') {
return {
insertDom: el,
appendToBody:
binding.value.appendToBody != undefined
? binding.value.appendToBody
: false,
text: undefined,
icon: true,
customClass: undefined,
}
}
}
export default {
inserted(el, binding, vnode) {
// 第一次进入页面
// 判断值类型
if (binding.value.constructor.name.toLowerCase() == 'object') {
// object类型
let params = getParams(el, binding, vnode, 'object')
if (binding.value.show == true) {
createElement(params)
}
} else if (binding.value.constructor.name.toLowerCase() == 'boolean') {
//布尔类型
let params = getParams(el, binding, vnode, 'boolean')
if (binding.value == true) {
createElement(params)
}
}
},
update(el, binding, vnode) {
// *** 当指令参数更改时,判断值若与前值不同,则代表使用了当前指令
if (binding.oldValue == binding.value) {
return
}
if (binding.value.constructor.name.toLowerCase() == 'object') {
// object类型
let params = getParams(el, binding, vnode, 'object')
if (binding.value.show == true) {
createElement(params)
} else {
//删除LOADING层
removeElement({ id: 'vi_loading' })
}
} else if (binding.value.constructor.name.toLowerCase() == 'boolean') {
//布尔类型
let params = getParams(el, binding, vnode, 'boolean')
if (binding.value == true) {
createElement(params)
} else {
//删除LOADING层
removeElement({ id: 'vi_loading' })
}
}
},
}
其实使了那么久的VUE了,确实都没注意到这个问题,而且一直觉得指令也是个副本,但是应用时才能发现问题,所以实践很重要啊,当然我的代码相对来说没有太完美,只是适应自己的项目即可,大神绕行。