04-Vue指令

2020-04-21  本文已影响0人  仰望_IT
image

什么是指令

官方解释: 指令 (Directives) 是带有 v- 前缀的特殊 attribute。指令 attribute 的值预期是单个 JavaScript 表达式 (v-for 是例外情况)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。

其实指令就是Vue内部提供的一些自定义属性,
这些属性中封装好了Vue内部实现的一些功能
只要使用这些指令就可以使用Vue中实现的这些功能

Vue中常用的指令

1. v-once

在将这个指令之前我们先来回顾Vue数据绑定的特点: 只要数据发生变化, 界面就会跟着变化。
而这个指令的作用就是让界面不要跟着数据变化, 只渲染一次, 这样可以优化更新性能。
例如:

<div id="app">
    <p v-once>原始数据: {{message}}</p>
    <p>当前数据: {{message}}</p>
</div>
<script>
    let vue = new Vue({
        el: "#app",
        data: {
            message: "v-once指令"
        }
    });
</script>

在这个代码中, 无论你这么修改message的值, 第一个 P标签 中的数据总是 v-once指令 , 而第二个 P标签 中的数据会随着message的值的改变而改变

2. v-cloak

v-cloak指令的作用: 数据渲染之后自动显示元素

在讲这个指令之前我们先来回顾一下Vue数据绑定过程

  1. 会先将未绑定数据的界面展示给用户
  2. 根据模型中的数据和控制的区域生成绑定数据之后的HTML代码
  3. 将绑定数据之后的HTML渲染到界面上

因为在最终的HTML被生成渲染之前会先显示模板内容,所以如果用户网络比较慢或者网页性能比较差, 那么用户会看到模板内容。
例如:

<div>
    {{message}}
</div>

<script>
    let vue = new Vue({
        el: "#app",
        data: {
            message: "v-cloak指令"
        }
    });
</script>

在这里如果网速比较慢会先看到{{message}}, 过一段时间才会看到v-cloak指令,所以这样的话用户体验不是很好。
如何解决这个问题
利用 v-cloak 配合 [v-cloak]:{display: none} 默认先隐藏未渲染的界面,等到生成HTML渲染之后再重新显示。
具体用法请看下面的例子:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>06-常用指令v-cloak</title>
    <style>
        [v-cloak] { display: none }
    </style>
</head>
<body>
<div id="app">
    <p v-cloak>{{message}}</p>
</div>
<script src="js/vue.js"></script>
<script>
    let vue = new Vue({
        el: "#app",
        data: {
            message: "v-cloak指令"
        }
    });
</script>
</body>
</html>

3. v-model

这个指令用于在表单上创建双向数据绑定
只有在<input>、<textarea> 及 <select>元素上可以用 v-model 指令创建双向数据绑定, 而且会忽略所有表单元素的valuecheckedselected特性的初始值。因为它选择Vue实例数据做为具体的值。
例如:

<div id="app">
    <input v-model="somebody">
    <p>hello {{somebody}}</p>
</div>
<script>
    var app = new Vue({
        el: '#app',
        data: {
            somebody:'小明'
        }
    })
</script>

这个例子中直接在浏览器input中输入别的名字,下面的p的内容会直接跟着变。这就是双向数据绑定。

4. v-text 和 v-html

v-text 就相当于JS中的 innerText
v-html 就相当于JS中的 innerHTML

v-text 主要用来更新元素的文本内容, 并且会 覆盖 原有的内容,不会解析HTML。
例如:

<span v-text="message">原有内容</span>
<span v-text="html">原有内容</span>

<script>
    let vue = new Vue({
        el: "#app",
        data: {
            message: "v-text指令",
            html: "<span>我是span</span>"
        }
    });
</script>

这个例子的第一个span会打印 v-text指令, 第二个指令会打印 <span>我是span</span>

v-html 注意用于输出真正的HTML,并且也会 覆盖 原有的内容, 但是 解析HTML。
例如:

<span v-html="message">原有内容</span>
<span v-html="html">原有内容</span>

<script>
    let vue = new Vue({
        el: "#app",
        data: {
            message: "v-html指令",
            html: "<span>我是span</span>"
        }
    });
</script>

这个例子的第一个span会打印 v-html指令, 第二个指令会打印 我是span

5. v-if、v-else、v-else-if

v-if
可以实现条件渲染,如果 v-if 取值是 true 就渲染该元素, 如果不是就 不渲染 (不创建)该元素。
v-if 可以从模型中获取数据,也可以直接赋值一个表达。

