Vue

1、入门

什么是vue

vue是一套用于构建用户页面的渐进式JavaScript框架

特点:

  1. 采用组件化模式,提高代码复用率、且让代码更好的维护
  2. 声明式编码,让编码人员无需直接操作DOM,提高开发效率
  3. 使用虚拟DOM+优秀的DIff算法,尽量复用DOM节点

如何开始Vue

首先先去官网下载Vue.js包,然后放在项目下,然后在Google商店下载开发资源,然后在打开导入

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>初识vue</title>
<script type="text/javascript" src="js/vue.js"></script>
</head>
<body>

</body>
</html>

这时控制台会输出一串字符

image-20221102200311821

那么我们可以在项目中关闭,

方法一可以在Vue.js中关闭,

方法二可以在我们的HTML代码中关闭,

这时我们会发现一个问题,为什么将Vue.config.prodution=false放在body里 还会出现呢,问题如下,js代码都由上到下构成的,我们一开始body中的代码为false,那么在执行JS样式的时候配置文件就会变成true,

解决方案:用方法1

启动Vue程序

1
2
3
4
5
6
7
1.想让Vue工作,就必须创建一个Vue实例,而且要传一个配置对象
2.root容器中的代码依然符合HTML规范,只不过混入了一些特殊的Vue语法
3.root容器里的代码被称为:Vue模板
4.Vue实例和容器是一一对应的
5.真实开发中只有一个Vue实例,并且会配合着组件一起使用
6.{{xxx}}中xxx要写成JS表达式,并且xxx可以自动读取到data中的所有属性
7.一旦data中的数据发生改变,那么页面中用到的数据也会自动更新

实现第一个vue

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</title>
<script type="text/javascript" src="js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>hi {{name}} {{BJ}}</h1>
</div>
</body>
<script type="text/javascript">
Vue.config.devtools = true
new Vue({
el:'#root',//用于指定容器
data:{//data中用于存储数据,数据供el所指定的容器使用,暂时我们先写成一个对象
name:'尚硅谷',
BJ:'北京'
}
})
</script>
</html>

image-20221103175751420

我们可在开发者工具(F12)中找到Vue,然后修改数据所对应的值

image-20221103175814830

2、初步学习

Vue模板语法有两大类:

  1. ​ 插值语法
    • 功能:用于解析标签体内容
    • 写法: ,xxx是js表达式,且可以直接读取到data中的所有属性
  2. 指令语法
    • 功能:用于解析标签(包括:属性标签、标签体内容、绑定事件……)
    • 举例:v-bind:href=”xxx”,或者简写为:href=”xxx”,xxx同样要写js表达式,且可以直接读取到data中所有属性
    • 备注:Vue中有很多属性,而且都是v-????

数据绑定

数据绑定分为双向绑定和单向绑定

  1. 单向绑定(v-bind):数据只能从data流向页面(快捷书写方式 :)

  2. 双向绑定(v-model):数据不仅能从data流向页面也能从页面流向data

    备注:

    1. 双向绑定一般都应用在表单类元素上(如:input,select)
    2. v-model:value 可以缩写成 v-model 应为v-model默认收集的就是value值
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">
<title>Title</title>
<script type="text/javascript" src="js/vue.js"></script>
</head>
<body>
<div id="root">
单向绑定:<input type="text" v-bind:value=" name">
<br/>
双向绑定:<input type="text" v-model:value=" name">
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
name:'尚硅谷'
}
})
</script>
</body>
</html>

image-20221103185434082

在修改data后image-20221103185502019

当在单项绑定的表单项中修改信息:我们可以发现 双向绑定表单项和data中的数据并没有被修改

image-20221103185643054

当修改双向绑定表单时

image-20221103185853754

el 与data的两种写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
el既可以写成 
<script type="text/javascript">
const v=new Vue({
el:'#root',
data:{
name:'尚硅谷'
}
})
</script>
又可以写成
<script type="text/javascript">
const v=new Vue({
data:{
name:'尚硅谷'
}
})
v.$mount('#root');
</script>

这两个的实际效果相同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
第一种写法
<script type="text/javascript">
const v=new Vue({
data:{
name:'尚硅谷'
}
})
v.$mount('#root');
</script>
第二种写法
<script type="text/javascript">
const v=new Vue({
data:function (){
return{
name:'尚硅谷'
}
}
})
v.$mount('#root');
</script>

MVVM模型

  1. M:模型(model):对应data中的内容
  2. V:视图(View): 模板
  3. VM:视图模型(ViewModel):Vue实例对象

image-20221104092412359

image-20221104091154979

data中的所有属性,最后都出现在vm身上

vm中的所有属性,和vue原型中的所有属性,都可以在Vue模板中直接使用

数据代理

数据代理的定义:

通过一个对象代理对另一个对象中的属性进行操作

Object.defineproperty(数据代理原理)

数据代理的方法:

Object.defineproperty(a,b,c),该方法用于给对象增加属性及其参数,其方法中的a为所传对象,b为属性名,c为配置项

配置:

  • enumerable 控制属性是否能被枚举,默认为false
  • writable 控制属性是否能被修改,默认为false
  • configurable:控制属性能否被删除,默认为false
1
2
3
4
5
6
7
8
9
10
<script type="text/javascript">
let person={
name:'人',
sex:'男'
}
Object.defineProperty(person,'age',{
value:18
})
console.log(person)
</script>

输出结果为:

image-20221104162416938

我们可以发现,这种写法的age和直接在对象中添加age:18的方法有一些不同,直接添加age:18的方式的输出结果age是深紫色的而这个age居然是浅紫色的

同样的,我们通过枚举的方式输出的结果为

1
2
3
for(let key in person){
console.log('@',person[key])
}

image-20221104162845602

如果我们不使用直接添加的方式还想要去可以枚举得到,那我们可以通过向defineProperty方法中修改第三个参数,向其中添加enumerable(可列举的):true,即可被枚举到

​ 我们又发现,使用方法definproperty()得到的属性,通过控制台输出颜色又变成了深紫色,但在控制台中修改age的值后输出的结果还是原先的值

image-20221104163831022

这时我们可以在第三个参数中添加配置writable:true,即可修改

image-20221104164147884

当我们想删除这个age属性的时候,普通方法随便删,但是此方法添加的属性却无法删除

image-20221104164622346

添加后

image-20221104164828667

当有人读取person的age属性时,get函数(getter)就会被调用,其返回值就是age的值

当有人修改person的age属性时,set函数(setter)就会被调用,且会受到修改的具体值

