> 文章列表 > vue2.x指令总结与自定义指令

vue2.x指令总结与自定义指令

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 并抛出不同的事件:

  • texttextarea 元素使用 valueinput 事件;

    <template><input v-model="message" /><p>{{ message }}</p>
    </template>
    <script>
    export default {data() {return {message: ''}}
    }
    </script>
    
  • checkboxradio 使用 checkedchange 事件;

    <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-iffalse时进行渲染。

<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-ifelse-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-elsev-else-if 也必须紧跟在带 v-if 或者 v-else-if 的元素之后。

v-show

v-showv-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-showfalse时,元素其实已经存在于DOM树中,只不过给元素添加了一个css中的display: none属性,使得该元素并不会被用户感知。而v-iffalse时,该元素并不会添加到DOM树中。

注意,v-show 不支持 <template> 元素,也不支持 v-else

v-ifv-show的区别

v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换(display的值在noneblock之间切换)。
一般来说,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-forv-if

不推荐在同一元素上使用 v-ifv-for

v-ifv-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:只调用一次,指令与元素解绑时调用。

上面的几个钩子方法都接收一样的参数:

  1. el:指令所绑定的元素,可以用来直接操作 DOM
  2. binding: 一个对象,包含以下几个属性:
    • name: 指令名,不包括v-前缀
    • value:指令的绑定值
    • oldValue: 指令绑定的前一个值,仅在 updatecomponentUpdated 钩子中可用。无论值是否改变都可用。
    • 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 来进行。