Vue 简介

Vue 的安装和配置

首先安装node在官网搜索然后安装node,可以安装16.x版本的,然后下载安装管理node版本的工具nvm也是在网上搜索安装下载即可,这里安装nodejs就好了

【2024最新版】最新node.js安装及环境变量+vue安装,手把手教学,非常简单,包教包会!!!_哔哩哔哩_bilibili

image-20240812023804026

Vue.js - 渐进式 JavaScript 框架 | Vue.js (vuejs.org)

VUE是MVVM框架,他是渐进式框架,是初创公司的首选框架,是轻量级的,有很多根据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
25
26
27
28
29
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=<device-width>, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>
0
</h1>
<button>按我加1</button>
<button id=" “btn">按我减1</button>
<script>
var elH1 = document.getElementsByTagName("h1")[0];
var elBtn = document.getElementsByTagName("button")[0];
var elBtn2 = document.getElementsById("btn")
var num = 0;
elBtn.onclick = function(){
num++;
elH1.innerHTML = num;
}
elBtn2.onclick = function(){
num--;
elH1.innerHTML = num;
}
</script>
</body>
</html>

在这个例子中,实现了+1和-1的操作,但是当业务更加庞大的时候,每操作一次都要进行渲染就很繁琐,而看看Vue是如何实现的。

使用Vue的步骤:

1、引入Vue.js和jQuery类似

https://unpkg.com/browse/vue@2.7.16/dist/vue.js 这是Vue2的以后想用可以自己引入Vue3的

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h1>0</h1>
<button @click="add">按我加1</button>
<button @click=""minus>按我减1</button>
</div>
<script src="js/Vue2.js">
new Vue({
// 挂载点
el:"#app",
//数据
data: {
a: 0
},
//方法
method: {
add(){
this.a++
},
minus(){
this.a--
}
}
})
</script>
</body>
</html>

这里不用渲染视图,视图模型已经帮我们弄好了。

相比于原生JS需要自己手动重新绘制视图,而Vue的数据驱动视图

更加方便‘

Vue的基本使用

1、首先将源码复制下来然后导入Vue.js

区分生产环境和开发环境

生产环境对代码进行了压缩,文件大小更小

2、如何查看成功引入Vue文件

首先创建一个Vue文件,然后alert Vue

如果此时浏览器弹出一个function对象则引入成功

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="js/Vue2.js"></script>
<script>
alert(Vue)
</script>

</body>
</html>

image-20240812182253237

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="js/Vue2.js"></script>
</head>
<body>
<div id="app">
{{a}}
</div>

<script>
var vue = new Vue({
//挂载点,所有的内容Vue的方法和属性都必须在对应的挂在跟标签中使用
el: '#app',
//数据管理中心,所有Vue的数据都在data对象中
data:{
a: 100
},
//方法,对应的Vue时间方法清单
methods:{

}

})
</script>

</body>
</html>

我们需要注意的是,所有的对象中的清单内容,都是Vue中已经定义好的,不能自由发挥

Vue的插值语法

插值语法是通过两个大括号,来进行书写,内部是对data数据管理中心属性进行渲染,也可以是表达式

moustache(胡子)是双大括号的学名

image-20240812195127379

表达式

image-20240812195301726

表达式只能放简单的判断或者三元判断,不能使用if等复杂判断

Vue的常用指令

什么是指令:directive,vue的指令的作用是通过带有v-的特殊属性,实现对dom的响应式加载

v-if

v-if的作用是通过一个布尔表达式进行对dom的上树和下树进行渲染。

1
2
3
4
<div id="app">
<p v-if="false">我是第一行dom元素</p>
<p v-if="true">我是第二行dom元素</p>
</div>

我们通常不是使用布尔值直接进行渲染,而是通过带有布尔值的表达式进行渲染,而是通过data带有布尔值来进行判断

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 lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="js/Vue2.js"></script>
</head>
<body>
<div id="app">
<p v-if="boo">我是第一行dom元素</p>
<p v-if="!boo">我是第二行dom元素</p>
</div>
<script>
var vue = new Vue({
el: '#app',
data:{
boo: true
}
})
</script>
</body>
</html>