1
2
3
4
5
6
7
8
9
10
Object.defineProperty(person,'age',{
get:function(){
console.log("有人查询了age属性")
return number;
},
set:function (value){
console.log("有人修改age属性,且值为:"+value)
number=value
}
})

数据代理的原理

image-20221104181012528

3、事件处理

基本事件处理

若在Vue中绑定事件处理对象,对应的命令为v-on(快捷书写方式@),例如我们要设置一个按钮点击就会弹窗(点你咋滴)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  <script rel="stylesheet" src="js/vue.js"></script>
</head>
<body>
<div id="root">
<h1> 欢迎来到{{name}}学习</h1>
<button v-on:click="showInfo">你点你哥儿你试试</button>
</div>

<script type="text/javascript">
const vm=new Vue({
el:'#root',
data:{
name:'石家庄学院'
},
methods:{
showInfo(){
alert("点你咋滴")
}
}
})
</script>

我们事件的回调函数都写入methods中,showInfo()中也有事件对象event,showInfor(event)

1
2
3
4
5
methods:{
showInfo(event){
console.log(event.target.innerText)
}
}

image-20221104193756023

methods中的回调函数切不可写成箭头函数的形式,不然函数中的this指定对象将会发生变化

1
console.log(this)

image-20221104194057628

当改为箭头函数时

1
2
3
4
5
methods:{
showInfo:(event)=>{
console.log(this)
}
}

image-20221104194201383

在事件处理中传参

1
<button @click="showInfo2('张慧敏是头猪',$event)">提示信息2</button>

倘若我需要在事件处理中传递参数则我们需要在事件名称中加入(),在括号中加入对应的数据,要想保留事件对象就在后面添加$event,这时我们就可以在事件中接受这个参数了

1
2
3
showInfo2(string,event){
alert(string);
}

 image-20221104200726922

事件修饰符

v-事件.prevent(阻止默认事件)

当我们在超链接中绑定一个对应的事件,但是我们并不想跳转超链接中href属性的地址,我们在JS中可以采用事件对象.preventDefault()方法组织默认行为,

但在Vue中可以直接在事件种类属性中调用.prevent即可阻止默认行为,以下配置我们发现并不跳转,而我们对应的prevent叫做对事件的修饰

1
2
3
4
5
<a href="https://www.sjzc.edu.cn/" @click.prevent="showInfo2">点我提示信息</a>
________________________________________________________________________________
showInfo2(event){
alert('来学习咯');
}

image-20221104203205608

v-事件.stop (阻止事件冒泡)

v-事件.once(事件只触发一次)

v-事件.capture(当前事件是捕获模式)

v-事件.self(只有event.target 是当前操作的元素时才会触发条件)

大概意思就是既不接受冒泡的触发,也不接受捕获的触发,只会接受切切实实的事件触发

v-事件.passive(事件的默认行为立即执行,无需等待事件回调执行完毕)

滚动条滚动事件(scroll)和鼠标的滚轮滚动事件(wheel)的差别

当绑定@scroll事件时,滚动条滚动到末尾事件不再触发,当绑定@wheel事件时,不管滚动条在何位置(即使在滚动条末尾),不管滚动条移动多少,只要鼠标的滑轮滚动,事件就会触发

以wheel事件为例,当我们处理一些复杂事件的时候,事件对象的会先接收到事件触发,然后执行回调函数,之后才会执行默认操作(滚动条向下滑动)

但这样会影响我们浏览动态的页面,所以就要用到passive修饰符了

没有加passive修饰符的事件(注意到滚动条并没有向下滑)

image-20221104214031753

加了passive修饰符,我们可以注意到,回调函数并没有执行完毕,但默认事件已经触发

image-20221104214129986

注意:并非所有事件的默认事件都会这样,scroll不会有这种情况

键盘事件

Vue中常见的按键别名

  • 回车:enter 删除:delete
  • 退出:esc 空格:space
  • 换行:tab 上下左右: up down left right

1、Vue没有提供别名按键可以使用原始的Key值去绑定,但注意要转为keybab-case(短横线命名)

2、系统修饰按键(用法特殊)ctrl、alt、shift、meta(win)

  1. 配合keyup使用:按下修饰键的同时,在按下其他键,随后放下其他键事件才被触发
  2. 配合keydown使用:正常触发事件
  3. Vue.config.keyCodes.自定义键名=键码 可以定制按键别名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div id="root">
<h2>欢迎来到{{name}}学习</h2>
<input type="text" @keyup.ctrl="demo1" placeholder="输入后回车进行弹窗">
</div>
<script type="text/javascript">
const vm=new Vue({
el:'#root',
data:{
name:'石家庄学院'
},
methods:{
demo1(e){
alert(e.target.value);
}
}
}
)
</script>

实现效果

当一起按下ctrl键和其他键并不会弹窗,当松开后者时则会弹窗,这就是keyup,keydown在ctrl和其他键按下的时候就会弹窗

image-20221105185527734

4、计算属性(computed)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div id="root">
<input type="text" placeholder="请输入姓" v-model:value="firstName">
<br/>
<br/>
<input type="text" placeholder="请输入名" v-model:value="lastName">
<h2>此人的姓名是:{{fullName}}</h2>
</div>

<script>
new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'锋'
},
computed:{
fullName(){
console.log('调用了get方法');
return this.firstName+this.lastName
}
}
})

vue开发工具中的展示

image-20221105150638367

  1. 定义:要用的属性不存在,要通过以有属性计算得来
  2. 原理:底层接住了Objcet.defineproperty方法提供的getter和setter
  3. get函数什么时候执行?
    1. 初次读取的时候会执行一次
    2. 当以来的数据发生改变的时候会再次调用
  4. 优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试更方便。
  5. 备注:
    1. 计算属性最终会出现在VM上,直接读取使用即可
    2. 如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变

因为计算属性多数都只用作数据的展示,并不用于数据修改,也可以简写计算属性直接写成函数的形式

5、监视属性(watch)

监视属性可以监视某个属性,倘若这个值发生变化时,就可以调用方法handler执行一定的操作

1
2
3
4
5
6
7
8
watch:{
isHot:{
handler(newValue,oldValue){
console.log("原先的值为:"+oldValue)
console.log("现在的值为:"+newValue)
}
}
}

也可以这么写(写在new Vue以外)

1
2
3
4
5
6
vm.$watch('isHot',{
handler(newValue,oldValue){
console.log("原先的值为:"+oldValue)
console.log("现在的值为:"+newValue)
}
})

其中handler()中有属性immediate属性可以让handler在初始化的时候调用一下

深度监视(deep)

