我们都知道在vue中主要通过组件的方式来实现代码的复用和抽象。但是有些情况下,我们还是需要对普通的DOM元素进行底层操作,那这个时候我们就需要用的vue的指令系统来完成或实现不同的功能。
那vue指令除了内置了16个核心功能的指令以外,还允许我们创建自定义的指令(全局指令、局部指令)。
自定义指令也能帮助我们实现部分代码功能的复用。
v-xxx:不带参数的指令v-xxx="value":带变量值参数传入指令中v-xxx="'string'":带字符串参数传入指令中v-xxx:[argument]="value":增加动态argument参数到指令中v-xxx:argument.modifier="value":增加修饰符到指令中以上为我们常用的使用方式,具体细节我们在实现自定义指令中会具体这几种方式的使用。
v-textv-htmlv-prev-ifv-elsev-else-ifv-showv-forv-oncev-memo: 3.2版本新增指令v-modelv-bindv-onv-slotv-cloakv-is : 已在 3.1.0 版本中被废弃。请换用带有 vue: 前缀的 is attribute。下面我们来详细的介绍部分指令的作用。
v-text<p v-text="msg"></p>
<!-- 等价于 -->
<p>{{ msg }}</p>
v-html<template>
<div v-html="vHtml"></div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
const vHtml = ref(
'<h2>这是v-html的h2标签啊</h2><h3 style="color: red;">这是v-html的h3标签啊,红色字体</h3>'
);
</script>
v-pre跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签。跳过大量没有指令的节点会加快编译。
通俗点说:比如你显示{{ xxx }}这样的文字,如果带上v-pre指令,编译的时候就不会去解析这个节点,你原本是什么样的就给你展示成什么样的
<p v-pre>{{ this will not be compiled }}</p>
v-showv-show该节点会一直存在,只是切换元素的 display
v-once只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。
v-memo3.2新增指令
记住一个模板的子树,元素和组件都可使用。该指令接受一个固定长度的数组作为依赖值进行记忆比对。如果数组中的每个值和上次渲染的时候相同,则整个该子树的更新都会被跳过。
v-memo 仅供性能敏感场景的针对性优化,会用到的场景应该很少。渲染 v-for 长列表 (长度大于 1000) 可能是它最有用的场景:
<div v-for="item in list" :key="item.id" v-memo="[item.id === selected]">
<p>ID: {{ item.id }} - selected: {{ item.id === selected }}</p>
</div>
const list = reactive([
{ name: '张三', id: 1 },
{ name: '李四', id: 2 },
{ name: '王二', id: 3 },
{ name: '赵五', id: 4 },
{ name: '曹六', id: 5 },
]);
const selected = ref(1);
当组件的 selected 状态发生变化时,即使绝大多数 item 都没有发生任何变化,大量的 VNode 仍将被创建。此处使用的 v-memo 本质上代表着“仅在 item 从未选中变为选中时更新它,反之亦然”。这允许每个未受影响的 item 重用之前的 VNode,并完全跳过差异比较。注意,我们不需要把 item.id 包含在记忆依赖数组里面,因为 Vue 可以自动从 item 的 :key 中把它推断出来。
v-model该指令只能用于:input、textarea、select、components这几个标签
修饰符有以下几种:
.lazy:把默认的监听 input 事件改为监听 change.number:输入字符串转为有效的数字.trim:去除输入的首尾空格<input type="text" v-model.trim="value" placeholder="请输入" />
v-on绑定事件监听器。事件类型由参数指定。表达式可以是一个方法的名字或一个内联语句,如果没有修饰符也可以省略。
缩写为:@[event]=“xxx”
修饰符有以下几种:
.stop:阻止单击事件继续冒泡.prevent:阻止浏览器默认行为.capture:添加事件侦听器时使用事件捕获模式.self:只执行直接作用在该元素身上的事件,会忽略其他元素的冒泡或者捕获事件.once:事件只会触发一次.passive:告诉浏览器不用去查询,我们没用preventDefault阻止默认行为.{keyAlias}:仅当事件是从特定键触发时才触发回调。.left:只当点击鼠标左键时触发。.right:只当点击鼠标右键时触发。.middle:只当点击鼠标中键时触发。上面几个修饰符详细介绍可以看我另外一篇文章:修饰符详细介绍
<!-- 方法处理器 -->
<button v-on:click="doThis"></button>
<!-- 动态事件 -->
<button v-on:[event]="doThis"></button>
<!-- 内联语句 -->
<button v-on:click="doThat('hello', $event)"></button>
<!-- 缩写 -->
<button @click="doThis"></button>
<!-- 动态事件缩写 -->
<button @[event]="doThis"></button>
<!-- 停止冒泡 -->
<button @click.stop="doThis"></button>
<!-- 阻止默认行为 -->
<button @click.prevent="doThis"></button>
<!-- 阻止默认行为,没有表达式 -->
<form @submit.prevent></form>
<!-- 串联修饰符 -->
<button @click.stop.prevent="doThis"></button>
<!-- 键修饰符,键别名 -->
<input @keyup.enter="onEnter" />
<!-- 点击回调只会触发一次 -->
<button v-on:click.once="doThis"></button>
<!-- 对象语法 -->
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button>
v-bind动态地绑定一个或多个 attribute,或一个组件 prop 到表达式。
缩写为: :或 . (当使用 .prop 修饰符时)
修饰符有以下几种:
.camel:将 kebab-case attribute 名转换为 camelCase。.attr:将一个绑定强制设置为一个 DOM attribute。(3.2新增).prop:将一个绑定强制设置为一个 DOM property。(3.2新增)<!-- 绑定 attribute -->
<img v-bind:src="imageSrc" />
<!-- 动态 attribute 名 -->
<button v-bind:[key]="value"></button>
<!-- 缩写 -->
<img :src="imageSrc" />
<!-- 动态 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" 必须在 my-component 声明 -->
<my-component :prop="someThing"></my-component>
<!-- 将父组件的 props 一起传给子组件 -->
<child-component v-bind="$props"></child-component>
<!-- XLink -->
<svg><a :xlink:special="foo"></a></svg>
v-slot提供具名插槽或需要接收 prop 的插槽。
插槽名 (可选,默认值是 default)
缩写为:#
适用于:template标签上面
<template #default></template>
v-cloak这个指令保持在元素上直到关联组件实例结束编译。和 CSS 规则如 [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 Mustache 标签直到组件实例准备完毕。
作用:为了防止在页面加载时先出现变量名闪烁的情况,造成不好的用户体验
例如:{{ msg }} (闪一下)=> hello
用法:
一定要搭配css一起用,一定要搭配css一起用,一定要搭配css一起用
<div v-cloak>{{ msg }}</div>
<style lang="scss">
[v-cloak] {
display: none;
}
</style>
以上这些就是部分内置指令的介绍和用法。
created:在绑定元素的 attribute 或事件监听器被应用之前调用。在指令需要附加在普通的 v-on 事件监听器调用前的事件监听器中时,这很有用。beforeMount:当指令第一次绑定到元素并且在挂载父组件之前调用。mounted:在绑定元素的父组件被挂载后调用。beforeUpdate:在更新包含组件的 VNode 之前调用。updated:在包含组件的 VNode 及其子组件的 VNode 更新后调用。beforeUnmount:在卸载绑定元素的父组件之前调用。unmount:当指令与元素解除绑定且父组件已卸载时,只调用一次。el:指令绑定到的元素。这可用于直接操作 DOM。
binding:包含以下 property 的对象。
instace:使用指令的组件实例。value:传递给指令的值。例如,在v-my-directive="1 + 1"中,该值为2。oldValue:先前的值,仅在beforeUpdate和updated中可用。无论值是否有更改都可用。arg:传递给指令的参数(如果有的话)。例如在v-my-directive:foo中,arg 为"foo"。modifiers:包含修饰符(如果有的话) 的对象。例如在v-my-directive.foo.bar中,修饰符对象为{foo: true,bar: true}。dir:一个对象,在注册指令时作为参数传递。
vnode:一个真实 DOM 元素的蓝图,对应上面收到的 el 参数。
prevNode:上一个虚拟节点,仅在 beforeUpdate 和 updated 钩子中可用。
app.directive('my-directive', {
mounted: (el) => {
el.style.background = '#4598d2';
},
});
setup中局部注册自定义指令必须以 vNameOfDirective 的形式来命名本地自定义指令,以使得它们可以直接在模板中使用。
// Directive是从vue导出的一个ts类型声明
const vMyDirective: Directive = {
mounted: (el) => {
el.style.background = 'red';
},
};
防抖和切换背景色的自定义指令directivesdirectives 目录创建 index.ts、background.ts、debounce.ts 三个文件index.ts、background.ts、debounce.ts 三个文件代码如下:// background.ts
import { Directive } from 'vue';
export const background: Directive = (el, binding) => {
el.style.background = binding.value;
};
export default background;
// debounce.ts
import { Directive } from 'vue';
export const debounce: Directive = {
mounted(el, binding) {
let timer: number;
el.addEventListener('click', () => {
timer && clearTimeout(timer);
timer = window.setTimeout(() => binding.value(), 300);
});
},
};
export default debounce;
// index.ts
import { App, Directive } from 'vue';
import debounce from '@/directives/debounce';
import background from '@/directives/background';
const directives = {
debounce,
background,
};
export default {
install(app: App) {
Object.keys(directives).forEach((key) => {
// 这里是批量注册指令
app.directive(key, (directives as { [key: string]: Directive })[key]);
});
},
};
main.ts 代码中引入我们刚才创建的 index.ts,并通过 app.use()注册进去,代码如下:import { createApp } from 'vue';
import App from './App.vue';
import directives from './directives';
const app = createApp(App);
app.use(directives).mount('#app');
// template
<el-button v-debounce="click">这里是防抖指令,快速点击试试:{{ debounceText }}</el-button>
<el-button v-background="background" style="color: white" @click="changeBackground">
这里是设置背景色指令:{{ background }}
</el-button>
// setup
const debounceText = ref(new Date().getTime());
const click = () => (debounceText.value = new Date().getTime());
const background = ref('#4598d2');
const changeBackground = () =>
(background.value = `#${('00000' + ((Math.random() * 0x1000000) << 0).toString(16)).substr(-6)}`);
推荐大家有空可以自己动手试试,毕竟百看不如一试加深体验。