初识Vue

  1. 引入Vue.js文件
  2. 想让Vue工作,必须创建一个Vue实例,且要传入一个配置对象。
  3. root容器里的代码依然能符合html规范,只不过混入了一些特殊的Vue语法。
  4. root容器里的代码被称为【Vue】模板。
  5. Vue实例和容器是一一对应的。
  6. 真实开发中只有一个Vue实例,并且会配合着组件一起使用。
  7. 中的xxx要写js表达式,且xxx可以自动读取到data中的所有属性。
  8. 一旦data中的数据发生变化,那么页面中用到该数据的地方也会自动更新。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div class="root">
<h1>Hello,{{name}}</h1>
</div>
<div class="root">
<h1>Hello,{{name}}</h1>
</div>
<script>
Vue.config.productionTip = false;

// 创建一个Vue实例
new Vue({
el: '.root', //el用于指定当前Vue实例为哪个容器服务,值通常为css选择器字符串。
data: { //data中用于存储数据,供el所指向的容器所使用。
name: '小红',
age: 22
}
});
</script>

Vue模板语法

Vue模板语法有两大类:
1.插值语法:
功能:用于解析标签体内容
写法:{ {xxx} },可以直接读取到data内的所有属性
2.指令语法:
功能:用于解析标签(包括:标签属性、标签体内容、绑定事件等等)
写法:v-bind:href=”url”或简写为:href=”url”,引号中可以直接读取到data中的所有属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div id="root">
<h1>插值语法</h1>
<h3>你好,{{name}}</h3>
<hr>
<h1>指令语法</h1>
<!-- 此种写法已经被弃用 -->
<!-- <a href={{url}}>点我去我的博客</a> -->
<!-- 使用Vue的指令,v-bind实现动态绑定的功能 -->
<a v-bind:href="url">点我去我的博客</a>
<!-- v-bind的简写方式,: -->
<a :href="url2">点击去我的好基友的博客</a>
</div>
<script>
Vue.config.productionTip = false
new Vue({
el: '#root',
data: {
name: 'jack',
url: 'https://yuxiaohong.cn/',
url2: 'https://jaggerr.cn'
}
})
</script>

数据绑定

单向数据绑定

<input type=”text” v-bind:value=”name”>

双向数据绑定

<input type=”text” v-model:value=”name”>

el和data的两种写法