当我们想监视对象中的某个属性变化时,我们可以利用多级调用的方式

监视对象numbers中a属性的变化

1
2
3
4
5
'numbers.a':{
handler(newValue, oldValue) {
console.log('a变化了')
}
}

但是当我们想检测numbers中任意属性变化时,再使用普通的监视属性就会发现即使对象属性中的值发生变化,也无法监视到这时我们就可以使用深度监视的方法,监视numbers中所有属性,当有发生变化时,就会执行handler方法

1
2
3
4
5
6
numbers:{
deep:true,//Vue中深度监视的开启
handler(newValue,oldValue){
console.log("numbers发生变化")
}
}

计算属性和监视属性的差别

  • 计算属性不可以开启异步任务去维护数据、
  • computed能完成的任务watch都可以完成

两个重要的原则

  • 被Vue所管理的函数最好写成普通函数,不要写成箭头函数,这样vm所指的才是vm或者组件的实例对象
  • 不被Vue所管理的函数例如回调函数或ajax回调函数等,这样vm所指的才是vm或组件实例对象

6、绑定样式

通过类来绑定样式有许多方式,首先我们需要了解如何通过Vue来绑定类修改样式,这时我们可以 直接:class绑定

  1. 绑定class样式字符串写法:这种方法适用于样式的类不确定,需要动态指定
1
2
3
<div class="basic" :class="mood" @click="changeMood"><!--需要动态绑定的加上:class,不需要的就直接class-->
{{name}}
</div>

2.数组写法:这种方法适用于要绑定的样式个数不确定,名字也不确定

1
2
3
<div class="basic" :class="classArr">
{{name}}
</div>

3.对象写法:适用于个数也确定,名字也确定,只需用户决定用不用

1
2
3
4
5
6
7
8
9
<div class="basic" :class="classObj">
{{name}}
</div>
data{
classObj:{
样式1:true,
样式2:false
}
}

绑定Style样式

1
2
3
4
5
6
7
8
:style绑定样式    
<div :style="styleObj"></div>
在data中添加styleObj对象
styleObj:{
width: 300+'px',
height: 300+'px',
backgroundColor:'red'
}

image-20221107134957644

7、条件渲染

我们可以利用v-show指令来控制该标签中的样式是否渲染,原理是依据display:no来对标签中的属性进行隐藏

1
<div :style="styleObj" v-show="false"></div>

image-20221107140915851

image-20221107141845669

也可用v-if,但v-if判定为false的时候会直接把节点删除变成一个空注解

image-20221107141919282

当我们使用v-if或v-else-if,并且判断条件中两个属性值的判断值都是true那么就执行v-if中的语句而不是v-else-if的语句

1
2
3
<div v-else-if="n===1">Augular</div>
<div v-if="n===1">React</div>
<div v-else-if="n===2">Vue</div>

image-20221107164209791

v-else是相当于以上所有判断结果都为false时所执行的语句

1
2
3
<div v-if="n===1">Augular</div>
<div v-else="n===1">React</div>
<div v-else-if="n===2">Vue</div>

image-20221107164416558

当我们想通过判断直接输出许多元素时,我们可以吧一个<.template>元素当成不可见的包裹元素,并在上面使用v-if。最终的渲染结果将不包含<.tempate>元素

1
2
3
4
5
<template v-if="n===1">
<div>Augular</div>
<div>React</div>
<div>Vue</div>
</template>

image-20221107165333723

1
<template>标签只能和v-if一起使用而不能和v-show一起使用 	

8、列表渲染

我们的数据中有一个数组中含有许多对象,要将其对象以列表的形式呈现到浏览器中我们可以使用列表渲染v-for

1
2
3
4
5
6
7
8
9
  <ul>
<li v-for="(p,index) in persons" :key="p.id">
{{index}}-----{{p}}
</li>
</ul>

persons: [{id:1,name: '张锋',age: 21},
{id:2,name: '张慧敏',age: 20},
{id:3,name: '二周年',age: 999}]

v-for中的 in前面的部分类似与java的增强for,而in就像其中的:一样,而in后就是要遍历的数组,不仅如此Vue中对v-for还有另一个参数 index, 指此个对象在数组中的位置,另外 可动态绑定一个主键用来做该数组中元素的唯一标识

image-20221107203827124

不仅如此Vue中的v-for命令还能遍历对象,我们测试性的传入三个参数a,b,c,来测试其中的参数代表什么

1
2
3
4
5
6
7
8
9
10
11
<h2>汽车信息</h2>
<ul>
<li v-for="(a,b,c) of car">
{{a}}-----{{b}}------{{c}}
</li>
</ul>
car:{
name:'奥迪A8',
price:'80w',
color:'黑色'
}

image-20221107204023848

其中第一个参数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
24
25
26
27
28
29
30
31
32
33
34
35
36
 <ul>
<h2>人员信息</h2>
<input type="text" placeholder="请输入名字" v-model="keyWord">
<li v-for="(p,index) in filpersons" :key="p.id">
{{p.id}}-{{p.name}}-{{p.age}}---{{p.sex}}
</li>
</ul>
<button @click="orderType=2">年龄升序</button>
<button @click="orderType=1">年龄升序</button>
<button x@click="orderType=0">原顺序</button>
const vm=new Vue({
el:'#root',
data: {
orderType:0,
keyWord:'',
persons: [
{id:1,name: '张锋',age: 21,sex:'男'},
{id:2,name: '张慧敏',age: 20,sex:'女'},
{id:3,name: '二周年',age: 999,sex:'无'},
{id:4,name: '张慧锋',age: 0,sex: '男'},
],
}
computed:{
filpersons(){
const arr=this.persons.filter((p)=>{
return p.name.indexOf(this.keyWord)!==-1
})
if(this.orderType){
arr.sort((a,b)=>{
return this.orderType===1?b.age-a.age:a.age-b.age;
})
}
return arr;
}
}
})

Vue的set()方法

当我们在页面中想显示View中定义而model中并未定义的对象属性我们可以使用set方法

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="root">
<div>姓名:{{student.name}}</div>
<div>年龄:
周岁:{{student.age.rage}}
虚岁:{{student.age.sage}}
</div>
<div>性别:{{student.sex}}</div>
<div v-for="(f,index) of firends">
{{f.name}}-----{{f.age}}
</div>
</div>
<script>
const vm=new Vue({
el:'#root',
data:{
student:{
name:'张锋',
age:{
rage:21,
sage:22
}
},
firends:[{name:'tom',age:20},{name:'jack',age:21}]
}
})
</script>

这里并未定义student中的sex属性但页面中已经调用,这时页面显示为

