本文主要讲述的是Vue自定义指令内容,为后续实现级联组件做铺垫。vue
首先以经常使用的v-model指令的使用为例:node
<input type="text" v-model.lazy="age"/>{{age}}
Vue指令主要用于 须要进行一些DOM操做的时候,经过Vue指令 将DOM操做进行封装。
使用指令的时候分为 左右两部分,等号左边是 指令名(v-model)和 修饰符(.lazy),等号右边是 指令表达式(age),指令表达式虽然是一个单纯的字符串,可是指令表达式的值倒是 当前组件上的同名属性对应的值,上面input输入框中显示的将是 this.age的值
这里补充一下v-model指令的三个修饰符的用法:express
<input type="text" v-model.number="age"/>{{age}}
当用户在输入框中输入"18a"后,最右侧显示的age的值 仍然为18,由于其会将"18a"先转换为数字类型,因此值仍然为 数字18;当用户输入"a18"后,最右侧显示的age值就会变为"a18",由于"a18"没法转换为数字,因此仍然显示 字符串"a18"
指令 实际上是一个对象。若是想在Vue组件中建立自定义的指令,那么能够在组件上添加 directives属性,其 属性值为一个对象,而后将自定义指令注册到directives属性值上,对象的 属性名为 指令名称,对象的 属性值为 自定义指令对象。须要注意的是,指令名称采用的驼峰命名法, 不包括v-部分,使用的时候 驼峰命名大写部分转换为小写并用-隔开,如:
// 某个组件ide
export default { directives: { // 在当前组件上注册一个指令 clickOutside: { // 指令名称为clickOutside,使用的时候使用v-click-outside // 指令对象 } } }
<!--使用指令--> <div v-click-outside:bar.foo="close"></div>
指令对象内部主要是一些钩子函数,如: bind、 inserted、 update、 componentUpdated、 unbind,比较经常使用的是inserted, 在使用了该指令的元素被插入到父元素内时候执行,固然最重要的就是传递给钩子函数处理的参数,由于指令最重要的做用就是 操做DOM,因此其
① 第一个参数就是el,是绑定了指令的DOM元素对象,若是绑定指令的是一个组件,也就是说指令用在了组件上,那么el就表明这个组件渲染后的整个DOM元素对象。函数
② 第二个参数就是binding,是一个对象,包含了当前指令的一些属性,好比: binding.name值为click-outside,不包含v-部分,binding.expression值为close,binding.value值就是this.close的值,即当前组件上close属性对应的值,能够是一个单纯的数据,也能够是一个方法名,这里close就是一个组件上的close()方法,binding. modifiers值为{foo:true}就是当前指令的修饰符,是一个对象,若是没有修饰符则是一个空的对象{},binding.arg值为bar,是传递给指令的参数,即冒号修饰的部分this
注意: 指令的参数必须写在修饰符的前面,即冒号必须在点号前。双向绑定
③第三个参数为vnode,即Vue编译生成的虚拟节点,这个参数很是有用,能够获取到很是多Vue实例相关的东西,vnode.context能够获取到当前vue组件实例,vnode.componentInstance,若是指令是用在某个组件上,那么componentInstance获取的就是使用了该指令的那个Vue组件实例code
export default { directives: { // 在当前组件上注册一个指令 clickOutside: { // 指令名称为clickOutside,使用的时候使用v-click-outside inserted(el, binding) { document.addEventListener("click", (e) => { // 给整个document添加click事件 if (e.target === el || el.contains(e.target)) { // 若是点击的区域是使用了该指令的DOM元素的内部,那么不作任何处理 return; } binding.value(); // 即调用close()方法 }); } } } }
该指令实现的是点击了 绑定指令的元素的外面后,执行指令绑定的方法
实现一个 v-my-model指令,具备v-model的大部分功能,如下指令代码并非源码的实现,仅仅是简单模拟一下v-model指令的功能,包括v-model的双向绑定功能、lazy修饰符、number修饰符、trim修饰符。
export default { directives: { // 注册v-my-model到组件上 myModel: { // 指令名称采用驼峰命名法 inserted(el, binding, vnode) { // 在绑定元素插入父元素后执行 el.value = binding.value; // 获取指令表达式的值做为input元素的value值 const eventName = binding.modifiers.lazy ? "change": "input"; // 处理lazy修饰符,若是有lazy属性则换成change事件 el.addEventListener(eventName, (e) => { // input元素监听input或者change事件 let result = e.target.value; // 获取用户输入 if (binding.modifiers.number && !Number.isNaN(parseInt(e.target.value))) { // 若是指令带有number修饰符,而且用户的输入可以转换为number result = parseInt(e.target.value); // 将用户的输入转换为对应的数字 } if (binding.modifiers.trim) { // 若是指令带有trim修饰符 result = result.trim(); // 去除用户输入的首尾空格 } vnode.context[binding.expression] = result; // 将最终的结果保存到当前组件实例上即view --> model中 }); vnode.context.$watch(binding.expression, (newValue, oldValue) => { // 获取组件实例并监听其中的绑定的值变化 el.value = newValue; // 若是组件实例上数据发生变化,那么更新view数据 }); } } } }
v-my-model指令主要就是监听 input或者 change事件,同时经过 监听组件实例中指定数据的变化实现数据的 双向绑定。大致上实现了v-model指令的功能。