image-20240812200518460

第二种情况是使用表达式来进行逻辑判断(根据数据的实时情况进行渲染)

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="js/Vue2.js"></script>
</head>
<body>
<div id="app">
<p v-if="boo == 100">我是第一行dom元素</p>
<p v-if="boo == 100">我是第二行dom元素</p>
<button @click="add">按我加1</button>
</div>
<script>
var vue = new Vue({
el: '#app',
data:{
boo: 95
},
methods: {
add(){
this.boo++
}
}
})
</script>
</body>
</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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="js/Vue2.js"></script>
</head>
<body>
<div id="app">
<p v-if="boo >= 0 && boo <=5">我是5</p>
<p v-if="boo > 15 && boo <= 25">我是15</p>
<p v-if="boo > 25 && boo <= 35">我是25</p>
<p v-if="boo > 35 && boo <= 45">我是35</p>
<button @click="add">按我加1</button>
</div>
<script>
var vue = new Vue({
el: '#app',
data:{
boo: 0
},
methods: {
add(){
this.boo++
}
}
})
</script>
</body>
</html>

上面的代码是通过v-if一层一层进行判断,实际上可以通过v-lese-if进行分支判断

1
2
3
4
5
<p v-if="boo >= 0 && boo <=5">我是5</p>
<p v-else-if="boo > 15 && boo <= 25">我是15</p>
<p v-else-if="boo > 25 && boo <= 35">我是25</p>
<p v-else="boo > 35 && boo <= 45">我是35</p>
<button @click="add">按我加1</button>

需要注意的是v-else的使用前提是必须有v-if并且中间不允许有任何的元素间隔

v-show

v-show和v-if的显示情况类似,但是原理不一样,v-show是通过控制元素的display属性,对元素的显示和隐藏进行逻辑判断,并没有实现对元素的下树

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="js/Vue2.js"></script>
</head>
<body>
<div id="app">
<h1>{{boo}}</h1>
<p v-show="boo > 5">我是5</p>
<button @click="add">按我加1</button>
</div>
<script>
var vue = new Vue({
el: '#app',
data:{
boo: 0
},
methods: {
add(){
this.boo++
}
}
})
</script>
</body>
</html>

image-20240812204605636

v-show和v-if使用场景区分是如果页面切换的特别频繁使用v-show,如果页面的整体逻辑特别大,页面设计范围大,并且不是特别频繁的切换页面使用v-if,设计到页面的加载性能。

v-for

v-for是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
25
26
27
28
29
30
31
32
33
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="js/Vue2.js"></script>
</head>
<body>

<div id="app">
<ul>
<li v-for="item in arr" >
{{item}}
</li>
</ul>
</div>
<script>
var vue = new Vue({
el: '#app',
data:{
arr:[
'苹果',
'橘子',
'香蕉'
]

}

})
</script>
</body>
</html>

item是arr数组的每一项枚举值

v-for中还有index和key属性

1
<li v-for="(item,index) in arr" :key="index">{{index}} {{item}}</li>

item指的是被遍历的数组(对象)的每一个值,item的命名不是规定的,可以自定义命名,

index指的是每一项被遍历的值的下标索引值

key是用来给每一项值加元素标识,作用是为了区分元素,为了实现最小量的更新

v-for还可以用来遍历对象

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 lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="js/Vue2.js"></script>
</head>
<body>

<div id="app">
<ul>
<li v-for="(item,index) in obj" :key="index">{{index}} {{item}}</li>
</ul>
</div>

<script>
var vue = new Vue({
el: '#app',
data:{
obj:{
name: '小明',
age: '17岁',
height: '175'
}

}

})
</script>

</body>
</html>

image-20240812210525645

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="js/Vue2.js"></script>
</head>
<body>

<div id="app">
<ul>
<li v-for="(item,key,index) in obj" :key="index"> {{index}} {{key}} {{item}}</li>
</ul>
</div>
<script>
var vue = new Vue({
el: '#app',
data:{
obj:{
name: '小明',
age: '17岁',
height: '175'
}

}

})
</script>
</body>
</html>

