vue 的指令

180 阅读8分钟

v-text

v-text 指令,会把该元素下面的所有内容替换掉。

<div v-text="hello vue">hello world</div>

现实结果是:hello vue

v-html

v-html 指令,会用一个HTML标签字符串,替换该元素下面的所有内容。

但是,不建议使用v-html指令,因为它会导致被恶意者进行XSS攻击的潜在风险。

<div v-html="'<span style=&quot;color:red&quot;>hello vue</span>'">
  hello world
</div>

现实结果是:字体颜色为红色的 hello vue

v-for

v-for 指令,for循环,基于源数据多次渲染元素或模板块。
v-for 既可以渲染一个数组,也可以渲染一个对象。

v-for="(item, index) in arr" :key="index"

  • item 数据
  • index 数组下标
  • key 唯一标识,便于遍历和查找dom

(1)v-for 渲染一个数组

<div v-for="(item, index) in [1, 2, 3]" :key="index">
    {{item}}
</div>
// 渲染的结果:
// 1
// 2
// 3

(2)v-for 渲染一个对象

<div v-for="(val, key) in {one: 1, two: 2}" :key="key">
    {{key}}: {{val}}
</div>
// 渲染的结果:
// one: 1
// two: 2

(3)v-if 与 v-for 不可以同时使用

当 v-if 与 v-for 一起使用时:
在 vue2 中 v-for 比 v-if 有更高的优先级。这意味着 v-if 将分别重复运行于每个 v-for 循环中。
在 vue3 中 v-if 比 v-for 有更高的优先级。这意味着 v-if 的条件将无法访问到 v-for 作用域内定义的变量别名。

(4)v-for 之 key

  • 为什么需要给 v-for 设置 key?

这牵扯到 vue 的 vnode 的 Diff 算法的特点

  • 在 v-for 中直接用 index 作为 key 的值有什么不好?例如:
<template>
	<div v-for="(item, index) in list" :key="index" >{{item.name}}</div>
</template>
<script>
export default {
  data() {
    return {
        list: [
        {
            id: 1,
            name: "Person1"
        },
        {
            id: 2,
            name: "Person2"
        },
        {
            id: 3,
            name: "Person3"
        },
        {
            id:4,
            name:"Person4"
        }]
    }
  },
}
</script>

此时,删除 “Person4” 是正常的,但是如果我删除 “Person2” 就会出现问题。

删除前

idindexname
10Person1
21Person2
32Person3
43Person4

删除后

idindexname
10Person1
31Person3
42Person4

可见,数组的 index 下标始终是从 0 开始依次递增不间断的,当其中某一项被删除后,被删节点之后的 index 下标会自动全部做减 1 更新。所以,删除了 id 是 2 的节点时,被删节点之后的 index 下标全部做减 1 更新了。所以,当 DOM 内容比较复杂时,建议设置并使用唯一的 id 属性,来作为 key 的值

v-show

v-show 指令,控制元素的显示隐藏,元素存在并占据空间。

元素隐藏时,相当于给该元素添加了 CSS 样式:display:none;

<div v-show="show">hello vue</div>
<button @click="show = !show">changeShow</button>

v-if/v-esle-if/v-else

(1)v-if
v-if指令,控制元素是否加载。
v-esle-if/v-else指令不能单独使用,必须配合v-if一起使用。

<div v-if="number===1">hello vue {{number}}</div>
<div v-else-if="number===2">hello world {{number}}</div>
<div v-else>hello someone {{number}}</div>

(2)v-if 与 v-show的区别

  • v-if:有更高的切换开销;
  • v-show:有更高的初始化开销。
  • 若需要频繁的切换则使用 v-show 比较好,否则使用 v-if 比较好。
  • v-if是一种条件渲染指令,它会根据表达式的值来插入或删除元素。当表达式的值为真时,元素会被插入到DOM中,否则会从DOM中删除。
  • v-show是一种简单的显示/隐藏指令,它会根据表达式的值来显示或隐藏元素。当表达式的值为真时,元素会被显示,否则会被隐藏。

v-on

v-on指令,可简写为“@”,绑定事件监听器。

<button v-on:click="number = number + 1">number++</button>

v-on 指令的修饰符

.stop - 调用 event.stopPropagation(),禁止事件冒泡
<a @click.stop="doThis"></a>
.prevent - 调用 event.preventDefault(),禁止事件的默认行为
<form @submit.prevent="onSubmit"></form>
.passive - (2.3.0) 以 { passive: true } 模式添加侦听器

立即执行事件的默认行为,会导致 event.preventDefault() 无效:

<div @scroll.passive="onScroll">...</div> 
// 滚动事件的默认行为 (即滚动行为) 将会立即触发,而不会等待 `onScroll` 完成。
.capture - 添加事件侦听器时使用 capture 模式

内部元素触发的事件先在此处理,然后才交由内部元素进行处理