<div id="app">
    <!--从模型中获取数据-->
    <p v-if="show">我是true</p>     // 打印 '我是true'
    <p v-if="hidden">我是false</p>  // 不会被打印
    <!--赋值一个表达-->
    <p v-if="age >= 18">成年人</p>    // 打印 '成年人'
    <p v-if="age < 18">未成年人</p>     // 不会被打印
</div>

<script>
    let vue = new Vue({
        el: '#app',
        data: {
            show: true,
            hidden: false,
            age: 18
        }
    });
</script>

v-else
v-else 是搭配 v-if 使用的,它必须紧跟在 v-if 或者 v-else-if 后面,否则不起作用。
v-else 不能单独出现,v-if 和 v-else 中间不能出现除 v-else-if 外的其它内容。

<div id="app">
    <p v-if="age >= 18">成年人</p>
    <p>中间的内容</p>       // 报错
    <p v-else>未成年人</p>
</div>

<script>
    let vue = new Vue({
        el: '#app',
        data: {
            age: 18
        }
    });
</script>

v-else-if
v-else-i f可以和 v-if 指令配合使用, 当 v-if 不满足条件时就依次执行后续 v-else-if , 哪个满足就显示哪个。
和 v-else一样, 必须紧跟在 v-if 或者 v-else-if 后面,这三种语句的中间都是不允许插入其他代码的。

<div id="app">
    <p v-if="score >= 80">优秀</p>
    <p v-else-if="score >= 60">良好</p>
    <p v-else>不及格</p>
</div>

<script>
    let vue = new Vue({
        el: '#app',
        data: {
            score: 80
        }
    });
</script>

6. v-show

也是用于根据条件展示元素。和v-if不同的是,如果v-if的值是false,则这个元素被销毁,不在dom中。但是v-show的元素会始终被渲染并保存在dom中,它只是简单的切换css的dispaly属性。

<div id="app">
    <p v-show="show">我是段落1</p>      // 打印 '我是段落1'
    <p v-show="hidden">我是段落2</p>    // 不会被打印, 但是会被渲染, 并且在DOM中
</div>

<script>
    let vue = new Vue({
        el: '#app',
        data: {
            show: true,
            hidden: false
        }
    });
</script>

由于取值为false时v-if不会创建元素, 所以如果需要切换元素的显示和隐藏, 每次v-if都会创建和删除元素
由于取值为false时v-show会创建元素并设置display为none, 所有如果需要切换元素的显示和隐藏,
不会反复创建和删除, 只是修改display的值

所以:
v-if有更高的切换开销。

v-show有更高的初始渲染开销。
因此,如果要非常频繁的切换,则使用v-show较好;如果在运行时条件不太可能改变,则v-if较好

7. v-for

相当于JS中的for in循环, 可以根据数据多次渲染元素。
v-for 可以遍历 数组, 字符, 数字, 对象。

<div id="app">
    <ul>
        <li v-for="(value, index) in list">{{index}}---{{value}}</li>   // index是一个可选参数,表示当前项的索引
        <li v-for="(value, index) in 6">{{index}}---{{value}}</li>
        <li v-for="(value, index) in 'abcdef'">{{index}}---{{value}}</li>
        <li v-for="(value, key) in obj">{{key}}---{{value}}</li>
    </ul>
</div>

<script>
    let vue = new Vue({
        el: '#app',
        data: {
            list: ['张三', '李四', '王五', '赵六'],
            obj: {
                name: 'lisi',
                age: 22,
                gender: "man"
            }
        }
    });
</script>

在v-for中,拥有对父作用域属性的完全访问权限。

<ul id="app">
    <li v-for="item in items">
        {{parent}}-{{item.text}}
    </li>
</ul>
<script type="text/javascript">
    var example = new Vue({
      el:'#app',
      data:{
        parent:'父作用域'
        items:[
          {text:'文本1'},
          {text:'文本2'}
        ]
      }
    })
</script>

会被渲染为:

<ul id="app">
    <li>父作用域-文本1</li>
    <li>父作用域-文本2</li>
</ul>
注意:当v-for和v-if同处于一个节点时,v-for的优先级比v-if更高。这意味着v-if将运行在每个v-for循环中

8. v-bind

想要给"元素"绑定数据, 我们可以使用 {{}}, v-text, v-html
但是如果想给"元素的属性"绑定数据, 就必须使用 v-bind
所以 v-bind 的作用是专门用于给"元素的属性"绑定数据的。
虽然通过v-model可以将数据绑定到input标签的value属性上,但是v-model是有局限性的, v-model只能用于input/textarea/select。

