前言
学习过程中的笔记,用于日后复习,如果有错误或者更好的方法,希望看到的大佬指出
vue中有很多常用的指令,比如v-on,v-if等等,也可以自定义一个指令来满足特殊的需求
此篇笔记将记录什么是自定义指令,什么情况下需要创建自定义指令以及如何在vue体系中创建自定义指令
什么情况下需要创建
为了保证Methods方法只有纯粹的数据逻辑(和DOM解耦,易于单元测试),不去处理DOM相关的操作,而将用于处理DOM的操作委托出来,约定成特定的修饰符,所以就是说,如果Methods中的方法如果需要操作DOM,就需要考虑自定义指令了
自定义指令
如何创建
Vue.directive("demo",{})
钩子函数
bind:function(el,binding,vnode){}
只会调用一次,指令第一次绑定到元素时调用,可以进行一次性的初始化设置,
此时 el.parentNode 为null
inserted:function(el,binding,vnode){}
被绑定的元素插入父节点时调用(仅保证父节点存在,但是不一定插入到文档中)
el.parentNode 可以访问当前节点的父节点
update:function(el,binding,vnode,oldVnode){}
所在组件的vnode更新时调用,但是可能发生在其子vnode更新之前,
指令的值可能发生了变化,也可能没有,可以通过比较更新前后的值来忽略不必要的更新,从而一定程度的提高性能
componentUpdate:function(el,binding,vnode,oldVnode){}
指令所在组件的vnode以及其子vnode全部更新之后调用
unbind:function(el,binding,vnode){}
只调用一次,指令与元素解绑时调用
钩子函数参数
1 el:指令所绑定的元素,可以用来直接操作DOM
2 binding2-1 name :指令名,不好含v-前缀
2-2 value: 指令的绑定值,比如v-my-directive="1+1",绑定值为2
2-3 oldValue: 指令绑定的前一个值,仅在update和componentUpdate中可以使用
2-4 expression: 字符串形式的指令表达式 比如v-my-directive="1+1",表达式为"1+1"
2-5 arg:传给指令的参数 v-my-directive:foo 参数为foo
2-6 modifiers: 包含指令修饰符的对象 v-my-directive.stop.self ,modifires为{stop:true,self:true}
3 vnode vue编辑生成的虚拟节点
4 oldVnode 上一个虚拟节点,仅在update 和componentUpdate中可以使用
实际使用
下面有个需求,用来监听浏览器窗口宽度或者高度的变化,并且实时反应在页面上
基础代码
下面这段代码很简单,div上面绑定了自定义指令,触发时会调用onResize函数,获取数据
<template>
<div v-getResize="onResize">浏览器{{ height }}</div>
</template>
export default {
data() {
return {
direction: "1", //后续使用,1表示获取高度,2表示获取宽度
height: 0,
};
},
methods: {
onResize(height) {
this.height = height;
},
},
};
自定义指令
很简单,下面的代码就可以简单的实现浏览器改变高度的时候,获取浏览器的高度
//main.js
Vue.directive("getResize", {
inserted(el, binding) {
console.log(binding);
let callback = binding.value;
window.addEventListener("resize", () => {
callback(window.innerHeight);
});
},
});
参数和修饰符
上面的代码并不能在页面刚刚打开的时候获取高度,只能在移动的时候获取,并且只能获取高度,而且也没有在unbind的时候,解除监听
所以我们需要传入一个参数来判断获取高度还是宽度,传入一个修饰符判断是否立即获取,并且在unbind的时候解除监听
需要注意的是,因为direction 传入的是变量,所以需要用[]包裹起来,还有removeEventListener里面的函数不能是匿名函数,
当然实际项目中,可能还需要加上防抖或者节流来优化代码,这里就不演示了
<div v-getResize:[direction].quiet="onResize">浏览器{{ height }}</div>
//main.js
Vue.directive("getResize", {
inserted(el, binding) {
let callback = binding.value;
let result = () => {
return binding.arg == 1 ? window.innerHeight : window.innerWidth;
};
let onResize = () => {
callback(result());
};
window.addEventListener("resize", onResize);
if (binding.modifiers && binding.modifiers.quiet) onResize();
el._onResize = onResize;
},
unbind(el) {
if (!el._onResize) return;
window.removeEventListener("resize", el._onResize);
delete el._onResize;
},
});
封装
由于上述指令是写在main.js中的,但是为了优雅一点点,还是不要直接写在里面的好
结构
resize.js
const resize = {
inserted: function(el, binding) {
//console.log(binding);
let callback = binding.value;
let arg = binding.arg;
let modifiers = binding.modifiers;
let result = () => {
return arg === "1" ? window.innerHeight : window.innerWidth;
};
let onResize = () => {
callback(result());
};
window.addEventListener("resize", () => {
onResize();
});
if (modifiers && modifiers.quiet) {
onResize();
}
el._onResize = onResize;
},
unbind: function(el) {
if (!el._onResize) return;
window.removeEventListener("resize", el._onResize);
delete el._onResize;
},
};
export default resize;
directive.js
import resize from "./resize";
export default {
getResize: resize,
};
main.js
import directive from "./util/directive/directive";
for (const key in directive) {
Vue.directive(key, directive[key]);
}
这就是全部代码,封装的方法纯粹是自己想出来写着玩的,如果有更好的方法,希望大佬指教