<div @click.capture="doThis">...</div>
.self - 只当事件是从侦听器绑定的元素本身(event.target)触发时才触发回调
<div @click.self="doThat">...</div>
.native - 监听组件根元素的原生事件:
<base-input v-on:focus.native="onFocus"></base-input>
.once - 只触发一次回调:
<a @click.once="doThis"></a>
.left - (2.2.0) 只当点击鼠标左键时触发。
.right - (2.2.0) 只当点击鼠标右键时触发。
.middle - (2.2.0) 只当点击鼠标中键时触发。
.{keyCode | keyAlias} ——键盘事件——只当事件是从特定键触发时才触发回调
  • keyCode:
    • keydown:当键盘按键被按下时触发
    • keyup:当键盘按键被松开时触发
    • keypress:当按下字符键时触发(“重复”事件将在短时间内连续发生,直到键盘按键停止)。
    • @keydown.ctrl.alt:当组合按键(例如Ctrl + Alt)被按下时触发。
  • keyAlias:
    • 回车: .enter
    • 删除: .delete(捕获"删除"和"退格"键)
    • 退出: .esc
    • 空格: .space
    • 换行: .tab(特殊,必须配合keydown去使用)
    • 上: .up
    • 下: .down
    • 左: .left
    • 右: .right
    • .ctrl
    • .alt
    • .shift
    • .meta (Mac 键盘上的 Command 键/windown 键盘上的 win 键)
  • 系统修饰键(用法特殊):ctrl、alt、shift、meta
    • 配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
    • 配合keydown使用:正常触发事件
  • 使用键盘别名注意事项
    • html元素使用
       <div @keydown.enter="enterKeyDown"> </div>
      
    • 自定义组件和ElementUI上面需要加上.native修饰符
       <el-button @keydown.enter.native="enterKeyDown"> </div>
      
    • 键盘事件不触发的问题
      由于键盘事件的触发需要有焦点,在input中绑定是没有问题的(光标在input框内),但是在元素或者按钮上面是没有焦点的,所以需要人为添加监听
         /* 创建keyDown事件 */
          createKeyDown() {
            document.onkeydown = (e) => {
              console.log(e);
              if (e.code === "Enter") {
                // this.handleSaveAndAddBox();
              }
            };
          },
          /* 销毁keyDown事件 */
          destroyedKeyDown() {
            document.onkeydown = (e) => {
              if (e.code === "Enter") {
              }
            };
          },
      
       created() {
          this.createKeyDown();
        },
        beforeDestroy() {
          this.destroyedKeyDown();
        },
      

在内联事件处理器中访问事件参数

有时我们需要在内联事件处理器中访问原生 DOM 事件。你可以向该处理器方法传入一个特殊的 $event 变量,或者使用内联箭头函数:

<template>
    <!-- 使用特殊的 $event 变量 -->
    <button @click="warn('Form cannot be submitted yet.', $event)">
      Submit
    </button>
 
    <!-- 使用内联箭头函数 -->
    <button @click="(event) => warn('Form cannot be submitted yet.', event)">
      Submit
    </button>
</template>
 
<script setup lang="ts">
    function warn(message, event) {
      // 这里可以访问原生事件
      if (event) {
        event.preventDefault()
      }
      alert(message)
    }
</script>

vue 中的特殊事件

vue 监听页面滚动事件

export default {
  mounted () {
    window.addEventListener('scroll', this.handleScroll)
  },
  beforeDestroy () {
    window.removeEventListener('scroll', this.handleScroll)
  },
  methods: {
    // 监听页面滚动
    handleScroll (e) {
      // ...
    }
  }

v-bind

v-bind 指令,可简写为“:”,动态地绑定一个或多个属性,或一个来自父组件的 prop 里的表达式。

在自定义组件时,若要对 prop 进行“双向绑定”,可以用“v-bind 指令与 .sync 修饰符”相结合来实现。 详情看父子组件数据双向绑定

v-model

双向绑定的语法糖

v-model 指令,是双向绑定的语法糖。

双向绑定:当数据变化视图同步更新,当视图更新数据也会同步更新。

双向绑定的原理请戳此链接:vue 的双向绑定原理

v-model 实际上是 value 属性和 input 事件结合的简写形式。

<input v-model="msg"/> 
// 等同于:
<input :value="msg" @input="handleChange">
<script>
export default {
    data() {
        return {
            msg: ""
        }
    },
    methods: {
        handleChange(e){
            this.msg = e.target.value;
        }
    },
}
</script>

v-model 的修饰符——表单

  • .lazy:在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步 。如果你想实现在 change 事件之后进行同步,那么建议给 v-model 添加 lazy 修饰符。
<!-- 在“change”时而非“input”时更新 -->
<input v-model.lazy="msg" />
  • .number:将用户输入的字符串转换成 number。
<input v-model.number="age" type="text" />
  • .trim:将用户输入的前后的空格去掉。
<input v-model.trim="msg" />

【拓展】

v-model 在内部为不同的输入元素使用不同的 property(属性) 并抛出不同的事件:

  • text 和 textarea 元素使用 value 属性和 input 事件;
  • checkbox 和 radio 使用 checked property 和 change 事件;
  • select 字段将 value 作为 prop 并将 change 作为事件。

v-slot

v-slot 指令,用来定义一个 具名插槽 或 作用域插槽。可以缩写为 #。

插槽:用来分发内容,传递复杂的内容。

具体请戳这里:vue 插槽

v-pre(使用频率很低)

v-pre 指令,跳过这个元素和它的子元素的编译过程。

<span v-pre>{{ this will not be compiled }}</span>

v-once(使用频率很低)

v-once 指令,只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。

<span v-once>This will never change: {{msg}}</span>

自定义指令 与 指令的生命周期

指令的周期: 5个 (bind、inserted、update、componentUpdated、unbind)。

  • bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
  • inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
  • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
  • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
  • unbind:只调用一次,指令与元素解绑时调用。

注册自定义指令

(1)注册全局指令——Vue.directive() 方法

<script>
    // 注册一个全局自定义指令 `v-focus`
    Vue.directive('focus', {
    // 当被绑定的元素插入到 DOM 中时……
    inserted: function (el) {
        // 聚焦元素
        el.focus()
    }
    })
</script>

(2)注册局部指令——directives 属性

<script>
    directives: {
        focus: {
            // 指令的定义
            inserted: function (el) {
                el.focus()
            },
            // ...
        }
    }
</script>