v-bind有两种格式:
v-bind:属性名称="绑定的数据"
:属性名称="绑定的数据"

v-bind特点
赋值的数据可以是任意一个合法的JS表达式
例如: :属性名称="age + 1"

<div id="app">
    <!--第一种格式: v-bind:属性名称="绑定的数据"-->
    <input type="text" v-bind:value="str">
    <!--第二种格式(简写格式): :属性名称="绑定的数据" -->
    <input type="text" :value="str">
    
    <!--赋值的数据可以是任意一个合法的JS表达式-->
    <input type="text" :value="age + 1">
</div>

<script>
    let vue = new Vue({
        el: '#app',
        data: {
            str: 'v-bind指令',
            age: 18
        }
    });
</script>

v-bind指令给"任意标签"的"任意属性"绑定数据,
对于大部分的属性而言我们只需要直接赋值即可, 例如: value="name"
但是对于 classstyle 属性而言, 它的格式比较特殊

通过v-bind绑定类名

格式: :class="['需要绑定类名', ...]"
需要绑定的类名要放到一个数组中, 并且还要用引号括起来, 否就会去Module(也就是Vue实例对象中的data)中查找这个类名的数据。

<style>
        .size{
            font-size: 50px;
        }
        .color{
            color: red;
        }
        .bg{
            background: skyblue;
        }
    </style>
<div id="app">
    <p :class="['size', 'color', 'bg']">我是段落</p>
</div>

<script>
    let vue = new Vue({
        el: '#app',
        data: {
        }
    });
</script>

如果是通过v-bind类绑定类名, 那么在绑定的时候可以编写一个三目运算符来实现按需绑定
格式: 条件表达式 ? '需要绑定的类名' : ''

<div id="app">
    <p :class="['size', 'color', flag ? 'bg': '']">我是段落</p>
</div>

<script>
    let vue = new Vue({
        el: '#app',
        data: {
            flag: false
        }
    });
</script>

还可以使用对象来替代数组中的三目运算符按需导入
格式: {'需要绑定的类名' : 是否绑定}

<div id="app">
    <p :class="['size', 'color', {'bg': flag}]">我是段落</p>
</div>

绑定的类名太多可以将类名封装到Model中, 使用Model中的对象来替换数组

<div id="app">
    <p :class="obj">我是段落</p>
</div>

<script>
    let vue = new Vue({
        el: '#app',
        data: {
            obj: {
                size: true,
                color: true,
                bg: false
            }
        }
    });
</script>


通过v-bind绑定样式

<div id="app">
    <p :style="obj">我是段落</p>
</div>

<script>
    let vue = new Vue({
        el: '#app',
        data: {
            obj: {
                color: 'red',
                'font-size': '50px'
            }
        }
    });
</script>
<div id="app">
    <p :style="[obj1, obj2]">我是段落</p>
</div>

<script>
    let vue = new Vue({
        el: '#app',
        data: {
            obj1: {
                color: 'red'
            },
            obj2: {
                'font-size': '50px'
            }
        }
    });
</script>
如果属性名称包含 `-` , 那么必须用引号括起来。例如:'font-size': '50px'

9. v-on

v-on主要用来监听dom事件,以便执行一些代码块。
格式:

v-on:事件名称="回调函数名称"  
@事件名称="回调函数名称"(缩写)

如果是通过v-on来绑定监听事件, 那么在指定事件名称的时候不需要写on,在赋值的时候必须赋值一个回调函数的名称。
例如:

<div id="app">
    <button v-on:click="myFn">我是按钮</button>
    <button @click="myFn">我是按钮</button>
</div>

当绑定的事件被触发后, 会调用Vue实例的methods对象中对应的回调函数。

<script>
    let vue = new Vue({
        el: '#app',
        methods: {
            myFn(){
                alert('v-on指令');
            }
        }
    });
</script>

事件修饰符

<!--默认情况下事件的回调函数只要事件被触发就会执行, 可以反复的执行-->
<!--如果想让事件监听的回调函数只执行一次, 那么就可以使用.once修饰符-->
<button @click.once="myFn">我是按钮</button>

<!--如果想阻止元素的默认行为, 那么可以使用.prevent修饰符-->
<a href="www.baidu.com" @click.prevent="">我是a标签</a> // 这里可以阻止a标签的自动跳转

