04-Vue指令

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绑定样式

  • 将数据放到对象中
    :style="{color:'red','font-size':'50px'}"
  • 将数据放到Model对象中
<div id="app">
    <p :style="obj">我是段落</p>
</div>

<script>
    let vue = new Vue({
        el: '#app',
        data: {
            obj: {
                color: 'red',
                'font-size': '50px'
            }
        }
    });
</script>
  • 如果需要绑定Model中的多个对象, 可以放到一个数组中赋值
<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 - 只触发一次回调。
  • .prevent - 调用 event.preventDefault()。阻止元素的默认行为。
  • .stop - 调用 event.stopPropagation()。阻止事件冒泡。
  • .self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。
  • .capture - 添加事件侦听器时使用 capture 模式。 事件捕获。
<!--默认情况下事件的回调函数只要事件被触发就会执行, 可以反复的执行-->
<!--如果想让事件监听的回调函数只执行一次, 那么就可以使用.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中的数据必须加上this
data: {
   gender: 'man'
},
methods: {
   myFn(){
       console.log(this.gender);
   }
}

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

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

按键修饰符分类

  • 系统 预定义 修饰符
    • .enter
    • .tab
    • .delete (捕获“删除”和“退格”键)
    • .esc
    • .space
    • .up
    • .down
    • .left
    • .right
  • 自定义修饰符
    当预定义的修饰符不能满足需求时, 我们还可以对照 Keycode 自定义修饰符
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"
, 在我们自定义的指令中我们也可以传递参数

  • el:指令所绑定的元素,可以用来直接操作 DOM。
  • binding:一个对象,包含以下属性:
    • 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 编译生成的虚拟节点。移步 VNode API 来了解更多详情。
  • oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。
    例如:
<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>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容

  • Vue 实例 属性和方法 每个 Vue 实例都会代理其 data 对象里所有的属性:var data = { a:...
    云之外阅读 2,198评论 0 6
  • 1. Vue 实例 1.1 创建一个Vue实例 一个 Vue 应用由一个通过 new Vue 创建的根 Vue 实...
    王童孟阅读 1,014评论 0 2
  • 1概述 上一章我们说了Vue是用于构建用户界面的框架,它最终呈现给用户的是一个一个HTML标签。Vue可以使用HT...
    书上得来终觉浅阅读 497评论 1 0
  • 一、了解Vue.js 1.1.1 Vue.js是什么? 简单小巧、渐进式、功能强大的技术栈 1.1.2 为什么学习...
    蔡华鹏阅读 3,311评论 0 3
  • vue学习笔记 安装 Vue 提供一个官方命令行工具,可用于快速搭建大型单页应用。只需几分钟即可创建并启动一个带热...
    EL_PSY_CONGROO阅读 1,054评论 0 5