image-20221109133428183

当我们在控制台中使用,vm.set()方法后 ,这时视图也会进行更新

1
vm.$set(vm._data.student,'sex','男')

image-20221109133634889

Vue向响应式对象中添加一个property,确保这个property同样也是响应式的,且触发试图更新,他必须用于向响应式对象中添加新的property,因为Vue无法探测普通新增的property,对象不能是Vue实例或者Vue实例的根对象

如果Vue修改数组那么如果不用Set方法的话,就必须要用JS原生的几个操作数组的API(push,pop ,unshift ,shift )

用set的形式来修改数组

1
vm.$set(数组,索引,索引值)

收集表单数据

  • 若 <.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:输入首尾空格过滤

Vue的一些指令

v-text:将name当做text进行展示

v-html:将后面所接的字符串以HTML的方式展现

v-cloak

  1. 本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删除v-cloak属性
  2. 使用css配合v-cloak可以解决网速过慢而页面展示出的情况
1
2
3
4
5
6
7
8
<style>
[v-cloak]{
display: none;
}
</style>
<div v-cloak>
{{time}}
</div>

v-once该指令对应标签中的内容在第一次动态渲染后则会变成静态资源例如

1
2
3
4
5
6
7
8
9
10
11
12
13
<div id="root">
<div v-once>n1:{{n}}</div>
<div> n2:{{n}}</div>
<button @click="n++">点我n+1</button>
</div>
<script>
new Vue({
el:'#root',
data:{
n:1
},
})
</script>

image-20221110125107425image-20221110125136839image-20221110125149868

v-pre:

  1. 跳过其所在节点的编译过程
    1. 可以利用它跳过没有指令语法、没有使用差值语法的节点,会加快编译

自定义对象属性

directives

  • 函数式

在Vue创建一个函数值指令则要加属性directives,然后定义所要创建的指令,这个指令可以有两个形参去接收

第一个参数为所操作的元素节点,第二个参数绑定的参数对象

1
2
3
4
5
6
7
8
9
10
11

当前A的数值为:<span v-text="n"></span><br/>
增大到十倍之后的A的数值为:<span v-big="n"></span>
<button @click="n++">点我n+1</button>

directives:{
big(element,binding){
console.log('big')
element.innerText=binding.value*10
}
}
  • 对象式

在对象式中要把我们所要自定义的属性写成对象的形式,然后需要写出三个指令的绑定

  1. bind(),在指令与元素成功绑定时会执行该函数
  2. inserted(),在指令所在元素被插入页面时执行该函数
  3. update(),在指令所在模板被重新解析时执行该函数

倘若我们又一个需求,需要一个指令绑定一个表单中的值,然后页面一加载好时光标就聚焦在表单中

这时候我们需要利用对象式绑定了

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>
当前A的数值为:<span v-text="n"></span><br/>
增大到十倍之后的A的数值为:<span v-big="n"></span>
<button @click="n++">点我n+1</button>
<input v-fbind:value="n">
</div>

directives:{
big(element,binding){
element.innerText=binding.value*10
},
fbind:{
bind(element,binding){
element.value=binding.value;
console.log('bind')
},
inserted(element,binding){
element.focus();
console.log('inserted')
},
update(element,binding){
element.value=binding.value;
console.log('update')
}
}
}

定义全局指令

Vue.directives

倘若我们要定义一个名叫fbind2的全局指令(对象式)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Vue.directives('fbind2',{
bind(element,binding){
element.value=binding.value;
console.log('bind')
},
inserted(element,binding){
element.focus();
console.log('inserted')
},
update(element,binding){
element.value=binding.value;
console.log('update')
}
})

我们要定义一个名为big2的全局指令(函数式)

1
2
3
Vue.directive('big2',function (element,binding){
element.innerText=binding.value*10
})

Vue的生命周期

img

利用Vue的生命周期来解决部分问题

以字体颜色渐变为例,我们利用定时器来使得字体无线渐变,当我们取消渐变时,对应的计时器要进行消除,但是我们还可以通过其他手段来改变VM中的该定时器,这时我们需要借助beforedestroy()

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
<div id="root">
<h1 :style="{opacity}">你看我会渐变,{{a}}</h1>
<button @click="stopOpa">点我停止变换</button>
</div>

<script>
new Vue({
el:'#root',
data:{
a:6,
opacity:1
},
methods:{
stopOpa(){
this.$destroy()
}
}
,
mounted(){
console.log('mouted上了')
this.ivId =setInterval(()=> {
console.log(1)
this.opacity -= 0.01
if (this.opacity <= 0) {
this.opacity=1}
} ,16)
},
beforeDestroy(){
console.log('定时器我啊,寄了')
clearInterval(this.ivId)
}
})
</script>

未停止销毁前

销毁后

image-20221112162100653

常用的生命周期钩子:

  1. mouted:发送ajax请求,启动定时器,绑定自定义事件,订阅消息等【初始化操作】
  2. beforeDestroy:清除定时器,解绑自定义事件、取消订阅信息等【收尾工作】

销毁Vue实例

  1. 销毁后借用Vue开发工具看不到任何信息
  2. 销毁后的自定义事件会失效,但原生的DOM事件依然有效
  3. 一般不会在beforeDestroy操作数据,即时操作了数据,也不会触发更新流程了

组件

非单文件组件

组件文件中含有其他组件

组件用法:编写组件->注册组件 ->使用组件(写组件标签)

如何定义一个组件:

使用Vue.extend(options)创建,其中的options和new Vue(options)传入的options很相似,但是有以下区别

  1. 组件中不写入el,因为最终所有的组件都要经过一个VM的处理,由vm中的el决定服务哪个容器
  2. data必须写成函数,因为避免组件被复用的时候,数据出现引用的关系

提示:使用template可以配置组件结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 const student=Vue.extend({
template:`
<div>
<h1>学生姓名:{{stuname}}</h1>
<h1>家庭住址:{{stuaddr}}</h1>
</div>`
,
data(){
return{
stuname:'张锋',
stuaddr:'沧州'
}
}
})

如何注册组件

  1. 局部注册:靠new Vue的时候传入componets选项
1
2
3
4
5
6
7
new Vue({
el:'#root',
components:{
school:school,
student:student
}
})

2.全局注册:靠vue.componet(组件名,组件来表示)

1
Vue.component(`hello`,hello)

image-20221112194058737