<!--默认情况下嵌套的元素中, 如果都监听了相同的事件, 那么会触发事件冒泡-->
<!--如果想阻止事件冒泡, 那么可以使用.stop修饰符-->
<div class="a" @click="myFn1">
    <div class="b" @click.stop="myFn2">
        <div class="c" @click="myFn3"></div>
    </div>
</div>

<!--如果想让回调只有当前元素触发事件的时候才执行, 那么就可以使用.self修饰符-->
<div class="a" @click="myFn1">
    <div class="b" @click.self="myFn2">
        <div class="c" @click="myFn3"></div>
    </div>
</div>

<!--默认情况下是事件冒泡, 如果想变成事件捕获, 那么就需要使用.capture修饰符-->
<div class="a" @click.capture="myFn1">
    <div class="b" @click.capture="myFn2">
        <div class="c" @click.capture="myFn3"></div>
    </div>
</div>



注意

<button @click="myFn">我是按钮</button>
<button @click="myFn()">我是按钮</button>
<button @click="myFn('zs', 22)">我是按钮</button>
<button @click="myFn('zs', 22, $event)">我是按钮</button>
data: {
   gender: 'man'
},
methods: {
   myFn(){
       console.log(this.gender);
   }
}

按键修饰符
我们可以通过按键修饰符监听特定按键触发的事件
例如: 可以监听当前事件是否是回车触发的, 可以监听当前事件是否是ESC触发的等

<!--只有按下enter的时候才会调用myFn方法-->
<input type="text" @keyup.enter="myFn">

按键修饰符分类

Vue.config.keyCodes.f1 = 112

10. 自定义指令

Vue除了提供了默认内置的指令外,还允许开发人员根据实际情况自定义指令,它的作用价值在于当开发人员在某些场景下需要对普通DOM元素进行操作的时候。
Vue自定义指令和组件一样存在着全局注册和局部注册两种方式。

全局指令
通过 Vue.directive( id, [definition] ) 方式注册全局指令,
第一个参数为自定义指令名称(指令名称不需要加 v- 前缀,默认是自动加上前缀的,使用指令的时候一定要加上前缀),
第二个参数可以是对象数据,也可以是一个指令函数。

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

这个简单案例当中,我们通过注册一个 v-focus 指令,实现了在页面加载完成之后自动让输入框获取到焦点的小功能。其中 inserted 是自定义指令的钩子函数

指令生命周期方法
自定义指令时一定要明确指令的业务逻辑代码更适合在哪个阶段执行
bind:   指令被绑定到元素上的时候执行。指令业务逻辑代码中没有用到元素事件, 那么可以在bind阶段执行
inserted:  绑定指令的元素被添加到父元素上的时候执行。指令业务逻辑代码中用到了元素事件, 那么就需要在inserted阶段执行
例如:

Vue.directive('color', {
    // 这里的el就是被绑定指令的那个元素
    bind:  function (el) {
        el.style.color = 'red';
    }
});

这里的el并没有用到元素的事件, 所以在bind阶段执行

Vue.directive('focus', {
  inserted: function (el) {
    // 聚焦元素
    el.focus()
  }
})

这里的el并用到了元素的focus事件, 就在inserted阶段执行, 如果在bind阶段执行就无效。

自定义指令的参数
在使用官方指令的时候我们可以给指令传参
例如: v-model="name"
, 在我们自定义的指令中我们也可以传递参数

<div id="app">
    <p v-color=" 'skyblue' ">我是段落</p>
</div>

<script>
    Vue.directive('color', {
        bind: function (el, binding) {
            el.style.color = binding.value.color;
        }
    });
    let vue = new Vue({
        el: '#app'
    });
</script>

</br>局部指令
局部自定义指令,通过在Vue实例中添加 directives 对象数据注册局部自定义指令。

directives: {
    // key: 指令名称
    // value: 对象
    'color': {
        bind: function (el, binding) {
            el.style.color = binding.value;
        }
    }
}

局部指令只能在自定义的那个Vue实例中使用
在下面这个例子中,段落2就没法使用段落1中的v-color指令

<div id="app1">
    <p v-color="'blue'">我是段落1</p>
</div>
<div id="app2">
    <p v-color="'blue'">我是段落1</p>
</div>

<script>
    let vue1 = new Vue({
        el: '#app1',
        directives: {
            'color': {
                bind: function (el, binding) {
                    el.style.color = binding.value;
                }
            }
        }
    });
    
    let vue2 = new Vue({
        el: '#app2'
    });
</script>
上一篇下一篇

猜你喜欢

热点阅读