data与el的两种写法:
1.el有两种写法:
(1).new Vue的时候配置el属性
(2).先创建Vue实例,随后再通过v.$mount(‘#root)指定el的值
2.data有两种写法:
(1).对象式
(2).函数式
目前两种写法都可以,但是以后学到组件的时候,data必须使用函数式,否则会报错
3.一个重要的原则,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue的实例了,而变成了全局的Window

el的两种写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
const v = new Vue({
el: '#root',
data: {
name: '小红'
}
})
console.log(v);
//另外一种挂载方法,不使用el
//这种方法较为灵活,比如可以有如下的应用:2s后进行解析显示在用户界面上
setTimeout(() => {
v.$mount('#root')
},2000)
</script>

data的两种写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script>
new Vue({
el: '#root',
// data的第一种写法:对象式
// data:{
// name:'小红'
// }

// data的第二种写法:函数式
data:function(){ //或者可以直接简写为data(){},省略掉冒号和function
return{
name:'小红'
}
}
})
</script>

MVVM模型

1.M:模型(Model):data中的数据
2.V:视图(View):模板代码
3.VM:视图模型(ViewModel):Vue实例

观察发现:
1.data中所有的属性,最后都出现在了vm身上
2.vm身上所有的属性及Vue原型上所有的属性,在Vue模板中都可以直接使用

数据模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<script>
let number = 18
let person = {
name:'张三',
sex:'男',
age:number
}
//1.通过这个方法添加的属性来进行某些限制
//2.还可以将无关的数据进行双向绑定
//三个参数,分别是给哪个对象添加属性,第二个是添加的属性名,第三个是配置项
Object.defineProperty(person,'age',{
// value:18,
// enumerable:true, //控制属性是否可以枚举,默认为false
// writable:true, //控制属性是否可以被修改,默认为false
// configurable:true //控制属性是否可以被删除,默认为false
//当读取person的age属性时,get函数就会被调用,且返回值就是age的值
get:function(){
return number
},
//当修改person.age的属性时,set函数就会调用
set:function(value){
console.log('有人修改了age属性,其值是',value);
number = value
}
})
//Object.keys()可以将所有对象里面所有属性的属性名列举出来变成一个数组
console.log(Object.keys(person));
console.log(person);
</script>

数据代理

1.Vue中的数据代理:
通过vm对象来代理data对象中属性的操作(读\写)。
2.Vue中数据代理的好处:
更加方便的操作data中的数据
3.基本原理:
通过Object.defineProperty()把data对象中所有属性添加到vm上。
为每一个添加到vm上的属性,都指定一个getter和setter。
在getter,setter内部去操作(读\写)data中相应的属性。
数据代理(通过obj2去访问和修改obj中的属性值):

控制台操作

数据代理图示

Vue中的数据代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div id="root">
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
</div>
<script>
Vue.config.productionTip = false
const vm = new Vue({
el:'#root',
data:{
name:'GEO',
address:'石家庄裕华区'
}
})
//在浏览器控制台查看data内容:vm._data
</script>

vm._data中的内容

事件处理

事件的基本使用:
1.使用v-on:xxx或者@xxx绑定事件,xxx是事件名。
2.事件的回调需要配置在methods对象中,最终会在vm上。
3.methods中配置的函数,不要用箭头函数。
4.methods中配置的函数,都是Vue管理的函数,this指向是vm或组件实例对象。
5.@click=”demo”和@click=”demo($event)”效果一致,但后者可以在括号中进行额外的传参。

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
<div id="root">
<h2>欢迎来{{name}}主页</h2>
<!-- 通过Vue指令来绑定点击事件,另外Vue模板中调用的函数只能写在Vue实例中 -->
<!-- <button v-on:click="showInfo">点我提示信息</button> -->
<!-- 简写方式,用@click替代v-on: -->
<button @click="showInfo1">点我提示信息(不传参)</button>
<!-- 可以在小括号中进行传参,但是会把event的位置挤掉,所以要给event占位 -->
<button @click="showInfo2(66,$event)">点我提示信息(传参)</button>
</div>
<script>
const vm = new Vue({
el:'#root',
data:{
name:'GEO'
},
// Vue模板中调用的函数要写在methods对象中
methods:{
showInfo1(event){
alert('你好!');
//event.target就可以拿到被点击的元素
console.log(event.target);
//event.target.innerText可以拿到元素内部的文字
console.log(event.target.innerText);
//
console.log(vm === this);//true,说明此处的this就是vm
},
showInfo2(number,event){
console.log(number,event);
alert('你好!!!');
console.log(event.target);
console.log(event.target.innerText);
console.log(vm === this); //true
}
}
})
</script>

点击不传参按钮:

点击传参按钮:

事件修饰符

Vue中的事件修饰符:
1.prevent:阻止默认事件(常用)
2.stop:阻止事件冒泡
3.once:事件只触发一次
4.capture:使用事件的捕获模式
5.self:只有event.target是当前操作的元素时才触发事件
6.passive:事件的默认行为立即执行,无需等待回调函数执行完毕
最后,修饰符是可以连着写的,如:.stop.passive

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
<!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>
<script src="../../JS/vue.js"></script>
<style>
*{
margin-top: 20px;
}
.demo1{
background-color: aqua;
}
.box1{
padding:5px;
background-color: aqua;
}
.box2{
padding: 5px;
background-color: gold;
}
.list{
width: 200px;
height: 200px;
background-color: silver;
overflow: auto;
}
/* 由于子元素总高度高于父元素,会从父元素中溢出,所以需要加一个滚动条 */
li{
height: 100px;
}
</style>
<body>
<div id="root">
<h1>欢迎来到{{name}}</h1>
<!-- 绑定了点击事件之后发现在弹出提示之后还会跳转到指定的网站去
所以我们可以进行
-->
<!-- 阻止默认事件(常用) -->
<a href="https://yuxiaohong.cn" @click.prevent='showInfo'>点我去我的博客</a>
<!-- 阻止事件冒泡(常用) -->
<!-- 点击内部按钮时会弹出一次提示,点击后外层div也会弹出一层提示,即冒泡 -->
<div class="demo1" @click="showInfo">
<button @click.stop="showInfo">点我提示信息</button>
</div>
<!-- 事件只触发一次(常用) -->
<button @click.once="showInfo">点我提示信息</button>
<!-- 使用事件的捕获模式 -->
<!-- 先捕获后冒泡,在冒泡阶段处理事件,使用capture就可以在捕获阶段处理事件 -->
<div class="box1" @click.capture="showMsg(1)">
div1
<div class="box2" @click="showMsg(2)">
div2
</div>
</div>
<!-- 只有event.target是当前操作的元素时才触发事件 -->
<!-- 在控制台我们可以看到点击按钮出来的两个event.target都是按钮,而没有div,所以在外层div加上修饰符self就可以不让他执行函数
从一定程度来说也实现了停止冒泡的效果
-->
<div class="demo1" @click.self="showInfo">
<button @click="showInfo">点我提示信息</button>
</div>
<!-- 事件的默认行为立即执行,无需等待事件回调执行完毕 -->
<!-- 绑定滚动事件,scroll滚动滚轮,就会触发事件,直到滚动条到尽头,wheel只要滚动鼠标滚轮就会触发事件,无论滚动条是否到达尽头 -->
<!-- 在滚动滚轮之后,由于回调函数过于繁琐,所以会会发生回调函数长时间执行,执行完之后滚动条才会发生变化,加上passive就会变成异步操作,回调函数执行的同时,滚动条就会发生变化 -->
<ul @wheel.passive="demo" class="list">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
</div>
</body>
<script>
const vm = new Vue({
el:'#root',
data:{
name:'河北地质大学'
},
methods:{
showInfo(e){
console.log(e);
console.log(e.target);
//可以阻止a标签原本的跳转功能,在标签里可以直接使用
// e.preventDefault();
// 可以阻止外层继续冒泡行为,在标签里可以直接使用
// e.stopPropagation();
// alert('你好')
},
showMsg(msg){
console.log(msg);
// aler('你好')
},
demo(){
for (let i = 0; i < 100000; i++) {
console.log('@');
}

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

键盘事件

1.Vue中常用的按键别名:
回车:enter
删除:delete
退出:esc
空格:space
换行:tab(特殊,需配合keydown使用,因为tab本身有切换焦点的功能)
上:up
下:down
左:left
右:right
2.Vue中未提供别名的按键,可以使用按键原始的key值去绑定,像大小写键,要写成caps-lock这种形式
3.系统修饰键:ctrl、alt、shift、meta(win)
(1).配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
(2).配合keydown使用:正常触发事件。
4.也可以使用keyCode去指定具体的按键(不推荐)
5.Vue.config.keyCodes.自动以键名=键码,可以定制按键别名
6.如果想用组合按键,用点进行链接,如:ctrl.y(就是需要同时按下ctrl键和y键)

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 http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<script src="../../JS/vue.js"></script>
<body>
<div id="root">
<!-- 因为如果只写@keyup的话,每次输入在输入框中都会显示在控制台,加上.enter就会在按下回车之后执行回调函数 -->
<input type="text" placeholder="按下回车提示输入" @keyup.caps-lock="showInfo">
</div>
</body>
<script>
const vm = new Vue({
el: '#root',
data: {

},
methods: {
showInfo(e){
console.log(e.target.value);
}
}
})
</script>
</html>

计算属性

姓名案例_插值语法实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div id="root">
姓:<input type="text" v-model:value="firstName"><br><br>
名:<input type="text" v-model:value="lastName"><br><br>
<!-- 如果想要截取输入框的前三位,则要使用到slice这个API -->
全名:<span>{{firstName.slice(0,3)}}-{{lastName}}</span>
</div>
<script>
new Vue({
el: '#root',
data: {
firstName: '张',
lastName: '三'
},
methods: {

}
})
</script>

展示效果:

姓名案例_methods实现

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" v-model:value="firstName"><br><br>
名:<input type="text" v-model:value="lastName"><br><br>
全名:<span>{{fullName()}}</span>
</div>
<script>
new Vue({
el: '#root',
data: {
firstName: '张',
lastName: '三'
},
//实际上就是定义了一个函数进行拼串的操作
methods: {
fullName () {
let name = ''
name += this.firstName.slice(0, 3) + '-' + this.lastName
return name
}
}
})
</script>

姓名案例_计算属性的实现

计算属性:
1.定义:要用的属性不存在,需要通过已有属性(不能是变量)计算得来。
2.原理:底层借助了Object.defineproperty方法提供的getter和setter。
3.get函数什么时候执行:
(1).初次读取时会执行一次。
(2).当依赖的数据发生改变时会再次被调用。
4.优势:与methods相比,内部有缓存机制(复用),效率更高,调试方便。
5.备注:
(1).计算属性最终会出现在vm上,直接读取使用即可。
(2).如果计算属性要被修改,那必须写set函数去响应修改,且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
27
28
29
30
31
32
33
34
35
<div id="root">
姓:<input type="text" v-model:value="firstName"><br><br>
名:<input type="text" v-model:value="lastName"><br><br>
全名:<span>{{fullName}}</span>
</div>
<script>
const vm = new Vue({
el: '#root',
//属性
data: {
firstName: '张',
lastName: '三'
},
//计算属性
computed: {
//需要把计算的过程配成一个对象
fullName: {
//当有人读取fullName读取时,get就会被调用,且返回值就作为fullName的值
//get什么时候被调用:1.初次读取fullName时。2.所依赖的数据发生变化时。
get () {
console.log('get被调用了');
return this.firstName.slice(0, 3) + '-' + this.lastName
},
//当fullName被修改时调用
set (value) {
console.log('set被调用了');
//split()可以按照指定字符进行分割,并填充为数组
const arr = value.split('-');
this.firstName = arr[0];
this.lastName = arr[1];
}
}
}
})
</script>

计算属性的简写形式

当需求只用读取数据,而不需要修改时,则可以使用简写方法:

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" v-model:value="firstName"><br><br>
名:<input type="text" v-model:value="lastName"><br><br>
全名:<span>{{fullName}}</span>
</div>
<script>
const vm = new Vue({
el: '#root',
//属性
data: {
firstName: '张',
lastName: '三'
},
//计算属性
computed: {
fullName() {
consloe.log('get被调用了');
return this.firstName + '-' + this.lastName;
}
}
}
})

监视属性

天气案例

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">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气</button>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
isHot: true
},
methods: {
changeWeather () {
this.isHot = !this.isHot
}
},
computed: {
info () {
return this.isHot ? '炎热' : '凉爽'
}
}
})
</script>

效果展示:

监视属性

监视属性(watch):
1.当监视属性发生变化时,回调函数自动调用,进行相关操作。
2.监视的属性必须存在,才能进行监视。
3.监视的两种写法:
(1).new Vue时传入watch配置
(2).通过vm.$watch监视

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
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气</button>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
isHot: true
},
methods: {
changeWeather () {
this.isHot = !this.isHot
}
},
computed: {
info () {
return this.isHot ? '炎热' : '凉爽'
}
},
//监视属性
// watch: {
// isHot: {
// //immediate默认为false,改为true之后,就算事件不发生变化也会自动执行handler
// immediate: true,
// //当isHot发生改变时,调用handler,括号中的两个参数分别是改变后的值和改变前的值
// handler (newValue, oldValue) {
// console.log('isHot被修改了。', newValue, oldValue);
// }
// }
// }


})
// 另外一种监视方法:
vm.$watch('isHot', {
immediate: true,
handler (newValue, oldValue) {
console.log('isHot被修改了。', newValue, oldValue);
}
})
</script>

