文章

04.Vue2指令

04.Vue2指令

Vue 指令

指令是带有 v-xxx 前缀的特殊属性。指令用于在表达式的值改变时,将某些行为应用到 DOM 上。

内置指令

v-text 和 v-html

这 2 个指令会覆盖标签内容

  • v-text
    • 向其所在的节点中渲染文本内容
    • 不会解析 html 标签,原本输出
    • 与插值语法的区别:v-text 会替换掉节点中的内容,`` 则不会。
  • v-html
    • 向指定节点中渲染包含 html 结构的内容。
    • v-html 会替换掉节点中所有的内容,则不会。
    • v-html 可以识别 html 结构
    • v-html 有安全性问题
      • 网站上动态渲染任意 HTML 是非常危险的,容易导致 XSS 攻击
      • 一定要在可信的内容上使用 v-html,永不要用在用户提交的内容上
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>v-text指令</title>
		<!-- 引入Vue -->
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<!-- 
			v-text指令:
				1.作用:向其所在的节点中渲染文本内容。
				2.与插值语法的区别:v-text会替换掉节点中的内容,则不会。
		-->
		<div id="root">
			<div>你好,</div>
			<div v-text="name">你好的</div>
			<div v-text="str">我很好啊</div>
		</div>
	</body>
	<script type="text/javascript">
		Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
		
		new Vue({
			el:'#root',
			data:{
				name:'大圣',
				str:'<h3>你好啊!</h3>'
			}
		})
	</script>
</html>

v-cloak 遮挡,不显示

v-cloak 指令(没有值):

  • 本质是一个特殊属性,Vue 实例创建完毕并接管容器后,会删掉 v-cloak 属性。
  • 使用 css 配合 v-cloak 可以解决网速慢时页面展示出 `` 的问题。

加载 vue.js 慢或失败会显示 `` 出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!DOCTYPE html>
<html>

<head>
	<meta charset="UTF-8" />
	<title>v-cloak指令</title>
	<style>
		[v-cloak] {
			display: none;
		}
	</style>
</head>

<body>
	<div id="root">
		<h2 v-cloak></h2>
	</div>
	<script type="text/javascript" src="http://localhost:8080/resource/5s/vue.js"></script>
</body>

<script type="text/javascript">
	console.log(1)
	Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。

	new Vue({
		el: '#root',
		data: {
			name: '大圣'
		}
	})
</script>

</html>

v-once 只渲染一次

  • v-once 所在节点在初次动态渲染后,就视为静态内容了
  • 以后数据的改变不会引起 v-once 所在结构的更新,可以用于优化性能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<!DOCTYPE html>
<html>

<head>
	<meta charset="UTF-8" />
	<title>v-once指令</title>
	<!-- 引入Vue -->
	<script type="text/javascript" src="../js/vue.js"></script>
</head>

<body>
	<!-- 
			v-once指令:
				1.v-once所在节点在初次动态渲染后,就视为静态内容了。
				2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。
		-->
	<!-- 准备好一个容器-->
	<div id="root">
		<h2 v-once>初始化的n值是(有v-once):</h2>
		<h2>当前的n值是(没有-vonce):</h2>
		<button @click="n++">点我n+1</button>
	</div>
</body>

<script type="text/javascript">
	Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。

	new Vue({
		el: '#root',
		data: {
			n: 1
		}
	})
</script>

</html>

点了 5 次后:

dsvui

v-pre

  • 跳过其所在节点的编译过程
  • 可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!DOCTYPE html>
<html>

<head>
	<meta charset="UTF-8" />
	<title>v-pre指令</title>
	<script type="text/javascript" src="../js/vue.js"></script>
</head>

<body>
	<div id="root">
		<h2 v-pre>Vue其实很简单</h2>
		<h2>当前的n值是:</h2>
		<button @click="n++">点我n+1</button>
	</div>
</body>

<script type="text/javascript">
	new Vue({
		el: '#root',
		data: {
			n: 1
		}
	})
</script>

</html>

条件渲染

v-if、v-else、v-else-if

  • 适用于:切换频率较低的场景。
  • 特点:不展示的 DOM 元素直接被移除。
  • 注意:v-if 可以和:v-else-if、v-else 一起使用,但要求结构不能被 “ 打断 “。

v-if

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - xxx</title>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
    <p v-if="seen">现在你看到我了</p>
    <template v-if="ok">
      <h1>xxx</h1>
      <p>学的不仅是技术,更是梦想!</p>
      <p>哈哈哈,打字辛苦啊!!!</p>
    </template>
</div>
    
<script>
new Vue({
  el: '#app',
  data: {
    seen: true,
    ok: true
  }
})
</script>
</body>
</html>

z1yg5

v-if 指令将根据表达式 seen 的值 (true 或 false ) 来决定是否插入 p 元素。

` v-else `

v-else 指令给 v-if 添加一个 “else” 块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div id="app">
    <div v-if="Math.random() > 0.5">
      Sorry
    </div>
    <div v-else>
      Not sorry
    </div>