image-20240812210850070

上面的v-for有三个参数,items表示对象的内容

key表示键值对键的名称

index表示下标索引

实际上我们用来遍历JSON的情况多一点

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="js/Vue2.js"></script>
<style>
table,td,:target-within{
border: 1px solid
}
</style>
</head>
<body>

<div id="app">
<table>
<tr>
<th>姓名</th>
<th>年龄</th>
</tr>
<tr v-for="item in arr" :key="item">
<td>{{item.name}}</td>
<td>{{item.age}}</td>
</tr>

</table>

</div>
<script>
var vue = new Vue({
el: '#app',
data:{
arr:[
{name: '小明',age: '17'},
{name: '小明',age: '18'},
{name: '小明',age: '19'},
]

}

})
</script>
</body>
</html>

image-20240812214403395

v-for是可以进行嵌套的

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="js/Vue2.js"></script>
<style>
table,td,th{
border: 1px solid
}
</style>
</head>
<body>

<div id="app">
<table>
<tr v-for="i in number" :key="i" >
<td v-for="j in i" :key="j">
{{i}}x{{j}} = {{i*j}}
</td>

</tr>

</table>

</div>
<script>
var vue = new Vue({
el: '#app',
data:{
number: [1,2,3,4,5,6,7,8,9]
}

})
</script>
</body>
</html>

image-20240812215200142

v-for遍历出来的不是数据而是元素

如果使用item和index的时候一定使用括号包裹起来

1
(item,index) in arr

v-html和v-text

v-html和v-text都是渲染文本的指令,使用场景会有不同

现在我们学习的渲染方法

1、双大括号插值

2、v-text

3、v-html

双大括号和v-text渲染类似都是只渲染文本会以文本类型进行编译

1
2
3
4
5
<div id="app">
<p>{{a}}</p>
<p v-text="a"></p>

</div>

image-20240812220025153

那么这两个有什么不同呢?

区别:

1、双大括号在渲染结果之前,会有编译之前的文本显示,v-text是没有

2、双大括号插值更加灵活,中间是可以添加内容的,v-text是只能渲染data中的内容,中间不能填充内容

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="js/Vue2.js"></script>
<style>
table,td,th{
border: 1px solid
}
</style>
</head>
<body>

<div id="app">
<p>{{a}}</p>
<p v-text="a">-----------------</p>

</div>
<script>
var vue = new Vue({
el: '#app',
data:{
a: '我是要插入的内容-----'
}

})
</script>
</body>
</html>

image-20240812220547088

而这两个的渲染方式和v-html都不同,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 lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="js/Vue2.js"></script>
<style>
table,td,th{
border: 1px solid
}
</style>
</head>
<body>

<div id="app">
<p>{{a}}</p>
<p v-text="a">-----------------</p>
<p v-html="a"></p>

</div>
<script>
var vue = new Vue({
el: '#app',
data:{
a: '<h1>我是要插入的内容-----</h1>'
}

})
</script>
</body>
</html>

image-20240812220705585

v-html也是不允许中间插入内容的

v-cloak

v-cloak指令的作用是vue实例渲染后结束关联

双大括号插值语法在遇到网络延迟的时候会显示编译前的文本,这时候我们需要用到v-cloak

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="js/Vue2.js"></script>
<style>
[v-cloak]{
display: none;
}
</style>
</head>
<body>

<div id="app" v-cloak>
<p>{{a}}</p>
</div>
<script>
var vue = new Vue({
el: '#app',
data:{
a: '我是v-cloak的内容'
}

})
</script>
</body>
</html>

我们利用v-cloak结合css解决渲染的问题

【v-colak】选择器,选择的是文件结构中有v-cloak属性的元素,有这个属性的元素设置display状态设置为none,由于v-cloak属性的作用是,实例加载后关联结束,我们可以实现网络延迟时,不显示编译前的文本

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="js/Vue2.js"></script>

</head>
<body>