深度监视

1.Vue中的watch默认不监测对象内部值的改变。
2.配合deep:true可以监测对象内部值的改变。
备注:
1.Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以。
2.使用watch时可以根据数据的具体结构,决定是否采用深度监视

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
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气</button>
<hr>
<h3>a的值是{{numbers.a}}</h3>
<button @click="numbers.a++">点我让a+1</button>
<h3>b的值是{{numbers.b}}</h3>
<button @click="numbers.b++">点我让b+1</button>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
isHot: true,
numbers: {
a: 1,
b: 1
}
},
methods: {
changeWeather () {
this.isHot = !this.isHot
}
},
computed: {
info () {
return this.isHot ? '炎热' : '凉爽'
}
},
// 监视属性
watch: {
isHot: {
//当isHot发生改变时,调用handler,括号中的两个参数分别是改变后的值和改变前的值
handler (newValue, oldValue) {
console.log('isHot被修改了。', newValue, oldValue);
}
},
//监视多级结构中某个属性的变化,使用原始表达方式'numbers.a',不加引号会报错
// 'numbers.a': {
// handler (newValue, oldValue) {
// console.log('a被改变了');
// }
// 监视numbers内部值的改变
numbers: {
//开启深度监视
deep: true,
handler () {
console.log('numbers发生改变了');
}
},
//简写,使用简写方式就不可以再配置deep和immediate属性了
isHot(newValue,OldValue){
console.log('isHot被修改了。',newValue,OldValue);
}
}
//在外部配置的简写方式
vm.$watch('isHot', function(newValue,OldValue){
console.log('isHot被修改了。',newValue,OldValue);
})
})
</script>

