vue2.x之指令
一、什么是指令
Vue指令是Vue.js框架中的一种特殊语法,用于在HTML模板中直接操作DOM。通过使用Vue指令,我们可以对DOM元素进行动态的绑定、条件渲染、列表渲染等操作,实现数据驱动的交互效果。
⏰ 指令:
1. 本质就是自定义属性
2. Vue中指定都是以 v- 开头,不管是内置指令,还是自定义指令
二、内置指令
条件渲染指令
条件渲染相关指令主要包括v-if、v-else-if、v-else、v-show这几个,用于条件性地渲染、隐藏一块内容。
v-if 系列
⏰ 基于表达式值的真假性,来条件性地渲染元素或者模板片段。
我们来直接看代码会更好理解:
<div v-if="type === 'A'">Type A</div> <div v-else-if="type === 'B'">Type B</div> <div v-else>Default Type</div>
模板最终生成,我们可以这样理解:
function genThisHTML(scopeData) { // scopeData 为 Vue 实例里绑定的 data 数据 if (scopeData.type === "A") { return `<div>Type A</div>`; } else if (scopeData.type === "B") { return `<div>Type B</div>`; } else { return `<div>Default Type</div>`; } }
这样一看,是不是很清晰明朗。条件渲染指令其实是将常见的 Javascript 语法,通过 HTML 属性的形式附加在模板里,然后在 Vue 编译器编译的时候识别出来,然后匹配对应的执行逻辑而已。
⏰ key:使用
v-if指令有个需要注意的地方
Vue 中的虚拟 DOM 算法,在 Diff 过程中会优先使用现有的元素进行调整,而并非删除原有的元素再重新插入一个元素。这样的算法背景下,当我们绑定的数据发生变更时,可能会存在这样的情况:
<template v-if="type === 'phone'"> <input type="number" placeholder="Enter your phone" /> </template>
<template v-else> <input type="text" placeholder="Enter something" /> </template>
当我们的type从phone切换到其他值的时候,该<input>元素只会更新属性值type和placeholder,但原先输入的内容还在:
-
未绑定
key前,更新type前的 DOM 元素![]()
-
未绑定
key前,更新type后的 DOM 元素![]()
如果我们希望能精确命中对应的元素,可以通过绑定key的方式:
<template v-if="type === 'phone'"> <input type="number" placeholder="Enter your phone" key="phone" /> </template>
<template v-else> <input type="text" placeholder="Enter something" key="something-else" /> </template>
这种情况下,input 会根据key是否匹配,来控制是否重新渲染(即移除元素再重新插入)。可以理解为我们给有这样特殊需要的 input 添加了个性化的 ID,它不跟其他 input 共享页面中的 HTML 元素:
-
绑定
key后,更新type前的 DOM 元素![]()
-
绑定
key后,更新type后的 DOM 元素![]()
上面我们只讲了v-if、v-else-if、v-else这几个,条件渲染的指令还有一个v-show:
<div v-show="isShow">Something</div>
v-show
v-show和v-if不一样,v-if会在条件具备的时候才进行渲染,而v-show的逻辑是一定渲染,但在条件具备的时候才显示:
function genVShowHTML(scopeData) { // scopeData 为 Vue 实例里绑定的 data 数据 // 这里的 hide 类名具有样式 display: none; return `<div ${scopeData.isShow ? "" : 'class="hide"'}>Something</div>`; }
带有v-show的元素始终会被渲染并保留在 DOM 中。
⏰ v-show 与 v-if 区别:
一般来说,v-if有更高的切换开销(因为要不停地重新渲染),而v-show有更高的初始渲染开销。
因此,如果需要非常频繁地切换,则使用v-show较好;如果在运行时条件很少改变,则使用v-if较好。
列表渲染指令
列表渲染相关的指令主要是v-for这个指令,用来渲染列表。
v-for
v-for指令需要使用item in items形式的特殊语法,除了遍历数组以外,v-for还能遍历对象、数字:
-
遍历数组时<!-- 其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名,可选的第二个参数 index 为当前项的索引 --> <ul> <li v-for="(item, index) in items"> {{ index }}: {{ item.message }} </li> </ul>
模板最终生成,我们可以这样理解:
// 遍历数组的可以解析成这样 function genVForArrayHTML(scopeData) { // scopeData 为 Vue 实例里绑定的 data 数据 let htmlString = "<ul>"; scopeData.items.forEach((item, index) => { htmlString += `<li>${index}: ${item.message}</li>`; }); htmlString += "</ul>"; return htmlString; }
- 遍历对象时
模板最终生成,我们可以这样理解<!-- 在遍历对象时,会按 Object.keys() 的结果遍历 --> <!-- 其中 object 是源数据对象,而 value 则是被遍历的对象值,可选的第二个参数 key 为当前值的键名,可选的第三个参数 index 为当前项的索引 --> <div v-for="(value, key, index) in object"> {{ index }}.{{ key }}: {{ value }} </div>
// 遍历对象的可以解析成这样 function genVForObjectHTML(scopeData) { // scopeData 为 Vue 实例里绑定的 data 数据 let htmlString = ""; Object.keys(scopeData.object).forEach((key, index) => { htmlString += `<div>${index}.${key}: ${scopeData.object[key]}</div>`; }); return htmlString; }
- 还能遍历数字
模板最终生成,我们可以这样理解<p v-for="n in 10">{{n}}</p>
// 遍历数字的可以解析成这样 function genVForNumberHTML(num) { let htmlString = ""; for (let i = 1; i <= num; i++) { htmlString += `<p>${i}</p>`; } return htmlString; }
使用v-for指令有个需要注意的地方
⏰ key:
同样的,由于 Vue 中虚拟 DOM 的 Diff 方法和更新页面的方式,v-for指令渲染也会存在上面v-if一样的问题,即 input 这样的依赖临时 DOM 状态或子组件状态的元素,需要使用key来绑定使得可以重新渲染:
<div v-for="item in items" v-bind:key="item.id"> <!-- 内容 --> </div>
⏰ 数据更新检测:
在 Vue 中,当我们在data里绑定对象或者数组的时候,需要注意以下问题:
(1) data中的对象:Vue 无法检测到对象属性的添加或删除,当实例被创建时就已经存在于data中的属性才是响应式的,新增的属性等都不会触发视图的更新。
(2) data中的数组:除了特殊的数组操作如push()、pop()、shift()、unshift()、splice()、sort()、reverse()这些方法之外,数组中某个元素被替换、更新这种操作是无法触发视图更新的。
对于上面这两种情况,我们一般可以使用以下处理方式:
// 数组处理方法1: 返回新数组 this.items = [...this.items, newItem]; // 数组处理方法2: Vue.set 或 vm.$set Vue.set(vm.items, indexOfItem, newValue); vm.$set(vm.items, indexOfItem, newValue);
// 对象处理方法1: 返回新对象 this.object = { ...this.object, key: newValue }; // 对象处理方法2: Vue.set 或 vm.$set Vue.set(vm.object, key, value); vm.$set(vm.object, key, value);
⚠️注意:另外,当
v-if与v-for处于同一节点,v-for的优先级比v-if更高,这意味着v-if将分别重复运行于每个v-for循环中。(不推荐同时使用v-if和v-for,因为会对可读性产生影响。)
双向绑定指令
v-model
v-model指令在表单<input>、<textarea>及<select>元素上创建双向数据绑定。实际上v-model是语法糖,它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理:
<template> <input v-model="val" /> <!-- v-model 指令其实是下面的语法糖 --> <input :value="val" @input="updateValue" /> <!-- 也可以这么写 --> <input :value="val" @input="val = $event.target.value" /> </template>
<script> export default { data() { return { val: "" }; }, methods: { updateValue(event) { this.val = event.target.value; } } }; </script>
⏰ 使用 Tips:
当v-model使用在多选或者选择框上时,需要注意的是:
(1) 多选时,v-model会绑定到一个数组。
(2) 对于单选按钮,复选框及选择框的选项,v-model绑定的值通常是静态字符串。
(3) 复选框可以使用true-value和false-value来设置绑定的值。
<!-- 当选中时,`picked` 为字符串 "a" --> <input type="radio" v-model="picked" value="a" /> <!-- `toggle` 为 true 或 false --> <input type="checkbox" v-model="toggle" /> <!-- `toggle` 为 'yes' 或 'no' --> <input type="checkbox" v-model="toggle" true-value="yes" false-value="no" /> <!-- 当选中第一个选项时,`selected` 为字符串 "abc" --> <select v-model="selected"> <option value="abc">ABC</option> </select>
⏰ 修饰符:
除此之外,v-model还支持修饰符:
⏰ 自定义 v-model:
我们在很多场景下,需要对一些表单组件封装一些逻辑,如日期选择、常见的搜索功能等。前面也说过,v-model是语法糖:
<input v-model="something" /> <!-- 其实相当于下面的简写 --> <input :value="something" @input="something = $event.target.value" />
所以我们如果需要自定义v-model,需要做两个事情:
(1) 接受一个
valueprop。
(2) 在有新的值时触发input事件并将新值作为参数。
默认情况下,一个组件的v-model会使用valueProp 和input事件。但是诸如单选框、复选框之类的输入类型可能把value用作了别的目的。model选项可以避免这样的冲突:
Vue.component("my-checkbox", { model: { prop: "checked", // 绑定的值 event: "change" // 自定义事件 }, props: { checked: Boolean, // 这样就允许拿 `value` 这个 prop 做其它事了 value: String } // ... });
很多时候,我们会直接使用开源的库,例如 DatetimePicker。很多以前的工具库都依赖了 jQuery(说明它是真的好用),而开发业务的我们通常没有特别多的时间去一个个造轮子,我们会直接拿别人造好的轮子来用。这里我们来讲述下一个使用 select2 插件的自定义下拉组件的封装:
<template>
<div>
<select
class="form-control"
:placeholder="placeholder"
:disabled="disabled"
></select>
</div>
</template>
<script>
export default {
name: "Select2",
data() {
return {
select2: null
};
},
model: {
event: "change", // 使用change作为自定义事件
prop: "value" // 使用value字段,故这里其实不用写也可以
},
props: {
placeholder: {
type: String,
default: ""
},
options: {
type: Array,
default: []
},
disabled: {
type: Boolean,
default: false
},
value: null
},
watch: {
options(val) {
// 若选项改变,则更新组件选项
this.setOption(val);
},
value(val) {
// 若绑定值改变,则更新绑定值
this.setValue(val);
}
},
methods: {
setOption(val = []) {
// 更新选项
this.select2.select2({ data: val });
// 若默认值为空,且选项非空,则设置为第一个选项的值
if (!this.value && val.length) {
const { id, text } = val[0];
this.$emit("change", id);
this.$emit("select", { id, text });
this.select2.select2("val", [id]);
}
// 触发组件更新状态
this.select2.trigger("change");
},
setValue(val) {
this.select2.select2("val", [val]);
this.select2.trigger("change");
}
},
mounted() {
// 初始化组件
this.select2 = $(this.$el)
.find("select")
.select2({
data: this.options
})
.on("select2:select", ev => {
const { id, text } = ev["params"]["data"];
this.$emit("change", id);
this.$emit("select", { id, text });
});
// 初始化值
if (this.value) {
this.setValue(this.value);
}
},
beforeDestroy() {
// 销毁组件
this.select2.select2("destroy");
}
};
</script>
需要注意的是change自定义事件和value绑定值相关的内容,而关于 Select2 和 jQuery 相关的,大家可以去搜索一下对应的使用方式。而这个select2组件也被封装打包成一个 npm 依赖包了,对源码感兴趣的可以搜索v-select2-component的 npm 包来查看。
事件绑定指令
v-on
v-on指令是事件绑定指令,当用户需要点击,按下键盘,滚动鼠标等操作时,想要添加一些自己的逻辑处理时,就可以为特定的元素绑定一些特定的事件。
我们先在被vue实例挂载的标签div中,放入一个按钮为input标签,在标签内部写入v-on指令,冒号(:)后面就是事件名,等号(=)后面就是需要绑定的事件名称。
<div id="app"> <input type="button" value="事件绑定" v-on:事件名="方法名"> </div>
v-on的简写形式
在绑定的事件都是v-on开头,Vue其实提供了一个简洁的写法,用@符号替代v-on
<div id="app"> <input type="button" value="事件绑定" @click="todo"> <input type="button" value="事件绑定" @mouseenter="todo"> <input type="button" value="事件绑定" @dblclick="todo"> </div>
⏰ v-on事件函数中传入参数
1、如果事件直接绑定函数名称,那么默认会传递事件对象作为事件函数的第一个参数
<!-- 1、如果事件直接绑定函数名称,那么默认会传递事件对象作为事件函数的第一个参数 --> <button v-on:click='handle1'>点击1</button>
<!--Methods配置项中的方法-->> handle1: function(event) { console.log(event.target.innerHTML) },
2、如果事件绑定函数调用,那么事件对象必须作为最后一个参数显示传递,并且事件对象的名称必须是$event
<!-- 2、如果事件绑定函数调用,那么事件对象必须作为最后一个参数显示传递,并且事件对象的名称必须是$event --> <button v-on:click='handle2(123, 456, $event)'>点击2</button>
handle2: function(p, p1, event) { console.log(p, p1) console.log(event.target.innerHTML) this.num++; }
⏰ v-on事件修饰符
.stop:等同于JavaScript中的event.stopPropagation(),防止事件冒泡
.prevent:等同于JavaScript中的event.preventDefault(),防止执行预设的行为(如果事件可取消, 则取消该事件,而不停止事件的进一步传播)
.capture:与事件冒泡的方向相反,事件捕获由外到内
.self:只会触发自己范围内的事件,不包含子元素
.once:只会触发一次
-
.stop 防止事件冒泡
冒泡事件:嵌套两三层父子关系,然后每层都有点击事件,当点击最内层的子节点,就会触发从内至外子节点->父节点的点击事件。
<!-- HTML --> <div id="app"> <div class="outeer" @click="outer"> <div class="middle" @click="middle"> <button @click="inner">点击我(^_^)</button> </div> </div> <p>{{ message }}</p> </div> let app = new Vue({ el: '#app', data () { return { message: '测试冒泡事件' }; }, methods: { inner: function () { this.message = 'inner: 这是最里面的Button' }, middle: function () { this.message = 'middle: 这是中间的Div' }, outer: function () { this.message = 'outer: 这是外面的Div' } } })
防止冒泡事件的写法是:点击事件加上.stop相当于在每个方法中调用了等同于event.stopPropagation(),点击子节点不会捕获到父节点的事件。
<!-- HTML --> <div id="app"> <div class="outeer" @click.stop="outer"> <div class="middle" @click.stop="middle"> <button @click.stop="inner">点击我(^_^)</button> </div> </div> </div>
-
.prevent取消默认事件
.prevent等同于JavaScript的event.preventDefault(),用于取消默认事件。
默认事件指对DOM的操作会引起自动执行的动作,比如点击超链接的时候会进行页面的跳转,点击表单提交按钮时会重新加载页面等,使用".prevent"修饰符可以阻止这些事件的发生。
<a href="http://www.baidu.com" @click.prevent="doSomething"></a>
此时点击超链接不会进行页面的跳转。
-
.capture 捕获事件
捕获冒泡,即有冒泡发生时,有该修饰符的dom元素会先执行,如果有多个,从外到内依次执行,然后再按自然顺序执行触发的事件。
<!--template模版-->> <div style="width: 500px; height: 500px; background-color: aqua;" @click.capture="outer"> <div style="width: 200px; height: 200px; background-color: #a71d5d;" @click="middle"> <div style="width: 100px; height: 100px; background-color: #404040;" @click="inner"></div> </div> </div>
<!--methods配置项-->> methods: { inner: function () { console.log('this is the most inner one') }, middle: function () { console.log('this is inner') }, outer: function () { console.log('this is outer') } },
此时点击最内层div,结果如下:
![]()
多个捕获事件 :
<!--template模版-->> <div style="width: 500px; height: 500px; background-color: aqua;" @click.capture="outer"> <div style="width: 200px; height: 200px; background-color: #a71d5d;" @click.capture="middle"> <div style="width: 100px; height: 100px; background-color: #404040;" @click="inner"></div> </div> </div>
<!--methods配置项-->> methods: { inner: function () { console.log('this is the most inner one ') }, middle: function () { console.log('this is inner') }, outer: function () { console.log('this is outer') } },
点击最内层结果:
![]()
-
. self:
将事件绑定到自身,只有自身才能触发,通常用于避免冒泡事件的影响。
<!--template模版-->> <div style="width: 500px; height: 500px; background-color: aqua;" @click="outer"> <div style="width: 200px; height: 200px; background-color: #a71d5d;" @click.self="middle"> <div style="width: 100px; height: 100px; background-color: #404040;" @click="inner"></div> </div> </div> <!--methods配置项-->> methods: { inner: function () { console.log('this is the most inner one') }, middle: function () { console.log('this is inner') }, outer: function () { console.log('this is outer') } },
此时点击最内层:
-
.once 只执行一次点击
如果我们在@click事件上添加.once修饰符,只要点击按钮只会执行一次。
⏰v-on按键修饰符
在做项目中有时会用到键盘事件,在监听键盘事件时,我们经常需要检查详细的按键。Vue 允许为 v-on 在监听键盘事件时添加按键修饰符
记住所有的keyCode比较困难,所以Vue为最常用的键盘事件提供了别名:
.enter:回车键
.tab:制表键
.delete:含delete和backspace键
.esc:返回键
.space: 空格键
.up:向上键
.down:向下键
.left:向左键
.right:向右键
<!-- 只有在 `keyCode` 是 13 时调用 `vm.submit()` --> <input v-on:keyup.13="submit"> <!-- -当点击enter 时调用 `vm.submit()` --> <input v-on:keyup.enter="submit"> <!--当点击enter或者space时 时调用 `vm.alertMe()` --> <input type="text" v-on:keyup.enter.space="alertMe" > <script> var vm = new Vue({ el:"#app", methods: { } }) </script>
⏰ v-on 鼠标修饰符修饰符
鼠标修饰符用来限制处理程序监听特定的滑鼠按键。常见的有:
.left:鼠标左键
.middle:鼠标中间滚轮
.right:鼠标右键
⏰ v-on 自定义按键修饰符别名
在Vue中可以通过config.keyCodes自定义按键修饰符别名。例如,由于预先定义了keycode 116(即F5)的别名为f5,因此在文字输入框中按下F5,会触发prompt方法,出现alert。
<!-- HTML --> <div id="app"> <input type="text" v-on:keydown.f5="prompt()"> </div> Vue.config.keyCodes.f5 = 116; let app = new Vue({ el: '#app', methods: { prompt: function() { alert('我是 F5!'); } } });
属性绑定指令
v-bind
v-bind 是 Vue.js 中的一个重要指令,它用于动态地绑定一个或多个属性,或一个组件 prop 到表达式。
在默认情况下,如果没有使用 v-bind,属性值被当作纯字符串来处理。这意味着如果你尝试绑定一个对象或变量,它们会被直接转换为字符串。使用 v-bind,你可以绑定各种类型的值,包括对象,数组,数字,字符串,布尔值,甚至 JavaScript 表达式。
🌰例如,你可以使用 v-bind 来绑定 href 属性:
<a v-bind:href="url">链接</a>
在这个例子中,url 是一个变量,v-bind:href 会把 a 标签的 href 属性和 url 变量绑定在一起。当 url 变量的值变化时,href 属性也会更新。
还可以使用 v-bind 来绑定样式:
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
在这个例子中,activeColor 和 fontSize 是变量,v-bind:style 会把 div 标签的颜色和字体大小与这两个变量绑定在一起。当这两个变量的值变化时,颜色和字体大小也会更新。
⏰ v-bind简写
: 或者 . (当使用 .prop 修饰符)
值可以省略 (当 attribute 和绑定的值同名时) 3.4+
总的来说,v-bind 指令可以让你创建动态的、响应式的绑定,使得你的 UI 能够与数据保持同步。
⏰ 修饰符
.camel - 将短横线命名的 attribute 转变为驼峰式命名。 .prop - 强制绑定为 DOM property。3.2+ .attr - 强制绑定为 DOM attribute。3.2+
⏰ 示例
<!-- 绑定 attribute --> <img v-bind:src="imageSrc" /> <!-- 动态 attribute 名 --> <button v-bind:[key]="value"></button> <!-- 缩写 --> <img :src="imageSrc" /> <!-- 缩写形式的动态 attribute 名 (3.4+),扩展为 :src="src" --> <img :src /> <!-- 动态 attribute 名的缩写 --> <button :[key]="value"></button> <!-- 内联字符串拼接 --> <img :src="'/path/to/images/' + fileName" /> <!-- class 绑定 --> <div :class="{ red: isRed }"></div> <div :class="[classA, classB]"></div> <div :class="[classA, { classB: isB, classC: isC }]"></div> <!-- style 绑定 --> <div :style="{ fontSize: size + 'px' }"></div> <div :style="[styleObjectA, styleObjectB]"></div> <!-- 绑定对象形式的 attribute --> <div v-bind="{ id: someProp, 'other-attr': otherProp }"></div> <!-- prop 绑定。“prop” 必须在子组件中已声明。 --> <MyComponent :prop="someThing" /> <!-- 传递子父组件共有的 prop --> <MyComponent v-bind="$props" /> <!-- XLink --> <svg><a :xlink:special="foo"></a></svg>
内容渲染指令
内容渲染指令用来辅助开发者渲染 DOM 元素的文本内容。常用的内容渲染指令有如下 2 个:v-text,v-html
v-text
更新元素的文本内容。
v-text 指令用于操作纯文本,它会替代显示对应的数据对象上的值。当绑定的数据对象上的值发生改变,插值处的内容也会随之更新。
v-text 指令的简写形式为 {{}},只支持单向绑定,同时可以支持逻辑运算。
v-text 指令与 Vue.js 插值表达式语法略有区别,v-text 指令会覆盖元素中原本的内容,但是插值表达式只会替换自己的这个占位符。
🌰示例:
<span v-text="msg"></span> <!-- 等同于 --> <span>{{msg}}</span>
v-html
更新元素的 innerHTML。
v-html 的内容直接作为普通 HTML 插入—— Vue 模板语法是不会被解析的。如果你发现自己正打算用 v-html 来编写模板,不如重新想想怎么使用组件来代替。
🌰示例:
在某些时候我们不希望直接输出<a href='http://www.baidu.com'>百度一下</a>这样的字符串,而输出被html自己转化的超链接。此时可以使用v-html。
<!--template 模版-->>
<template>
<div>
<h2>插值语法:{{ url }}</h2>
<h2 v-html=" 'v-html指令:'+url"></h2>
</div>
</template>
<!--script data配置项-->
data() {
return {
url:"<a href='http://www.baidu.com'>百度一下</a>"
};
},

其它内置指令
v-slot
在Vue中, v-slot 指令用于定义插槽的模板内容。它用于在父组件中传递内容到子组件中的插槽。 v-slot 指令可以用于 标签或组件标签上,以便在子组件中使用插槽。
⏰ 使用 v-slot 指令时,可以使用以下两种语法:
- 缩写语法: # 符号表示 v-slot 指令,后面跟插槽名称。
<template #插槽名称> <!-- 插槽内容 --> </template>
- 完整语法: v-slot 指令后面跟着 : ,后面是插槽名称。
<template v-slot:插槽名称> <!-- 插槽内容 --> </template>
⏰ v-slot 指令的使用场景包括但不限于以下几种:
-
场景一:在组件中使用插槽,将父组件中的内容传递给子组件。
父组件
<template> <div> <child-component> <template v-slot:default> <!-- 插槽内容 --> <p>This is the content passed from the parent component.</p> </template> </child-component> </div> </template>
子组件
<template> <div> <slot></slot> </div> </template>
-
场景二:在子组件中使用具名插槽,根据插槽名称渲染不同的内容。
父组件<template> <div> <child-component> <template v-slot:header> <!-- 插槽内容 --> <h1>Header Content</h1> </template>
<template v-slot:body> <!-- 插槽内容 --> <p>Body Content</p> </template> </child-component> </div> </template>
子组件
<template> <div> <slot name="header"></slot> <slot name="body"></slot> </div> </template>
- 场景三:在子组件中使用作用域插槽,将子组件中的数据传递到父组件中进行渲染。
父组件
<template> <div> <child-component> <template v-slot:default="slotProps"> <!-- 插槽内容 --> <p>{{ slotProps.message }}</p> </template> </child-component> </div> </template>
子组件
<template> <div> <slot :message="message"></slot> </div> </template> <script> export default { data() { return { message: "Hello from child component!" }; } }; </script>
v-pre
v-pre 指令用于跳过这个元素和它的子元素的编译过程。
⏰ 用于包含大量静态内容的元素,提高渲染性能。
🌰示例:
<div v-pre> <p>{{ willNotBeInterpreted }}</p> </div>

v-once
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>
❗️ 从 3.2 起,你也可以搭配
v-memo的无效条件来缓存部分模板。
v-cloak
v-cloak 指令常用在插值表达式的标签中,用于解决当网络加载很慢或者频繁渲染页面时,页面显示出源代码的情况。
⏰ 所以为了提高用户的体验性,使用指令 v-cloak,搭配着 CSS 一起使用,在加载时隐藏挂载内容,等到加载完毕再显示渲染后的数据。
先通过CSS 属性选择器的方式,那就是拿到 v-cloak 这个属性,设置隐藏挂载内容。等到数据渲染完成后,v-cloak 属性会自动去除,页面会显示最终效果。v-cloak的使用
- 在插值语法所在的标签处加上v-cloak指令
<h1 v-cloak> {{ name }} </h1>
- 在 css 中设置 v-cloak 的属性为 display: none
<style type="text/css"> [v-cloak] { display: none; } </style>
三、自定义指令
除了内置指令以外,Vue 也支持自定义指令。通常我们会在需要给某个元素添加简单的事件处理逻辑的时候,会使用到自定义指令。
使用场景
我们来看看简单的一个表单自动聚焦的例子:
// 注册一个全局自定义指令 `v-focus` // 当然这里也支持通过 Vue 选项来进行局部注册指令 Vue.directive("focus", { // 当被绑定的元素插入到 DOM 中时 inserted: function(el) { // 聚焦元素 el.focus(); } });
然后我们可以在模板中任何元素上使用新的v-focus属性:
<!-- 当页面加载时,该元素将获得焦点 --> <input v-focus />
指令的生命周期函数
export default { // 这个函数只会被调用一次,就是绑定元素的时候,可以在这里做初始化工作 bind(el,binding,vnode,oldVode){ // el 绑定的 dom 元素 // bingding 一些参数,很有用的,建议去读一下官方文档,传递的一些参数也在里面 // vnode 虚拟 dom // oldVode 上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用 }, // 被绑定的元素 插入父元素的时候调用该函数--仅保证父节点存在 inserted(el,binding,vnode,oldVode){ }, // 所在组件的 vnode 更新时调用 update(el,binding,vnode,oldVode){ }, // 指令所在的组件的 vnode 及其 子组件的 vnode 全部更新之后调用 componentUpdated(el,binding,vnode,oldVode){ }, // 指令与元素解绑时调用,只调用一次 unBind(el,binding,vnode,oldVode){ } }
❗️这里是把指令书写在单独的js文件中
定义自定义指令
⏰ 自定义指令-局部
- 第一种:直接在 directives 中书写自定义指令
<template> <div> 用户名: <input type="text" v-default-value="val"> </div> </template> <script> export default { data() { return { val: 'username' } }, // 自定义指令 directives: { defaultValue: function (el, binding) { console.log(binding); el.value = binding.value // binding.value 的值 保存的是指令的 等号 后面的属性值 } } }; </script>
-
第二种:指令书写在单独的js文件中
指令书写在src新建一个directives文件夹,在里面新建一个focus.js文件。
export default { // 被绑定元素插入父节点的调用 inserted(el) { el.focus() }, }
组件:在组件中引入书写指令的 js 模块,并且在 directives 选项中注册即可使用。
<template> <div> 用户名: <input v-focus type="text"> </div> </template> <script> import focus from "@/utils/directives/focus"; export default { directives: { focus, } }; </script>
-
第三种:动态指令参数
写法:v-mydirective:[argument]="value"
就是可以给指令传递一些参数,argument是指令的参数,value是指令的属性值,argument 可以在binding参数的arg中拿到,value可以在binding.value中拿到,value可以是基础数据类型,也可以是复杂数据类型,数组,对象等。export default { bind(el, binding) { console.log(binding); el.style[binding.arg] = binding.value + "px" } }组件:在组件中引入书写指令的 js 模块,并且在 directives 选项中注册即可使用。
<template> <div v-fixed:[direction]="300" class="box"></div> </template> <script> import fixed from "@/utils/directives/fixed"; export default { data() { return { direction: 'right', } }, directives: { fixed, } }; </script> <style lang="scss" scoped> .box { height: 200px; width: 200px; background-color: skyblue; position: fixed; } </style>
⏰ 自定义指令-全局
-
直接在 man.js 中定义指令
import Vue from 'vue' import App from './App.vue' import router from './router' import store from './store' // 这里定义的是全局指令 Vue.directive("font-color", { bind(el, binding) { el.style.color = binding.value } }) new Vue({ router, store, render: h => h(App) }).$mount('#app')
使用:全局指令无需注册,直接在组件中使用即可。
<template> <div v-font-color="colorVal"> 文字文字 </div> </template> <script> export default { data() { return { colorVal: '#00ff00' }; } }; </script>
函数式自定义指令和对象式自定义指令
自定义指令可以按照两种方式去配置:函数式和对象式
⏰ 函数式自定义指令
函数式指令就是将指令定义成回调函数,这样在触发的时候就会触发回调函数,回调函数包含两个参数:el和binding。
-
基本写法
<template> <div class="test-wrapper"> <div v-test>自定义指令</div> </div> </template>
<script> export default { data () { return { }; }, directives: { test(el, binding) { console.log('el', el); console.log('binding', binding); } } };
根据打印信息,我们可以看到el表示dom元素,binding表示包含元素有关的所有属性的对象
![]()
-
传递参数
指令也是可以传递参数的
<template> <div class="test-wrapper"> <div v-test="number">自定义指令</div> <div>number的值: {{number}}</div> <button @click="addNumber">增加number</button> </div> </template> <script> export default { data () { return { number: 1 }; }, directives: { test(el, binding) { console.log('el', el); console.log('binding', binding); } }, methods: { addNumber() { this.number++; } } }; </script>
参数binding的value属性可以拿到传递过来的参数。效果如下:
![]()
如果这个参数被组件中别的部分修改了,那么指令也会被执行,而且能拿到最新的值。比如增加number的值,参数也变了。效果如下:
![]()
⏰ 对象式自定义指令
对象式也就是在指定定义对象中添加几个钩子函数,这些钩子都是可选的。
- bind: 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置
- inserted: 被绑定元素插入父节点时调用(仅保证父节点存在,但不一定被插入文中)
- update:所在组件的VNode更新时调用,但是可能发生在其子VNode更新之前。指令的值可能发生变化也有可能没有。
- componentUpdated:指令所在组件的VNode及其子VNode全部更新后调用
- unbind: 只调用一次,指令与元素解绑时调用
这些钩子函数具有以下参数:
- el: 指令所绑定的元素,可以用来直接操作DOM
- binding: 一个包含元素属性的对象
- vnode: Vue编译生成的虚拟节点
- oldVnode: 上一个虚拟节点,仅在update和componentUpdated钩子中使用
<template> <div class="test-wrapper"> <div>number的值: {{number}}</div> <button @click="addNumber">增加number</button> <input type="text" v-test="number"> </div> </template> <script> export default { data () { return { number: 1 }; }, directives: { test: { bind(el, binding) { el.value = binding.value; }, inserted(el, binding) { el.focus(); }, update(el, binding) { el.value = binding.value; } } }, methods: { addNumber() { this.number++; } } }; </script>
初始化的时候,会执行bind和inserted,效果如下:

改变number的值,会执行update,效果如下:

小结
函数式定义指令相当于是简写版,只会在初始化和模板变化(解析)的时候被触发,而对象式可以根据钩子函数在特定的时候执行对应的操作
指令定义的时候不加v-, 但使用时要加v-
指令名如果是多个单词,要使用kebab-case命名方式,不要用驼峰命名。





浙公网安备 33010602011771号