<div id="app">
<p v-once>{{a}}</p>
<button @click="add">按我+1</button>
</div>
<script>
var vue = new Vue({
el: '#app',
data:{
a: 10
},
methods:{
add(){
this.a ++
console.log(this.a)
}
}

})
</script>
</body>
</html>

image-20240812223129554

使用场景通常是没用动态的元素内容比如一些文章,一些固定标题

v-pre

v-pre属性的作用是跳过该元素的翻译过程,直接显示元素内部的文本,特点是跳过大量没有指令的节点

1
<p v-pre>{{a}}</p>  

image-20240812232523359

浏览器显示的就是没有编译之前的p标签中的文本,优点是提高页面加载的性能

v-on

v-on的作用是给元素添加事件监听可以简写为@

我们学习的JS的事件监听都可在Vue中使用

原生的事件监听

事件名称 方法
点击 onclick
双击 ondblclick
鼠标移上 onmounseenter
鼠标离开 onmouseleave
鼠标滑过 onmousemove
鼠标移出 oumouseout
失去焦点 onblur
聚焦 onfocus
键盘事件 onkeydown

上表的方法都是JS原生的事件方法。在Vue中的使用一律去除on然后添加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
27
28
29
30
31
32
33
34
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h1>0</h1>
<button @click="add">按我加1</button>
<button @click=""minus>按我减1</button>
</div>
<script src="js/Vue.js">
new Vue({
// 挂载点
el:"#app",
//数据
data: {
a: 0
},
//方法
method: {
add(){
this.a++
},
minus(){
this.a--
}
}
})
</script>
</body>
</html>

注意不能在methods外部罗列方法

其次,如果是同名的方法,后写的方法会覆盖前面的方法

如果方法中没有传入值,默认输出会有该方法的事件参数

1
2
3
add(event){
console.log(event)
}

v-bind

v-bind的属性的作用是将普通的w3c属性变为动态属性,让属性具有动态能力

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="js/Vue2.js"></script>

</head>
<body>

<div id="app">

<img v-bind:src="'images/'+url+'.jpg'">
<button @click="add">点击+1</button>
<button @click="minus">点击-1</button>

</div>
<script>
var vue = new Vue({
el: '#app',
data:{
url: 0
},
methods:{
add(){
this.url ++

},
minus(){
this.url --

}
}

})
</script>
</body>
</html>

此时vue会编译带有v-bind的属性返回编译后的结果

工作中我们可以将v-bind简写为 :

1
<img :src="'images/'+url+'.jpg'">
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="js/Vue2.js"></script>

</head>
<body>

<div id="app">

<img :src="'images/'+url+'.jpg'">
<button @click="add">点击+1</button>
<button @click="minus">点击-1</button>

</div>
<script>
var vue = new Vue({
el: '#app',
data:{
url: 0
},
methods:{
add(){
if(this.url >= 5){
this.url = 0
}
this.url ++

},
minus(){
if(this.url <= 0){
this.url = 5
}
this.url --

}
}

})
</script>
</body>
</html>

这样就实现了简单轮播图的功能

我们可以使用动态属性实现class类名的动态效果

1
<p :class"{red:a >=5,pink: a >= 10}"></p>

v-model

v-model属性是使用在表单元素中的作用是实现表单和数据的双向绑定

1
姓名:<input type="text" v-model="val">

我们说过vue是mvvm框架,Vue的核心之一就是双向数据绑定

这样输入框的内容的改变也会改变val实际值的改变,这样就能利用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
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="js/Vue2.js"></script>

</head>
<body>

<div id="app">

<p>
姓名:<input type="text" v-model="name">
{{name}}
</p>
</div>
<script>
var vue = new Vue({
el: '#app',
data:{
name: '姓名是张三'
},
methods:{
submit(){
//汇总信息提交到后端,利用axios
name: this.name

}
}


})
</script>
</body>
</html>

Vue的修饰符

修饰符是Vue功能的拓展,拓展的是vue的事件或者是系统操作等等进行功能的补充

事件修饰符

1、stop修饰符

能够阻止默认修饰符的冒泡