组件的几个注意项

  1. 关于组件名:

    1. 一个单词组成:
      • 第一种写法:首字母小写 schllo
      • 第二种写法: 首字母大写 School
    2. 多个单词组成:
      • 第一种写法(kebab-case命名):my-school
      • 第二种写法(CameCase命名):MySchool(需要Vue脚手架支持)
    3. 注意项:
      1. 组件名尽可能回避HTML中已有元素名称,例如:h2、H2都不行
      2. 可以使用name配置项指定组件在开发者工具中呈现的名字
  2. 关于组件标签:

    1. 第一种写法:
    2. 第二种写法:

    备注:不用脚手架时,会导致后续组件不能渲染

  3. 第一个简写方式

​ const school=vue.extend(options)可以简写成:const school= options

VueCLI

1、快速入门创建一个脚手架

VueCLI(Vue command line interface) Vue的命令行接口工具 被大众叫做Vue脚手架

以下的每一步都要不要在校园网的环境中运行否则就会出现严重的超时报错

首先要在命令行工具中安装淘宝源

1
npm install -g cnpm --registry=https://registry.npm.taobao.org

其次安装VueCLI

1
npm install --global @vue/cli

然后创建项目

1
vue create 项目名

然后进入到项目内运行项目

输入 npm run serve就会运行该项目并且显示当地地址号和该局域网下的地址号

image-20221114150923424

然后再浏览器中输入对应的网址即可

image-20221114172355450

在脚手架创建好后我们可以打开脚手架生成包的下的文件

image-20221114172638862

index.html中各行代码的注释,在配置页签图像时,为什么我们的href不会直接写./而是写了一个<%= BASE_URL %>

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="">
<head>
<meta charset="utf-8">
<!--针对IE浏览器的一个特殊配置,含义是让IE浏览器以最高级别的渲染页面-->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!--开启移动端的理想视口-->
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<!--配置页签图像-->
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<!--配置网页标题-->
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<!--当浏览器不支持js时noscript中的元素就会被渲染-->
<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>

components是我们写的组件,而外面就是典型的Vue所写的结构App.vue和main.js分别是Vue的管理组件文件另一个是Vue的项目入口文件

2、render函数

3、修改默认配置

我们在VueCLI文档中给了我们许多可供开发者修改的配置

image-20221114201749494

当我们在使用CLI时我们可以在CLI的vue.config.js文件中对这些属性进行修改

1
2
3
module.exports={
配置项
}

4、ref属性

  1. 用来给元素或者子组件注册引用信息(id替代者)
  2. 应用在html标签上获取的是真实的DOM元素,应用在组建标签上是组件实例对象(vc)
  3. 使用方式:
    • 打标识:

      ……..

      或者

    • 获取:this.$refs.xxx
1
2
3
4
5
6
7
8
9
10
  <school ref="school"></school>
<button @click="showDOM">点我输出上方的DOM元素</button>



methods:{
showDOM(){
console.log(this.$refs.school)
}
}

image-20221115151758442

5、props属性

功能:让组件接受外部传过来的数据

  1. 传递数据

​ 2.接收数据:

​ 第一种方式(只接收):

1
props:['name']

​ 第二种方式(限制类型):

1
2
3
4
5
props:{

​ name:Number

}

​ 第三种方式(限制类型,限制必要性,指定默认值)

1
2
3
4
5
6
7
props:{

name:{
type:String,//类型
required:true,//必要性
defalut:'石家庄学院'//默认值
}}

props是只读的,Vue底层会检测你对props的修改,如果进行修改那么就会提出警告,若业务需求确实需要修改,那么就复制props内容到data中,去data修改数据

6、mixin 属性(混入)

功能:可以把多个组件公用的配置提取成一个混入对象

使用方式

  • 第一步定义混合,例如:

    1
    2
    3
    4
    5
    6
    7
     export const aaa={
    methods:{
    showName(){
    alert(this.name)
    }
    }
    }
  • 第二部使用混入,例如:

    • 全局混入
    1
    Vue.mixin(aaa)
    • 局部混入
1
mixins:[aaa]

7、插件

功能:增强Vue

本质:包含一个install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件的使用者传递的数据

定义插件:

1
2
3
4
5
6
7
8
9
10
11
12
13
	对象.install=funcation(Vue,options){

​ //1、添加全局过滤器
Vue.filter(........)
//2、添加全局指令
Vue.directive(......)
//3、配置全局混入
Vue.minin(.......)
//4、添加实例方法
Vue.prototype.$myMethod=function(){....}
Vue.prototype.$myProperty=xxxx
}
使用插件:Vue.use(插件名)

image-20221117095905951

8、scoped样式

​ 作用:让样式在局部生效,防止冲突

用法<.style scoped>

组件化编码流程(通用)

  1. 实现动态组件:抽取组件,使用组件实现页面静态的效果
  2. 展示动态数据:
    1. 数据类型、名称是什么?
    2. 数据保存在哪个组件?
  3. 交互——从绑定时间监听开始

浏览本地存储

在JS 中有原生的API可供用户存储或删除Session或者Application中的数据

例如:localstorage为JS提供的原生API对象其中有方法

  • localstorage.setItem(String,String)

  • localstorage.removeItem(String)

  • localstorage.getItem(String)

  • sessionstorage为JS提供的原生API对象其中有方法

  • sessionstorage.setItem(String,String)

  • sessionstorage.removeItem(String)

  • sessionstorage.getItem(String)

组件自定义事件绑定与解绑

自定义事件在组件中的通信方式:子组件=》父组件

$emit

当我们在组建中绑定自定义事件时,我们会用到由子组件传递数据到父组件再由父组件

第一种方法,利用props属性对事件进行接收

1
2
3
4
5
6
7
8
9
10
11
App.vue
<school :getSchoolName="getSchoolName"></school>
getSchoolName(name){
console.log('app收到了学校名称:',name)
}
School.vue
<button @click="sendSchoolName">把学校姓名给app</button>

props:['getSchoolName']
sendSchoolName(){
this.getSchoolName(this.name)}

第二种方法,利用方法$emit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
School.vue
<Student ref="student" name="张锋" :age="21"></Student>
getStudentName(name){
console.log("学校收到了学生的名称",name)
}
mounted(){
setTimeout(()=>{
this.$refs.student.$on('atminmin',this.getStudentName)
},3000)
}
Student.vue
<button @click="SendStudentName">把学生姓名给学校</button>
methods:{
SendStudentName(){
this.$emit('atminmin',this.name)
}
}

$off

当我们要解绑一个事件的时候,我们就会用到该API

解绑一个事件:unbind(){

this.$off(’事件名’)

}

解绑多个自定义事件:unbind(){

this.$off([‘事件1’,’事件2’])

}

native

可以将自定义事件当成原生事件来使用

