vue 指令应用多次时,其动态值更新时,update会执行多次

前几天在开发时,心血来潮自己开发了一个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了,确实都没注意到这个问题,而且一直觉得指令也是个副本,但是应用时才能发现问题,所以实践很重要啊,当然我的代码相对来说没有太完美,只是适应自己的项目即可,大神绕行。

猜你喜欢

转载自blog.csdn.net/tdjqqq/article/details/124314024