# 内置指令
>[success] 可在此处查看运行文档中的代码示例结果:[https://github.com/jianyaoo/Vue](https://github.com/jianyaoo/Vue)
[toc]
> 什么是指令?指令是指在Vue中带有`v-`前缀的特殊html属性。这些属性绑定了一个表达式,并将这些特性应用到dom上。
## 基础指令
基础指令指不需要表达式的基本简单指令,主要包括`v-cloack`和`v-once`指令
### v-cloak
v-cloak指令一般配合style样式使用,在vue实例编译结束后自动消失。一般配合dispaly:none使用用来避免页面渲染的闪动。
>[success] 小提示:一般什么情况会导致页面闪动?网络较慢、数据量较大时,vue.js还没有完全加载完而已经渲染了页面会导致页面闪动。
### v-once
v-once指令表示当前元素只渲染一次,包括当前元素及元素的所有子节点。即当元素首次渲染完毕后,数据不会再发生变化。一般用于渲染静态内容。
```html
<div id="app">
<h3>01 - v-cloak</h3>
<span v-cloak>{{message}}</span>
<div v-cloak>
<span>{{message}}</span>
</div>
<h3>02 - v-once</h3>
<span v-once>{{message}}</span>
<div v-once>
<span>{{message}}</span>
</div>
<button @click="handle">修改message信息</button>
</div>
```
<p class="run">
运行结果:点击按钮时,只有上面的元素内容发生了改变,而v-once标题下的元素内容并未发生改变
</p>
## 条件渲染指令
条件渲染指令是指根据既定的条件来编译元素或者显示隐藏元素。主要分为`v-if`、`v-else-if`、`v-else`、`v-show`四个指令。
### v-if、v-else-if、v-else
v-if指令一般配合v-esle指令使用,当v-if指令后的表达式为true时编译当前元素,否则的话编译v-else指令上的元素。在vue2.1之后新增了v-else-if指令,该指令只能紧跟v-if指令使用。
**<span style="padding-top:15px;display:inline-block;">代码示例</span>**
```html
<h3>v-if / v-else-if / v-else</h3>
<p v-if="status === 1">当前是第一种工作类型</p>
<p v-else-if="status === 2">当前是第二种工作类型</p>
<p v-else="status === 3">当前是第三种工作类型</p>
<button @click="changeStatus">切换状态</button>
```
```javaScript
changeStatus:function () {
this.status = (this.status - 1 === 0 ? 3 : this.status - 1);
}
```
<p class="run">
运行结果:点击按钮,依次显示当前三种工作类型。点击控制台可以查看到只有显示的元素进行了编译而其他两条元素并没有进行编译。
</p>
### v-show
v-show指令与v-if指令的渲染方式不同,v-show指令是在初始时就编译完所有的元素,通过给元素添加`display:none`的方式控制元素的显示和隐藏。
~~~html
<h3>v-show</h3>
<p v-show="isShow === true">你能看见我</p>
<button @click="changeShow">切换show的状态</button>
~~~
~~~JavaScript
changeShow:function () {
this.isShow = (this.isShow === true ? false : true);
}
~~~
<p class="run">
运行结果:通过点击按钮,切换元素的隐藏与显示,通过控制台可查看当元素不可见时在页面中也是渲染了当前元素,只是添加了`display:none`的属性控制隐藏。
</p>
### v-if指令的特性
**<span style="padding-top:15px;display:inline-block;">多元素控制</span>**
当一条指令需要控制多条元素时,可将指令添加在标签`template`中,最后的渲染结果不会渲染该元素。
**<span style="padding-top:15px;display:inline-block;">元素复用性</span>**
v-if元素具有元素复用性,即当切换元素后如果元素类型没有改变只是属性发生改变则会复用该元素而只是修改发生变化的部分。解决元素的复用性可在不启用复用元素上添加key属性且key值不同。
代码示例
~~~html
<template v-if="form === 'name'">
<label>请输入姓名:</label>
<input type="text" placeholder="请输入姓名" key="name">
</template>
<template v-else="form === 'email'">
<label>请输入邮箱:</label>
<input type="text" placeholder="请输入邮箱" key="email">
</template>
~~~
~~~JavaScript
changeForm:function () {
this.form = (this.form == "name" ? "email" :"name");
},
~~~
<p class="run">
运行结果:点击按钮切换不同的登录状态。<br />
代码解析:当需要控制多个元素时如上所示添加template标签控制。当在输入框中输入内容后切换状态,输入框中的内容依旧还在只是placeholder等属性发生了改变,说明input框被复用了只是修改了属性值。在input上添加不同的key值后再次切换,输入框内的内容被清空,说明input框被重新编译。
</p>
### v-if和v-show的对比
v-if指令具有懒惰性,在初始化时如果不满足条件则就不会进行编译,直到第一次满足后在进行编译,但是在切换时会重新编译元素,所以在切换状态时具有很大的开销
v-show指令在初始化时需要编译全部的html元素,所以在初始化时具有很大的想开销。
如果项目需要经常切换元素,宜使用`v-show`指令,如果运行时条件不经常改变则使用`v-if`更好。
## 列表渲染指令 v-for
`v-for`是指循环渲染指令,通过使用`v-for`指令,将一个数组渲染成一组元素。使用语法:
```
<span v-for="item in items"></span>
```
其中,items表示数组,item是每一个数组值得别名,用于循环。
### 基本使用
**<span style="padding-top:15px;display:inline-block;">使用数组渲染一组元素</span>**
```html
<ul>
<li v-for="book in books"> {{book}}</li>
</ul>
```
```JavaScript
data:{
books:["高数","英文","语文"],
},
```
<div class="run">
执行结果:
<ul>
<li>高数</li>
<li>英文</li>
<li>语文</li>
</ul>
</div>
**<span style="padding-top:30px;display:inline-block;">添加位置索引</span>**
```html
<ul>
<li v-for="(book , index) in books"> {{index}} - {{book}}</li>
</ul>
```
```JavaScript
data:{
books:["高数","英文","语文"],
},
```
<div class="run">
执行结果:
<ul>
<li>0 - 高数</li>
<li>1 - 英文</li>
<li>2 - 语文</li>
</ul>
</div>
>[info] 可以获取到当前值在数组中的位置,即在别名之后添加一个index的变量。别名和位置使用括号括起来。
**<span style="padding-top:30px;display:inline-block;">直接使用数字</span>**
~~~
<span v-for="n in 10">{{n}}</span>
~~~
<div class="run">
执行结果:
<span>12345678910</span>
</div>
**<span style="padding-top:30px;display:inline-block;">使用对象循环渲染</span>**
```
<ul >
<li v-for="(item , name , index) in items">{{index}} : {{name}} = {{item}}</li>
</ul>
```
~~~JavaScript
items:{
title:"vue实战",
name:"云之遥",
year:"2019年"
},
~~~
<div class="run">
执行结果:
<ul>
<li>0 : title = Vue实战</li>
<li>1 : name = 云之遥</li>
<li>2:yera = 2019年</li>
</ul>
</div>
**<span style="padding-top:30px;display:inline-block;">一次渲染多个元素</span>**
```html
<template v-for="bookInfo in bookInfos">
<span>{{bookInfo.name}}</span>
<span>{{bookInfo.author}}</span>
</template>
```
```javaScript
bookInfos:[
{name:"高数",author:"lili"},
{name:"英语",author:"July"},
{name:"语文",author:"之遥"}
]
```
<div class="run">
执行结果:
<span>高数</span><span>lili</span>
<span>英语</span><span>July</span>
<span>语文</span><span>之遥</span>
</div>
### 数组更新检测
对数组的操作方法分为**变异方法**和**非变异方法**,变异方法即使数组本身发生改变,从而影响了视图更新。
**<span style="padding-top:30px;display:inline-block;">变异方法</span>**
>[info] 变异方法即直接影响数组本身的方法,Vue将变异方法进行了包裹,所以变异方法会直接影响视图变化。
* `push()` // 在原数组的最后追加一个值
* `pop()` // 将原数组截取掉最后一个值
* `shift()` // 截取掉原数组的第一个值
* `unshift()` // 在原数组前添加值
* `splice()` // 截取数组
* `sort()` // 对数组进行排序
* `reverse()` // 对数组进行反转
代码实例
~~~html
<p>变异方法</p>
<ul>
<li v-for="book in books">{{book}}</li>
</ul>
<button @click="handleBooks(1)">添加数组</button>
<button @click="handleBooks(2)">删除数组</button>
<button @click="handleBooks(3)">shift</button>
<button @click="handleBooks(4)">unshift</button>
<button @click="handleBooks(5)">sort</button>
<button @click="handleBooks(6)">reverse</button>
~~~
~~~JavaScript
handleBooks:function (index) {
switch (index) {
case 1 :
this.books.push("物理");
break;
case 2 :
this.books.pop();
break;
case 3 :
this.books.shift();
break;
case 4 :
this.books.unshift("生物","化学","计算机");
break;
case 5:
this.books.sort();
break;
case 6 :
this.books.reverse();
break;
}
},
~~~
<div class="run">
<p>执行结果:</p>
<p>点击添加数组界面显示:【高数、英文、语文、物理】</p>
<p>接续点击删除数组界面显示:【高数、英文、语文】</p>
<p>接续点击shift界面显示:【英文、语文】</p>
<p>接续点击unshift界面显示:【生物、化学、计算机、英文、语文】</p>
<p>接续点击sort界面显示:【化学、生物、英语、计算机、语文】</p>
<p>接续点击reverse界面显示:【语文、计算机、英语、生物、化学】</p>
</div>
**<span style="padding-top:30px;display:inline-block;">非变异方法</span>**
>[info] 非变异方法即使用方法后会放回一个新的数组,但是不会改变原数组。所以非变异方法直接使用后不会更新视图显示。
* `filter()` // 数组过滤函数
* `concat()` // 数组拼接函数
* `slice()` // 数组截取函数
代码实例
~~~html
<p>替换方法</p>
<ul>
<li v-for="array in arrays">{{array.message}}</li>
</ul>
<button @click="handleEvent(1)">filter</button>
<button @click="handleEvent(2)">concat</button>
<button @click="handleEvent(3)">slice</button>
~~~
~~~JavaScript
data:{
arrays:[
{ message:"Foo" },
{ message:"Bar" }
],
},
handleEvent:function (index) {
switch(index){
case 1:
this.arrays = this.arrays.filter(function (item) {
return item.message.match(/Foo/);
})
break;
case 2:
var concatStr = [{
message:"line"
}]
this.arrays = this.arrays.concat(concatStr);
break;
case 3:
this.arrays = this.arrays.slice(1);
break;
}
},
~~~
<div class="run">
<p>执行结果:</p>
<p>点击filter界面显示:【Foo】</p>
<p>接续点击concat界面显示:【Foo、line】</p>
<p>接续点击slice界面显示:【line】</p>
</div>
>[success] 小提示:非变异方法不会改变原数组的值,所以不会响应视图。如果在开发中使用非变异方法且要响应视图,可以使用新数组替换掉旧数组。在Vue中为了使元素最大化的实现复用启用了一些智能方式,所以使用含有相同元素的数组去替换掉旧数组也是比较高效的一种操作。
### 数组变更注意事项
变更注意事项指要<span style="color:red"> 特别注意以下两点不会导致视图响应。</span>
* 第一种:通过索引值直接改变数组的值
* 第二种:通过length直接改变数组的长度
**<span style="padding-top:30px;display:inline-block;">问题展示</span>**
```JavaScript
var arr = ["Foo","Ban"];
arr[1] = "Line"; // 不会触发响应
arr.length = 1; // 不会触发反应
```
**<span style="padding-top:20px;display:inline-block;">解决方式</span>**
```
// 解决第一种问题可以使用下面两种方式
Vue.set(this.arr , 1 , "Line");
this.arr.splice(2,1,"Line");
// 解决第二种问题直接使用splice变异函数
this.arr.splice(newLength);
```
>[info] 关于解决方式:使用Vue的set方法,该方法接收三个参数(数组 , 位置 , 新值);也可以使用splice等变异函数。本质上就是改变数组的值。
**<span style="padding-top:20px;display:inline-block;">代码示例</span>**
~~~html
<p>注意事项</p>
<ul>
<li v-for="book in books">{{book}}</li>
</ul>
<button @click="changeIndex">注意事项1</button>
<button @click="changeIndex1">解决方法</button>
~~~
~~~JavaScript
data:{
books:["高数","英文","语文"],
type:1
}
changeIndex:function () {
this.books[2] = "编译原理"
},
changeIndex1:function () {
if(this.type === 1){
this.books.splice(2 , 1 , "编译原理");
this.type = 2;
}else if (this.type === 2){
Vue.set(this.books , 2 , "操作系统");
this.type = 3;
}else if (this.type === 3){
this.$set(this.books , 2 ,"数字电路");
this.type =1;
}
},
~~~
<div class="run">
<p>执行结果:</p>
<p>点击注意事项1按钮页面无反应,不会改变数组值</p>
<p>接续点击解决方式按钮界面发生改变,以三种不同的方式修改数组值都可以发生改变</p>
</div>
### 对象变更注意事项
对象变更注意事项和数组变更事项类似,动态添加、修改、删除对象的属性都不会导致视图响应。
**<span style="padding-top:20px;display:inline-block;">问题展示</span>**
```
this.items.age = "27"; //该语句不会在视同发生改变
```
**<span style="padding-top:20px;display:inline-block;">解决方式</span>**
* 第一种:使用Vue.set(projectName , key值 , value值)函数
* 第二种:使用实例方法 vm.$set()
**<span style="padding-top:20px;display:inline-block;">代码实例</span>**
~~~html
<ul>
<li v-for="(item , name) in items">{{name}} - {{item}}</li>
</ul>
<button @click="changeKey">注意事项1</button>
<button @click="changeKey1">解决方法</button>
~~~
~~~
data:{
items:{
title:"vue实战",
name:"云之遥",
year:"2019年"
},
}
changeKey:function () {
this.items.age = "27";
},
changeKey1:function () {
if (this.type ===1 ){
Vue.set(this.items , "age" , "27");
this.type = 2;
}else if (this.type === 2){
this.items = Object.assign({} , this.items , {
age:27,
area:"北京"
})
}
}
~~~
<div class="run">
<p>执行结果:</p>
<p>点击注意事项1按钮页面无反应,不会改变数组值</p>
<p>接续点击解决方式按钮界面发生改变,以两种不同的方式修改数组值都可以发生改变</p>
</div>
### v-for与v-if
>[danger] 官方文档提示:一般**不**建议在同一个元素上同时使用v-for和v-if
当一个元素上同时含有`v-for`指令和`v-if`指令时,`v-for`指令的优先级高于`v-if`.会先执行循环,然后在每个循环里进行判断。如果有这种需求可以这样使用。
**<span style="padding-top:20px;display:inline-block;">代码实例</span>**
~~~html
<p>v-if和v-for</p>
<ul>
<li v-for="(todo , index) in todoList" v-if="!todo.isDone">{{index}} - {{todo.event}}</li>
</ul>
~~~
~~~
todoList:[
{ event:"事件1" , isDone:true },
{ event:"事件2" , isDone:false },
{ event:"事件3" , isDone:false },
{ event:"事件4" , isDone:true },
]
~~~
<div class="run">
<p>执行结果:</p>
<ul><!----><li>1 - 事件2</li><li>2 - 事件3</li><!----></ul>
</div>
## 方法与事件指令
>[info] 方法与事件指令 `v-on`指令,语法糖简写为@
### v-on指令的基本使用
**<span style="padding-top:20px;display:inline-block;">五种使用方法</span>**
* 直接在指令后写表达式
* 使用函数名称
* 使用无参函数名
* 使用传参函数名
* 使用传参函数且使用原生事件
**<span style="padding-top:20px;display:inline-block;">代码实例</span>**
```html
<h3>v-on指定的基本使用</h3>
<span>点击次数:{{count}}</span><br />
<button @click="count++">表达式</button>
<button @click="addCount1">直接调用</button>
<button @click="addCount3()">含()调用</button>
<button @click="addCount3(10)">传参调用</button>
<a @click="addCount4(10 , $event)" href="https://www.baidu.com/">event参数</a>
```
~~~JavaScript
addCount1:function () {
this.count ++ ;
},
addCount3:function (param) {
param = param || 1 ;
this.count += param;
},
addCount4:function (param , event) {
event.preventDefault();
this.count += param;
},
~~~
**<span style="padding-top:20px;display:inline-block;">重点说明</span>**
1. 在html中直接写表达式的方式不便于代码维护且只适用于简单逻辑,所以日常开发中很少会使用第一种模式。
2. <div style="background:#fdf7f7;color: #d9534f;border:1px solid #d9534f;padding:3px 10px">第二种直接调用和第三种含括号调用的方式不同,直接调用默认表示传递了一个原生事件参数,所以如果使用直接调用的方式执行`addCount3`时,param不会undefined,而是一个事件对象</div>
3. 如果在使用参数的同时还需要接受一个原生的事件对象,vue提供了$event 参数,**说明:$event参数必须要放在参数的最后一个**。
### 事件修饰符
>[info] 在事件处理程序中调用`event.preventDefault()`或`event.stopPropagation()`是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。而事件修饰符的存在就是为了解决这一问题。
>
**<span style="padding-top:20px;display:inline-block;">常用修饰符</span>**
* `.stop`
* `.prevent`
* `.capture`
* `.self`
* `.once`
**<span style="padding-top:20px;display:inline-block;">修饰符各个含义</span>**
`.prevent`:阻止元素的默认事件发生,即阻止a标签的自动跳转事件、submit的自动提交事件等等默认事件。
`.stop`:阻止冒泡事件发生
`.capture`:修改冒泡事件的触发顺序
`.self`:不对子元素的事件所触发
`.once`:当前点击事件只触发一次
**<span style="padding-top:20px;display:inline-block;">代码实例说明</span>**
~~~html
<h3>事件修饰符</h3>
<div @click="eventClick">
<div @click="eventClick1">
<a href="https://www.baidu.com/" @click="eventClick2">点击我</a>
</div>
</div>
~~~
~~~javascript
eventClick:function () {
console.log("eventClick事件");
},
eventClick1:function () {
console.log("eventClick1事件");
},
eventClick2:function () {
console.log("eventClick2事件");
}
~~~
<div class="run">
<p>执行结果:</p>
<p>1、默认执行结果:eventClick2事件、eventClick1事件、eventClick事件,之后跳转百度首页</p>
<p>2、.stop:eventClick2事件,之后跳转百度首页</p>
<p>3、.prevent:eventClick2事件、eventClick1事件、eventClick事件,不跳转百度首页</p>
<p>4、.prevent:eventClick事件、eventClick1事件、eventClick2事件,跳转百度首页</p>
</div>
**<span style="padding-top:20px;display:inline-block;">修饰符更多详情链接</span>**
[https://blog.csdn.net/qq\_37468455/article/details/95175054](https://blog.csdn.net/qq_37468455/article/details/95175054)
### 按键修饰符
**<span style="padding-top:20px;display:inline-block;">常用的按键修饰符</span>**
* `.enter`
* `.tab`
* `.delete`(捕获“删除”和“退格”键)
* `.esc`
* `.space`
* `.up`
* `.down`
* `.left`
* `.right`
### 系统修饰键
* `.ctrl`
* `.alt`
* `.shift`
* `.meta`
### 在HTML中监听事件的解读
1. 扫一眼 HTML 模板便能轻松定位在 JavaScript 代码里对应的方法。
2. 无须在 JavaScript 里手动绑定事件,你的 ViewModel 代码可以是非常纯粹的逻辑,和 DOM 完全解耦,更易于测试。
3. 当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除。你无须担心如何清理它们。