使用Vue脚手架

初始化Vue脚手架

说明

1.Vue脚手架是Vue官方提供的标准化开发工具(开发平台)
2.最新的版本是4.x。
3.文档:https://cli.vuejs.org/zh/

具体步骤

第一步(仅第一次执行):全局安装@vue/cli
npm install -g @vue/cli

如果下载速度太慢可以换到华为镜像:
npm config set registry=https://mirrors.huaweicloud.com/repository/npm/

第二步:切换到要创建项目的目录,然后使用命令创建项目
vue creat xxxx

第三步:启动项目
npm run serve

文件结构

nide_module文件夹:存放脚手架相关文件
src文件夹下的assets文件夹:通常放网站的静态资源
src文件夹下有个main.js:是整个项目的入口文件(Vue实例对象)
src文件夹下有个App.vue:统合所有组件
src文件夹下的components文件夹:存储各个组件
.gitignore:规定哪些文件不需在推送至远程仓库
babel.config.js:babel的配置文件(ES6转ES5)
package.json:包的说明书
package-lock.json:包版本控制文件
README.md:对整个工程或项目的一个描述

ref属性

在原生JS中,我们通过操作DOM来获得元素节点的信息,因为在Vue中要避免亲自操纵DOM,所以Vue提供了一个属性
ref,通过在标签中添加该属性,从而可以直接获得该元素节点的信息,而不必再操作DOM

ref属性
1.被用来给元素或子组件注册引用信息(id的替代者)
2.应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)
3.使用方式:
打标识:<h1 ref=”xxx”>…..</h1> 或 <School ref=”xxx”></School>
获取:this.$refs.xxx

1
2
3
4
5
<h1 ref="title">666</h1>

<script>
console.log(this.$refs.title) //<h1>666</h1>
</script>

props属性

功能:让组件接受外部传过来的数据
(1)传递数据:
<Demo name =”xxx”/>
(2)接收数据:
第一种方式(只接收):
props:[‘name’]
第二种方式(限制类型):
props:{
name:String
}
第三种方式(限制类型、限制必要性、指定默认值):
props:{
name:{
type:String, //类型
required:true, //必要性
default:’小红’ //默认值
}
}

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

mixin属性