事件总线

用于任意组件之间的通信

首先我们要注册全局事件总线

main.js

1
2
3
4
5
6
7
new Vue({
el:'#app',
render :h=>h(App),
beforeCreate(){
Vue.prototype.$bus=this//安装全局事件总线
}
})

image-20221118143844534

Student.Vue

1
2
3
4
5
6
7
<button @click="sendName">我要发送学生数据</button>

methods:{
sendName(){
this.$bus.$emit('hello',this.name)
}
}

School.vue

1
2
3
4
mounted(){
this.$bus.$on('hello',(name)=>{
console.log('School组件收到数据data:',name)
})

image-20221118144129380

消息订阅与发布

首先我们需要先导一个包pubsub.js

1
npm i pubsub-js

然后继续利用事件总线的例子

我们可以将pubsub导入两个Vue组件中

pubsub中的方法subscribe中可加入两个参数第一个是订阅的事件(hello),另外一个是该事件的回调函数在回调函数中第一个接收形参是该事件的事件名,第二个形参是该事件所发布的消息

School.vue

1
2
3
4
5
mounted(){      
pubsub.subscribe('hello',function(message,data){
console.log('该事件事件名为',message,'该事件发布的数据为',data)
})
}

Student.vue

1
2
3
4
5
methods:{
sendName(){
PubSub.publish('hello',666)
}
}

image-20221118150534428

每次订阅 pubsub.subscribe()都会返回一个数值ID,类似于定时器,我们可以将此订阅删除

1
2
3
4
5
6
7
8
9
mounted(){
this.pbID=pubsub.subscribe('hello',function(message,data){
console.log('该事件事件名为',message,'该事件发布的数据为',data)
})


beforeDestroy(){
pubsub.unsubscribe(this.pbId)
}

Vue.$nextTick

Vue在观察数据变化时并不是直接更新DOM,而是开启一个队列,并缓冲在同一事件循环中发生的所有数据改变,在缓冲时会去除重复数据,避免不必要的计算和DOM操作,然后再下一个事件循环tick中,Vue刷新队列队列并执行(已去重的)工作,所以,如果你用一个for循环来动态改变数据100次,其实只会应用最后一次改变,这样的机制也并非没有道理,如果没有这种运行渲染机制,DOM重新渲染100次,效率过低而花销过大

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 id="msg">{{msg}}</h2>
<button @click="changeIt">点我改变msg中内容</button>
</div>
</body>
<script>
const vm=new Vue({
el:'#root',
data(){
return{
msg:'没变!'
}
},
methods:{
changeIt(){
this.msg='变了'
console.log('noNextTick:',document.getElementById('msg').innerHTML)
}
}
})
</script>

image-20221118230526521

由图可见,虽然执行了changeIt()但是仿佛 this.msg=’没有也一样用’,这句并没有执行一样,显然这是不可能的,这是由于数据虽然改变,但是并没有渲染到页面上,所以当我们直接取DOM的时候就会出现貌似改变还没有发生一样

这种情况下我们就需要用到$nextTick()函数

1
2
3
4
5
6
7
changeIt(){
this.msg='变了'
this.$nextTick(()=>{
console.log('useNextTick:',document.getElementById('msg').innerHTML)
})
console.log('noNextTick:',document.getElementById('msg').innerHTML)
}

image-20221118230719936

配置代理

当我们开启两台服务器对信息进行测试时

我们会得到两个地址http://localhost:5000/students

http://localhost:5001/cars

当我们对这两个服务器进行访问时会发生如下错误

image-20221119152226895

因为我们触发了跨域错误解释如下图:

image-20221119152637485

二者的网络协议和主机号都没有问题,当浏览器向服务器发送ajax请求时,服务器也响应返回了数据,但是当浏览器拿到数据时检查了二者的端口号发现并不相同出于安全考虑并没有将数据呈现出来

如何解决这个问题呢?

  1. cors
  2. jsonp
  3. 代理服务器

当然我们要使用代理服务器的方法,而这种方法有两种解决方式

  1. Nginx
  2. Vue-CLI

我们本次使用Vue-CLI中修改配置文件解决方式

在Vue.config.js中添加配置,如果我们要将请求转发给谁,proxy的值就是谁

1
2
3
devServer:{
proxy:'http://localhost:5000'
}

这时我们通过axios发送请求的url地址就要发生变化,变成代理服务器的url地址以及所需要的请求

1
http://localhost:8080/students

image-20221121143306597

代理服务器的运行流程图为

image-20221121143333150

但是这么配置诸多不方便的问题,例如倘若本地public 文件中有请求的数据,那么代理服务器并不会进行转发,并且我们配置代理服务器只能转发一个地址

第二种方法:添加请求前缀

Vue.config.js

1
2
3
4
5
6
7
8
proxy:{  
'/zf':{ //匹配所有以'/zf'开头的请求路径
target:'http://localhost:5000', //代理目标的基础路径
pathRewrite:{'^/zf':''},//正则表达式,将/zf请求路径掩饰为''空字符串
ws:true, //用于支持webSocket
changeOrigin: true //是否掩饰原地址
}
}
1
2
3
4
5
6
7
8
9
10
getStudents(){
axios.get('http://localhost:8080/zf/students').then(
response=>{
console.log('请求成功了',response.data)
},
error=>{
console.log('请求失败了',error.message)
}
)
}

image-20221121153630513

默认插槽(slot)

当我们在一个子组件中定义了一个div ,在父组件多次复用该组件时,div中的内容形式不容易改变,这时候就需要slot来占用一个位置,当父组件复用子组件

<子组件> <.div> XXXX<./div> </字组件> 时 <.div>XXXX</.div>中的内容会取代子组件中<.slot></.slot>的内容

VueX

理解VueX

VueX是什么

专门在Vue中实现集中式状态(数据)管理的一个Vue插件,对Vue应用中的多个组件的共享状态进行集中式管理(读/写),也是组件之间的一种通信方式,且适用于任何组件之间的通信

什么时候用VueX

  1. 多个组件依赖于同一状态
  2. 来自不同的组件行为

VueX原理

image-20221127220229102

store掌管着Vuex中的三大部分

VueX的环境搭建

导入vuex包

1
npm i vuex

如我们使用的是Vue2的话,那么我们需要在Vuex安装时另加版本号vuex3

1
npm i vuex@3

按照Vue的官方文档,我们需要在src包下创建一个index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import Vue from 'vue'
import vuex from 'vuex'
Vue.use(vuex)
//action用于响应组件中的动作
const actions={

}
//mutations用于操作数据(state)
const mutations={

}
const state={

}
const store=new vuex.Store({
actions,
mutations,
state
})
//暴露store
export default store

我们需要在index.js包下让Vue使用Vuex插件,然后对Vuex中的三个组件功能进行操作

1
为什么我们要这么写,因为当我们在搭建vue的环境时,对main.js进行操作,当我们的执行import时,势必先导入store,此时就开始解析store中的代码,但Vue此时的Vue还并未执行Vue.use(VueX)的代码,不管impore store 放在main.js的哪行,vue都会默认先执行import ,所以我们一开始就直接在index.js中直接使用vue.use(vuex)

然后由main.js 导入store包

1
2
3
4
5
6
7
8
9
10
11
12
//引入Vue
import Vue from'vue'
//引入 App
import App from './App.vue'
//关闭Vue的生产配置
Vue.config.productionTip=false
import store from './store'
const vm=new Vue({
el:'#app',
render :h=>h(App),
store
})

如何实现Vuex中的基本任务

在index.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
import Vue from 'vue'
import vuex from 'vuex'
Vue.use(vuex)
//action用于响应组件中的动作
const actions={
jia(context,value){
context.commit('jia',value)
},
jian(context,value){
context.commit('jian',value)
}
}
//mutations用于操作数据(state)
const mutations={
jia(state,value){
state.sum+=value
},
jian(state,value){
state.sum-=value
}
}
const state={
sum:0
}
const store=new vuex.Store({
actions,
mutations,
state
})
//暴露store
export default store

在组件Count中

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
<template>
<div>

<h2>当前求和为:{{$store.state.sum}}</h2>
<select v-model.number="n">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="incrementOdd">当前为奇数再加</button>
<button @click="incrementWait">等一会儿再加</button>
</div>
</template>

<script>
export default {
name:'count'
,
data(){
return{
n:1}
},
methods:{
increment(){
this.$store.dispatch('jia',this.n)
},
decrement(){
this.$store.dispatch('jian',this.n)
},
incrementOdd(){
if(this.$store.state.sum%2!=0)
this.$store.dispatch('jia',this.n)
},
incrementWait(){
setTimeout(()=>{
this.$store.dispatch('jia',this.n)
},500)
}
}
}
</script>

<style>
button{
margin: 5px;
}
</style>

组件中读取VueX中的数据 $store.state.sum

组件中修改Vuex中的数据 $store.dispatch(‘actions中的方法名’,数据)或 $store.commit(‘mutations的方法名’,数据)

getters

  • 概念:当state中的数据需要加工后才能使用时,可以使用getters进行加工
  • 在store.js和getters配置
1
2
3
4
5
6
7
8
9
10
11
const getters={
bigNum(){
return state.sum*10
}
}
const store=new vuex.Store({
actions,
mutations,
state,
getters
})
  • 组件中读取数据:$store.getters.bigNum

  • mapstate

帮助我们映射state中的数据作为计算属性

1
2
3
4
5
6
 computed:{
...mapState(['school','subject']) //数组写法
}
mounted(){
const x = mapState({school:'school',school:'subject'})//对象写法
}
  • mapGetters

帮助我们映射getters中的数据作为计算属性

1
...mapGetters(['bigNum'])//当要映射的名字和getter中数据的名字一致时
  • mapActions

mapActions方法用于我们生成与actions对话的方法,即$store.dispatch(xxx)的函数

1
...mapActions({increment:'jia',decrement:'jian'}),

当调用时,要传入对应的参数

1
<button @click="increment(n)">+</button>

路由(Vue-router)

  • Vue-router的理解

Vue的一个插件库,专门用来实现SPA应用

  • 对SPA应用的理解

  1. 单页Web应用(single page web appliction SPA)
  2. 整页应用只有一个完整的页面
  3. 点击野蛮中的导航链接不会甩你页面,只会对页面进行局部的更新
  4. 数据需要ajax请求获取
  • 路由的理解

  1. 什么是路由?
    1. 一个路由就是一组映射关系(key-value)
    2. key为路径,value可能是function或component
  2. 路由分类
    1. 后端路由
      1. 理解:value是function,用于处理客户端提交的请求
      2. 工作过程:服务器收到一个请求时,根据请求路径找到匹配的函数来处理请求响应数据
    2. 前端路由
      1. 理解:value是component,用于展示页面内容
      2. 工作过程:当浏览器的路径改变时,对应的组件就会显示
      3. 生命周期:在切换其他组件时,原组件就会销毁

Vue-router的基本使用

首先要创建一个router包下创建index.js

首先在main.js中导入Vue-router包 再导入 index.js包 并使用包,在App组件中使用router时,要使用router-link标签 ,对之前a标签的href属性改为to,跳转的页面改为路由器中的路径,同时在变化的部分加入标签<.router-view>

index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import VueRouter from "vue-router";
import About from '../components/About.vue'
import Home from '../components/Home.vue'

export default new VueRouter({

routes:[
{
path:'/about',
component:About
},
{
path:'/home',
component:Home
}
]
})

main.js

1
2
3
4
5
6
7
8
9
10
11
import Vue from 'vue'
import VueRouter from 'vue-router'
import App from './App.vue'
import router from './router/index.js'
Vue.config.productionTip = false
Vue.use(VueRouter)
new Vue({
render: h => h(App),
router:router
}).$mount('#app')

App.vue

1
2
3
4
5
<div class="list-group">
<router-link class="list-group-item active" to="/about">About</router-link>
<router-link class="list-group-item" to="/home">Home</router-link>
</div>
<router-view></router-view>

Home.vue

1
2
3
4
5
6
7
8
9
10
<template>
<div>
<h2>我是Home的内容</h2>
</div>
</template>
<script>
export default {
name:'Home'
}
</script>

About.vue

1
2
3
4
5
6
7
8
9
10
11
12
<template>
<div>
<h2>我是About的内容</h2>
</div>
</template>

<script>
export default {
name:'About'
}
</script>

其中Home.vue 和 About.vue被称为路由组件

image-20221204142019715

image-20221204142838737

多级(嵌套)路由

多级嵌套路由顾名思义:在原有的路由组件中嵌套新的路由组件

index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
export default new VueRouter({
routes:[
{
path:'/about',
component:About
},
{
path:'/home',
component:Home,
children:[ //子组件
{
path:'news',//children中的路径不要加 /
component:news
},
{
path:'message',
component:message
}
]
}
]
})

跳转

1
2
3
4
5
6
7
8
<li>
<router-link class="list-group-item" active-class="active"
to="/home/news">News</router-link>//路径写全
</li>
<li>
<router-link class="list-group-item" active-class="active"
to="/home/message">Message</router-link>
</li>

query查询

在$route中有属性query,我们可以在标签<.router-link>中的to属性中加入要查询的path和query,query可以查询存在其中的对象和属性,

detail是 message的子组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
<div>
<ul>
<li>消息编号:{{$route.query.id}}</li>
<li>消息标题:{{$route.query.title}}</li>
</ul>
</div>
</template>

<script>
export default {
name:'detail'
}
</script>

Message.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
<template>
<div>
<ul>
<li v-for="m in messageList" :key="m.id ">
<router-link :to="{
path:'/home/message/detail',
query:{
id:m.id,
title:m.title
}
}">{{m.title}}</router-link>
</li>
</ul>
<h2><router-view></router-view></h2>
</div>
</template>

<script>

export default {
name: 'Message',
data(){
return{
messageList:[
{id:'001',title:'消息001'},
{id:'002',title:'消息002'},
{id:'003',title:'消息003'}
]
}
}
}
</script>

image-20221204163017174

关于命名:对于较长的路由命名可在index.js中的加上name属性,然后在query查询中的to 中将path变为name

index.js中的detail路由

1
2
3
4
{ name:'xijie',
path:'detail',
component:detail
}

Message.vue中的路由查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div>
<ul>
<li v-for="m in messageList" :key="m.id ">
<router-link :to="{
name:'xijie',
query:{
id:m.id,
title:m.title
}
}">{{m.title}}</router-link>
</li>
</ul>
<h2><router-view></router-view></h2>
</div>