1
2
3
4
5
<div class="outer" @click="outer">
<div class="center" @click="center">
<div class="inner" @click="inner"></div>
</div>
</div>

点击了内层后

image-20240813114600693

加了stop修饰符

1
2
3
4
5
<div class="outer" @click="outer">
<div class="center" @click.stop="center">
<div class="inner" @click="inner"></div>
</div>
</div>

在点击内层

image-20240813114703437

阻止了继续冒泡到外层

2、self修饰符

.self修饰符的作用是只有点击元素本身的时候才能触发事件,不接受冒泡上来的事件,同时也不能阻止事件的冒泡

1
2
3
4
5
6
<div class="outer" @click.self="outer">
<div class="center" @click.self="center">
<div class="inner" @click.self="inner"></div>
</div>
</div>

3、 prevent修饰符

如果一个超级链接,既有链接,又有事件,此时点击超级链接后会发生什么?

1
<a href="http://www.baidu.com" @click.prevent="alertDialog">点击跳转到百度</a>

答案是先会执行事件的内容,然后会执行a默认的跳转事件

此时我们不需要a标签跳转到百度,执行完事件监听后就停止,通过.prevent修饰符阻止

1
<a href="http://www.baidu.com" @click.prevent="alertDialog">点击跳转到百度</a>

4、capture修饰符

capture修饰符是对事件捕获的监听,vue的事件监听,默认都是获取冒泡阶段的,所以使用capture去监听捕获阶段的事件

image-20240813115416242

5、once修饰符

once修饰符的作用就是使事件只触发一次

1
<button @click.once="add">按我加1</button>
1
2
3
4
5
6
7
methods:{
add(event) {
this.a++
console.log(this.a)
}
}

image-20240813115525644

按键修饰符

我们之前在JavaScript的课程中接触过onkeydown和onokeyup,这两个是键盘的事件监听,在vue中有对应的事件修饰符

基本使用方法

1
<input type="text" @keyup.space='add'>

.space是vue封装的对keyCode别名,和下面的代码表示内容相同

1
<input type="text" @keyup.32='add'>
keyCode值 别名 键盘按键
13 .enter 回车
32 .space 空格
37 .left 键盘左键
38 .up 键盘上键
39 .right 键盘右键
40 .down 键盘下键
9 .tab 键盘tab
46或者8 .delete delete或者backspace

系统修饰符

系统修饰符指的是通过一些指定的按键配合鼠标点击或者键盘事件进行事件监听

比如.ctrl系统修饰符的使用

1
<button  @click.ctrl='add'>按我加1 </button>

此时按住键盘ctrl键然后鼠标点击才能实现add加1

常用的系统修饰符

修饰符名称 对应的键盘键名称
.ctrl ctrl
.alt alt
.shfit shift
.meta 如果是windows系统代表的是键盘的徽标键,如果是IOS系统,单表的是common键

此时有一个问题,如果我们设置了系统修饰符,并不希望有其他的按键组合,比如我们设置ctrl修饰符,此时如果按住ctrl和其他键,然后再点击鼠标,此时也会触发事件监听,所以我需要使用其他的修饰符来设置精确匹配事件

exact修饰符

exact修饰符的作用是辅助其他修饰符进行精确匹配的

1
<button  @click.ctrl.exact='add'>按我加1 </button>

方法就是在对应的修饰符的后面进行连续打点

鼠标按键修饰符

鼠标按键修饰符修饰的是鼠标的左、滚轮、右键

如果我们需要点击某个事件,但是我们可以需要使用鼠标右键

1
<button  @click.right='add'>按我加1 </button>

此时我们发现右键点击按钮的时候确实可以实现点击,但是也会出现右键的默认弹窗

此时我们可以结合prevent修饰符实现右键的事件监听

1
<button  @click.right.prevent='add'>按我加1 </button>

需要注意的是,prevent修饰符必须在.right修饰符的后面,否则会失效

还有鼠标滚轮键按下

1
<button  @click.middle='add'>按我加1 </button>

表单修饰符

表单修饰符一共有三个,分别是.lazy,.number,.trim

表单修饰符都是用来修饰v-model属性的

