vue自定义指令要点

vue自定义指令的基础使用这里就不阐述,看官网文档:https://cn.vuejs.org/v2/guide/custom-directive.html

本文用一个实例描述自定义指令的要点,自定义一个数据上报的指令。

 

你可能会这样写demo:

// 自定义v-datacenter命令埋点,点击节点发送埋点数据  // demo : <div v-datacenter="{ei: 'learning_center_click'}">进入学习中心</div>  const dataCenter = function(data){      // 这里处理数据上报  }  Vue.directive('datacenter', {      bind(el, binding) {          el._dataCenter = function(el) {              dataCenter(binding.value);          }          el.addEventListener('click', el._dataCenter)      },      unbind(el) {          // 移除监听          el.removeEventListener('click', el._dataCenter);          delete el._dataCenter;      }  })

 这个demo对节点的点击做了响应,处理了数据埋点。有什么问题呢?只处理了初次绑定的数据,如果你的数据是通过ajax异步获取的,就可能出现问题,比如

<div v-datacenter="{ei: info.dataEvent}">进入学习中心</div>

 其中info.dataEvent最开始是空字符,从后台拉取数据以后info.dataEvent才有值。那么上面的自定义指令中bind中的binding.value的值应当是为空字符。点击上报数据时“ei”的值一直为空字符

 

改进:

// 自定义v-datacenter命令埋点,点击节点发送埋点数据  // demo : <div v-datacenter="{ei: 'learning_center_click'}">进入学习中心</div>  Vue.directive('datacenter', {      bind(el, binding) {          el._dataCenter = function(el) {              dataCenter(binding.value);          }          el.addEventListener('click', el._dataCenter)      },      update(el, binding) {          // 处理value一开始没有值,后面才有值的情况          if (binding.value && (JSON.stringify(binding.value) !== JSON.stringify(binding.oldValue))) {              // 移除之前的监听              el.removeEventListener('click', el._dataCenter);              delete el._dataCenter;                // 新增监听              el._dataCenter = function(el) {                  dataCenter(binding.value);              }              el.addEventListener('click', el._dataCenter)          }      },      unbind(el) {          // 移除监听          el.removeEventListener('click', el._dataCenter);          delete el._dataCenter;      }  })

 添加了update(当所在组件的 VNode 更新时调用),由于update时指令的value可能完全没有改动,所以要判断当值有更改且有效时重新绑定click监听。这样和bind配合就满足了同步/异步的所有场景。

真的就OK了么?显然不是,还有一种异常情况:在特殊情况下(如路由切换),节点既要响应click监听,也要移除节点。unbind就会在响应click监听之前调用。监听在响应之前就被移除,导致失败。

 

二次改进:

// 自定义v-datacenter命令埋点,点击节点发送埋点数据  // demo : <div v-datacenter="{ei: 'learning_center_click'}">进入学习中心</div>  Vue.directive('datacenter', {      bind(el, binding) {          el._dataCenter = function(el) {              dataCenter(binding.value);          }          el.addEventListener('click', el._dataCenter)      },      update(el, binding) {          // 处理value一开始没有值,后面才有值的情况          if (binding.value && (JSON.stringify(binding.value) !== JSON.stringify(binding.oldValue))) {              // 移除之前的监听              el.removeEventListener('click', el._dataCenter);              delete el._dataCenter;                // 新增监听              el._dataCenter = function(el) {                  dataCenter(binding.value);              }              el.addEventListener('click', el._dataCenter)          }      },      unbind(el) {          // 移除监听          // 在特殊情况下节点既要响应click,也要移除节点。避免在响应click之前就被移除监听,          // 所以要延时移除,放到下一个宏任务          setTimeout(() => {              el.removeEventListener('click', el._dataCenter);              delete el._dataCenter;          })      }  })

 要避免在响应监听前监听被移除,所以将移除监听放到下一个宏任务。OK,收工!

 

如果觉得本文不错,请点击右下方【推荐】!