</div>
    
<script>
new Vue({
  el: '#app'
})
</script>

v-else-if

v-else-if 在 2.1.0 新增,顾名思义,用作 v-if 的 else-if 块。可以链式的多次使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div id="app">
    <div v-if="type === 'A'">
      A
    </div>
    <div v-else-if="type === 'B'">
      B
    </div>
    <div v-else-if="type === 'C'">
      C
    </div>
    <div v-else>
      Not A/B/C
    </div>
</div>
    
<script>
new Vue({
  el: '#app',
  data: {
    type: 'C'
  }
})
</script>

注意,不能被其他标签给打断,否则 v-else 和 v-else-if 的标签就失效了

用 key 管理可复用的元素

Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这么做除了使 Vue 变得非常快之外,还有其它一些好处。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<template v-if="loginType === 'username'">
			<label>Username</label>
			<input placeholder="Enter your username">
		</template>
		<template v-else>
			<label>Email</label>
			<input placeholder="Enter your email address">
		</template>
		<br>
<button @click="changeLoginType">切换登录方式</button>

<script type="text/javascript">
	const vm = new Vue({
		data: {
			loginType: 'username'
		},
		methods: {
			changeLoginType() {
				console.log('changeLoginType, current loginType is', this.loginType)
				this.loginType = this.loginType === 'username' ? 'email' : 'username'
			}
		}
	})
</script>

那么在上面的代码中切换 loginType 将不会清除用户已经输入的内容。因为两个模板使用了相同的元素,<input> 不会被替换掉——仅仅是替换了它的 placeholder

t74l8

如果不需要复用,Vue 为你提供了一种方式来表达 “ 这两个元素是完全独立的,不要复用它们 “。只需添加一个具有唯一值的 key attribute 即可:

1
2
3
4
5
6
7
8
<template v-if="loginType === 'username'">
  <label>Username</label>
  <input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
  <label>Email</label>
  <input placeholder="Enter your email address" key="email-input">
</template>

现在,每次切换时,输入框都将被重新渲染;<label> 元素仍然会被高效地复用,因为它们没有添加 key attribute。

v-show 是否展示,不移除 DOM 元素

  • 适用于:切换频率较高的场景。
  • 特点:不展示的 DOM 元素未被移除,v-show 只是简单地切换元素的 CSS property display
1
<h1 v-show="ok">Hello!</h1>

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

列表渲染

v-for 循环语句

v-for 语法

  • 用于展示列表数据
  • 语法:v-for="(item, index) in xxx" :key="yyy";也可以用 of 替代 in 作为分隔符,因为它更接近 JavaScript 迭代器的语法
  • 可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<!DOCTYPE html>
<html>

<head>
	<meta charset="UTF-8" />
	<title>基本列表</title>
	<script type="text/javascript" src="../js/vue.js"></script>
</head>

<body>
	<!-- 
			v-for指令:
				1.用于展示列表数据
				2.语法:v-for="(item, index) in xxx" :key="yyy"
				3.可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
		-->
	<!-- 准备好一个容器-->
	<div id="root">
		<!-- 遍历数组 -->
		<h2>人员列表(遍历数组)</h2>
		<ul>
			<li v-for="(p,index) of persons" :key="index">
				-
			</li>
		</ul>

		<!-- 遍历对象 -->
		<h2>汽车信息(遍历对象)</h2>
		<ul>
			<li v-for="(value,k) of car" :key="k">
				-
			</li>
		</ul>

		<!-- 遍历字符串 -->
		<h2>测试遍历字符串(用得少)</h2>
		<ul>
			<li v-for="(char,index) of str" :key="index">
				-
			</li>
		</ul>

		<!-- 遍历指定次数 -->
		<h2>测试遍历指定次数(用得少)</h2>
		<ul>
			<li v-for="(number,index) of 5" :key="index">
				-
			</li>
		</ul>
	</div>

	<script type="text/javascript">
		Vue.config.productionTip = false

		new Vue({
			el: '#root',
			data: {
				persons: [
					{ id: '001', name: '张三', age: 18 },
					{ id: '002', name: '李四', age: 19 },
					{ id: '003', name: '王五', age: 20 }
				],
				car: {
					name: '奥迪A8',
					price: '70万',
					color: '黑色'
				},
				str: 'hello'
			}
		})
	</script>

</html>

g1km3

v-for 中的 key

key 的内部原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
<!DOCTYPE html>
<html>

<head>
	<meta charset="UTF-8" />
	<title>key的原理</title>
	<script type="text/javascript" src="../js/vue.js"></script>
</head>