.lazy修饰符是将Input输入框的数据绑定v-model从实时监听的状态变为change状态

1
2
3
4
5
<div id="app">
<input type="text" v-model.lazy='a'>
<p>{{a}}</p>
</div>

image-20240813120523484

当鼠标的光标失去焦点之后

image-20240813120533145

.number修饰符的作用是将内容转化为数字类型

1
<input type="text" v-model.number='a' @input='add'>

需要注意的是.number修饰符会将非数字内容进行过滤,得到的是纯数字类型,是可以进行计算的非NaN数字

如果没有使用number修饰符,得到的输入框的数字都是string类型

.trim修饰符的作用是过滤用户输入的收尾空格

比如我们没有设置trim之前

image-20240813123054194

此时我们给v-model修饰trim

1
<input type="text" v-model.trim='a' @input='add'>

image-20240813123116018

得到的是纯没有收尾空白的内容

axios

image-20240813175141494

跟ajax类似

vue-cli

vue-cli是vue的脚手架工具,帮我们快速生成了vue的起步项目,内置一些必备的比如打包工具,比如配置文件等等

地址:https://cli.vuejs.org/zh/

如何安装cli?

第一步

1
npm install -g @vue/cli

如何查看是否安装成功

1
vue --version

我们使用vue create命令创建自己的项目

1
vue create my-project

cli目录结构

image-20240813124501128

  • node_modules项目的依赖中心

  • public:静态资源文件夹,和assets不同的是public不会被webpack进行打包,使用路径的时候要使用绝对路径

  • src:项目的核心区域,所有的开发文件的核心内容区域,包括组件,静态资源等等

  • asstet:静态资源文件夹,和public不同的是assets文件夹会被webpack打包,所以要使用相对路径

  • components:Vue功能组件的存放位置,内部存放的是公用的组件

  • views:公共文件,主要以各个独立页面为主

  • APP.vue:整个vue的根组件,所有的vue组件都是从这个组件中拓展的,App根组件最后通过编译后将内容渲染到.html文件中

  • main.js:vue的入口文件,vue文件初始化位置

  • .gitignore:GitHub相关配置文件,作用是git再提交代码的时候指定相关忽略格式文件

  • babel.config.js:babel的配置文件

  • package.json:配置(记录了)项目的相关模块,项目相关配置信息等等

  • package-lock.json:作用是记录的当前项目安装的相关依赖版本,并且记住了当前所有依赖的关联关系,如果下次vue进行编译的时候会按照这个文件进行编译

  • README.md:文件的作用是项目相关的使用方法,和使用说明

cli文件的编译过程

我们知道vue-cli的起步命令是npm run serve,那么这个命令干了一些什么,为什么可以执行相关的编译程序?

首先我们要知道npm run 执行一个命令或者是一个文件内容

我们看package.json文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"name": "my-project",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
},
"dependencies": {
"core-js": "^3.6.5",
"vue": "^2.6.11"
},
"devDependencies": {
....
},
"eslintConfig": {
...
},
"browserslist": [
...
]
}

我们知道scripts是npm执行目录,也就是在scripts这个对象中的key就是npm run的命令值

vue-cli提供了三个命令

  • serve:这个是我们开发使用的命令,执行编译和热更新(ctrl+s的时候浏览器会实时更新)

  • build:工作中如果我们本地调试完后需要部署代码前,进行打包的命令

  • lint:命令的作用是检验文件代码的合格性(对eslint的校验)

我们以npm run serve为例

第一步命令会找到node_modules文件夹中.bin文件夹内部的vue-cli-service文件

image-20240813163910488

然后我们根据路径查找到了相关文件

image-20240813163931116

核心代码路径

image-20240813163946326

severice.js文件是整个编译过程的核心文件

image-20240813164138003

vue-cli的起步文件

  • main.js文件
1
2
3
4
5
6
7
8
9
10
11
12
13
import Vue from 'vue'
// 相对路径引入的App.vue文件
import App from './App.vue'

// 这行命令的作用是给生产环境配置的提示消息,如果为true或者默认不配置,会有相关提示语
Vue.config.productionTip = false

