Vue自定义指令和自定义过滤器
一、自定义指令:
自定义指令分为两种:全局自定义指令和局部自定义指令
全局指令指所有组件都可以使用,局部指令是只有在当前组件中才可以使用。
如,我们现在有个需求,当一个输入框获取焦点时,显示出一个div框,且可点击使之变色,当我们点击div框之外的地方,隐藏弹出的div框
<div v-click-outside="hide">
<input type="text" @focus="show">
<div v-if="isShow" style="width: 100px; height:100px;background: red;" @click="changeColor"></div>
</div>
vue实现方式如下:
let vm = new Vue({ el:"#app", data(){ return{ isShow:false, } }, methods:{ show(){ this.isShow = true; }, changeColor(e){ let c = [0,1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F']; let r = "#"; for(let i=0;i<c.length;i++){ r += c[Math.floor(Math.random()*c.length)]; } e.target.style.background=r; } }, directives:{ 'click-outside'(el,bindings,vnode){ document.addEventListener("click",function test(e){ if(!el.contains(e.target)){ vnode.context[bindings.expression](); document.removeEventListener("click",test); } }) } } });
上面自定义了一个简单的局部指令,指令函数有三个参数
el:当前dom节点,初始化div默认是不展示的:
bingings:当前节点所有绑定的属性:
name:指令名字
value:指令的绑定值
expression:字符串形式的指令表达式
oldValue:指令绑定的前一个值(仅在update 和 componentUpdated 钩子中可用)
arg:传给指令的参数(可选)
vnode:虚拟节点,它的上下文context里面有所有当前Vue对象上绑定的属性和方法
再看一个需求:有一个输入框,界面一刷新,输入框就会自动获得焦点。这个需求看起来很简单,但是这里涉及到了一个钩子的生命周期问题
<input type="text" v-focus="focus" />
Vue.directive("focus",function(el, bindings, vnode){
el.focus()
})
上面是最直观想到的方案,但是在实际执行过程中发现,这种做法是无效的。为什么呢?先看下自定义指令的生命周期:
自定义指令默认有5个生命周期:bind, inserted, update, componentUpdated, unbind
bind::在指令第一次绑定到元素时调用。只会执行一次,且此时dom元素还没有插入页面父节点
inserted:被绑定元素插入到父节点时调用
update:被绑定元素所在模板更新时调用,无论绑定值是否变化
componentUpdated:被绑定元素所在模板完成一次更新时调用
unbind:指令解绑时调用,也只会执行一次
看了上面的指令生命周期,再看说我们上面的自定义指令为何不能成功?原因是组件在初始化调用指令时,是在bind中调用的,而这时dom元素还没有插入到父节点。
正确的应该怎么实现呢?
<input type='text' v-focus="focus"> new Vue({ el:"app", directives:{ 'focus'(el, bindings, vnode){ bind(){ console.log("bind"); }, inserted(){ el.focus() }, } } })
注意:自定义指令中钩子里面没有Vue实例,this指向window
二、自定义过滤器
如,我们需要将用户英文名第一个字母大写,其他小写
使用方式 :{{ name | toUpper(1) }} // | :管道符
Vue.filter("toUpper",function(value, count){ count = count?count:value.length-1 return value.substr(0,count).toUpperCase()+value.substr(count); });