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, 收工!
如果觉得本文不错, 请点击右下方 [推荐] !
来源: https://www.cnblogs.com/chuaWeb/p/custom-directive.html