<body>
	<!-- 
			面试题:react、vue中的key有什么作用?(key的内部原理)
				
				1. 虚拟DOM中key的作用:
					key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】, 
					随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
								
				2.对比规则:
					(1).旧虚拟DOM中找到了与新虚拟DOM相同的key:                                               
					①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!
					②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。

					(2).旧虚拟DOM中未找到与新虚拟DOM相同的key
						创建新的真实DOM,随后渲染到到页面。
										
				3. 用index作为key可能会引发的问题:
					1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
					会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。

					2. 如果结构中还包含输入类的DOM:
						会产生错误DOM更新 ==> 界面有问题。

				4. 开发中如何选择key?:
					1.最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
					2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,
					使用index作为key是没有问题的。
		-->

	<!-- 准备好一个容器-->
	<div id="root">
		<!-- 遍历数组 -->
		<h2>人员列表(遍历数组)</h2>
		<button @click.once="add">添加一个老刘</button>
		<ul>
			<li v-for="(p,index) of persons" :key="index">
				-
				<input type="text">
			</li>
		</ul>
	</div>

	<script type="text/javascript">
		Vue.config.productionTip = false

		new Vue({
			el: '#root',
			data: {
				persons: [{
					id: '001',
					name: '张三',
					age: 18
				},
				{
					id: '002',
					name: '李四',
					age: 19
				},
				{
					id: '003',
					name: '王五',
					age: 20
				}
				]
			},
			methods: {
				add() {
					const p = {
						id: '004',
						name: '老刘',
						age: 40
					}
					this.persons.unshift(p)
				}
			},
		})
	</script>

</html>

在 v-for 使用数组

v-for 还支持一个可选的第二个参数,即当前项的索引。

  • v-for="item in items"
  • v-for="item,i in items" :key="i"
  • v-for="item,index of/in items"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<ul>
    <li v-for="(p,index) in persons" :key="index">
      --
    </li>
    <hr />
    <li v-for="(p,i) in persons" :key="i">
      --
    </li>
    <hr />
    <li v-for="(p,index) of persons">
      --
    </li>
</ul>

data: {
  persons: [
    { id: '001', name: '张三', age: 18 },
    { id: '002', name: '李四', age: 19 },
    { id: '003', name: '王五', age: 20 }
  ]
}

在 v-for 里使用对象

也可以提供第二个的参数为 property 名称 (也就是键名):

1
2
3
<div v-for="(value, name) in object">
  : 
</div>

还可以用第三个参数作为索引:

1
2
3
<div v-for="(value, name, index) in object">
  . : 
</div>

在遍历对象时,会按 Object.keys() 的结果遍历,但是不能保证它的结果在不同的 JavaScript 引擎下都一致。

v-for 迭代字符串

1
2
3
4
5
6
7
<!-- 遍历字符串 -->
<h2>测试遍历字符串(用得少)</h2>
<ul>
  <li v-for="(char,index) of str" :key="index">
    -
  </li>
</ul>

v-for 迭代整数

v-for 也可以接受整数。在这种情况下,它会把模板重复对应次数。

1
2
3
4
5
6
7
<div id="app">
  <ul>
    <li v-for="n in 10">
     
    </li>
  </ul>
</div>

显示过滤/排序后的结果

有时,我们想要显示一个数组经过过滤或排序后的版本,而不实际变更或重置原始数据。在这种情况下,可以创建一个计算属性,来返回过滤或排序后的数组。

1
2
3
4
5
6
7
8
9
10
11
12
<li v-for="n in evenNumbers"></li>

data: {
  numbers: [ 1, 2, 3, 4, 5 ]
},
computed: {
  evenNumbers: function () {
    return this.numbers.filter(function (number) {
      return number % 2 === 0
    })
  }
}

在计算属性不适用的情况下 (例如,在嵌套 v-for 循环中) 你可以使用一个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
<ul v-for="set in sets">
  <li v-for="n in even(set)"></li>
</ul>
data: {
  sets: [[ 1, 2, 3, 4, 5 ], [6, 7, 8, 9, 10]]
},
methods: {
  even: function (numbers) {
    return numbers.filter(function (number) {
      return number % 2 === 0
    })
  }
}
案例:过滤
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<!DOCTYPE html>
<html>

<head>
	<meta charset="UTF-8" />
	<title>列表过滤</title>
	<script type="text/javascript" src="../js/vue.js"></script>
</head>

<body>
	<!-- 准备好一个容器-->
	<div id="root">
		<h2>人员列表</h2>
		<input type="text" placeholder="请输入名字" v-model="keyWord">
		<ul>
			<li v-for="(p,index) of filPerons" :key="index">
				--
			</li>
		</ul>
	</div>

	<script type="text/javascript">
		Vue.config.productionTip = false
		//用computed实现
		new Vue({
			el: '#root',
			data: {
				keyWord: '',
				persons: [
					{ id: '001', name: '马冬梅', age: 19, sex: '' },
					{ id: '002', name: '周冬雨', age: 20, sex: '' },
					{ id: '003', name: '周杰伦', age: 21, sex: '' },
					{ id: '004', name: '温兆伦', age: 22, sex: '' }
				]
			},
			computed: {
				filPerons() {
					return this.persons.filter((p) => {
						return p.name.indexOf(this.keyWord) !== -1
					})
				}
			}
		}) 
	</script>

