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,收工!

 

如果覺得本文不錯,請點擊右下方【推薦】!