new Vue({
// 渲染节点
render: h => h(App),
// 挂载函数,内部#app是vue的根节点
}).$mount('#app')

  • public>index.html根文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!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>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

noscript标签的作用是当script标签加载不出来或者抛出错误的时候替换内容,目的是在浏览器中提供友好提示

1
<div id="app"></div>是整个vue项目的根标签,vue需要挂载的标签
  • App.vue文件

App.vue文件是整个项目的根组件,项目中所有的页面切换页面显示都是在这个组件基础上渲染的

我们将整个App.vue文件清空后发现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<div id="app">

</div>
</template>

<script>

export default {
components: {

}
}
</script>

<style>

上面的结构是.vue文件的基础结构

  • template元素的作用是搭建vue文件的结构

  • script元素的作用是对当前文件逻辑进行交互

  • style元素的作用是对当前文件的样式进行修饰

我们之前开发中.js文件只有逻辑.css文件只有样式.html文件只有结构,但是.vue的组建就是将视图和逻辑进行了一个整合

组件

所有的vue组件都是以.vue格式结尾的文件

我们看到的vue-cli中的App.vue文件就是整个vue的根组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<div>

</div>
</template>

<script>

export default {
data(){

}
}
</script>

<style>

一个vue文件就是一个类

需要注意的地方是data必须是一个函数

最外层必须有默认暴露

1
2
3
4
5
6
7
8
9
10
11
<script>

export default {
data(){
return {

}
}
}
</script>

我们后面写的所有的vue的对象清单都必须在export default里面去罗列

在我们之前写data时候是一个对象

1
2
3
4
5
6
var app = new Vue{
data: {
name:'小明'
}
}

我们发现data是一个对象

如果在我们组件中data必须是一个函数,是因为防止数据之间冲突

1
2
3
4
5
6
7
8
9
10
<script>

export default {
data(){
return {
a: '小明'
}
}
}
</script>

data是一个函数,返回的是一个对象,目的是为了让每个组件数据隔离,这个是JavaScript的原理,并不是vue去设计的

为什么vue的data必须是一个函数

我们从JavaScript的面向对象开始

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script>
var config = {
data:{
name: '小明'
}
}
var People = function(config) {
this.data = config.data
}

var xiaoming = new People(config)
var xiaohong = new People(config)
xiaohong.data.name = '小红'
console.log(xiaoming.data.name)
console.log(xiaohong.data.name)
</script>

此时浏览器会输出两个小红

image-20240813165812192

我们知道复杂数据类型指的都是同一个地址,所以,当xiaohong.data.name修改的时候,xiaoming.data.name也就随之修改了

解决办法就是config对象返回一个data函数,这个函数return一个对象

1
2
3
4
5
6
7
8
9
10
var config = {
data(){
return {
name: '小明'
}
}
}
var People = function(config) {
this.data = config.data()
}

​ this.data = config.data()

}

此时People的data返回的是一个对象,不是一个地址,每一个实例拿到的都是个独立,个体的对象,互相不会数据冲突,这个就是vue组件data为什么是函数的原因

image-20240813170054322

如何使用自定义组件?

第一步创建自己的组件

比如在components文件夹中创建一个Bitzh.vue文件

App.vue文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
<div>
<Bitzh></Bitzh>
</div>
</template>

<script>
// 引入组件
import Bitzh from './components/Bitzh.vue'

export default {
// 注册组件
components:{
Bitzh
},
data(){

}
}
</script>

总结使用组件三步走

\1) 创建vue文件

\2) 在要渲染的地方引入该组件

\3) 注册该组件

组件是可以复用的,并且数据是隔离的

在给App.vue多添加几个Bitzh.vue文件

父子组件传值

我们使用组件一定是依托在某一个父组件的身上,比如我们上面写的Mashibing.vue文件就是依托在App.vue文件中,此时我们如果要使用App.vue文件中的值怎么办

vue提供了一个props的入口,也是父子组件之间唯一的传值方式,父组件通过v-bind自定义属性传入值,子组件通过props接受对应的参数