</html>

1vqmu

案例:排序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<!DOCTYPE html>
<html>

<head>
	<meta charset="UTF-8" />
	<title>列表排序</title>
	<script type="text/javascript" src="../js/vue.js"></script>
</head>

<body>
	<!-- 准备好一个容器-->
	<div id="root">
		<h2>人员列表</h2>
		<input type="text" placeholder="请输入名字" v-model="keyWord">
		<button @click="sortType = 2">年龄升序</button>
		<button @click="sortType = 1">年龄降序</button>
		<button @click="sortType = 0">原顺序</button>
		<ul>
			<li v-for="(p,index) of filPerons" :key="p.id">
				--
				<input type="text">
			</li>
		</ul>
	</div>

	<script type="text/javascript">
		Vue.config.productionTip = false

		new Vue({
			el: '#root',
			data: {
				keyWord: '',
				sortType: 0, //0原顺序 1降序 2升序
				persons: [
					{ id: '001', name: '马冬梅', age: 30, sex: '' },
					{ id: '002', name: '周冬雨', age: 31, sex: '' },
					{ id: '003', name: '周杰伦', age: 18, sex: '' },
					{ id: '004', name: '温兆伦', age: 19, sex: '' }
				]
			},
			computed: {
				filPerons() {
					const arr = this.persons.filter((p) => {
						return p.name.indexOf(this.keyWord) !== -1
					})
					//判断一下是否需要排序
					if (this.sortType) {
						arr.sort((p1, p2) => {
							return this.sortType === 1 ? p2.age - p1.age : p1.age - p2.age
						})
					}
					return arr
				}
			}
		})

	</script>

</html>

flybw

<template> 上使用 v-for

类似于 v-if,你也可以利用带有 v-for 的 <template> 来循环渲染一段包含多个元素的内容。比如:

1
2
3
4
5
6
<ul>
  <template v-for="item in items">
    <li></li>
    <li class="divider" role="presentation"></li>
  </template>
</ul>

v-for 与 v-if 一同使用

v-for 与 v-if 一同使用
当它们处于同一节点,v-for 的优先级比 v-if 更高,这意味着 v-if 将分别重复运行于每个 v-for 循环中。当你只想为部分项渲染节点时,这种优先级的机制会十分有用,如下:

1
2
3
<li v-for="todo in todos" v-if="!todo.isComplete">
  
</li>

在组件上使用 v-for

在自定义组件上,你可以像在任何普通元素上一样使用 v-for。

1
<my-component v-for="item in items" :key="item.id"></my-component>

数组更新检测

数组更新问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<!DOCTYPE html>
<html>

<head>
	<meta charset="UTF-8" />
	<title>更新时的一个问题</title>
	<script type="text/javascript" src="../js/vue.js"></script>
</head>

<body>
	<!-- 准备好一个容器-->
	<div id="root">
		<h2>人员列表</h2>
		<button @click="updateMei">更新马冬梅的信息</button>
		<ul>
			<li v-for="(p,index) of persons" :key="p.id">
				--
			</li>
		</ul>
	</div>

	<script type="text/javascript">
		Vue.config.productionTip = false

		const vm = new Vue({
			el: '#root',
			data: {
				persons: [
					{ id: '001', name: '马冬梅', age: 30, sex: '' },
					{ id: '002', name: '周冬雨', age: 31, sex: '' },
					{ id: '003', name: '周杰伦', age: 18, sex: '' },
					{ id: '004', name: '温兆伦', age: 19, sex: '' }
				]
			},
			methods: {
				updateMei() {
					// this.persons[0].name = '马老师' // ok
					// this.persons[0].age = 50 // ok
					// this.persons[0].sex = '男' // ok
					// this.persons[0] = {id:'001',name:'马老师',age:50,sex:'男'} // no
					this.persons.splice(0, 1, { id: '001', name: '马老师', age: 50, sex: '' }) // ok
				}
			}
		})

	</script>

</html>

属性指令

参数在指令后以 冒号 指明。例如, v-bind 指令被用来响应地更新 HTML 属性:

v-bind

v-bind:href

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - baidu</title>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
    <pre><a v-bind:href="url">baidu</a></pre>
</div>
	
<script>
new Vue({
  el: '#app',
  data: {
    url: 'http://www.baidu.com'
  }
})
</script>
</body>
</html>

在这里 href 是参数,告知 v-bind 指令将该 <a> 标签的 href 属性与表达式 url 的值绑定,即 href 指向百度

v-on

v-on 它用于监听 DOM 事件:

v-on:click 点击事件

示例:在用户点击按钮后对字符串进行反转操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - xxx</title>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
    <p></p>
    <button v-on:click="reverseMessage">反转字符串</button>
</div>
	
