Vue3:指令之v-if、v-show、v-for和自定义指令

4,140 阅读6分钟

1、v-for

1.1 简介

作用:可以用 v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名

<ul id="example-1"> 
    <li v-for="item in items" :key="item.message"> 
        {{ item.message }} 
    </li> 
</ul>
var example1 = new Vue({ 
    el: '#example-1', 
    data: { 
        items: [ 
            { message: 'Foo' }, 
            { message: 'Bar' } 
        ] 
    } 
})

1.2 使用ref

  • vue2:在 v-for 中使用的 ref attribute 会用 ref 数组填充相应的 $refs property。当存在嵌套的 v-for 时,这种行为会变得不明确且效率低下。
<ul> 
    <li v-for="item in list" :key="item.message"> 
        {{ item.message }} 
    </li> 
</ul>
this.$refs.list[0];
this.$refs.list[1];
  • vue3:此类用法将不再自动创建 $ref 数组。要从单个绑定获取多个 ref,请将 ref 绑定到一个更灵活的函数上 (这是一个新特性,需要手动维护一个数组用来保存每一个子元素对象)
<ul> 
    <li :ref="addRef" v-for="item in list" :key="item.message"> 
        {{ item.message }} 
    </li> 
</ul>
export default {
    data(){
        return {
          //保存列表元素的元素对象
          refList:[]
        }
    },
    methods:{
        //el:系统自动注入的参数,子元素的元素对象
        addRef(el){
            this.refList.push(el);
        }
    },
    //在组件更新之前清空refList
    beforeUpdate(){
        this.refList = [];
    } 
}
  • refList 不必是数组:它也可以是一个对象,其 ref 可以通过迭代的 key 被设置。
  • 如有需要,refList 也可以是响应式的,且可以被侦听。

2、v-if、v-show

2.1 v-if的使用

  • v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 truthy 值的时候被渲染。
<h1 v-if="awesome">Vue is awesome!</h1>

也可以用 v-else 添加一个“else 块”:

<h1 v-if="awesome">Vue is awesome!</h1> 
<h1 v-else>Oh no 😢</h1>

2.2 v-show的使用

另一个根据条件展示元素的选项是 v-show 指令,不同的是带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS property display

<h1 v-show="ok">Hello!</h1>

不同的是带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS property display

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

3、v-if和v-show区别

  • v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。

  • v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。

  • 相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。

  • 一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。

4、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 里的变量。

<li v-for="todo in todos" v-if="!todo.isComplete">
  {{ todo.name }}
</li>

可以把 v-for 移动到 <template> 标签中来修正:

<template v-for="todo in todos" :key="todo.name">
  <li v-if="!todo.isComplete">
    {{ todo.name }}
  </li>
</template>

5、自定义指令

5.1 vue2

①钩子函数

  • 对普通 DOM 元素进行底层操作,一个指令定义对象可以提供如下几个钩子函数 (均为可选):
    • bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
    • inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
    • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
    • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
    • unbind:只调用一次,指令与元素解绑时调用。
Vue.directive('指令名',function(){ 
})
  • 例如:当页面加载时,该元素将获得焦点 (注意:autofocus 在移动版 Safari 上不工作)。事实上,只要你在打开这个页面后还没点击过任何内容,这个输入框就应当还是处于聚焦状态。现在让我们用指令来实现这个功能:
// 注册一个全局自定义指令 `v-focus` 
Vue.directive('focus', { 
    // 当被绑定的元素插入到 DOM 中时…… 
    inserted: function (el) { 
        // 聚焦元素 
        el.focus() 
    } 
})

如果想注册局部指令,组件中也接受一个 directives 的选项:

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

然后你可以在模板中任何元素上使用新的 v-focus property,如下:

<input v-focus>

②钩子函数参数

除了 el 之外,其它参数都应该是只读的,切勿进行修改。如果需要在钩子之间共享数据,建议通过元素的 dataset 来进行。

  • el:指令所绑定的元素,可以用来直接操作 DOM。
  • binding:一个对象,包含以下 property:
    • name:指令名,不包括 v- 前缀。
    • value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2
    • oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
    • expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"
    • arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"
    • modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }
  • vnode:Vue 编译生成的虚拟节点。
  • oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

5.2 vue3

全局指令(例子参照上面的vue2)

const app = Vue.createApp({})
// 注册一个全局自定义指令 `v-focus`
app.directive('focus', {
  // 当被绑定的元素挂载到 DOM 中时……
  mounted(el) {
    // 聚焦元素
    el.focus()
  }
})

如果想注册局部指令,组件中也接受一个 directives 的选项:

directives: {
  focus: {
    // 指令的定义
    mounted(el) {
      el.focus()
    }
  }
}

然后你可以在模板中任何元素上使用新的 v-focus attribute,如下:

<input v-focus />
directives: {
  focus: {
    // 指令的定义
    mounted(el) {
      el.focus()
    }
  }
}

钩子函数

  • created:在绑定元素的 attribute 或事件监听器被应用之前调用。在指令需要附加须要在普通的 v-on 事件监听器前调用的事件监听器时,这很有用。
  • beforeMount:当指令第一次绑定到元素并且在挂载父组件之前调用。
  • mounted:在绑定元素的父组件被挂载后调用。
  • beforeUpdate:在更新包含组件的 VNode 之前调用。
  • updated:在包含组件的 VNode 及其子组件的 VNode 更新后调用。
  • beforeUnmount:在卸载绑定元素的父组件之前调用
  • unmounted:当指令与元素解除绑定且父组件已卸载时,只调用一次。