image-20221204163914949

params传参

跳转路由并携带params参数,to的对象写法

使用方法和query类似,只是把query换为params,但是只能用name,不能用path,并且路由路径上必须添加占位符

image-20221205220111993

Message.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
35
<template>
<div>
<ul>
<li v-for="m in messageList" :key="m.id ">
<router-link :to="{
name:'xijie',
params:{
id:m.id,
title:m.title
}
}">{{m.title}}</router-link>
</li>
</ul>
<h2><router-view></router-view></h2>
</div>
</template>

<script>

export default {
name: 'Message',
data(){
return{
messageList:[
{id:'001',title:'消息001'},
{id:'002',title:'消息002'},
{id:'003',title:'消息003'}
]
}
}
}
</script>

<style>
</style>

index.js

1
2
3
4
5
6
7
8
9
10
{
path:'message',
component:message,
children:[
{ name:'xijie',
path:'detail/:id/:title',
component:detail
}
]
}

​ detail.vue

1
2
3
4
5
6
7
8
<template>
<div>
<ul>
<li>消息编号:{{$route.params.id}}</li>
<li>消息标题:{{$route.params.title}}</li>
</ul>
</div>
</template>

props属性

用于传递数据从路由传递到路由组件中,并且可以简化书写,路由组件中的属性例如 $route