<script>
new Vue({
  el: '#app',
  data: {
    message: 'xxx123!'
  },
  methods: {
    reverseMessage: function () {
      this.message = this.message.split('').reverse().join('')
    }
  }
})
</script>
</body>
</html>

v-bind 和 v-on 缩写

Vue.js 为两个最为常用的指令提供了特别的缩写:

  • v-bind 缩写
1
2
3
4
<!-- 完整语法 -->
<a v-bind:href="url"></a>
<!-- 缩写 -->
<a :href="url"></a>
  • v-on 缩写
1
2
3
4
<!-- 完整语法 -->
<a v-on:click="doSomething"></a>
<!-- 缩写 -->
<a @click="doSomething"></a>

事件指令

事件的基本使用:

  1. 使用 v-on:xxx@xxx 绑定事件,其中 xxx 是事件名;
  2. 事件的回调需要配置在 methods 对象中,最终会在 vm 上;
  3. methods 中配置的函数,不要用箭头函数(=>),否则 this 就不是 vm 了,而是 window(针对浏览器环境来说);
  4. methods 中配置的函数,都是被 Vue 所管理的函数,this 的指向是 vm 或 组件实例对象;
  5. @click="demo"@click="demo($event)" 效果一致,但后者可以传参;

v-on:click@click

  • 事件监听可以使用 v-on 指令:
1
2
3
4
5
6
7
8
9
10
11
12
13
<div id="app">
  <button v-on:click="counter += 1">增加 1</button>
  <p>这个按钮被点击了  次。</p>
</div>
 
<script>
new Vue({
  el: '#app',
  data: {
    counter: 0
  }
})
</script>
  • 调用方法

通常情况下,我们需要使用一个方法来调用 JavaScript 方法。
v-on 可以接收一个定义的方法来调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<div id="app">
   <!-- `greet` 是在下面定义的方法名 -->
  <button v-on:click="greet">Greet</button>
</div>
 
<script>
var app = new Vue({
  el: '#app',
  data: {
    name: 'Vue.js'
  },
  // 在 `methods` 对象中定义方法
  methods: {
    greet: function (event) {
      // `this` 在方法里指当前 Vue 实例
      alert('Hello ' + this.name + '!')
      // `event` 是原生 DOM 事件
      if (event) {
          alert(event.target.tagName)
      }
    }
  }
})
// 也可以用 JavaScript 直接调用方法
app.greet() // -> 'Hello Vue.js!'
</script>
  • 也可以用内联 JavaScript 语句:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div id="app">
  <button v-on:click="say('hi')">Say hi</button>
  <button v-on:click="say('what')">Say what</button>
</div>
 
<script>
new Vue({
  el: '#app',
  methods: {
    say: function (message) {
      alert(message)
    }
  }
})
</script>

表单指令

v-model 双向绑定

v-model 实现双向数据绑定
v-model 指令用来在 inputselecttextareacheckboxradio 等表单控件元素上创建双向数据绑定,根据表单上的值,自动更新绑定的元素的值。
示例:在 input 输入框中我们可以使用 v-model 指令来实现双向数据绑定:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - xxx</title>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
    <p></p>
    <input v-model="message">
</div>
	
<script>
new Vue({
  el: '#app',
  data: {
    message: 'Runoob!'
  }
})
</script>
</body>
</html>

修饰符

修饰符是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。
例如,.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault():

1
<form v-on:submit.prevent="onSubmit"></form>

Vue.js 通过由点 . 表示的指令后缀来调用修饰符。

事件修饰符

Vue.js 为 v-on 提供了事件修饰符来处理 DOM 事件细节,如:event.preventDefault()event.stopPropagation():

  • .stop - 阻止事件的冒泡
  • .prevent - 阻止默认事件传递
  • .once - 只触发一次,多次点击只有第 1 次有效
  • .capture - 阻止事件的捕获
  • .self - 只监听触发该元素的事件
  • .left - 左键事件
  • .right- 右键事件
  • .middle - 中间滚轮事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- 阻止单击事件冒泡 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联  -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件侦听器时使用事件捕获模式 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当事件在该元素本身(而不是子元素)触发时触发回调 -->
<div v-on:click.self="doThat">...</div>

<!-- click 事件只能点击一次,2.1.4版本新增 -->
<a v-on:click.once="doThis"></a>

  • 示例 1:.prevent 阻止 a 标签调整到 baidu 首页,只执行 onClick 的 js 函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<div id="root">
	<h2>欢迎来到学习</h2>
	<!-- 阻止默认事件(常用) -->
	<a href="http://www.atguigu.com" @click.prevent="showInfo">点我提示信息</a>
</div>
<script type="text/javascript">
	Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
  new Vue({
    el:'#root',
    data:{
      name:'大圣课堂'
    },
    methods:{
      showInfo(e){
        e.productionTip = false;	// 阻止默认事件, 这里表示阻止打开网址
        alert('同学你好!')
        console.log(e.target)
      }
    }
  })
