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 次后:
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>
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
如果不需要复用,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>
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>
案例:排序
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>
在 <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>
事件指令
事件的基本使用:
- 使用
v-on:xxx
或@xxx
绑定事件,其中 xxx 是事件名; - 事件的回调需要配置在
methods
对象中,最终会在 vm 上; - methods 中配置的函数,不要用箭头函数(=>),否则 this 就不是 vm 了,而是 window(针对浏览器环境来说);
- methods 中配置的函数,都是被 Vue 所管理的函数,this 的指向是 vm 或 组件实例对象;
@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 指令用来在 input
、select
、textarea
、checkbox
、radio
等表单控件元素上创建双向数据绑定,根据表单上的值,自动更新绑定的元素的值。
示例:在 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 样式绑定
class
与 style
是 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>
以上实例 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>
表单输入绑定
基础
可以用 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>
点击提交按钮后输出:
{“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>