1
2
3
props($route){
return {id:$route.query.id,title:$route.query.title}
}

detail.vue 接收数据 ,展示数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<div>
<ul>
<li>消息编号:{{id}}</li>
<li>消息标题:{{title}}</li>
</ul>
</div>
</template>

<script>
export default {
name:'detail',
props:['id','title']
}
</script>
  1. 作用:控制路由跳转时操作浏览器历史记录的模式
  2. 浏览器的历史记录有两种写入方式:分别为push和replace,push是追加记录,replace是替换当前记录。路由跳转时默认为push
  3. 如何开启repalce模式:<.router-link repalce………>New<./router>

router的缓存

我们在路由的生存周期可知路由的生存周期,在不展示它的时候就会销毁,这时候我们加入有输入框,里面填写着暂时未提交的数据,那么回被肢解销毁,为了避免这种状况,我们可以利用<.keep-alive>标签进行操作

1
2
3
<keep-alive include="news"><!--include=“XX”为要保存的组件,其中要填写组件名(index.js中component:XX)-->
<router-view></router-view>
</keep-alive>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template>
<div>
<ul>
<li>news001<input type="text"></li>
<li>news002<input type="text"></li>
<li>news003<input type="text"></li>
</ul>
</div>
</template>

<script>
export default {
name:'news',
beforeDestroy(){ <!--测试,如果被销毁控制台就会输出内容-->
console.log('1G了')
}
}
</script>

image-20221206003226559

在切换message之后再点回News,控制台仍然不会输出内容

两个重要的生命周期钩子(activated 、deactivated)

  1. 作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态
  2. 具体名字:
    1. activated:路由组件被激活时触发
    2. deactivate:路由组件失活时触发

路由守卫

  1. 作用:对路由进行权限控制(和保安一样,让谁进门不进门)
  2. 分类:全局守卫,独享守卫,组件内守卫

全局守卫

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
import VueRouter from "vue-router";
import About from '../pages/About.vue'
import Home from '../pages/Home.vue'
import news from '../pages/News.vue'
import message from '../pages/Message.vue'
import detail from '../pages/detail.vue'

const router= new VueRouter( {

routes:[
{ name:'guanyu',
meta:{title:'关于'},
path:'/about',
component:About
},
{ meta:{title:'主页'},
path:'/home',
component:Home,
children:[ //子组件
{ meta:{isAuth:true,title:'新闻'},
name:'News',
path:'news',//children中的路径不要加 /
component:news
},
{meta:{isAuth:true,title:'消息'},
path:'message',
component:message,
children:[
{ meta:{title:'内容'},
name:'xijie',
path:'detail',
component:detail,
props($route){
return {id:$route.query.id,title:$route.query.title}
}
}
]
}
]
}
]
})
//全局前置守卫,初始化时执行,每次路由切换前执行
router.beforeEach((to,from,next)=>{
console.log(to,from)
if(to.meta.isAuth){//判断路由是否需要进行权限控制
if(sessionStorage.getItem('school')==='atguigu'){//权限控制的具体规则
next()//放行
}
else{
alert('学校名称不对,无权限')
}
}
else{
next()
}
})
//全局后置守卫,初始化时执行,每次路由切换后执行
router.afterEach((to,from,)=>{
document.title=to.meta.title||'我的小案例'
})
export default router

独享守卫

专注于单个组件的守卫(beforeEnter)

组件内路由守卫

1
2
3
4
5
6
7
8
9
10
//进入守卫,通过路由规则,进入该组件时被调用  
beforeRouteEnter(to,from,next){
console.log('beforeRouteEnter')
next()
},
//离开守卫,通过路由规则,离开该组件时被调用
beforeRouteLeave(to,from,next){
console.log('beforeRouteLeave')
next()
}