谈到 Vue 指令,我们脑海里浮现的第一个疑问便是 指令 是什么:
指令是告诉计算机从事某一特殊运算的代码。如:数据传送指令、算术运算指令、位运算指令、程序流程控制指令、串操作指令、处理器控制指令。
那么 Vue 指令又是什么呢?是用来干什么的?作为一名攻城狮的我们又如何去使用它?
v-if
:根据其后表达式的 bool 值进行判断是否渲染该元素v-show
:其后表达式的 bool 值为 false 时,对渲染的出标签添加display:none;
的样式v-else
:紧跟着v-if或者v-show一起使用v-for
:v-for的用法 person in people ,前者是后者的元素,类似于数组的用法v-bind:(:)
:用于响应地更新 html 特性v-on:(@)
:用于监听指定元素的 DOM 事件v-once
:一次性插入文本,随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。v-html
:输出 {{ message }} 内包含 html 代码的数据tips: 不能使用 v-html 来复合局部模板,因为 Vue 不是基于字符串的模板引擎,反之,对于用户界面(UI),组件更适合作为可重用和可组合的基本单位。另外,只对可信内容使用 HTML 插值,绝不要对用户提供的内容使用插值,避免xss攻击。
一般用于指出 v-on 指令以特殊方式绑定
事件修饰符
<a v-on:submit.prevent="onSubmit">...a>
<div @click='doThis' style="width:100px;height: 100px; background: red;">
// 点击父元素
<a v-on:click.stop="doThis">点击子元素a>
div>
descripation:当点击父元素的时候,执行 doThis ,当点击子元素 a 的时候,这个点击动作不单单触发了 a 标签,同时也触发了div标签,这就是事件冒泡,所以假设上述例子中 a 标签为v-on:click='doThis'
,则 doThis 会被执行两次,父元素和子元素都执行了一次 click 事件,而 .stop 则是阻止事件冒泡,再次点击 a 标签,click 事件只会执行一次.
按键修饰符
Vue 允许为 v-on 在监听键盘事件时添加按键修饰符:
记住所有的 keyCode 比较困难,所以 Vue 为最常用的按键提供了别名:
按键别名包括:
.enter
.tab
.delete
(捕获 “删除” 和 “退格” 键) .esc
.space
.up
.down
.left
.right
.ctrl
.shift
.meta
(windows 键,mac-command 键,)
Vue 推崇数据驱动视图的理念(数据交互,状态管理),但并非所有情况都适合数据驱动( DOM 的操作)。自定义指令就是一种有效的补充和扩展,不仅可用于定义任何的 DOM 操作,并且是可复用的。
Vue.directive(id,definition)
description: 传入两个参数,指令ID和定义对象,定义对象提供了一些钩子函数。
Vue.directive('my-directive', {
bind: function(){
// 指令第一次绑定到元素时调用,做绑定的准备工作
// 比如添加事件监听器,或是其他只需要执行一次的复杂操作
},
inserted: function(){
// 被绑定标签的父节点加入 DOM 时立即触发
},
update: function(){
// 根据获得的新值执行对应的更新
// 对于初始值也会调用一次
},
componentUpdated: function(){
// 指令所在组件的 VNode 及其子 VNode 全部更新后调用,一般使用 update 即可
},
unbind: function(){
// 做清理操作
// 比如移除bind时绑定的事件监听器
}
})
当只是用到 update 函数的时候,可以简化写法
Vue.directive('my-directive', function(){
// update 内的代码块
})
目前,对 5个钩子函数的触发时机有了初步的认识。存疑的是 bind 和 inserted、update 和 componentUpdated 的区别了。
<script>
// 注册一个全局自定义指令v-focus
Vue.directive('focus', {
// 当绑定元素插入到DOM中
inserted: function (el) {
// 聚焦元素
el.focus()
}
//聚焦不到元素
bind: function(el){
el.focus()
}
});
var app = new Vue({
el: '#app'
});
</script>
description: 以上例子中,如果将代码写在 bind
钩子函数内,el.focus()
并未生效,这是因为在 bind
钩子函数被调用时,虽然能够通过 bind 的第一个参数 el 拿到对应的 DOM 元素,但是此刻该 DOM 元素还未被插入进 DOM 树中,因此在这个时候执行 el.focus()
是无效的。
当 DOM 元素被插入进 DOM 树中时,inserted
钩子就会被调用,因此在 inserted 中执行 el.focus()
是可以生效的。
update: function (el, binding, vnode) {
console.log('update')
console.log(el.innerHTML) // Hello
},
componentUpdated: function (el, binding, vnode) {
console.log('componentUpdated')
console.log(el.innerHTML) // Hi
}
update
钩子函数触发时机是自定义指令所在组件的 VNode 更新时, componentUpdated
触发时机是指令所在组件的 VNode 及其子 VNode 全部更新后。此处使用 el.innerHTML
获取 data 值,从运行结果上看 update 和 componentUpdated 是 DOM 更新前和更新后的区别。
所有的钩子函数会被复制到实际的指令对象中,而这个指令对象将会是所有钩子函数的this上下文环境。指令对象上暴露了一些有用的公开属性。
el: 指令所绑定的元素,可以用来直接操作DOM 。
binding: 一个对象,包含以下属性:
name: 指令名,不包括 v- 前缀。
value: 指令的绑定值, 例如: v-directive="1 + 1"
,value 的值是 2
。
oldValue: 指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
expression: 绑定值的字符串形式。 例如 v-directive="1 + 1"
, expression 的值是 "1 + 1"
。
arg: 传给指令的参数。例如 v-directive:foo, arg
的值是 "foo"
。
modifiers: 一个包含修饰符的对象。 例如: v-directive.foo.bar
, 修饰符对象 modifiers 的值是 { foo: true, bar: true }
。
vnode: Vue 编译生成的虚拟节点。
oldVnode: 上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。
tips: 这些属性是只读的,不要修改它们。你也可以给指令对象附加自定义的属性,但是注意不要覆盖已有的内部属性。
eg: 定义一个使用了 binding 参数的指令,以下的是都是生成的虚拟的节点,插入到 div:#example3
节点中:
Vue.directive('parameter', {
bind: function(el, binding, vnode){
el.style.color = '#fff'
el.style.backgroundColor = binding.arg
el.innerHTML ='指令名name - '+ binding.name + '
' +'指令绑定值value - '+ binding.value + '
' +'指令绑定表达式expression - ' + binding.expression + '
'+'传入指令的参数argument - '+ binding.arg + '
'
},
});
var demo = new Vue({
el: '#example3',
data: {
message: 'hello,v-parameter'
}
})
// 运行结果为(实际运行结果背景色应该为红色,字体颜色应该为白色)
// "指令名 name-parameter"
// "指令绑定值 value-hello,v-parameter!"
// "指令绑定表达式 express-message"
// "传入指令的参数 argument-red"
定义这两个简单的指令
Vue.directive("block",{
inserted:function (el) {
el.style.display = "block";
}
})
Vue.directive("none",{
inserted:function (el) {
el.style.display = "none";
}
})
尽管Vue推崇数据驱动视图的理念,但并非所有情况都适合数据驱动。自定义指令就是一种有效的补充和扩展,不仅可用于定义任何的 DOM 操作,并且是可复用的。
eg: 很多时候我们会遇到图片加载慢的问题,那么,在图片未完成加载前,可以用随机的背景色占位,图片加载完成后才直接渲染出来。˙这里,用自定义指令可以非常方便的实现这个功能。
tips: 本例的调试需要在控制台上如下操作:Network
-> Offline Oline
-> Slow 3G
,在网络延迟的情况下更容易看出占位效果来。
Vue.directive('img',{
//DOM
inserted:function(el,binding){
var color =Math.floor(Math.random()*1000000);
el.style.backgroundColor = '#' + color;
var img = new Image();
img.src = binding.value;
img.onload = function(){
el.style.backgroundImage = 'url(' + binding.value + ')';
}
}
})
//此处图片路径为示意结果,为了能够更好的看出本段测试代码的效果,建议大家选择网上比较高清的图片
list:[
{url:'1.jpg'},
{url:'1.jpg'},
{url:'1.jpg'}
]
我们知道任何软件开发领域都可以分为四层:底层是原生的API,上层是通用框架,再上层是通用组件,最上层才是具体的业务代码。一个通用框架,必须搭配一套完整的通用组件,才能够很快的被广泛认可。
在前端开发领域,以前的通用框架是 jQuery,jQuery 以及基于 jQuery 构建的通用组件形成了一个庞大的生产系统。现在的通用框架是 Angular、React和Vue ,每个框架都需要基于自身构建新的组件库。自定义指令好就好在:原先的那些通用组件,无论是纯js的也好,基于 jQuery 的也好,都可以拿来主义直接吸收,而不需要改造或重构。
eg: 写文档通常会用到 highlight.js,我们可以直接将其封装为一个自定义指令,这样 highlight.js 就变成了 Vue 的一个新功能。
var hljs = require('highlight.js');
Vue.directive('highlight',function(el){
hljs.hightlightBlock(el);
})
<alert-menu
:menudata="menu"
:e="eventObj"
ref="menu"
v-on:menuEvent="handle">
</alert-menu>
运行结果:
输出
标签里的所有内容,而且按照 html 的高亮显示规则显示。
tips: 所以但凡遇到第三方插件如何与 Vue.js 集成的问题,都可以尝试用自定义指令实现。
很多时候,对于初学者来说,看完指令的使用会发现组件的使用和指令的自定义有几分相似之处。其实,并非如此,组件和指令完全不是一个层级上的概念。打个比方:组件是一个房子,它可以嵌套使用,房子里边又有窗户,门,桌子,床,柜子等这些子组件。而指令是附着在组件上的某种行为或者功能,门和窗户可以打开关闭,桌子可以折叠,柜子可以打开关上等等。以下是对于组件和指令的定义,希望能够让大家更清晰的理解:
根据需求的不同,我们要选择恰当的时机去初始化指令、更新指令调用参数以及释放指令存在时的内存占用等。一个健壮的库通常会包含:初始化实例、参数更新和释放实例资源占用等操作。
Vue.directive('hello', {
bind: function (el, binding) {
// 在 bind 钩子中初始化库实例
// 如果需要使用父节点,也可以在 inserted 钩子中执行
el.__library__ = new Library(el, binding.value)
},
update: function (el, binding) {
// 模版更新意味着指令的参数可能被改变,这里可以对库实例的参数作更新
// 酌情使用 update 或 componentUpdated 钩子
el.__library__.setOptions(Object.assign(binding.oldValue, binding.value))
},
unbind: function (el) {
// 释放实例
el.__library__.destory()
}
})
通过以上 Vue 指令的学习,以及诸多 demo 的实现,我们便可清晰的认识 Vue 指令了,并且能逐一解答我们最开始内心的疑虑了:
v-if
。bind
inserted
update
componentUpdated
unbind
五个钩子函数的理解,以及对钩子函数中el
binding
(属性值) vnode
oldVnode
四个参数的使用,只要这些内容能够熟练使用,我们便可以自己去定义适合各种开发场景的 Vue 指令了。