功能:可以把多个组件共用的配置提取成一个混入对象
使用方式:
第一步定义混合:
{
data(){…},
methods(){…}

}
第二步使用混入:
(1)全局混入:Vue.mixin(xxx)
(2)局部混入:mixins:[‘xxx’]

插件

功能:用于增强Vue
本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据
定义插件:
对象.install = function(Vue,options){
//1.添加全局过滤器
Vue.filter(…)
//2.添加全局指令
Vue.directive(…)
//3.配置全局混入
Vue.mixin(…)
//4.添加实例方法
Vue.prototype.$myMethod = function(){…}
Vue.prototype.$myProperty = xxxx
}
使用插件:Vue.use()

scoped样式

作用:让样式仅在局部生效,防止命名冲突
写法:<style scoped>

总结TodoList案例

组件化编码流程

1.拆分静态组件:组件要按照功能点拆分。命名不要与html元素冲突。
2.实现动态组件:考虑好数据存放位置,数据是一个组件在用,还是一些组件在用。
一个组件在用:放在组件自身即可
一些组件在用:放在他们共同的父组件上(状态提升)
3.实现交互:从绑定事件开始。

props适用于

1.父组件 ===> 子组件 通信
2.子组件 ===> 父组件 通信(要求父先给子一个函数)

使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的

props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做

webStorage

1.存储内容一般支持5MB左右(不同浏览器可能不同)
2.浏览器通过window.Storage和window.sessionStorage属性来实现本地存储机制
3.相关API

1
2
3
4
5
6
7
8
9
10
11
该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值
xxxxxStorage.setItem('key','value')

该方法接受一个键名作为参数,返回键名所对应的值
xxxxxStorage.getItem('person)

该方法接受一个键名作为参数,并把该键名从存储中删除
xxxxxStorage.removeItem('key')

该方法会清空存储中的所有数据
xxxxxStorage.clear()

4.备注:
(1).SessionStorage存储的内容会随着浏览器窗口的关闭而消失。
(2).LocalStorage存储的内容,需要手动清除才会消失。
(3).xxxxxStorage.getItem(xxx)如果xxx对应的value获取不到,那么getItem的返回值是null。
(4).JSON.parse(null)的结果依然是null。

组件的自定义事件

1.一种组件间通信的方式,适用于 子组件===>父组件
2.使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在App中)
3.绑定自定义事件:
(1).第一种方式:在父组件中:

1
2
3
4
5
6
7
<Demo ref="demo"></Demo>
.....
<script>
mounted(){
this.$refs.xxx.$on('atguigu',this.test)
}
</script>

(2).第二种方式,在父组件中:

1
2
3
4
5
6
<Demo ref="demo"></Demo>
<script>
mounted(){
this.$refs.xxx.$on('atguigu',this.test)
})
</script>

(3).若想让自定义事件只触发一次,可以使用once修饰符,或$once方法。

4.触发自定义事件:this.$emit(‘atguigu’,数据)
5.解绑自定义事件:this.$off(‘atguigu’)
6.组件上也可以绑定原生DOM事件,但必须使用native修饰符
7.注意:通过thios.$refs.xxx.$on(‘atguigu’,回调)绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this的指向会出现问题

全局事件总线(GlobalEventBus)

1.一种组件间的通信方式,适用于任意组件间的通信。
2.安装全局事件总线:

1
2
3
4
5
6
new Vue({
...
beforeCreate(){
Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
}
})

3.使用事件总线:
(1).接收数据:A组件想接受数据,则在A组建中给$bus绑定自定义事件,事件的回调留在A组件自身

1
2
3
4
5
6
7
methods:{
demo(data){...}
}
...
mounted(){
this.$bus.$on('xxx',this.demo)
}

(2).提供数据:this.$bus.$emit(‘xxx’,数据)
4.最好在beforeDestroy钩子中,用$off解绑当前组件所用到的事件。

消息订阅与发布(pubsub)

1.一种组件间通信的方式,适用于任意组件间通信。
2.引入:import pubsub from ‘pubsub-js’
3.接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。

1
2
3
4
5
6
7
methods(){
demo(data){...}
}
...
mounted(){
this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
}

4.提供数据:pubsub.publish(‘xxx’,数据)
5.最好在beforeDestroy钩子中,用pubsub.unsubscribe(pid)去取消订阅

nextTick

1.语法:this.$nextTick(回调函数)
2.作用:在下一次DOM节点更新结束后执行其指定的回调
3.什么时候要用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行

Vue脚手架配置代理

方法一

在vue.config.js中添加如下配置:

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

说明:
1.优点:配置简单,请求资源时直接发送给前端(8080)即可。
2.缺点:不能配置多个代理,不能灵活控制请求是否走代理。
3.工作方式:若按照上述配置代理,当请求了前端不存在IDE资源时,那么该请求会转发给服务器(优先匹配前端资源)

方法二

编写vue.config.js配置具体代理规则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module.exports={
devServer:{
proxy:{
'/api1':{ //匹配所有以'/api1'开头的请求路径
target:'http://localhost:5000', //代理服务器的基础路径
changeOrigin:true,
pathRewrite:{'^/api1':''} //去掉路径中api1
},
'/api2':{
target:'http://localhost:5001',
changeOrigin:true,
pathRewrite:{'^/api2':''}
}
}
}
}

