2.Vue模板应用

模板基础

对于普通的HTML稳定,若要在数据变化时对其进行页面更新,则需要通过JavaScript的DOM操作来获取指定的元素,在对其属性或内部文本进行修改,操作其来时十分繁琐且容易出错。如果使用Vue的模板语法,则会变得很简单,我们只需将要变化的值定义成变量,之后将变量插入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 lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>模板插值</title>
<script src="https://unpkg.com/vue@3.2.47/dist/vue.global.js"></script>
</head>
<body>
<div style="text-align: center;" id="dianji">
<h1>这里是模板内容:{{count}} 点击</h1>
<button v-on:click="clickButton">点击+1</button>
</div>

<script>
const App = {
data(){
return {
count:0,
}
},
methods: {
clickButton(){
this.count = this.count+1
}
}
}
Vue.createApp(App).mount("#dianji")
</script>
</body>
</html>

在HTML的标签中使用{{}}可以进行变量赋值,这是Vue中的基础模板语法,并且这种插值默认实现绑定效果

某些条件下,某些组件的渲染是由变量控制的,但是我们想让它一旦渲染后就不能在被修改,这时可以使用模板语法的v-once指令来实现

1
2
<!-- 接收到第一渲染即 return count=0,再怎么点击按钮,值也不会发生改变 -->
<h1 v-once>这里是模板内容:{{count}} 点击</h1>

当要插入的文本为一段HTML代码时,则直接使用{{}}就不太好使了,{{}}会将其内的变量解析成纯文本。

1
2
3
4
5
6
7
8
9
<h1>这里是模板内容:{{countHTML}} 点击</h1>

data(){
return {
// 返回的数据目前只有 {{count}}
count:0,
countHTML:"<span style='color: red;'>0</span>",
}
},

运行出来的效果

1
这里是模板内容:<span style='color: red;'>0</span> 点击

这种效果明显不符合我们的预期,对于HTML代码插值,我们需要使用v-html指令来完成

1
<h1>这里是模板内容:<span v-html="countHTML"></span> 点击</h1>

标签除了其内部内容外,本身的属性设置也非常重要,例如我们可能需要动态改变标签的style属性,从而实现元素渲染样式的修改。

对于标签属性的插值,Vue中不在使用{{}}的方式,而是使用v-bind指令

1
<h1 v-bind:id="id1">这里是模板内容:{{count}} 点击</h1>

定义一个简单的CSS样式

1
2
3
4
5
6
<style>
#h1 {
color: red;
}

</style>

在添加一个名为id1的Vue组件属性

1
2
3
4
5
6
7
8
data(){
return {
// 返回的数据目前只有 {{count}}
count:0,
countHTML:"<span style='color: red;'>0</span>",
id1:'h1'
}
},

v-bind指令同样也试用其他HTML属性,只需要在其中使用冒号加属性名的方式指定即可。

无论是{{}}方式的标签内容插值还是v-bind方式标签属性插值,也可直接使用基本的Javascript表达式。

1
2
 <!-- 插值的地方使用表达式,只能使用单个表达式,否则会产生异常 -->
<h1 v-bind:id="id1">这里是模板内容:{{count + 10}} 点击</h1>

模板指令

Vue的模板指令也是HTML标签属性,通常由前缀v-开头,例如前边使用的v-bind,v-once等指令

1
2
3
4
5
6
7
8
9
10
11
12
13
<div id="Application">
<h1 v-if="show">标题</h1>
</div>
<script>
const App = {
data(){
return{
show:false
}
},
}
Vue.create(App).mount("#Application")
</script>

v-if是一个简单的选择渲染指令,设置布尔值为true时,当前标签元素才会被渲染。

某写特殊的Vue指令也可以指定参数,例如v-bind和v-on指令,对于参加参数的指令,参数和指令用冒号进行分割,例如:

1
2
v-bind:style
v-on:click

指令的参数本身也可以是动态的,例如我们可以定义区分id选择器和类选择器来定义不同的组件样式,之后动态地切换组件的属性。示例如下:

1
2
3
4
5
6
7
8
<style>
#h1 {
color:red;
}
.h1 {
color:blue
}
</style>

HTML标签定义如下:

1
<h1 v-bind:[prop]="name" v-if="show">标题</h1>

在Vue组件中定义属性数据如下:

1
2
3
4
5
6
7
8
9
10
11
12
<script>
const App = {
data() {
return{
show:true,
prop:'class',
name:'h1',
}
}
}
Vue.createApp(App).mount("#Application")
</script>

在浏览器运行代码后,可以看到h1标签被正确的绑定了class属性。

在参数后面还可以为Vue中的指令增加修饰符,修饰符会为Vue指令增加额外的功能。在网页中有信息输入框,通常不希望用户在首尾输入空格符,通过Vue的指令修饰符,可以很容易的实现自动去除首尾空格符的功能。

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

以上代码,使用v-model指令将输入框的文本与content属性进行绑定,当用户在输入框首尾有空格时,或者输入框失去焦点时,Vue会自动帮我们去除空格。

在Vue应用开发中,v-bindv-on两个指令使用非常频繁

例如:

1
2
v-bind:id="id" 缩写 :id="id"
v-on:click="myFunc" 缩写 @click="myFunc"

条件渲染

条件渲染v-if指令

在Vue模板使用v-if可以和v-else结合使用

1
2
<h1 v-if="show">标题</h1>
<p v-else>如果不显示标题就显示段落</p>

结合使用时v-else必须紧跟在v-ifv-else-if指令指定的元素后面,否则其不会被识别到。

1
2
3
<h1 v-if="show">标题</h1>
<h1>Hello</h1>
<p v-else>如果不显示标题就显示段落</p>

在实际应用中,多分支渲染逻辑也很常用,例如根据学生分数成绩进行分档

1
2
3
<h1 v-if="mark == 100">满分</h1>
<h1 v-else-if="mark >= 60">及格</h1>
<h1 v-else>不及格</h1>

v-if指令的使用必须添加到HTML标签上,如果我们需要使用条件控制多个标签元素的渲染,有两种方式可以实现,如:

1
2
3
4
5
6
7
8
9
10
<div v-if="show">
<p>aaa</p>
<p>aaa</p>
<p>aaa</p>
</div>
<template v-if="show">
<p>bbb</p>
<p>bbb</p>
<p>bbb</p>
</template>

通常推荐template分组的方式来控制一组元素的条件渲染逻辑,因为在HTML元素渲染时,使用div包装组件后,div元素本身会被渲染出来,而使用template进行分组的组件渲染后并不会渲染template标签本身。可以通过浏览器F12开发者工具验证这种特性。

条件渲染v-show指令

v-show与v-if类似,也是通过设置条件真假来决定元素的渲染情况的

1
<h1 v-show="show">show 标题</h1>

v-show并不支持template模板,同样也不可以和v-else结合使用

从元素本身的存在来说,v-if才是真正意义上的条件渲染,v-if采取的是懒加载,如果初始条件为假时,这个组件的任何渲染工作都不会进行,直到绑定的条件为真时。

v-show的渲染逻辑是一种视觉上的条件渲染,实际上无论v-show指令设置的条件是真是假,当前元素都会被渲染,v-show指令只是简单的通过切换元素CSS样式中的display属性来实现展示效果。

v-ifv-show这两种指令的渲染原理不同,通常v-if指令有更高的切换性能消耗,而v-show指令有更高的初始渲染性能消耗

实际开发中,如果组件渲染条件切换频繁用v-show,如果初始值指定后很少变化则建议用v-if.

循环渲染v-for指令

在Vue中,v-for指令可以将一个数组中的数据渲染为列表视图。v-for指令需要设置为一种特殊的语法。

1
item in list   # in为语法关键字,其也可以替换为of

item:是一个临时变量,其为列表中被迭代出的元素名

list:是列表变量本身

in:为语法关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<body>
<div id="Application">
<div v-for="item in list">
{{item}}
</div>
</div>

<script>
const App = {
data () {
return {
list:[1,2,3,4,5,6]
}
}
}
Vue.createApp(App).mount("#Application")
</script>
</body>

更多的时候,我们需要渲染的数据都是对象数据,使用对象来对列表元素进行填充,例如联系人

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
return{
list:[
{
'name':"张三",
'num':"123121"
},
{
'name':"李四",
'num':"9*99999"
},
{
'name':"王五",
'num':"66666"
}
]
}