父组件

1
2
3
4
5
6
<template>
<div>
<Bitzh :bitzh-value="a"></Bitzh>
</div>
</template>

公共v-bind自定义属性传值,注意由于vue的属性对大小写不敏感,所以如果需要写驼峰命名,需要使用-隔开

比如bitzh-value代表的就是bitzhValue

1
2
3
4
5
6
data(){
return {
a: 100
}
}

子组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script>
export default {
// 罗列父组件传进的属性值
props:['bitzhValue'],
data() {
return {
a: 100
}
},
methods:{
add() {
this.a ++
}
}
}
</script>


1
2
3
4
5
6
<template>
<div>
<h1>我接收到的父组件的值是:{{msbValue}}</h1>
</div>
</template>

子组件通过props罗列父组件的传值,接收的参数可以有多个,props可是数组,也可以是对象

父组件

1
2
3
4
<div>
<Bitzh :msb-value="a" :value-b="b" :value-c="c"></Bitzh>
</div>

子组件接受,数组方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<div>
<h1>我接收到的父组件的值是:{{bitzhValue}} {{valueB}}
{{valueC}}</h1>
</div>
</template>
<script>
export default {
// 罗列父组件传进的属性值
props:['bitzhValue','valueB','valueC'],
data() {
return {
a: 100
}
},
methods:{
add() {
this.a ++
}
}
}
</script>

可以使用对象去接收参数

1
2
3
4
5
6
props:{
bitzhValue: Number,
valueB: Number,
valueC: Number
}

如果props是一个对象,那么这个对象的key是自定义传入参数属性,value是希望传入的参数类型

比如我们限制的是数字类型

但是我们传入的是一个字符串,此时,控制台后报错,提示你希望传入number类型

如果props是对象的话,参数可以配置

比如配置参数必填项

1
2
3
4
5
6
7
8
9
props:{
bitzhValue: {
type: Number,
required: true
},
valueB: Number,
valueC: Number
},

如果props的值也是一个对象,type代表的是类型,required是一个布尔值,如果为ture代表的是设置必填项,如果不填,则会抛出错误

比如default代表的是默认的不传入的值的参数

1
2
3
4
5
bitzhValue: {
type: Number,
default: 500
}

如果props的default值是Object或者Array,需要使用函数return

1
2
3
4
5
6
7
bitzhValue: {
type: Object,
default: ()=>{
return {a:100}
}
}

image-20240813171836367

validator是数据的校验

1
2
3
4
5
6
7
8
msbValue: {
type: Number,
// 数据的校验
validator: function(value) {
return value > 101
}
}

子组件修改父组件的参数

需要注意的是子组件不可以直接修改父组件的值,只能传出一个自定义事件,父组件通过调用这个自定义事件后,然后在外部修改值

子组件代码

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
<template>
<div>
<h1>我接收到的父组件的值是:{{bitzhValue}} {{valueB}}
{{valueC}}</h1>
<button @click="add">按我加1</button>
</div>
</template>
<script>
export default {
// 罗列父组件传进的属性值
props:{
bitzhValue: Number,
valueB: Number,
valueC: Number
},
data() {
return {

}
},
methods:{
add() {
this.$emit("add")
}
}
}
</script>
<style>

</style>

$emit方法是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
25
26
27
28
29
30
31
32
33
34
<template>
<div>
<Bitzh @add="sumNum" :bitzh-value='a' :value-b="b" :value-c="c"></Bitzh>
</div>
</template>

<script>
// 引入组件
import Mashibing from './components/Bitzh.vue'

export default {
// 注册组件
components:{
Bitzh
},
data(){
return {
a: 100,
b: 200,
c: 300
}
},
methods: {
sumNum(){
this.a++
}
}
}
</script>

<style>

</style>

@add自定义事件是子组件通过$emit传出来的自定义事件,父组件通过这个事件设置事件方法sunNum,然后修改自己的值,从而时间子组件修改父组件的值

image-20240813172212136

这么做的目的就是为了让数据可观察性更强,可维护性更高,有一个更清晰的清单