1.优点:可以配置多个代理,且可以灵活控制请求是否走代理。
2.缺点:配置略微繁琐,请求资源是必须加前缀。

Slot插槽

1.作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件===>子组件
2.分类:默认插槽、具名插槽、作用域插槽
3.使用方式:
(1).默认插槽:

1
2
3
4
5
6
7
8
9
10
// 父组件中:
<Category>
<div>html结构</div>
</Category>
// 子组件中:
<template>
<div>
<slot>插槽默认内容</slot>
</div>
</template>

(2).具名插槽

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 父组件中:
<Category>
<template slot="center">
<div>html结构1</div>
</template>
<template slot="footer">
<div>html结构2</div>
</template>
</Category>
// 子组件中:
<template>
<div>
<slot name={center}>插槽默认内容</slot>
<slot name={footer}>插槽默认内容</slot>
</div>
</template>

(3).作用域插槽:
①.理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。
②.具体编码

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
// 父组件中:
<CateGory>
<template scope="scopeData">
<ul>
<li v-for="item in scopeData.games" :key="item">{{item}</li>
</ul>
</template>
</CateGory>
// 子组件中:
<template>
<div>
// 将数据传递给父组件
<slot :games="games"></slot>
</div>
</template>
<script>
export default{
name:'Category',
props:['title'],
// 数据在组件自身
data(){
return{
games:['AEPX','LOL','PUBG','R6S']
}
}
</script>

Vuex

vuex是什么

1.概念:专门在Vue中实现集中式状态(数据)管理的一个插件,对vue应用中多个组件的共享状态进行集中式的管理(读\写),且适用于任意组件间的通信。

什么时候使用Vuex

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

搭建vuex环境

1.创建文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 引入Vue核心库
import Vue from 'vue'
// 引入Vuex
import Vuex from 'vuex'
// 应用Vuex插件
Vue.use(Vuex)

// 准备actions对象——响应组件中用户的动作
const actions={}
// 准备mutations对象——修改state中的数据
const mutations={}
// 准备state对象——保存具体的数据
const state ={}

// 创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state
})

2.在main.js文件中创建vm时传入store配置项

1
2
3
4
5
6
7
8
9
// 引入store
import store from '@/vuex/store.js'

// 创建vm
new Vue({
el:'#app',
render:h=>h(App),
store
})

基本使用

1.初始化数据、配置actions、mutations,操作文件store.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 引入Vue核心库
import Vue from 'vue'
// 引入Vuex
import Vuex from 'vuex'
// 使用Vuex插件
Vue.use(Vuex)

const actions={
// 响应组件中加的动作
jia(context,value){
console.log('actions中的jia被调用了',context,value)
context.commit('JIA',value)
},
}

const mutations={
// 执行加这个操作
JIA(state,value){
console.log('mutations中的JIA被调用了',state,value)
}
}

// 初始化数据
const state={
sum:0
}

// 创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state
})

2.组件中读取vuex中的数据:$store.state.sum
3.组件中修改vuex中的数据:$store.dispatch(‘actions中的方法名’,数据)或$store.commit(‘mutations中的方法名’,数据)

若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写dispatch,直接编写commit

getters的使用

1.概念:当state中的数据需要经过加工后再使用时,可以使用getters进行加工。
2.store.js中追加getters配置

1
2
3
4
5
6
7
8
9
10
11
12
13
const getters={
bigSum(){
return state.sum*10
}
}
// 创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state,
getters

})

3.组件中读取数据:$store.getters.bigSum

四个map方法的使用

1.mapState方法:用于帮助我们映射state中的数据为计算属性

1
2
3
4
5
6
7
computed:{
// 借助mapState生成计算属性:sum、school、subject(对象写法)
...mapState({sum:'sum',school:'school',subject:'subject'}),

// 借助mapState生成计算属性:sum、school、subject(数组写法)
...mapState(['sum','school','subject'])
}

2.mapGetters方法:用于帮助我们映射getters中的数据为计算属性

1
2
3
4
5
// 借助mapGetters生成计算属性,bigSum(对象写法)
...mapGetters(bigSum:'bigSum'),

// 借助mapGetters生成计算属性:bigSum(数组写法)
...mapGetters(['bigSum'])

3.mapActions方法:用于帮助我们生成actions对话的方法,即:包含$state.dispatch(xxx)的函数

1
2
3
4
5
6
7
methods:{
// 靠mapActions生成:incrementOdd、incrementWait(对象形式)
...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'}),

// 靠mapActions生成:incrementOdd、incrementWait(数组形式)
...mapActions(['jiaOdd','jiaWait'])
}

4.mapMutations方法:用于帮助我们生成与mutations对话的方法,即:包含$store.commit(xxx)的函数