</script>
  • 示例 2: .stop 阻止事件冒泡

如果不加的话,点击 button showInfo 会执行两次

1
2
3
4
5
6
7
8
9
10
11
<!-- 阻止事件冒泡(常用) -->
<div class="demo1" @click="showInfo">
  <button @click="showInfo">点我提示信息</button>
</div>
<!-- 阻止冒泡 -->
<div class="demo1" @click="showInfo">
  <button @click.stop="showInfo">点我提示信息</button>
</div>

<!-- prevent的作用是阻止默认事件发生(这里表示打开网址) -->
<a href="http://www.atguigu.com" @click.prevent.stop="showInfo">点我提示信息2</a>
  • 示例 3:.once 事件只执行一次,多次点击只有第 1 次有效
1
2
<!-- 事件只触发一次(常用) -->
<button @click.once="showInfo">点我提示信息</button>
  • 示例 4:.self

div 没加 .self 的情况,点击 button showInfo 会执行 2 次(div 和 button 各一次),加了.self 后只有点击 div 才会执行 showInfo,点击 button 后 div 的 click 不会响应

1
2
3
4
<!-- 只有event.target是当前操作的元素时才触发事件; -->
<div class="demo1" @click.self="showInfo">
  <button @click="showInfo">点我提示信息</button>
</div>

按键修饰符

Vue 允许为 v-on 在监听键盘事件时添加按键修饰符:

1
2
<!-- 只有在 keyCode 是 13 时调用 vm.submit() -->
<input v-on:keyup.13="submit">

记住所有的 keyCode 比较困难,所以 Vue 为最常用的按键提供了别名:

1
2
3
4
<!-- 同上 -->
<input v-on:keyup.enter="submit">
<!-- 缩写语法 -->
<input @keyup.enter="submit">

常用按键别名:

  • .enter 回车
  • .tab (特殊,必须配合 keydown 去使用,tab 键是切换焦点,如果是配合 keyUp 就切走了不生效)
  • .delete (捕获 “ 删除 “ 和 “ 退格 “ 键)
  • .esc 退出
  • .space 空格
  • .up 上
  • .down 下
  • .left 左
  • .right 右

系统修饰键(用法特殊):ctrl、alt、shift、meta(Windows 下的 winkey 键):

  • 配合 keyup 时使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
  • 配合 keydown 使用:正常触发事件。

Vue 未提供别名的按键,可以使用按键原始的 key 值去绑定,但注意要转为 kebab-case(短横线命名)
自定义键别名:Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名

1
2
3
4
5
6
Vue.config.keyCodes.huiche = 13 //定义了一个别名按键

<div id="root">
  <h2>欢迎来到学习</h2>
  <input type="text" placeholder="按下回车提示输入" @keydown.huiche="showInfo">
</div>

Vue.js 样式绑定

classstyle 是 HTML 元素的属性,用于设置元素的样式,我们可以用 v-bind 来设置样式属性。
Vue.js v-bind 在处理 class 和 style 时, 专门增强了它。表达式的结果类型除了字符串之外,还可以是对象或数组。

