Vue
一. 邂逅Vue.js开发
1.1. Vue介绍
- Vue的介绍
- Vue在前端的地位
- react
- angular
1.2. Vue下载和使用
CDN引入
- ```html
1
2
3
4
5
- 本地引入
- ```html
<script src="../vue.js"></script>
- ```html
初体验Vue开发
1.3. Vue的三个案例
1.3.1. 动态数据展示
1.3.2. 动态展示列表
v-for
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20<body>
<div id="app"></div>
</body>
<script>
const app = Vue.createApp({
template:`
<h2>电影列表</h2>
<ul>
<li v-for="item in movies">{{item}}</li>
</ul>
`,
data(){
return {
message:'你好啊,世界',
movies:['你好,李焕英','大话西游','扫黑风暴']
}
}
})
app.mount('#app')
</script>
1.3.3. 计数器案例
counter
increment
decrement
```html
Document 当前计数:
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
### 1.4. 命令式和声明式编程的区别
- 命令式编程关注的是 “how to do”自己完成整个how的过程;
- 声明式编程关注的是 “what to do”,由框架(机器)完成 “how”的过程;
- 在原生的实现过程中,我们是如何操作的呢?
- 我们每完成一个操作,都需要通过JavaScript编写一条代码,来给浏览器一个指令;
- 这样的编写代码的过程,我们称之为命令式编程;
- 在早期的原生JavaScript和jQuery开发的过程中,我们都是通过这种命令式的方式在编写代码的;
- 在Vue的实现过程中,我们是如何操作的呢?
- 我们会在createApp传入的对象中声明需要的内容,模板template、数据data、方法methods;
- 这样的编写代码的过程,我们称之为是声明式编程;
- 目前Vue、React、Angular、小程序的编程模式,我们称之为声明式编程;
### 1.5. MVC和MVVM的模型区别
- MVC和MVVM都是一种软件的体系结构
- MVC是Model – View –Controller的简称,是在前期被使用非常框架的架构模式,比如iOS、前端;
- MVVM是Model-View-ViewModel的简称,是目前非常流行的架构模式;
- 通常情况下,我们也经常称Vue是一个MVVM的框架。
- Vue官方其实有说明,Vue虽然并没有完全遵守MVVM的模型,但是整个设计是受到它的启发的。
### 1.6. options api的data详解
- data必须是一个函数,函数返回一个对象
- data返回的对象,会被Vue进行劫持(放到响应式系统中),所以data的数据发生改变时,界面会重新渲染
- ```
- 所以我们在template或者app中通过 {{counter}} 访问counter,可以从对象中获取到数据;
- 所以我们修改counter的值时,app中的 {{counter}}也会发生改变;
1.7. options api的methods详解
- methods属性是一个对象,通常我们会在这个对象中定义很多的方法:
- 这些方法可以被绑定到 模板中;
- 在该方法中,我们可以使用this关键字来直接访问到data中返回的对象的属性;
- 对于有经验的同学,在这里我提一个问题,官方文档有这么一段描述:
- 问题一:为什么不能使用箭头函数(官方文档有给出解释)?
- 我们在methods中要使用data返回对象中的数据:
- 那么这个this是必须有值的,并且应该可以通过this获取到data返回对象中的数据。
- 那么我们这个this能不能是window呢?
- 不可以是window,因为window中我们无法获取到data返回对象中的数据;
- 但是如果我们使用箭头函数,那么这个this就会是window了;
- 为什么是window呢?
- 这里涉及到箭头函数使用this的查找规则,它会在自己的上层作用于中来查找this;
- 最终刚好找到的是script作用于中的this,所以就是window;
- 我们在methods中要使用data返回对象中的数据:
- 问题二:不使用箭头函数的情况下,this到底指向的是什么?(可以作为一道面试题)
- 事实上Vue的源码当中就是对methods中的所有函数进行了遍历,并且通过bind绑定了this
- 问题一:为什么不能使用箭头函数(官方文档有给出解释)?
二. 基础 - 模板语法
2.1. 添加代码片段
2.2. mustache语法(插值语法)
表达式
```html
Document 当前计数:
<!-- 2.表达式 --> <h2>计数双倍:{{counter * 2}}</h2> <h2>展示的信息:{{info.split(" ")}}</h2> <!-- 3.三元运算符 --> <h2>{{age >= 18?"成年人":"未成年人"}}</h2> <!-- 4.调用methods中的函数 --> <h2>{{formateData(time)}}</h2> </div>
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
### 2.3. 不算常用的指令
- v-once
- ```html
<body>
<!-- 指令v-once 用于指定元素或者组件只渲染一次:
当数据发生变化时,元素或者组件以及其所有的子元素将视为静态内容并且跳过;
该指令可以用于性能优化;
如果是子节点,也是只会渲染一次
-->
<div class="app">
<h2 v-once>{{message}}</h2>
<button @click="change">修改内容</button>
</div>
</body>
<script src="../vue.js"></script>
<script>
const app = Vue.createApp({
data:function(){
return{
message:'Hello World'
}
},
methods:{
change:function(){
this.message = "你好 世界"
}
}
})
app.mount(".app")
</script>v-text
- ```html
aa
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
- v-html
- ```html
<body>
<div class="app">
<h2>{{content}}</h2>
<!--默认情况下,如果我们展示的内容本身是 html 的,那么vue并不会对其进行特殊的解析。
如果我们希望这个内容被Vue可以解析出来,那么可以使用 v-html 来展示;
-->
<h2 v-html="content"> aaa</h2>
</div>
</body>
<script src="../vue.js"></script>
<script>
const app = Vue.createApp({
data:function(){
return{
content:'<span style="color:red">哈哈哈</span>'
}
},
methods:{
}
})
app.mount(".app")
</script>
- ```html
v-pre
- ```html
当前计数:
<!-- v-pre 用于跳过元素和它的子元素的编译过程,显示原始的Mustache标签: 跳过不需要编译的节点,加快编译的速度; 显示 {{}}-->
{{}}</p> </div> </div>
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
- v-cloak
- ```html
<!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>Document</title>
<style>
[v-cloak]{
display:none;
}
</style>
</head>
<body>
<!-- 这个指令保持在元素上直到关联组件实例结束编译。
和 CSS 规则如 [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 Mustache 标签直到组件实例准备完毕。
-->
<div class="app" v-cloak>
<h2>{{message}}</h2>
<h2>当前计数:{{counter}}</h2>
</div>
</body>
<script src="../vue.js"></script>
<script>
setTimeout(function(){
const app = Vue.createApp({
data:function(){
return{
message:'Hello World',
counter:0
}
},
methods:{
}
})
app.mount(".app")
},3000)
</script>
</html>
- ```html
2.4. 新的指令 v-memo
2.5. v-bind 绑定属性
- 某些属性我们也希望动态来绑定。
- 比如动态绑定a元素的href属性;
- 比如动态绑定img元素的src属性
- 动态绑定一些类、样式等等
2.5.1. v-bind绑定基本属性
src
href
```html
Document <!-- 2.绑定a的href属性 --> <a v-bind:href="imgUrl">百度一下</a> <div> <!-- v-on绑定点击事件 --> <button v-on:click="changeImg">切换图片</button> <button @click="changeImg">切换图片</button> </div> </div>
1
2
3
4
5
6
7
8
9
10
#### 2.5.2. v-bind绑定class
- 基本绑定方式:
- 对象语法
- ```
所以我们修改counter的值时,app中的 {{counter}}也会发生改变;- 数组语法
```html
Document <!-- 2.1对象语法的基本使用 --> <button @click="change" :class="{a:temp}">我是按钮</button> <!-- 2.2对象语法的多个键值对 --> <button :class="{a:temp,b:true,c:false}" @click="change">我是按钮</button> <!-- 2.3动态绑定的class是可以和普通的class同时使用的 --> <button class="a" :class="{a:temp,b:true,c:false}" @click="change">我是按钮</button> <!-- 2.4通过函数返回对象来动态绑定 --> <button :class="getClass()" @click="change">我是按钮</button> <!-- 4.动态class可以写数组语法 --> <h2 :class="["a","b"]">你好 世界</h2> <h2 :class="["a",className]">你好 世界</h2> <h2 :class="["a",className,{a:temp}]">你好 世界</h2> </div>
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
#### 2.5.3. v-bind绑定style
CSS property 名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名;
- 对象语法
- ````
{cssname:cssvalue}
````
- 数组语法
- [obj1,obj2]
- ```html
<!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>Document</title>
</head>
<body>
<div class="app">
<!-- 1.style中的某些值,来自data中 -->
<!-- 1.1动态绑定style,在后面跟上对象类型(驼峰fontSize或者加引号) -->
<h2 :style="{color:fontColor,'font-size':'30px'}">哈哈哈</h2>
<!-- 2.数组语法 -->
<h2 :style="[objStyle,{backgroundColor:'red'}]">哈哈哈</h2>
</div>
</body>
<script src="../vue.js"></script>
<script>
const app = Vue.createApp({
data:function(){
return{
fontColor:"red",
objStyle:{
fontSize:'30px'
}
}
},
methods:{
}
})
app.mount(".app")
</script>
</html>
2.6. 动态绑定属性名
- 在某些情况下,我们属性的名称可能也不是固定的:
- 前端我们无论绑定src、href、class、style,属性名称都是固定的;
- 如果属性名称不是固定的,我们可以使用 :[属性名]=“值” 的格式来定义;
- 这种绑定的方式,我们称之为动态绑定属性;
:[name]=” “
2.7. v-bind绑定对象
如果我们希望将一个对象的所有属性,绑定到元素上的所有属性,应该怎么做呢?
- 非常简单,我们可以直接使用 v-bind 绑定一个 对象;
```html
Document 你好 世界
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
### 2.8. 事件绑定 v-on的用法
#### 2.8.1. v-on的各种写法
- v-on:click="counter++"
- v-on:click
- @click
- 别的事件
- ```
v-on="{click:xxxx}"```html
Document <!-- 2.语法糖写法 --> <div class="box" @click="divClick"></div> <!-- 3.绑定方法的位置,也可以写成一个表达式 --> <h2>{{counter}}</h2> <button @click="increment">+1</button> <button @click="counter++">+1</button> <!-- 4.绑定其他事件 --> <div class="box" @mousemove="divMousemove"></div> <!-- 5.绑定多个事件 --> <div class="box" @click="divClick" @mousemove="divMousemove"></div> <div class="box" v-on="{click:divClick,mousemove:divMousemove}"></div> <div class="box" @="{click:divClick,mousemove:divMousemove}"></div> </div>
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
#### 2.8.2. 各种参数方式
- 默认传递event
- 自定义参数
- name,age,$event
- ```html
<body>
<div class="app">
<!-- 1.无参数 -->
<button @click="btn1Click">按钮1</button>
<!-- 2.只有自己的参数 -->
<button @click="btn2Click('wt',age)">按钮2</button>
<!-- 3.想拿到自己的参数和event对象 -->
<!-- 在模板中想要明确的获取event对象:$event -->
<button @click="btn3Click('wt',age,$event)">按钮3</button>
</div>
</body>
<script src="../vue.js"></script>
<script>
const app = Vue.createApp({
data:function(){
return{
age:18,
}
},
methods:{
// 1.默认参数 event对象
// 总结:如果在绑定事件的时候,没有传递任何的参数,那么event对象会被默认传递进来
btn1Click(event){
console.log(event);
},
// 2.调用参数
btn2Click(name,age){
console.log(name,age);
},
// 3.自己的参数和evet对象
btn3Click(name,age,event){
console.log(name,age,event);
}
}
})
app.mount(".app")
</script>
2.8.3. 修饰符
1 |
|
三、条件渲染和列表渲染
3.0. 条件渲染
3.0.1. v-if/else/else-if
v-if的渲染原理:
v-if是惰性的;
当条件为false时,其判断的内容完全不会被渲染或者会被销毁掉;
当条件为true时,才会真正渲染条件块中的内容;
1 | <body> |
3.0.2. template元素
v-if
v-for
```html
个人信息
- 姓名:
- 年龄:
<template v-else> <h2>无个人信息</h2> </template> </div>
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
#### 3.0.3. v-show
- if用法区别:
- v-show不能和template元素结合
- v-show不能和v-else结合
- if的本质区别:
- v-if为false元素会销毁、不存在
- v-show为false时元素的display为none
- 选择
- 切换频繁使用v-show
- 不频繁使用v-if
- ```html
<!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>Document</title>
<style>
img{
width: 1500px;
height: 1000px;
}
</style>
</head>
<body>
<div class="app">
<!-- v-show 不支持template 不能和v-else一起使用 -->
<!-- v-show本质是改变display的属性来实现元素是否隐藏 -->
<!-- 如果元素需要频繁的显示和隐藏切换 使用v-show 反之用v-if-->
<div>
<button @click="change">切换</button>
</div>
<div v-show="isShowImg">
<img src="https://img.dpm.org.cn/Uploads/Picture/2022/11/30/s6386b28128664.jpg" alt="">
</div>
<div v-if="isShowImg">
<img src="https://img.dpm.org.cn/Uploads/Picture/2022/11/30/s6386b28128664.jpg" alt="">
</div>
</div>
</body>
<script src="../vue.js"></script>
<script>
const app = Vue.createApp({
data:function(){
return{
isShowImg:true
}
},
methods:{
change(){
this.isShowImg = !this.isShowImg
}
}
})
app.mount(".app")
</script>
</html>
3.1. 列表渲染
3.1.1. v-for的基本使用
item in 数组
- 数组通常是来自data或者prop,也可以是其他方式;
- item是我们给每项元素起的一个别名,这个别名可以自定来定义;
(item,index) in 数组 拿到数组的索引
(item,index) of 数组
```html
Document 电影列表
- 第NaN部电影:
<h2>个人信息</h2> <div v-for="item in products" class="box"> <h3>姓名:{{item.name}}</h3> <h3>爱好:{{item.hobby}}</h3> </div> </div>
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
#### 3.1.2. v-for其他类型
- 对象
- (value,key,index) in obj
- 数字
- item in 数字
- 可迭代对象(字符串)
- ```html
<!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>Document</title>
</head>
<body>
<div class="app">
<!-- 1.遍历对象 -->
<ul>
<li v-for="(value,key,index) in info">{{key}}--{{value}}--{{index}}</li>
</ul>
<!-- 2.遍历字符串 -->
<ul>
<li v-for="str in message">{{str}}</li>
</ul>
<!-- 3.遍历数字 -->
<ul>
<li v-for="i in 10">{{i}}</li>
</ul>
</div>
</body>
<script src="../vue.js"></script>
<script>
const app = Vue.createApp({
data:function(){
return{
info:{
name:"李华",
age:18,
hobby:"烫头"
},
message:"I love you"
}
},
methods:{
}
})
app.mount(".app")
</script>
</html>
3.2. v-for绑定key属性
3.2.1. VNode/虚拟DOM
- template元素 -> VNode
- 虚拟DOM作用之一:
- 跨平台
3.2.2. key的作用
有key的操作
- 根据key找到之前的VNode尽量先进行复用
- 没有VNode可以复用,在创建新的VNode
没有key操作:
- diff算法,后续VNode复用性不强
认识VNode
虚拟DOM
3.3.0. key绑定id
四、Options API
4.1. 计算属性 computed
4.1.1. 复杂数据的处理方式
- mustache插值语法 自己写逻辑
- method完成逻辑
- 使用计算属性computed
4.1.2. 计算属性的用法
computed:{ function() {} }
```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
- 注意:计算属性看起来像是一个函数,但是我们在使用的时候不需要加(),这个后面讲setter和getter时会讲到;
- 我们会发现无论是直观上,还是效果上计算属性都是更好的选择;
- 并且计算属性是有缓存的;
#### 4.1.3. computed和methods区别
- computed底层会进行缓存 性能更高
- ![image-20230201173215791](Vue入门/image-20230201173215791.png)
#### 4.1.4. computed的完整写法
- set
- get
- ```html
<body>
<div class="app">
<h2>{{ fullName }}</h2>
<button @click="setFullname">修改内容</button>
</div>
</body>
<script src="../vue.js"></script>
<script>
const app = Vue.createApp({
data:function(){
return{
firstName:"Kobe",
lastName:"brayt",
}
},
computed:{
fullName:{
get:function(){
return this.firstName + " " + this.lastName
},
set:function(value){
const names = value.split(" ")
this.firstName = names[0]
this.lastName = names[1]
}
}
},
methods:{
setFullname(){
this.fullName = "kobe brant"
}
},
})
app.mount(".app")
</script>
4.2. 侦听器 watch
- 什么是侦听器呢?
- 开发中我们在data返回的对象中定义了数据,这个数据通过插值语法等方式绑定到template中;
- 当数据变化时,template会自动进行更新来显示最新的数据;
- 但是在某些情况下,我们希望在代码逻辑中监听某个数据的变化,这个时候就需要用侦听器watch来完成了;
- 常用语搜索框的值得侦听
4.2.1. 基本侦听watch
watch: { message(oldValue,newValue) { } }
注意:对象类型
- Proxy对象 -> Vue.toRaw(newValue)
```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
60
4.2.2. 侦听选项
- 我们先来看一个例子:
- 当我们点击按钮的时候会修改info.name的值;
- 这个时候我们使用watch来侦听info,可以侦听到吗?答案是不可以。
- 这是因为默认情况下,watch只是在侦听info的引用变化,对于内部属性的变化是不会做出响应的:
- 这个时候我们可以使用一个选项deep进行更深层的侦听;
- 注意前面我们说过watch里面侦听的属性对应的也可以是一个Object;
- 还有另外一个属性,是希望一开始的就会立即执行一次:
- 这个时候我们使用immediate选项;
- 这个时候无论后面数据是否有变化,侦听的函数都会有限执行一次;
- deep
- immediate
- ```html
<body>
<div class="app">
<h2>{{info.name}}</h2>
<button @click="change">修改</button>
</div>
</body>
<script src="../vue.js"></script>
<script>
const app = Vue.createApp({
data:function(){
return{
info:{name:"why",age:18}
}
},
methods:{
change(){
// this.info = {name:"kobe"}
this.info.name = "kobe"
},
},
watch:{
// info(newvalue,oldvalue){
// console.log("能侦听到info改变",newvalue,oldvalue)
// }
info:{
handler(newvalue,oldvalue){
console.log("能侦听到info改变",newvalue,oldvalue)
},
// 进行深度监听 对于内部属性的变化做出响应
deep:true,
// 第一次进行渲染就执行一次监听器
immediate:true
}
}
})
app.mount(".app")
</script>
4.2.3. 其他写法
- “info.name”
- 别的写法
- created -> this.$watch
五、阶段案例-购物车
1 |
|
六、v-model的双向绑定
6.1. v-model的基本使用
v-model指令可以在表单 input、textarea以及select元素上创建双向数据绑定;
- 它会根据控件类型自动选取正确的方法来更新元素;
- 尽管有些神奇,但 v-model 本质上不过是语法糖,它负责监听用户的输入事件来更新数据,并在某种极端场景下进行一些特殊处理;
原理
- v-bind绑定value属性的值;
- v-on绑定input事件监听到函数中,函数会获取最新的值赋值到绑定的属性中;
6.2. v-model绑定其他类型
textare
checkbox
- 单选
- v-model即为布尔值。
- 此时input的value属性并不影响v-model的值。
- 多选
- 当是多个复选框时,因为可以选中多个,所以对应的data中属性是一个数组。
- 当选中某一个时,就会将input的value添加到数组中。
- 单选
radio
select
- 单选
- v-model绑定的是一个值;
- 当我们选中option中的一个时,会将它对应的value赋值到fruit中;
- 多选
- v-model绑定的是一个数组;
- 当选中多个值时,就会将选中的option对应的value添加到数组fruit中;
- 单选
6.3. v-model的值绑定
6.4. v-model修饰符
默认情况下,v-model在进行双向绑定时,绑定的是input事件,那么会在每次内容输入后就将最新的值和绑定的属性进行同步;
lazy
- 如果我们在v-model后跟上lazy修饰符,那么会将绑定的事件切换为 change 事件,只有在提交时(比如回车)才会触发;
number
trim
- 如果要自动过滤用户输入的守卫空白字符,可以给v-model添加 trim 修饰符:
七、组件化基础
7.1. 组件化思想
- 我们需要通过组件化的思想来思考整个应用程序:
- 我们将一个完整的页面分成很多个组件;
- 每个组件都用于实现页面的一个功能块;
- 而每一个组件又可以进行细分;
- 而组件本身又可以在多个地方进行复用;
7.2. 注册全局组件
全局组件需要使用我们全局创建的app来注册组件;
通过component方法传入组件名称、组件对象即可注册一个全局组件了;
之后,我们可以在App组件的template中直接使用这个全局组件:
```javascript
app.component(“my-comp”,{})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
- ![image-20230207182548417](Vue入门/image-20230207182548417.png)
- ![image-20230207182556551](Vue入门/image-20230207182556551.png)
### 7.3.组件的名称
- 在通过app.component注册一个组件的时候,第一个参数是组件的名称,定义组件名的方式有两种:
- 方式一:使用kebab-case(短横线分割符)
- 当使用 kebab-case (短横线分隔命名) 定义一个组件时,你也必须在引用这个自定义元素时使用 kebab-case,例如 <my-component-name>;
- ![image-20230207182710146](Vue入门/image-20230207182710146.png)
- 方式二:使用PascalCase(驼峰标识符)
- 当使用 PascalCase (首字母大写命名) 定义一个组件时,你在引用这个自定义元素时两种命名法都可以使用。
- 也就是说 <my-component-name> 和 <MyComponentName> 都是可接受的;
- ![image-20230207182739698](Vue入门/image-20230207182739698.png)
### 7.4. 注册局部组件
- 全局组件往往是在应用程序一开始就会全局组件完成,那么就意味着如果某些组件我们并没有用到,也会一起被注册:
- 比如我们注册了三个全局组件:ComponentA、ComponentB、ComponentC;
- 在开发中我们只使用了ComponentA、ComponentB,如果ComponentC没有用到但是我们依然在全局进行了注册,那么就意味着类似于webpack这种打包工具在打包我们的项目时,我们依然会对其进行打包;
- 这样最终打包出的JavaScript包就会有关于ComponentC的内容,用户在下载对应的JavaScript时也会增加包的大小;
- 所以在开发中我们通常使用组件的时候采用的都是局部注册:
- 局部注册是在我们需要使用到的组件中,通过components属性选项来进行注册;
- 比如之前的App组件中,我们有data、computed、methods等选项了,事实上还可以有一个components选项;
- 该components选项对应的是一个对象,对象中的键值对是 组件的名称: 组件对象;
- ![image-20230207182835272](Vue入门/image-20230207182835272.png)
-
````html
const app = {
components:{
"my-comp":{}
}
}
````
## 八、Vue脚手架
### 8.1. Vue的开发模式
- html
- .vue
### 8.2. Vue CLI安装和使用
- 安装Vue CLI(目前最新的版本是v5.0.8)
- 我们是进行全局安装,这样在任何时候都可以通过vue的命令来创建项目;
- ```nginx
npm install @vue/cli -g升级Vue CLI:
如果是比较旧的版本,可以通过下面的命令来升级
```nginx
npm update @vue/cli -g1
2
3
4
5
- 通过Vue的命令来创建项目
- ```nginx
Vue create 项目的名称
创建项目的过程
- Vue CLI的运行原理
- Vue CLI的运行原理
8.3. Vue项目的目录结构
8.4. 从main.js入口开始,如何一步步创建自己的组件
- App.vue
8.5. jsconfig文件的作用
8.6. vue不同版本的作用
- runtime:运行时
- runtime + compile:运行 + 编译
8.7. css的scoped作用域
8.8. npm init vue @latest创建项目
九、组件间的通信
9.1.组件的嵌套关系
- 父组件、子组件
9.2. 父传子 - props(重要)
在开发中很常见的就是父子组件之间通信,比如父组件有一些数据,需要子组件来进行展示:
- 这个时候我们可以通过props来完成组件之间的通信;
什么是Props呢?
- Props是你可以在组件上注册一些自定义的attribute;
- 父组件给这些attribute赋值,子组件通过attribute的名称获取到对应的值;
Props有两种常见的用法:
方式一:字符串数组,数组中的字符串就是attribute的名称;
方式二:对象类型,对象类型我们可以在指定attribute名称的同时,指定它需要传递的类型、是否是必须的、默认值等等;
9.3. 非prop得attribute
数组用法中我们只能说明传入的attribute的名称,并不能对其进行任何形式的限制,接下来我们来看一下对象的写法是如何让
我们的props变得更加完善的。
当使用对象语法的时候,我们可以对传入的内容限制更多:
比如指定传入的attribute的类型;
比如指定传入的attribute是否是必传的;
比如指定没有传入时,attribute的默认值;
那么type的类型都可以是哪些呢?
对象类型的其他写法
Prop 的大小写命名
HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符;
这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名)
命名;
9.4. 子传父 - $emit(重要)
什么情况下子组件需要传递内容到父组件呢?
当子组件有一些事件发生的时候,比如在组件中发生了点击,父组件需要切换内容;
子组件有一些内容想要传递给父组件的时候;
我们如何完成上面的操作呢?
首先,我们需要在子组件中定义好在某些情况下触发的事件名称;
其次,在父组件中以v-on的方式传入要监听的事件名称,并且绑定到对应的方法中;
最后,在子组件中发生某个事件的时候,根据事件名称触发对应的事件;
9.5. 阶段案例练习 - TabControl的封装
9.6. 非父子组件 的通信
9.6.1. Provide/Inject
- 基本使用
- 函数写法
- 数据的响应式
- computed
9.6.2. 事件总线hy-event-store
- 在event-bus.js中创建eventBus对象
- 监听事件
- eventBus.on()
- 发出事件
- eventBus.emit()
十、组件的插槽slot
10.1. 认识Slot的作用
- 前面我们会通过props传递给组件一些数据,让组件来进行展示;
- 但是为了让这个组件具备更强的通用性,我们不能将组件中的内容限制为固定的div、span等等这些元素;
- 比如某种情况下我们使用组件,希望组件显示的是一个按钮,某种情况下我们使用组件希望显示的是一张图片;
- 我们应该让使用者可以决定某一块区域到底存放什么内容和元素;
10.2. Slot的基本使用和默认值(重要)
这个时候我们就可以来定义插槽slot:
插槽的使用过程其实是抽取共性、预留不同;
我们会将共同的元素、内容依然在组件内进行封装;
同时会将不同的元素使用slot作为占位,让外部决定到底显示什么样的元素;
如何使用slot呢?
Vue中将
元素作为承载分发内容的出口; 在封装组件中,使用特殊的元素
就可以为封装组件开启一个插槽; 该插槽插入什么内容取决于父组件如何使用;
插槽的默认值
插槽的基本使用
10.3. Slot的具名插槽 (重要)
事实上,我们希望达到的效果是插槽对应的显示,这个时候我们就可以使用 具名插槽:
具名插槽顾名思义就是给插槽起一个名字,
元素有一个特殊的 attribute:name; 一个不带 name 的slot,会带有隐含的名字 default;
1 | <template> |
1 | <nav-bar> |
10.4. 动态插槽名
什么是动态插槽名呢?
目前我们使用的插槽名称都是固定的;
比如 v-slot:left、v-slot:center等等;
我们可以通过 v-slot:[dynamicSlotName]方式动态绑定一个名称;
10.5. Vue编译作用域
10.6. 作用域插槽使用
- 核心:将子组件中的数据传递给父组件的插槽来使用
10.7.具名插槽使用的时候缩写
具名插槽使用的时候缩写:
跟 v-on 和 v-bind 一样,v-slot 也有缩写;
即把参数之前的所有内容 (v-slot:) 替换为字符 #;
十一、生命周期
11.1. 生命周期函数
- created
- mounted
- unmounted
11.2. refs引入元素/组件
- 在元素/组件中添加ref属性
- this.$refs.属性
11.3. 动态组件的属性
- component is
11.4. keep-alive
- 让组件缓存起来 存货下来
- include/exclude/max
- 存活生命周期函数
- activated
- deactivated
11.5. 异步组件的使用
11.5.1. webpack分包处理
- import()
11.5.2. 异步组件
1 | defineAsyncComponent(() => {}) |
11.6. v-model组件上
1 | <counter v-model="appCounter" /> |
11.7. 混入Mixin
- 组件通过mixins:[]
- 全局混入:app.mixin({})
十二、Composition API (一)
12.1. 认识组合API
- options API -> Composition API
12.2. 使用reactive定义响应式数据
- 复杂类型
12.3. 使用ref定义响应式数据
- 基本类型
- 复杂数据
- template自动解包
12.4. 开发中如何选择reactive/ref
12.5. readonly的使用
12.5.1. 单向数据流
- vue/react
12.6. reactive函数补充
- isProxy
- isReactive
- isReadonly
- shallowReactive
- shallowReadonly
12.7. ref函数的补充
- toRefs
- toRef
- unref
- toRaw
12.8. setup中不能使用this
十三、Composition API (二)
13.1. computed计算属性 (重点)
13.2. ref获取元素/组件(重点)
13.3. 生命周期注册函数(重点)
- beforeCreate/Created -> setup
13.4. provide/inject
13.5. watch / watchEffect
- 区别
- watch必须指定数据源 effect自动收集依赖
- watch监听到改变,可以拿到改变前后的value
- effect默认直接执行一次,watch在不设置immediate为true的情况下,第一次不执行
13.6. 自定义hook练习
13.6.1. useCount练习
13.6.2. useTitle练习
13.6.3. useScrollPosition
13.7. setup语法糖(重点)
- defineProps
- defineEmits
- defineExpose
十四、Vue-Router
14.1. 前端路由的发展历程
- 后端路由
- 前后端分离
- 单页面富应用
- SPA:single page web application
- 前端路由
14.2. 改变URL,页面不刷新的两种模式
- hash模式
- history模式
14.3. VueRouter的使用过程
- 安装
- npm install vue-router
- 使用:
- 创建router对象
- createRouter
- routes:映射关系
- history:createWebHashHistory()
- app.use(router)
- 使用路径:
- router-view:占位
- router-link
- 编程式导航
- 创建router对象
14.4. Vue-Router知识点补充
14.4.1. 默认路径
- path -> redirect
14.4.2. history模式
- createWebHistory()
14.4.3. router-link其他属性
- to
- replace
- active-class
- exact-active-class
14.4.4. 路由懒加载-分包处理
14.4.5. 其他属性
- name
- meta
- route.meta
14.5. 动态路由的使用
- path:/user/:id
14.6. NotFound页面匹配
- path:/:pathMatch(.*)
14.7. 路由的嵌套使用
- 在一层路由中添加children属性:
- {path:“recommend”,component: () => import(“./“)}
- 在HOme组件中添加
- 路径跳转
14.8. 编程式导航
14.8.1. 跳转的方式
- push(路径)
- push({path/query})
- replace()
14.8.2. 路径的切换
- back()
- forward()
- go(number)
十五、Vuex
什么是状态管理:
在开发中,我们的应用程序需要处理各种各样的数据,这些数据需要保存在我们应用程序中的某一个位置,对于这些数据的管理我们就称之为 状态管理
在前面我们是如何管理自己的状态呢?
- 在Vue开发中,我们使用组件化的开发方式
- 而在组件中我们定义data或者在setup中返回使用的数据,这些数据我们称之为state
- 在模块template中我们可以使用这些数据,模块最终会被渲染成DOM,我们称之为View
- 在模块中,我们会产生一些行为事件,处理这些行为事件时,有可能会修改state,这些行为事件我们称之为actions
JavaScript开发的应用程序,已经变的越来越复杂了:
- JavaScript需要管理的状态越来越多,越来越复杂
- 这些状态包括服务器返回的数据,缓存数据,用户操作产生的数据等
- 也包括一些UI状态,比如某些元素是否被选中,是否显示加载动效,当前分页
当我们的应用越到多个组件共享状态时,单向数据流的简洁性很容易被破坏:
- 多个视图依赖于同意状态
- 来自不同视图的行为需要变更为同意状态
我们是否可以通过组件数据的传递来完成呢?
对于一些简单的状态,确实可以通过props的传递或者Provide的方式来共享状态
但是对于复杂的状态管理来说,显然单纯的通过传递和共享的方式是不足以解决问题的,比如兄弟组件如何共享数据?、
这就是Vuex背后的思想,它借鉴了Flux,Redux,Elm(纯函数语言,redux有借鉴它的思想):
管理不断变化的state本身是非常困难的
状态之间相互会存在依赖,一个状态的变化会引起另一个状态的变化,View页面也可能会引起状态的变化
当应用程序复杂时,state在什么时候,因为什么原因而发生了变化,发生了怎么样的变化,会变得非常难以控制和追踪
因此,我们是否可以考虑将组件的内部状态抽离出来,以一个全局单例的方式来管理呢?
在这种模式下,我们的组件树构成了一个巨大的 视图View
不管在树的哪个位置,任何组件都能获取状态或者触发行为
通过定义和隔离状态管理中的各个概念,并通过强制性的规则来维护视图和状态间的独立性,我们的代码会变得更加结构化和易于维护、跟踪
创建Store:
- 每一个Vuex应用的核心就是Store仓库:
- store本质上是一个容器,它包含着你的应用中大部分的状态(state);
- Vuex的对象和单纯的全局对象有什么区别呢?
- 第一:Vuex的状态存储时响应式的
- 当Vue组件从store中读取状态的时候,若store中的状态发生变化,那么响应的组件也会被更新
- 第二:你不能直接改变store中的状态
- 改变store中的状态的唯一途径就是提交(commit)mutation
- 这样使得我们可以方便的跟踪每一个状态的变化,从而让我们能够通过一些工具帮助我们更好的管理应用的状态
- 第一:Vuex的状态存储时响应式的
- 使用步骤:
- 创建store对象
- 在app中通过插件安装
- app.use(store)
什么是Module?
- 由于使用单一状态树,应用的所有状态会集中到一个比较大的对象,当应用变的非常复杂时,store对象就有可能变的相当臃肿
- 为了解决以上问题,Vuex允许我们将store分割成模块module
- 每个模块拥有自己的state,mutation,action,getter,甚至是嵌套子模块
十六、Pinia
什么是Pinia
- Pinia开始于2019年,最初作为一个实验为Vue重新设计状态管理,让它用起来像组合式API(Composition API)
- 从那时到现在,最初的设计原则依然是相同的,并且目前兼容的Vue2、Vue3,也并不要求使用Composition API
- Pinia本质上依然是一个状态管理库,用于跨组件、页面进行状态共享(这点和Vuex,Redux一样)
Pinia和Vuex的区别
- Pinia最初为了探索Vuex的下一次迭代会是什么样子,结合了Vuex5核心团队讨论中的许多想法
- 最终,团队终于意识到Pinia已经实现了Vuex5中大部分内容,所以最终决定用Pinia来替代Vuex
- 与Vuex相比,Pinia提供一个更简单的API,具有更少的仪式,提供了Composition-API 风格的API
- 最重要的是,在与TypeScript一起使用时具有可靠的类型推断支持
Pinia的优点
- mutation不再存在
- 他们经常被认为是非常冗长的
- 他们最初带来了devtools集成,但这不再是问题
- 更友好的TypeScript支持,Vuex之前对TypeScript的支持不是很友好
- 不再有modules的嵌套结构
- 你可以灵活使用每一个store,它们是通过扁平化的方式相互使用
- 已不再有命名空间的概念,不需要记住他们的复杂关系
什么是Store?
- 一个Store是一个实体,他会持有为绑定到你组件树的状态和业务逻辑,也就是保存了全局的状态
- 它始终存在,并且每个人都可以读取和写入组件
- 你可以在你的应用程序中定义任意数量的Store来管理你的状态
Store有三个核心概念:
- state getters actions
- 等同于组建的data computed methods
- 一旦store被实例化,你就可以直接在store上访问state、getters、actions中定义的任何属性