1
2
3
4
5
6
7
methods:{
// 靠mapMutations生成:increment、decrement(对象形式)
...mapMutations({increment:'JIA',decrement:'JIAN'}),

// 靠mapMutations生成:increment、decrement(数组形式)
...mapMutations(['JIA','JIAN'])
}

模块化+命名空间

1.目的:让代码更好维护,让多种数据分类更加明确
2.修改store.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
const countOptions={
namespaced:true, //开启命名空间
state:{x:1},
mutations:{},
actions:{},
getters:{
bigSum(state){
return state.sum*10
}
}
}

const personOptions={
namespaced:trus, //开启命名空间
state:{},
mutations:{},
actions:{}
}

const store = new Vuex.Store({
modules:{
countOptions,
personOptions
}
})

3.开启命名空间后,组件中读取state数据:

1
2
3
4
// 方式一:自己直接读取
this.$store.state.personOptions.personList
// 方式二:借助mapState读取
...mapState('countOptions',['sum','school','subject'])

4.开启命名空间后,组件中读取getters数据:

1
2
3
4
// 方式一:自己直接读取
this.$store.getters['personOptions/firstPersonName',
// 方式二:借助mapGetters读取
...mapGetters('countOptions',['bigSum'])

5.开启命名空间后,组件中调用dispatch:

1
2
3
4
// 方式一:自己直接dispatch
this.$store.dispatch('personOptions/addPersonWang','person'),
// 方式二:借助mapActions:
...mapActoons('countOptions',{increment:'jiaOdd',inrenmentWait:'jiaWait'})

6.开启命名空间后,组件中调用commit:

1
2
3
4
// 方式一,自己直接commit
this.$store.commit('personOptions/ADDPERSON',person),
// 方式二:借助mapMutati:
...mapMutations('countOptions',{increment:'JIA',decrement:'JIAN'})

路由

简介:

1.Vue中的路由应用在单页面应用,即只有一个html文件,在导航区切换标签时,页面不会整体刷新也不会打开新的页面,只会在特地区域刷新新的组件内容。
生活中的路由器
Vue中的路由

对vue-router的理解:

是vue的一个插件库,专门实现SPA应用(单页面应用)

对SPA应用的理解:

(1)单页Web应用(single page web application)
(2)整个应用只有一个完整的页面
(3)点击页面中的导航链接不会刷新页面,只会做页面的局部刷新
(4)数据需要通过ajax请求获取

路由的理解:

(1)一个路由就是一组映射关系(key-value)
(2)key为路径,value可能是function或component

路由分类:

(1)后端路由:value是function,用于处理客户端提交的请求。
服务器接收到一个请求时,根据请求路径找到匹配的函数来处理请求,返回响应数据。
(2)前端路由:value是component,用于展示页面内容。
当浏览器的路径改变时,对应的组件就会显示。

基本使用

1.安装vue-router,命令:npm i vue-router
2.应用插件:Vue.use(VueRouter)
3.编写router配置项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 引入VueRouter
import VueRouter from 'vue-router'
// 引入路由组件
import About from '../components/About'
import Home from '../components/Home'

// 创建router实例对象,区管理一组一组的路由规则
const router = new VueRouter({
routes:[
{
path:'/about',
component:About
},
{
path:'/home',
component:Home
}
]
})

// 暴露router
export default router

4.实现切换:(active-class可配置高亮样式)

1
<router-link active-class="active" to="/about">About</router-link>

5.指定展示位置:

1
<router-view></router-view>

几个注意点

1.路由组件通常存放在pages文件夹,一般组件通常存放在components文件夹。
2.通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
3.每个组件都有自己的$route属性,里面存储着自己的路由信息。
4.整个应用只有一个router,可以通过组件的$router属性获取到。

嵌套(多级)路由

1.配置路由规则,使用children配置项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
routes:[
{
path:'/about',
component:About
},
{
path:'/home',
component:Home,
children:[ //通过children配置子级路由
{
path:'news', //此处一定不要写'/news'
component:News
},
{
path:'message', //此处一定不要写'/message'
component:Message
}
]
}
]

2.跳转:要写完整路径

1
<router-link to="/home/news">News</router-link>

路由的Query参数

1.传递参数:

1
2
3
4
5
6
7
8
9
10
11
12
// 跳转并携带query参数,to的字符串写法
<router-link :to="/home/message/detail?id=666&title=你好">跳转</router-linl>

// 跳转并携带query参数,to的对象写法
<router-link :to="{
path:'home/message/detail',
query:{
id:p.id,
title:p.title
}
}">
跳转</router-link>

2.接受参数

1
2
$route.query.id
$route.query.title

命名路由

1.作用:可以简化路由的跳转
2.如何使用
(1)给路由命名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
path:'/demo',
component:Demo,
children:[
{
path:'test',
component:Test,
children:[
{
name:'hello' //给路由命名
path:'welcome'
component:Hello
}
]
}
]
}

(2)简化跳转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 简化前,需要写完整的路径
<router-link to="/demo/test/welcome">跳转</router-link>

// 简化后,直接通过名字跳转
<router-link :to="{name:"hello"}"></router-link>