class 属性绑定 ([v-bind]:class='xxx'

写法: [v-bind]:class="xxx" xxx 可以是字符串、对象、数组。

  • 字符串写法适用于:类名不确定,要动态获取。
  • 对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。
  • 数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。

style(内联样式) ([v-bind]:style='xxx'

  • :style="{fontSize: xxx}" 其中 xxx 是动态值。
  • :style="[a,b]" 其中 a、b 是样式对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - xxx</title>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
	<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }">vue.js style</div>
</div>

<script>
new Vue({
  el: '#app',
  data: {
    activeColor: 'green',
	fontSize: 30
  }
})
</script>
</body>
</html>

kz75y

以上实例 div style 为:<div style="color: green; font-size: 30px;">vue.js style</div>

  • 也可以直接绑定到一个样式对象,让模板更清晰:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<body>
<div id="app">
  <div v-bind:style="styleObject">xxx</div>
</div>

<script>
new Vue({
  el: '#app',
  data: {
    styleObject: {
      color: 'green',
      fontSize: '30px'
    }
  }
})
</script>
</body>
</html>
  • v-bind:style 可以使用数组将多个样式对象应用到一个元素上:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<body>
<div id="app">
  <div v-bind:style="[baseStyles, overridingStyles]">xxx</div>
</div>

<script>
new Vue({
  el: '#app',
  data: {
    baseStyles: {
      color: 'green',
      fontSize: '30px'
    },
	overridingStyles: {
      'font-weight': 'bold'
    }
  }
})
</script>
</body>
</html>

注意:当 v-bind:style 使用需要特定前缀的 CSS 属性时,如 transform ,Vue.js 会自动侦测并添加相应的前缀。

class 和 style 案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
<!DOCTYPE html>
<html>

<head>
	<meta charset="UTF-8" />
	<title>绑定样式</title>
	<style>
		.basic {
			width: 400px;
			height: 100px;
			border: 1px solid black;
		}

		.happy {
			border: 4px solid red;
			;
			background-color: rgba(255, 255, 0, 0.644);
			background: linear-gradient(30deg, yellow, pink, orange, yellow);
		}

		.sad {
			border: 4px dashed rgb(2, 197, 2);
			background-color: gray;
		}

		.normal {
			background-color: skyblue;
		}

		.atguigu1 {
			background-color: yellowgreen;
		}

		.atguigu2 {
			font-size: 30px;
			text-shadow: 2px 2px 10px red;
		}

		.atguigu3 {
			border-radius: 20px;
		}
	</style>
	<script type="text/javascript" src="../js/vue.js"></script>
</head>

<body>
	<!-- 
		绑定样式:
		1. class样式
			写法:class="xxx" xxx可以是字符串、对象、数组。
				字符串写法适用于:类名不确定,要动态获取。
				对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。
				数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。
		2. style样式
			:style="{fontSize: xxx}"其中xxx是动态值。
			:style="[a,b]"其中a、b是样式对象。
		-->
	<!-- 准备好一个容器-->
	<div id="root">
		<!-- 绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定 -->
		<div class="basic" v-bind:class="mood" @click="changeMood">点击随机class:</div> <br /><br />

		<!-- 绑定class样式--数组写法,适用于:要绑定的样式个数不确定、名字也不确定 -->
		<div class="basic" :class="classArr">数组:绑定多个class:</div> <br /><br />

		<!-- 绑定class样式--对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 -->
		<div class="basic" :class="classObj">是否决定用class:</div> <br /><br />

		<!-- 绑定style样式--对象写法 -->
		<div class="basic" :style="styleObj">样式对象(vue中定义style):</div> <br /><br />
		<!-- 绑定style样式--数组写法 -->
		<div class="basic" :style="styleArr">数组:vue中定义sytle数组:</div>
	</div>
</body>

<script type="text/javascript">
	Vue.config.productionTip = false

	const vm = new Vue({
		el: '#root',
		data: {
			name: '齐天大圣',
			mood: 'normal',
			classArr: ['atguigu1', 'atguigu2', 'atguigu3'],
			classObj: {
				atguigu1: false,
				atguigu2: false,
			},
			styleObj: {
				fontSize: '40px',
				color: 'red',
			},
			styleObj2: {
				backgroundColor: 'orange'
			},
			styleArr: [{
					fontSize: '40px',
					color: 'blue',
				},
				{
					backgroundColor: 'gray'
				}
			]
		},
		methods: {
			changeMood() {
				const arr = ['happy', 'sad', 'normal']
				const index = Math.floor(Math.random() * 3)
				this.mood = arr[index]
			}
		},
	})
</script>

</html>

b5qn9

表单输入绑定

基础

可以用 v-model 指令在表单 <input><textarea> 及 `

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

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

表单数据收集

  • 若:<input type="text/password/number"/> ,则 v-model 收集的是 value 值,用户输入的就是 value 值。
  • 若:,则 v-model 收集的是 value 值,且要给标签配置 value 值。
  • 若:
    • 没有配置 input 的 value 属性,那么收集的就是 checked(勾选 or 未勾选,是布尔值)
    • 配置了 input 的 value 属性:
      • v-model 的初始值是非数组,那么收集的就是 checked(勾选 or 未勾选,是布尔值)
      • v-model 的初始值是数组,那么收集的的就是 value 组成的数组

v-model 的三个修饰符

.lazy

在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步 (除了 上述 输入法组合文字时)。你可以添加 lazy 修饰符,从而转为在 change 事件之后进行同步(失去焦点再收集数据)

1
2
<!-- 在“change”时而非“input”时更新 -->
<input v-model.lazy="msg">

.number

输入字符串转为有效的数字

1
<input v-model.number="age" type="number">

.trim

输入首尾空格过滤

1
<input v-model.trim="msg">

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
<!DOCTYPE html>
<html>

<head>
	<meta charset="UTF-8" />
	<title>收集表单数据</title>
	<script type="text/javascript" src="../js/vue.js"></script>
</head>

<body>
<!-- 
	收集表单数据:
	若:<input type="text"/>,则v-model收集的是value值,用户输入的就是value值。
	若:<input type="radio"/>,则v-model收集的是value值,且要给标签配置value值。
	若:<input type="checkbox"/>
		1.没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)
		2.配置input的value属性:
			(1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
			(2)v-model的初始值是数组,那么收集的的就是value组成的数组
	备注:v-model的三个修饰符:
		lazy:失去焦点再收集数据
		number:输入字符串转为有效的数字
		trim:输入首尾空格过滤
-->
	<!-- 准备好一个容器-->
	<div id="root">
		<form @submit.prevent="demo">
			账号:<input type="text" v-model.trim="userInfo.account" placeholder="输入账号"> <br /><br />
			密码:<input type="password" v-model="userInfo.password"> <br /><br />
			年龄:<input type="number" v-model.number="userInfo.age"> <br /><br />
			性别:
			男<input type="radio" name="sex" v-model="userInfo.sex" value="male"><input type="radio" name="sex" v-model="userInfo.sex" value="female"> <br /><br />
			爱好:
			学习<input type="checkbox" v-model="userInfo.hobby" value="study">
			打游戏<input type="checkbox" v-model="userInfo.hobby" value="game">
			吃饭<input type="checkbox" v-model="userInfo.hobby" value="eat">
			<br /><br />
			所属校区
			<select v-model="userInfo.city">
				<option value="">请选择校区</option>
				<option value="beijing">北京</option>
				<option value="shanghai">上海</option>
				<option value="shenzhen">深圳</option>
				<option value="wuhan">武汉</option>
			</select>
			<br /><br />
			其他信息:
			<textarea v-model.lazy="userInfo.other"></textarea> <br /><br />
			<input type="checkbox" v-model="userInfo.agree">阅读并接受<a href="http://www.baidu.com">《用户协议》</a>
			<button>提交</button>
		</form>
	</div>
</body>

<script type="text/javascript">
	Vue.config.productionTip = false

	const vm = new Vue({
		el: '#root',
		data: {
			userInfo: {
				account: '',
				password: '',
				age: 18,
				sex: 'female',
				hobby: [],
				city: 'beijing',
				other: '',
				agree: ''
			}
		},
		methods: {
			demo() {
				console.log(JSON.stringify(this.userInfo))
			}
		}
	})
</script>

</html>

rj54b

点击提交按钮后输出:

{“account”:”hacket”,”password”:”123”,”age”:18,”sex”:”female”,”hobby”:[“study”,”game”],”city”:”beijing”,”other”:”xxxxx”,”agree”:true}

自定义指令

局部

1
2
3
4
5
6
7
8
9
10
11
new Vue({
  // ... ,
  directives: {
    focus: {
      // 指令的定义
      inserted: function (el) {
        el.focus()
      }
    }
  }
})

全局

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

钩子函数 标签的回调

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

钩子函数参数

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
<!DOCTYPE html>
<html>

<head>
	<meta charset="UTF-8" />
	<title>自定义指令</title>
	<script type="text/javascript" src="../js/vue.js"></script>
</head>

<body>
<!-- 
	需求1:定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍。
	需求2:定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点。
	
	自定义指令总结:
		一、定义语法:
			(1).局部指令:
				new Vue({
					new Vue({
						directives:{指令名:配置对象} 
						或   directives{指令名:回调函数}
					}) 	
				})
			(2).全局指令:
				Vue.directive(指令名,配置对象) 或   Vue.directive(指令名,回调函数)

		二、配置对象中常用的3个回调:
			(1).bind:指令与元素成功绑定时调用。
			(2).inserted:指令所在元素被插入页面时调用。
			(3).update:指令所在模板结构被重新解析时调用。

		三、备注:
			1.指令定义时不加v-,但使用时要加v-;
			2.指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。
-->
	<!-- 准备好一个容器-->
	<div id="root">
		<h2></h2>
		<h2>当前的n值是:<span v-text="n"></span> </h2>
		<!-- <h2>放大10倍后的n值是:<span v-big-number="n"></span> </h2> -->
		<h2>放大10倍后的n值是:<span v-big="n"></span> </h2>
		<button @click="n++">点我n+1</button>
		<hr />
		<input type="text" v-fbind:value="n">
	</div>
</body>

<script type="text/javascript">
	Vue.config.productionTip = false

	//定义全局指令
	/* Vue.directive('fbind',{
		//指令与元素成功绑定时(一上来)
		bind(element,binding){
			element.value = binding.value
		},
		//指令所在元素被插入页面时
		inserted(element,binding){
			element.focus()
		},
		//指令所在的模板被重新解析时
		update(element,binding){
			element.value = binding.value
		}
	}) */

	new Vue({
		el: '#root',
		data: {
			name: '大圣',
			n: 1
		},
		directives: {
			//big函数何时会被调用?1.指令与元素成功绑定时(一上来)。2.指令所在的模板被重新解析时。
			/* 'big-number'(element,binding){
				// console.log('big')
				element.innerText = binding.value * 10
			}, */
			big(element, binding) {
				console.log('big', this) //注意此处的this是window
				// console.log('big')
				element.innerText = binding.value * 10
			},
			fbind: {
				//指令与元素成功绑定时(一上来)
				bind(element, binding) {
					element.value = binding.value
				},
				//指令所在元素被插入页面时
				inserted(element, binding) {
					element.focus()
				},
				//指令所在的模板被重新解析时
				update(element, binding) {
					element.value = binding.value
				}
			}
		}
	})

</script>

</html>

yxv4w

本文由作者按照 CC BY 4.0 进行授权