1.vue常用内置指令介绍
1.1 渲染控制类指令
1.1.1 v-if
- 作用:用来控制是否渲染页面元素。当传入表达式的结果为true时,渲染该元素。当传入表达式的结果为false时,不渲染该元素。
- 例子:
<html>
<header>
</header>
<body>
<div id="root">
<p v-if="showMessage">{{ message1 }}</p>
<p v-else>{{ message2 }}</p>
<button @click="showMessage = !showMessage">点我显示消息</button>
</div>
</body>
<script src="./js/vue.global.js"></script>
<script>
let vue = Vue.createApp({
data() {
return {
message1: '我是码小飞',
message2: '我不是码小飞',
showMessage: false
}
}
});
vue.mount('#root')
</script>
</html>
v-if还可以配合v-else-if指令进行多分支条件渲染控制,用法与v-if类似在此不在演示。
1.1.2 v-show
- 作用:用来控制页面元素是否显示。如果表达式结果为true,则显示该元素。如果表达式结果为false,则不显示该元素。
- 例子:
<html>
<header>
</header>
<body>
<div id="root">
<p v-show="showMessage">{{ message }}</p>
<button @click="showMessage = !showMessage">点我显示消息</button>
</div>
</body>
<script src="./js/vue.global.js"></script>
<script>
let vue = Vue.createApp({
data() {
return {
message: '我是码小飞',
showMessage: false,
}
}
});
vue.mount('#root')
</script>
</html>
v-show与v-if的异同:
v-show和v-if都可以用来控制页面元素是否显示,但实现原理也有不同。
v-if是控制页面元素是否真的渲染,当表达式结果发生变化时,页面dom元素会被重新渲染。
v-show是用来控制页面元素是否显示,不论表达式结果是真是假,页面元素都会被渲染好,只是元素的display属性会再表达式结果发生变化时进行调整。
当我们页面元素初次需要渲染很多时,建议使用v-if来减少初次渲染的元素数量来提升页面性能(因为判断条件结果为false的元素不会被渲染了)
当我们页面元素较少但需要频繁地进行显示/隐藏的切换时,建议使用v-show来优化页面性能(因为v-show显示/隐藏页面元素的成本较小)
1.1.3 v-for
- 作用:用来渲染页面的列表元素,会将传入的属性循环遍历显示在页面上。v-for既可以遍历一个列表也可以遍历一个对象中的所有属性。
- 例子:
<html>
<header>
</header>
<body>
<div id="root">
<!--遍历数组-->
<p v-for="(item, index) in userList" :key="item.id">
{{index}}-{{item.id}}-{{item.userName}}-{{item.age}}
</p>
<!--遍历对象-->
<p v-for="(value, key) of goods">
{{key}}-{{value}}
</p>
</div>
</body>
<script src="./js/vue.global.js"></script>
<script>
let vue = Vue.createApp({
data() {
return {
userList: [
{id: 'u1001', userName: 'tom', age: 28},
{id: 'u1002', userName: 'jerry', age: 25}
],
goods: {
id: 10001,
name: '水果手机',
prince: 9999
}
}
}
});
vue.mount('#root')
</script>
</html>
注意事项:
- v-for中使用in或of遍历的效果是一样的,可以任意选择
- 使用v-for中的得到的item、index、value、key等变量均为局部变量,超出v-for作用域范围是不生效的
- v-for支持嵌套遍历,即一个v-for遍历中可以嵌套另一个v-for
- v-for渲染出的列表,如果后期需要对列表进行修改(特别是增删),最好对每个dom节点设置属性key,且key的值需要在列表中是唯一的。key是vue用来对dom元素进行状态管理的一个重要属性,用来帮助vue来区分页面的dom元素,在页面中不可见。
- 尽量不要使用index作为key(因为index是可以变化的,当元素增或删都会引起下标的变化)。使用index作为key时,列表中元素顺序更新后,可能会引起vue的模板渲染错误,因为一些存有临时状态的元素会错乱(比如input标签),还会导致页面元素更新速度慢。引起问题的原因是由于vue更新dom时的diff比较算法导致的,感兴趣可以自行了解一下。
1.2 元素控制类指令
1.2.1 v-text
- 作用:用来更新元素的文本内容
- 用法:
<span v-text="msg"></span>
<!-- 等同于 -->
<span>{{msg}}</span>
1.2.2 v-html
- 作用:更新元素内的innerHTMl
- 用法:
<div v-html="html"></div>
注意事项:v-html只能对可信内容使用,不能对用户的输入内容使用v-html,因为这可能会导致XSS攻击
1.2.3 v-once
- 作用:仅渲染元素和组件一次,并跳过之后的更新。可以用来记录元素最开始的状态。
- 用法:
<!-- 单个元素 -->
<span v-once>This will never change: {{msg}}</span>
<!-- 带有子元素的元素 -->
<div v-once>
<h1>comment</h1>
<p>{{msg}}</p>
</div>
<!-- 组件 -->
<MyComponent v-once :comment="msg" />
<!-- `v-for` 指令 -->
<ul>
<li v-for="i in list" v-once>{{i}}</li>
</ul>
1.2.4 v-cloak
- 作用:用于隐藏尚未完成编译的 DOM 模板。我们在页面上编写的插值语法、指令等内容会在vue开始工作后才会被解析。但当我们的页面引入的css或js文件较大时,vue可能还来不及工作,此时页面上的插值语法等内容会被直接渲染在页面上,导致我们的页面出现了未处理的DOM元素,此时我们可以使用v-cloak来隐藏这些未处理的DOM元素
- 用法:
<div v-cloak>
{{ message }}
</div>
[v-cloak] {
display: none;
}
当vue开始工作后,会将我们dom元素的v-cloak指令给去除掉。使dom元素显现出来。
1.2.5 v-pre
- 作用:跳过该元素及其所有子元素的编译。当我们在元素上设置此指令后,vue会将元素中的所有内容原封不动的渲染出来
- 用法:
<span v-pre>{{ this will not be compiled }}</span>
上面例子中{{}}并不会被vue当作插值语法解析
2.自定义指令
在使用vue框架时,我们除了可以使用已经为我们封装好的内部指令,还可以根据实际需求封装我们的自定义指令。
当我们有许多共同的dom元素操作时,就可以这些操作封装成一个自定义指令,来简化我们的代码,这也是vue官方推荐我们的做法。
2.1 局部指令
我们在组件中使用directives关键字来声明自定义指令,用例如下:
<html>
<header>
</header>
<body>
<div id="root">
<p v-common-pre="'码小飞'">自定义指令</p>
</div>
</body>
<script src="./js/vue.global.js"></script>
<script>
let vue = Vue.createApp({
data() {
return {
}
},
directives: {
'common-pre': {
mounted(el, binding, vnode, prevVnode) {
el.innerText = binding.value + "-" + el.innerText;
},
updated(el, binding, vnode, prevVnode) {
console.log('updated');
el.innerText = binding.value + "-" + el.innerText;
}
}
}
});
vue.mount('#root')
</script>
</html>
在上述例子中,我们创建了一个名为common-pre的自定义指令。指令接收一个前缀值,作用是在使用指令的dom元素内将前缀拼接到dom元素的文本内容里。在自定义指令中我们声明了mounted、updated两个方法,vue会在处理到指令声明的html标签时自动调用我们的方法。mounted会在元素挂载到页面时被调用,updated会在元素所在模板发生更新时被调用。
注意事项:
- 我们在使用html标签中使用指令时需要加入v-的前缀,否则vue会把指令当作普通的attribute,并不会做处理。
- 指令定义推荐使用kebab-case的命名法则,不推荐使用camelCase命名法则,因为vue在处理html上的指令时都会按kebab-case命名法去处理。
上面的例子是自定义指令的完整写法,如果我们只声明mounted、updated方法,还可以使用简写方法
directives: {
'common-pre'(el, binding, vnode, prevVnode){
el.innerText = binding.value + "-" + el.innerText;
}
}
指令参数还可以传递对象字面量,如{color:'white'},感兴趣的同学可以自己试一试
2.2 全局指令
上面的例子是注册局部指令,局部指令的作用域只在其声明的组件内可以使用。当我们想要在整个应用中声明指令时,需要使用组件的directive方法进行注册,指令的声明方法与局部指令一样,注册后的指令即可在整个应用中使用。
const app = createApp({})
// 使 v-focus 在所有组件中都可用
app.directive('focus', {
/* 在此处编写组件的声明,简写和完整写法都可以 */
})
2.3 指令的钩子函数和函数参数
除了上述例子中mounted和updated两个钩子函数外,指令还有一些钩子函数
const myDirective = {
// 在绑定元素的 attribute 前
// 或事件监听器应用前调用
created(el, binding, vnode, prevVnode) {
// 下面会介绍各个参数的细节
},
// 在元素被插入到 DOM 前调用
beforeMount(el, binding, vnode, prevVnode) {},
// 在绑定元素的父组件
// 及他自己的所有子节点都挂载完成后调用
mounted(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件更新前调用
beforeUpdate(el, binding, vnode, prevVnode) {},
// 在绑定元素的父组件
// 及他自己的所有子节点都更新后调用
updated(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件卸载前调用
beforeUnmount(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件卸载后调用
unmounted(el, binding, vnode, prevVnode) {}
}
当我们需要对指令行为进行细粒度控制时,就要根据实际情况选择合适的钩子函数进行实现。比如当我们需要在获取元素的焦点,就不能把获取焦点的代码写在created钩子内,而需要写到mounted钩子内。
所有钩子函数都传递了四个参数,参数作用分别如下:
- el:代表指令所在的真实dom元素
- binding:代表指令的绑定对象,其内部属性如下
value:传递给指令的值。例如在 v-my-directive="1 + 1" 中,值是 2。
oldValue:之前的值,仅在 beforeUpdate 和 updated 中可用。无论值是否更改,它都可用。
arg:传递给指令的参数 (如果有的话)。例如在 v-my-directive:foo 中,参数是 "foo"。
modifiers:一个包含修饰符的对象 (如果有的话)。例如在 v-my-directive.foo.bar 中,修饰符对象是 { foo: true, bar: true }。
instance:使用该指令的组件实例。
dir:指令的定义对象。
- vnode:代表绑定元素的底层 VNode。
- prevNode:代表之前的渲染中指令所绑定元素的 VNode。仅在 beforeUpdate 和 updated 钩子中可用。