// 简化跳转配合传递参数
<router-link
:to="{
name:'hello',
query:{
id:666,
title:'你好'
}
}">跳转</router-link>

路由的params参数

1.配置路由,声明接收params参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
path:'/home',
component:Home,
children:[
{
path:'news',
component:News
},
{
component:Message,
children:[
{
name:'xiangqing',
path:'detali/:id/:title', //使用占位符声明接收params参数
component:Detail
}
]
}
]
}

2.传递参数

1
2
3
4
5
6
7
8
9
10
11
12
13
// 跳转并携带params参数,to的字符串写法
<router-link :to="/home/message/detail/666/你好">跳转</router-link>

// 跳转并携带params参数,to的对象写法
<router-link
:to="{
name:'xiangqing',
params:{
id:666,
title:'你好'
}
}"
>跳转</router-link>

特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置

3.接收参数:

1
2
$route.params.id
$route.params.title

路由的props配置

作用:让路由组件更方便收到参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
name:'xiangqing',
path:'detail/:id',
component:Detail,

//第一种写法,值为对象,该对象中的所有key-value都会以props的形式传给Detail组件
props: {
a: 1,
b: 'hello'
}

// props的第二种写法,值为布尔值,若布尔值为真,则会把该路由组件收到的所有params参数,以props的形式传给Detail组件
props: true

// props的第三种写法,值为函数,+j
props($route) {
return {
id: $route.query.id,
title: $route.query.title
}
}
}

<router-link>的两种属性

1.作用:控制路由跳转时操作浏览器历史记录的模式
2.浏览器的历史记录有两种写入方式:分别为push和replace,push是追加历史记录,replace是替换当前历史记录,路由跳转的时候默认为oush
区别:push模式中,浏览器可以回退到上一个页面,replace不能回退
3.如何开启replace模式:

1
<router-link replace>News</router-link>

编程式路由导航

1.作用:不借助<router-link>实现路由跳转,让路由跳转更加灵活。
2.具体编码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// $router的两个API
this.$router.push({
name:'xiangqing',
params:{
id:xxx,
title:xxx
}
})

this.$router.replace({
name:'xiangqing',
params:{
id:xxx,
title:xxx
}
})

// 控制浏览器前进、后退以及特定动作
this.$router.back()
this.$router.forward()
this.$router.go()

缓存路由组件

1.作用:让不展示的路由组件保持挂载,不被销毁。
2.具体编码:

1
2
3
4
5
6
7
8
9
// 缓存单个组件
<keep-alive include="NewS"> //include后要加上要缓存的路由组件名
<router-link></router-link>
</keep-alive>

// 缓存多个组件
<keep-alive :include="['NewS','MessagE']">
<router-view></router-view>
</keep-alive>

两个新的生命周期钩子

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

路由守卫

1.作用:对路由进行权限控制
2.分类:全局守卫、独享守卫、组件内守卫
3.全局守卫:

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
// 全局前置守卫,初始化时执行,每次路由切换前执行
router.beforeEach((to,from,next)=>{
if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
if(localStorage.getItem('name')==='xiaohong'){ //权限控制的具体规则
next() //放行,接着走下去
}
else{
alert('无权查看')
}

}
else{
next() //放行
}
})

// 全局后置守卫,初始化时执行,每次路由切换后执行
router.afterEach((to,from)=>{
if(to.meta.title){
document.title=to.meta.title //修改网页的title
}
else{
document.title='系统'
}
})

// 独享路由守卫,需要单独配置在组件路由里
beforeEnter:(to,from,next)=>{
if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
if(localStorage.getItem('name')==='xiaohong'){ //权限控制的具体规则
next() //放行,接着走下去
}
else{
alert('无权查看')
}
}
else{
next() //放行
}
}

路由器的两种工作模式

1.对于一个url来说什么事hash值————#及其后面的内容
2.hash值不会包含在HTTP请求中,即:hash值不会带给服务器
3.hash模式:
(1)地址中永远带着#,不美观。
(2)若以后将地址通过第三方App分享,若App校验严格,则地址会被标记为不合法
(3)兼容性高较好
4.history模式:
(1)地址干净美观
(2)兼容性和hash模式相比略差
(3)应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题

扩展:自己创建一个简易的服务器
前提:需要导包:express和connect-history-api-fallback(用于解决history模式下刷新404问题,hash模式不受影响)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const express = require('express')

const history = require('connect-history-api-fallback')

const app = express()
app.use(history())
app.use(express.static(__dirname + '/static'))

app.get('/person', (req, res) => {
res.send({
name: 'tom',
age: 18
})
})

app.listen(5005, (err) => {
if (!err) console.log('服务器启动成功了')
})

ElementUI

快速上手

1.下载包:npm i element-ui
2.引入包:

1
2
3
4
5
// 引入ElementUI的组件库
import ElementUI from 'element-ui';

// 引入ElementUI的全部样式
import 'element-ui/lib/theme-chalk/index.css';

3.直接从ElementUI官网复制所需代码

按需引入

1.下载包:npm install babel-plugin-component -D