vue2.x指令总结与自定义指令
指令
渲染相关
v-text
用于更新元素的 textContent
<div v-text="text"></div>
<script>
export default {data() {return {text: 'textContent'}}
}
</script>
v-html
用于更新元素的 innerHTML
<div v-html="html"></div>
<script>
export default {data() {return {html: '<span>使用v-html进行渲染</span>'}}
}
</script>
v-bind
当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM
,可以使用:
缩写。
<a v-bind:href="url">使用v-bind</a><!-- 缩写 -->
<a :href="url">使用v-bind</a>
<script>
export default {data() {return {url: 'https://vuejs.bootcss.com/api/#v-bind'}}
}
</script>
v-on
用于绑定事件,可以使用@
缩写。
<button v-on:click="handle">v-on使用</button><!-- 缩写 -->
<button @click="handle">v-on使用</button>
用在普通元素上时,只能监听原生 DOM
事件。用在自定义元素组件上时,也可以监听子组件触发的自定义事件。
<my-component @my-event="handle"></my-component>
在监听原生 DOM
事件时,方法以事件为唯一的参数。如果使用内联语句,语句可以访问一个 $event
属性:v-on:click="handle('ok', $event)"
。
<!-- 内联语句 -->
<my-component @my-event="handle('ok', $event)"></my-component>
v-model
用于在表单元素上创建双向数据绑定,随表单控件类型不同而不同。
用 v-model
指令在表单 <input>
、<textarea>
及 <select>
元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇,但 v-model
本质上不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。
v-model
在内部为不同的输入元素使用不同的 property
并抛出不同的事件:
-
text
和textarea
元素使用value
和input
事件;<template><input v-model="message" /><p>{{ message }}</p> </template> <script> export default {data() {return {message: ''}} } </script>
-
checkbox
和radio
使用checked
和change
事件;<template><input v-model="message" type="checkbox" /><p>{{ isCheck }}</p><input type="radio" value="radio1" v-model="value"><input type="radio" value="radio2" v-model="value"><span>{{value}}</span> </template><script> export default {data() {return {isCheck: true,value: 'radio1'}} } </script>
-
select
字段将value
作为prop
并将change
作为事件。<template><select v-model="selected"><option disabled value="">请选择</option><option>A</option><option>B</option><option>C</option></select><span>Selected: {{ selected }}</span> </template> <script> export default {data() {return {selected: ''}} } </script>
如果
v-model
表达式的初始值未能匹配任何选项,<select>
元素将被渲染为“未选中”状态。在 iOS 中,这会使用户无法选择第一个选项。因为这样的情况下,iOS
不会触发change
事件。因此,更推荐像上面这样提供一个值为空的禁用选项。
v-if
用于条件性地渲染内容,只有当值为true
时才会渲染。
<div v-if="show">v-if的使用</div>
<script>
export default {data() {return {show: true}}
}
</script>
当想要切换多个元素时,可以使用template
标签,template
并不会被渲染成一个DOM
。
<template><div id="app">v-if渲染的分组内容与此标签同级</div><template v-if="show"><h1>使用template渲染分组</h1><span>v-if的使用</span></template>
</template>
<script>
export default {data() {return {show: true}}
}
</script>
此时的渲染结果为:
<div id="app">v-if渲染的分组内容与此标签同级</div>
<h1>使用template渲染分组</h1>
<span>v-if的使用</span>
可以看到虽然需要渲染的分组是处于template
层级下,但是由于template
并不会解析生成一个DOM
,所以分组内的元素跟模板中template
的层级是一样的。
v-else
使用v-else
可以添加一个else
块,当v-if
为false
时进行渲染。
<div v-if="show">v-if的使用</div>
<div v-else>v-else的使用</div>
<script>
export default {data() {return {show: false}}
}
</script>
v-else
元素必须紧跟在带 v-if
或者 v-else-if
的元素的后面,否则它将不会被识别。
v-else-if
v-else-if
,顾名思义,充当 v-if
的 else-if
块,可以连续使用。
<template><div v-if="type === 'A'">A</div><div v-else-if="type === 'B'">B</div><div v-else-if="type === 'C'">C</div><div v-else>Not A/B/C</div>
</template>
<script>
export default {data() {return {type: 'A'}}
}
</script>
类似于 v-else
,v-else-if
也必须紧跟在带 v-if
或者 v-else-if
的元素之后。
v-show
v-show
跟v-if
的用法大致一样。根据v-show
的值来判断是否要展示元素。
<div v-if="show">v-show的使用</div>
<script>
export default {data() {return {show: true}}
}
</script>
渲染结果:
<div style="display: none">v-show的使用</div>
跟v-if
不同的是,当v-show
为false
时,元素其实已经存在于DOM
树中,只不过给元素添加了一个css
中的display: none
属性,使得该元素并不会被用户感知。而v-if
为false
时,该元素并不会添加到DOM
树中。
注意,v-show
不支持 <template>
元素,也不支持 v-else
。
v-if
和v-show
的区别
v-if
是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if
也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show
就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于CSS
进行切换(display
的值在none
跟block
之间切换)。
一般来说,v-if
有更高的切换开销,而v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用v-show
较好;如果在运行时条件很少改变,则使用v-if
较好。
v-for
v-for
指令基于一个数组来渲染一个列表。v-for
指令需要使用 item in items
形式的特殊语法,其中 items
是源数据数组,而 item
则是被迭代的数组元素的别名。
<ul><li v-for="item in items" :key="item.value">{{ item.value }}</li>
</ul>
<script>
export default {data() {return {items: [{value: 1},{value: 2}]}}
}
</script>
渲染结果:
<ul><li>1</li> <li>2</li>
</ul>
在v-for
块中,我们可以访问所有父作用域的值。v-for 还支持一个可选的第二个参数,即当前项的索引。
<ul><li v-for="(item, index) in items" :key="item.value">{{ index }} - {{ item.value }}</li>
</ul>
<script>
export default {data() {return {items: [{value: 1},{value: 2}]}}
}
</script>
也可以用 of
替代 in
作为分隔符,因为它更接近 js
迭代器的语法
<ul><li v-for="(item, index) of items" :key="item.value">{{ index }} - {{ item.value }}</li>
</ul>
在v-for
中使用对象
此时可以传入三个参数
value
: 值key
: 键名index
: 索引
<ul><li v-for="(value, key, index) in obj" :key="item.value">{{ index }} - {{ key }} - {{ value }}</li>
</ul>
<script>
export default {data() {return {obj: {a: 1,b: 2}}}
}
</script>
渲染结果为:
<ul><li>0 - a - 1</li><li>1 - b - 2</li>
</ul>
在遍历对象时,会按
Object.keys()
的结果遍历,但是不能保证它的结果在不同的JavaScript
引擎下都一致。
key
的作用
可以看到上面的例子中都有:key="xxx"
,这个key
究竟有什么作用呢?
- 主要是用在
vue
的虚拟Dom
算法,在新旧nodes
对比时辨识VNodes
,相当于唯一标识ID
vue
会尽可能的高效的渲染元素,通常恢复已有的元素而不是从头开始进行渲染,因此使用key
值可以提高渲染效率,同理,改变某一个元素的key
值会使该元素重新被渲染。
v-for
与v-if
不推荐在同一元素上使用
v-if
和v-for
当v-if
和 v-for
处于同一节点,v-for
的优先级比 v-if
更高,这意味着 v-if
将分别重复运行于每个 v-for
循环中。
v-pre
跳过这个元素和它的子元素的编译过程。可以用来显示原始大括号内表达式。跳过大量没有指令的节点会加快编译。
<span v-pre>{{ this will not be compiled }}</span>
渲染结果:
<span>{{ this will not be compiled }}</span>
v-cloak
这个指令保持在元素上直到关联实例结束编译。和 CSS
规则如 [v-cloak] { display: none }
一起用时,这个指令可以隐藏未编译的大括号内表达式直到实例准备完毕。防止刷新页面,网速慢的情况下出现{{ message }}
等数据格式
<div v-cloak>{{message}}</div>
v-once
只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。
<template><input v-model="message" /><p>{{ message }}</p><div v-once>{{ message }}</div>
</template>
<script>
export default {data() {return {message: 'message'}}
}
</script>
随着input
框内内容的改变,携带v-once
指令的元素并不会更新。
v-slot
对组件的扩展,通过slot
插槽向组件内部指定位置传递内容,通过slot
可以父子传参。使用#
缩写。
<!-- 父组件 -->
<Child v-slot:header><h1>从父组件中传递过来</h1>
</Child><!-- 子组件 -->
<div><slot name="header"></slot>
</div>
事件相关
v-on
v-on
指令监听 DOM
事件,并在触发时运行一些 js
代码。
<div><div v-on:click="counter+=1">add 1</div><span>{{counter}}</span>
</div>
<script>
export default {data() {return {counter: 1}}
}
</script>
然而许多事件处理逻辑会更为复杂,所以直接把 js
代码写在 v-on
指令中是不可行的。因此 v-on
还可以接收一个需要调用的方法名称。
<div><div v-on:click="add">add 1</div><span>{{counter}}</span>
</div>
<script>
export default {data() {return {counter: 1}},methods: {add() {this.counter++}}
}
</script>
除了直接绑定到一个方法,也可以在内联 js
语句中调用方法。
<div><div v-on:click="add(2)">add 2</div><span>{{counter}}</span>
</div>
<script>
export default {data() {return {counter: 1}},methods: {add(num) {this.counter += num;}}
}
</script>
除此之外,vue
提供了一个$event
变量用于访问原始的 DOM
事件。
<div><div v-on:click="add(2, $event)">add 2</div><span>{{counter}}</span>
</div>
<script>
export default {data() {return {counter: 1}},methods: {add(num, e) {console.log(e)this.counter += num;}}
}
</script>
自定义指令
在vue3
中创建的指令具有一组生命周期钩子:
app.directive('my-directive', {// 指令具有一组生命周期钩子:// 在绑定元素的 attribute 或事件监听器被应用之前调用created() {},// 在绑定元素的父组件挂载之前调用beforeMount() {},// 在绑定元素的父组件挂载之后调用mounted() {},// 在包含组件的 VNode 更新之前调用beforeUpdate() {},// 在包含组件的 VNode 及其子组件的 VNode 更新之后调用updated() {},// 在绑定元素的父组件卸载之前调用beforeUnmount() {},// 在绑定元素的父组件卸载之后调用unmounted() {}
})// 注册 (函数指令)
app.directive('my-directive', () => {
// 这将被作为 `mounted` 和 `updated` 调用
})
而vue2
创建的指令对象只有以下几个钩子:
-
bind
:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
-
inserted
:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
-
update
:所在组件的VNode
更新时调用,但是可能发生在其子VNode
更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新。
-
componentUpdated
:指令所在组件的VNode
及其子VNode
全部更新后调用。
-
unbind
:只调用一次,指令与元素解绑时调用。
上面的几个钩子方法都接收一样的参数:
el
:指令所绑定的元素,可以用来直接操作DOM
binding
: 一个对象,包含以下几个属性:name
: 指令名,不包括v-
前缀value
:指令的绑定值oldValue
: 指令绑定的前一个值,仅在update
和componentUpdated
钩子中可用。无论值是否改变都可用。expression
:字符串形式的指令表达式。例如v-my-directive="1 + 1"
中,表达式为"1 + 1"
。arg
: 传给指令的参数,可选。例如v-my-directive:foo
中,参数为"foo"
。modifiers
: 一个包含修饰符的对象。例如:v-my-directive.foo.bar
中,修饰符对象为{ foo: true, bar: true }
.
除了
el
之外,其它参数都应该是只读的,切勿进行修改。如果需要在钩子之间共享数据,建议通过元素的dataset
来进行。