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) {
     // 或者使用dataset(要注意兼容性):https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLElement/dataset 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,收工!

 

posted @ 2020-03-09 16:34  chua1989  阅读(636)  评论(0编辑  收藏  举报