修改要渲染的HTML结构

1
2
3
4
5
6
<div id="Application">
<div v-for="item in list">
<p>{{item.name}}</p>
<p>{{item.num}}</p>
</div>
</div>

在v-for指令中,我们也可以获取当前遍历项的索引,示例如下:

1
2
3
4
5
6
7
<ul>
<!-- index索引的取值是从0开始 -->
<li v-for="(item,index) in list">
<p>{{index + ":::" + item.name}}</p>
<p>{{item.num}}</p>
</li>
</ul>

我们也可以对一个对象进行v-for遍历

第一个参数:对象中属性的值

第二个参数:对象中属性的名字

第三个参数:对象中遍历的索引

1
2
3
4
5
6
7
8
9
10
11
12
<ol>
<li v-for="(value,key,index) in preson">
{{key}}:{{value}}
</li>
</ol>

preson:{
name:'张三',
age:'18',
num:'11111',
emali:"11111@qq.com"
}

结果:

  1. name:张三
  2. age:18
  3. num:11111
  4. emali:11111@qq.com

v-for 的默认方式是尝试就地更新元素而不移动它们。要强制其重新排序元素,你需要用特殊 attribute key 来提供一个排序提示:

1
2
3
4
5
<ol>
<li v-for="(value,key,index) in preson" :key="index">
{{key}}:{{value}}
</li>
</ol>

高级用法

1
2
3
4
5
6
7
push()      	// 向列表尾部追加一个元素
pop() // 删除列表尾部的一个元素
shift() // 向列表头部插入一个元素
unshift() // 删除列表头部的一个元素
splice() // 对列表进行分割操作
sort() // 对列表进行排序操作
reverse() // 对列表进行逆序

首先在页面添加一个按钮:

1
2
3
<button @click="click1">
反序
</button>

定义Vue函数:

1
2
3
4
5
methods:{
click1(){
this.list.reverse()
}
}

运行代码后可以看到单击页面上的按钮时,列表元素的渲染顺序会进行正逆切换。当我们需要对整个列表都进行替换时,直接对列表变量重新赋值即可。

1
2
3
4
5
6
<ul>
<li v-for="(item,index) in handle(list)">
<div>{{index + "." + item.name}}</div>
<div>{{item.num}}</div>
</li>
</ul>

handle为定义的处理函数,在进行渲染前,通过这个函数来对列表数据进行处理。例如,可以使用过滤器来进行列表数据的过滤渲染,实现handle函数如下:

1
2
3
handle(1) {
return l.filter(obj => obj.name != "张三")
}

当需要同事渲染多个元素时,与v-if指令类似,常用的方式是使用template标签进行包装,例如:

1
2
3
4
<template v-for="(item,index) in handle(list)">
<div>{{index + "." + item.name}}</div>
<div>{{item.num}}</div>
</template>

项目范例:代办任务列表应用

两块内容:表单输入框用来新建任务,有序列表用来显示当前待办任务。

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>任务列表</title>
<script src="https://unpkg.com/vue@3.2.47/dist/vue.global.js"></script>

</head>
<body>
<div id="Application">
<!-- 输入框元素,用来新建代办任务! -->
<form @submit.prevent="addTask">
<span>新建任务</span>
<input v-model="taskText" placeholder="请输入任务">
<button>添加</button>
</form>
<!-- 有序列表,使用v-for来构建 -->
<ol>
<li v-for="(item,index) in todos">
{{item}}
<button @click="remove(index)">删除任务</button>
<hr>
</li>
</ol>
</div>
</body>
<script>
const App = {
data(){
return{
// 代办任务列表数据
todos:[],
// 当前输入的代办任务
taskText:''
}
},
methods:{
// 添加一个代办任务
addTask(){
// 判断输入框是否为空
if (this.taskText.length == 0){
alert("输入为空")
return
}
this.todos.push(this.taskText)
this.taskText = ""
},
// 删除一个代办任务
remove(index){
this.todos.splice(index,1)
}
}
}
Vue.createApp(App).mount("#Application")
</script>
</html>