Vue

因为之前曾接触过小程序的开发,所以 入手 Vue 相对爽了一些,因为思想都是数据绑定,Vue 是华人尤雨溪开发的,这可是个大佬,B 站上有个关于大佬的纪律片,是 Up 主:鱼 C-小甲鱼 翻译的,有兴趣的话可以看看。

https://www.bilibili.com/video/BV1dT4y1V7Dn


M V VM

  • 他是一种架构方式、开发思想

  • M:Model 数据模型 (操作数据的类)

  • V:View 视图界面 (HTML)

  • VM:ViewModel 视图模型


Vue 的思想

Vue 是比较重要的是用数据来驱动页面,下面是一个简单的用 Vue 实现的页面

1
2
3
4
5
6
7
8
9
10
11
12
<body>
<div id="app">{{message}}</div>
</body>
<script src="js/vue.js"></script>
<script>
var app = new Vue({
el:"#app",
data:{
message:"hello world"
}
})
</script>

Vue 实例中常见对象

el

获取元素,你可以用#id.类名,类似 JQuery 的选择器等等,也可以用document.querySelectAll()之类的

data

实例中的数据都放在这里

computed 计算属性

计算属性的结果是放在缓存里的

method 方法

顾名思义,可以在这里定义方法


生命周期函数

需要记得有

  • beforeCreatedatamethods都不能使用(可以在此声明周期中打印 data,报undefined,或是执行method方法:报错

  • create:可以使用 datamethods

  • beforeMount:节点在内存中已经生成好了,但是没有挂载到页面上(我们可以通过document.querySelector('').innerHTML); 来获取页面上的信息,因为没有渲染到页面上,此时还是模板字符串的内容{{ msg }},说明数据还没有替换到页面上,但是内存里已经渲染好了)

  • mounted:挂载到页面上了,通过document.querySelector('').innerHTML) 也能获取到挂在上去的msg了(mounted 是 实例创建期间的最后一个生命周期函数,当执行完 mounted 就表示,实例已经被完全创建好了,此时,如果没有其他操作的话,这个实例,就静静的躺在内存中,不动弹了)
    在这里插入图片描述

  • beforeUpdata:数据已经更新了,并且内存中也更新完了,但是没有渲染到页面上(测试方法:写个按钮,改变data,触发此函数)

  • updated:渲染到页面上了,此时已经是新的数据

  • beforeDestroy:从运行阶段,进入到销毁阶段,此时Vue实例上所有的 datamethods 都可以使用

  • destroyed:销毁完毕,什么都没了~

其实有规律可循,创造-挂载-更新-销毁
在这里插入图片描述

测试生命周期如下

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
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>生命周期测试</title>
<script src="vue.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div id="app">
<div id="h3">{{ msg }}</div>
<input type="button" @click="msg = 'No'" value="点我变成No" />
</div>
</body>

<script type="text/javascript">
var vm = new Vue({
el: "#app",
data: {
msg: "我是信息",
},
methods: {
show() {
console.log(this.msg);
},
},
beforeCreate() {
// 这是第一个生命周期 beforeCreate,表示实例完全被创建出来之前,会执行它
// 此时 data 和 methods 是不能使用的。
console.log("1. beforeCreate:", this.msg);
},
created() {
// 这是第二个生命周期,完成了data 和 method 的加载。
console.log("2. created:", this.msg);
// this.show();
},
beforeMount() {
// 这是第三个生命周期,表示 模板已经加载到内存中编译完成了,但是没有把模板渲染到页面中
console.log("3. beforeMount:", document.querySelector("#h3").innerHTML); // {{ msg }}
// 在 beforeMount 执行的时候,页面中的元素,黑没有被真正替换过来,只是之前写的一些模板字符串
},
mounted() {
// 这是遇到的第4个生命周期函数,表示,内存中的模板已经挂载到页面中了,用户已经可以看到渲染好的页面了
console.log("4. mounted:", document.querySelector("#h3").innerHTML); // 我是信息
// 注意1:mounted 是 实例创建期间的最后一个生命周期函数,当执行完 mounted 就表示,实例已经被完全创建好了
// 此时,如果没有其他操作的话,这个实例,就静静的躺在内存中,不动弹了
console.log(
"********************************************************************"
);
console.log(
"********************************************************************"
);
// 注意2:如果要通过某些插件操作页面上的 dom 节点,最早要在 mounted 中进行
},

// 以下两个是:运行阶段的生命周期函数
// 这两个事件,会根据 data 的变化,有选择性的触发 0 次或 多次
beforeUpdate() {
// 这个时候,页面没有被更新,【数据更新了吗? 肯定更新了】
console.log("beforeUpdate:data 中的 msg 的值是:", this.msg);
console.log(
"beforeUpdate:页面中的节点内容:",
document.querySelector("#h3").innerHTML,
"(说明内存中的数据已经改变了,但是没有渲染到页面上)"
);
// 结论:当执行 beforeUpdate 的时候,页面中的显示的数据,还是旧的
// 此时 data 的数据是最新的,页面还是旧的
console.log(
"---------------------------------------------------------------------"
);
},
updated() {
console.log("updated:data 中的 msg 的值是:", this.msg);
console.log(
"updated:页面中的节点内容:",
document.querySelector("#h3").innerHTML
);
// 页面和data 的数据已经保持同步了,都是最新的
},
beforeDestroy() {
// 当执行beforeDestroy 钩子函数的时候,Vue实例就已经从 运行阶段,进入到了销毁阶段
// 注意:当执行 beforeDestory 的之后,实例身上所有的 data 和所有的 methods,以及过滤器,指令...
// 都处于可用的状态,此时,还没有真正的执行销毁过程
},
destroyed() {
// 组件已经完全被销毁了
},
});
</script>
</html>

参考资料 黑马程序员:Vue 的声明周期


常见 API 的使用

v-if 条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<body>
<!-- 条件语句必须if/else紧跟着 -->
<div id="app">
{{message}}
<div v-if="message == 'hello world'">if</div>
<div v-else-if="message == 'hello world1'">else-if</div>
<div v-else>else</div>
</div>
</body>
<script src="js/vue.js"></script>
<script>
var app = new Vue({
el: "#app",
data: {
message: "hello world",
},
});
</script>

v-show

  • v-show不能搭配template ,即使条件不对也会显示到页面上
1
2
3
<template v-show="1 == 2">
<h1>我是个标题</h1>
</template>
  • 如果v-show里面的条件不对的话,页面渲染的时候会使用display:none把他隐藏掉
1
2
3
<div v-show="1 == 2">
<h1>我是个标题</h1>
</div>

这里有个经典面试问题:v-ifv-show 有什么区别?如果频繁的显示隐藏,我用那个更好?

要回答这个问题就需要知道这两个命令是怎么工作的,v-if 它是根据条件的真假,从而是否创建元素,v-show 是控制css的属性display:none,也就是不管真假,他都会创建并渲染,只是控制css达到不显示的效果而已。

参考文档 Vue 中 v-if 和 v-show 区别与用法解析

v-for 循环

  • 比方说我要循环一个名字叫li的数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<body>
<div id="app">
<!-- 这里也可以写成 (each,index) of li,借用了ES6的语法-->
<div v-for="(each,index) in li">each:{{each}}-----index:{{index}}</div>
</div>
</body>
<script src="../js/vue.js"></script>
<script>
var app = new Vue({
el: "#app",
data: {
li: ["one", "two", "three", "four", "five"],
},
});
</script>

在这里插入图片描述


v-key**

参考博文 1
参考博文 2

  • 虚拟 DOM 的 Diff 算法,其核心是基于两个简单的假设:1. 两个相同的组件产生类似的 DOM 结构,不同的组件产生不同的 DOM 结构。2. 同一层级的一组节点,他们可以通过唯一的 id 进行区分。
  • 总结一句话:为了更好的让 Vue 更新虚拟 Dom,key 来给每个节点做一个唯一标识。
  • 影响:如果不加上标识的话,在插入的时候会发生错误
    在这里插入图片描述

在这里插入图片描述

v-cloak 取消抖动

用 Vue 渲染页面的时候,你刷新页面,会有页面没有加载数据就渲染出来的情况,一闪而过(尝试截取动图但是截不到),但是为了美,这种缺点怎么可能忍呢!!!

只需两步轻松解决

  • 在标签里写上 v-cloak
  • 在样式里写[v-cloak]{ display: none; }

举个小例子

1
2
3
4
5
6
7
8
<style>
[v-cloak] {
display: none;
}
</style>
<body>
<div id="app" v-cloak></div>
</body>

v-once

首次渲染之后,页面不再随着数据的变化而变化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<body>
<div id="app">
<div v-once>
<!--可以在控制台上改变数值 app.message = 'nihao' -->
{{message}}
</div>
</div>
</body>
<script src="js/vue.js"></script>
<script>
var app = new Vue({
el: "#app",
data: {
message: "hello world",
},
});
</script>

v-bind 绑定属性 **

v-bind 可以简略写成:,v-bind:后面接标签的属性,用来绑定标签的属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<body>
<div id="app">
<!-- 下面两个div的绑定类是一样的,都是name1 -->
<div v-bind:class="className">v-bind:class="className"</div>
<div :class="className">:class</div>
</div>
</body>
<script src="js/vue.js"></script>
<script>
var app = new Vue({
el: "#app",
data: {
className: "name1",
},
});
</script>

在这里插入图片描述


v-on 绑定事件

  • v-on 也可以写成 @
  • 可以传递参数,如果向添加事件因子,可以@click="函数名($event)这样传递
1
2
3
4
<!-- 下面两个div都绑定了click点击事件,效果一样 -->
<div v-on:click=""></div>
<div @click=""></div>
<button @click="hand2(10,$event)">btn2---$event</button>
  • v-on也可以绑定多个事件
1
<input type="text" v-on:click="click1()" v-on:change="change1" v-model="data1">
1
<input type="text" v-on:click="click1(),change1()" v-model="data1">

修饰符

.这个点就是修饰符,能修饰什么呢?下面就是例子

冒泡与捕获

  • 阻止冒泡:.stop
1
2
3
4
5
<div @click="fa">
fa
<div @click.stop="son">son</div>
<!-- 这样,点击son,就不会出发fa的点击事件了 -->
</div>
  • 捕获:.capture
1
2
3
4
5
<div @click.capture="fa">
fa
<div @click="son">son</div>
</div>
<!-- 这样,点击son,就会先触发fa,然后son -->

按键触发事件

  • 37 是键盘上的左方向键
  • 如果向要组合按键,触发连招的话,可以连着修饰符写,如下
1
2
3
4
<input type="text" @keydown.37="key1Handler" />
<input type="text" @keydown.space="key1Handler1" placeholder="space" />
<!-- 组合按键 -->
<input type="text" @keydown.shift.83="key1Handler2" placeholder="shift+S" />

v-html 编译 & v-pre 跳过

注意 v-html 的一些细节

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<body>
<div id="app">
<span v-html="tar">span1</span>
<span v-pre="tar1">span2</span>
</div>
</body>
<script src="js/vue.js"></script>
<script>
// v-html 转化成html
// 注意转化后的页面格式,是插入此标签内了,标签内原有的内容将被覆盖

// v-pre 跳过这个元素和它的子元素的编译过程。
// 可以用来显示原始 Mustache 标签。跳过大量没有指令的节点会加快编译。
var app = new Vue({
el: "#app",
data: {
tar: "<a href='#'>11</a>",
tar1: "<a href='#'>11</a>",
},
});
</script>

图片展示在这里插入图片描述


v-model 绑定输入

  • v-model可以绑定inputtextarea的文本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<body>
<div id="app" v-cloak>
<input type="text" v-model="message" />
{{message}}
<textarea rows="" cols="" v-model="message"> </textarea>
</div>
</body>
<script src="../js/vue.js"></script>
<script>
var app = new Vue({
el: "#app",
data: {
message: "hello world",
},
});
</script>
  • input 失焦时在改变 data,把输入框中的值变为 number、去掉输入框中值的首尾空格
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<body>
<div id="app">
<!-- .lazy 是change事件,当失去焦点的时候会改变 -->
<input type="text" v-model.lazy="mes" />

<!-- .number 把输入数据变成类型为number -->
<input type="text" v-model.number="mes" />

<!-- .trim 去首和尾空格 -->
<input type="text" v-model.trim="mes" />

<h1>{{mes}}</h1>
</div>
</body>
<script src="../js/vue.js"></script>
<script>
var app = new Vue({
el: "#app",
data: {
mes: "hello world",
},
});
</script>

单选框、复选框、下拉框

  • 都是通过v-model来分组,相当于校服,不同学校相当于不同的组。校服一样说明是一个学校的
  • 单选框
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<body>
<div id="app">
<!-- 单选:多个互斥 -->
<input type="radio" v-model="pick" value="male" /> <label></label>
<input type="radio" v-model="pick" value="female" /> <label></label>
<input type="radio" v-model="pick" value="???" /> <label>男女男</label>
</div>
</body>
<script src="../js/vue.js"></script>
<script>
// 单选框
// 页面上选择哪一项,pick的值就是哪一个value值
var app = new Vue({
el: "#app",
data: {
pick: "male",
},
});
</script>
  • 多选框
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<body>
<div id="app">
<!-- 复选框多选 -->
<input type="checkbox" v-model="check" value="checkBox1" />
<label>复选框1</label>
<input type="checkbox" v-model="check" value="checkBox2" />
<label>复选框2</label>
</div>
</body>
<script src="../js/vue.js"></script>
<script>
var app = new Vue({
el: "#app",
data: {
// check里存储谁被选中了
check: [],
},
});
</script>
  • 下拉框
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<body>
<div id="app">
<!-- select如果想添加多个属性,加multiple -->
<select v-model="select">
<!-- 默认是个option中的文本进行匹配
如果用了value的话优先value
-->
<option>1</option>
<option>2</option>
<option value="你好">3</option>
</select>
</div>
</body>
<script src="../js/vue.js"></script>
<script>
var app = new Vue({
el: "#app",
data: {
select: "你好",
},
});
</script>

组件

组件命名要求

  • 短横线分隔命名
  • 首字母大写命名

全局/局部组件定义

顾名思义,组件的使用范围不一样

  • 需要注意的是,全局组件要在实例化对象之前就要注册,说人话,就是
    在这里插入图片描述
  • 为什么要先注册在实例化呢?反过来行不行呢?我反过来会报如下的错误
1
2
3
[Vue warn]: Unknown custom element: <card> -
did you register the component correctly?
For recursive components, make sure to provide the "name" option.

报错信息提示说:不知道的元素card,是否正确注册组件?,看一下生命周期那张流程图:创建完 Vue 实例之后就已经渲染完template了,这时候在注册带有template的组件的话,Vue 是不认识的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<body>
<div id="app">
{{message}}
<else-name></else-name>
</div>
</body>
<script src="../js/vue.js"></script>
<script>
// 全局组件:
// else-name 是我自定义的标签名称
// 命名规范:不能用驼峰命名,推荐都用小写
Vue.component("else-name", {
template: "<div>这是组件中</div>",
});
</script>

局部的组件,这里写了两种写法

  • 直接把模板放到组件里
  • 把模板放到外边:方便修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 局部:只在当前vue中有效
var child = {
template: "<div>这是组件局部,内部中2</div>",
};
var app = new Vue({
el: "#app",
data: {
message: "hello world",
},
components: {
demo1: {
template: "<div>这是组件局部,内部中1</div>",
},
demo2: child,
},
});

组件中的模板

  • 模板里只能由只能有一个根元素,不能有两个以上的根元素

意思是说可以这样:<div> <div></div> </div> 不可以<div></div> <div></div>
前者两个div是父子关系,后者两个div是同级的哥们关系

组件中的变量

组件中的变量可以这样定义和使用

1
2
3
4
5
6
7
8
9
Vue.component("else-name", {
template: "<div>这是组件全局-外部中{{mess1}} {{mess2}}</div>",
data: function () {
return {
mess1: "<<这是组件中的data1>>",
mess2: "<<这是组件中的data2>>",
};
},
});

组件的通信

父传子: props 的属性

  • 是否要有必须值
  • 默认值
  • 约束数据类型
1
2
3
4
5
6
7
props: {
items: { // 必须提供字段
required: true,
default: 3,
type: Array
},
}
  • 先来个简单的,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
30
31
32
33
34
35
<body>
<div id="app">
<son :msgson="msgfa"></son>
</div>
</body>
<script src="../js/vue.js"></script>
<script>
// props单项传递,父向子传
var app = new Vue({
el: "#app",
data: {
msgfa: "我是你爸爸",
},
components: {
son: {
props: ["msgson"],
template: `<div>
介绍:{{msgson}}
<grandson :msggrandson='sonData'></grandson>
</div>`,
components: {
grandson: {
props: ["msggrandson"],
template: `<div>{{msggrandson}}</div>`,
},
},
data: function () {
return {
sonData: "儿子",
};
},
},
},
});
</script>

子传夫:自定义事件 $emit

儿子如何向爸爸传递信息呢,也就是子向父传递,这时候用到了自定义函数

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
<body>
<div id="app">
<show @myevent="showVal"></show>
</div>
</body>
<script src="../js/vue.js"></script>
<script>
// 子向父亲 $emit()触发事件,只是为了传值 v-on
Vue.component("show", {
data: function () {
return {
str: "我是组件中的string",
};
},
methods: {
sendMsgToFa: function () {
// 这里的this指向组件
// 自定义事件
this.$emit("myevent", this.str);
},
},
template: `<button @click="sendMsgToFa">点击弹出组件传递的数据</button>`,
});
var app = new Vue({
el: "#app",
data: {
message: "hello world",
},
methods: {
showVal: function (a) {
alert(a);
},
},
});
</script>
  • 如果我的孙子向给儿子传递信息呢?(就是组件中的组件组件 传递信息)
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
<body>
<div id="app">
我有三个阶级关系,分别是父亲--儿子--孙子
<son :msgson="msgfa"></son>
</div>
</body>
<script src="../js/vue.js"></script>
<script>
// props单项传递,父向子传
var app = new Vue({
el: "#app",
data: {
msgfa: "我是你爸爸",
},
components: {
son: {
props: ["msgson"],
template: `<div>
介绍:{{msgson}}
<grandson :msggrandson='sonData' @myevent='showVal'></grandson>

</div>`,
components: {
grandson: {
props: ["msggrandson"],
template: `<div>
{{msggrandson}}
<button @click="sendMsgToFa">孙子给儿子传值</button>
</div>`,
methods: {
sendMsgToFa: function () {
// 传值不是自发的,而是事件驱动的,所以需要个绑定事件
// 自定义事件确定了,父亲的接收函数名,和想要传递给父亲的数据,
this.$emit("myevent", "我是孙子里面的消息");
},
},
},
},
data: function () {
return {
sonData: "儿子",
};
},
methods: {
showVal: function (val) {
console.log(val);
},
},
},
},
});
</script>

中央事件总线 bus:数据的中介

如果我现在还是有三层关系,分别是父亲,儿子,和孙子,父亲是 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
36
37
38
<body>
<div id="app">
<abc :myval="btnVal"></abc>
</div>
</body>
<script src="vue.js"></script>
<script>
// 注册中央时间总线
var eventBus = new Vue();
// 注册一个名为 abc 的组件
Vue.component('abc', {
props:["myval"],
template: `<div>
<button @click="showVal">{{myval}}</button>
</div>`,
methods: {
showVal() {
// $emit 需要事件驱动,比方点击事件,来发送信息给 eventBus
eventBus.$emit('myevent','你好啊')
}
}
})
var app = new Vue({
el: '#app',
data: {
msgfa: '我是你爸爸',
btnVal:"点击"
},
mounted(){
// 我们可以在挂载的生命周期函数里接收这个自定义事件。
// 当点击事件触发的时候,这边的on 事件就接收到消息了
eventBus.$on('myevent',function(res){
console.log(res);
this.msgfa = res;
})
}
})
</script>

中央事件总线在项目中的使用

在这里插入图片描述
代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// bus.js
class Bus {
constructor() {
this.callback = {};
}
$on(eventName, fn) {
// 监听,相当于添加事件
this.callback[eventName] = this.callback[eventName] || [];
this.callback[eventName].push(fn);
}
$emit(eventName, argu) {
// 发送事件,其实就是执行时间是吧
this.callback[eventName].forEach((item) => {
item(argu);
});
}
}

module.exports = Bus;

父链 $parent

  • 子组件可以通过父链拿到父组件的所有,也可以修改父组件的数据
  • 取父组件中的数据可以,但是如果是修改的话,那么父子组件的关系就会更紧,这是解耦合的操作,不太推荐(组件之间的关系应该尽量独立,不要太过亲昵)在这里插入图片描述
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
<body>
<div id="app">
<abc :myval="btnVal"></abc>
</div>
</body>
<script src="vue.js"></script>
<script>
// 注册中央时间总线
var eventBus = new Vue();
// 注册一个名为 abc 的组件
Vue.component('abc', {
props:["myval"],
template: `<div>
<button @click="showVal">{{myval}}</button>
</div>`,
methods: {
showVal() {
console.log("this.$parent :>>", this.$parent);
}
}
})
var app = new Vue({
el: '#app',
data: {
message: '我是你爸爸',
btnVal:"点击"
}
})
</script>

在这里插入图片描述

子链 $children

子链跟父链差不多,不过子链打印出来的是数组~(这很好理解啊孩子可以有很多,但是你爹还能有两个嘛???)

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
<body>
<div id="app">
<button type="button" @click="seeChildComp">点击查看儿子</button>
<abc :myval="btnVal" ></abc>
</div>
</body>
<script src="vue.js"></script>
<script>
// 注册中央时间总线
var eventBus = new Vue();
// 注册一个名为 abc 的组件
Vue.component('abc', {
props:["myval"],
template: `<div>
<button @click="showVal">{{myval}}</button>
</div>`,
methods: {
showVal() {
console.log("this.$parent :>>", this.$parent);
},

}
})
var app = new Vue({
el: '#app',
data: {
message: '我是你爸爸',
btnVal:"点击"
},
methods:{
seeChildComp(){
console.log("seeChildComp :>>", this.$children);
}
}
})
</script>

在这里插入图片描述

给组件起名字,并通过名字获得组件 $refs

  • 给组件起名字 ref
  • 取值的话通过this.$refs.组件名字
    在这里插入图片描述
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
<body>
<div id="app">
<!-- 给组件起名字 -->
<abc ref="Wangergou"></abc>
</div>
</body>
<script src="vue.js"></script>
<script>
// 注册一个名为 abc 的组件
Vue.component('abc', {
data: function() {
return {
sonMsg: "我是组件中的数据"
}
},
template: `<div>
{{sonMsg}}
</div>`
})
var app = new Vue({
el: '#app',
data: {
message: 'hello word'
},
mounted() {
// 通过组件的名字拿到组件
console.log(this.$refs.Wangergou);
}
})
</script>

组件的插槽:内容的分发

  • 插槽的作用在于内容的分发
    在这里插入图片描述
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
<body>
<!-- 编译的作用域 -->
<!-- 父组件的模板的内容实在父组件的作用域编译 -->
<!-- 插槽 -->
<div id="app">
<comp>
<!-- 可以把这个p标签注释掉,观察页面 -->
<p>父亲模板的内容</p>
</comp>
</div>
</body>
<script src="vue.js"></script>
<script>
// slot是插槽
Vue.component("comp", {
template: `<div>
<slot>如果父模板没有内容,就显示我</slot>
</div>`,
});
var app = new Vue({
el: "#app",
data: {
message: "hello world",
},
});
</script>
  • 指定插槽的内容分发,我们可以给每个插槽起个名字
    在这里插入图片描述
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
<body>
<!-- 插槽: 具名的插槽分发-->
<div id="app">
<comp>
<!-- 注意这里不是v-slot -->
<!-- 具名插槽 -->
<p slot="top">top</p>
<p slot="foot">foot</p>
<!-- 无名插槽,注意他们插入的位置 -->
<p>我没有名字1</p>
<p>我没有名字2</p>
</comp>
</div>
</body>
<script src="vue.js"></script>
<script>
// slot是插槽
Vue.component('comp',{
template:`<div>
<slot name="top"></slot>
<slot name="foot"></slot>
<div>
<slot></slot>
</div>
</div>`
})
var app = new Vue({
el:"#app",
data:{
message:"hello world"
}
})
</script>

动态组件 is

  • component 标签上有个属性is,里面填写组件的名字
    在这里插入图片描述
    在这里插入图片描述

路由

简单路由

在这里插入图片描述

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
<script type="text/javascript" src="vue.js"></script>
<script type="text/javascript" src="vue-router.js"></script>
<body>
<div id="app">
<router-link to="/shangcheng">商城</router-link>
<router-link to="/shuma">数码</router-link>
<router-link to="/jiaju">家具</router-link>
<router-view></router-view>
</div>
</body>
<script>
// 定义一些组件
var sc = { template:`<h1>商城组件</h1>` }
var sm = { template:`<h1>数码组件</h1>` }
var jj = { template:`<h1>家具组件</h1>` }

// 定义路由,这个跟组件一样,都是要卸载实例化vue之前!
var router = new VueRouter({
routes:[
// redirect 重定向,里面写路径
{ path: '/',redirect: '/shuma' },
{ path: '/shangcheng',component: sc},
{ path: '/shuma',component: sm},
{ path: '/jiaju',component: jj}
]
})

var app = new Vue({
el:"#app",
router, // 这里需要注册路由
})
</script>

动态路由

  • 动态路由可以这样写,
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
<script type="text/javascript" src="vue.js"></script>
<script type="text/javascript" src="vue-router.js"></script>
<body>
<div id="app">
<!-- 点击,路由跳转到 hello/65 -->
<!-- 不过这里有个问题,这里的url是拼接关系,请看下面动图 -->
<router-link to="hello/65">点击</router-link>
<router-view></router-view>
</div>
</body>
<script>
// 动态路由
var Hello = {
template: `<h1>你好 {{$route.params.id}} </h1>`
}
var router = new VueRouter({
routes:[
{ path:'/hello/:id', component: Hello }
]
})
var app = new Vue({
el:"#app",
router
})
</script>

在这里插入图片描述

路由前进/后退

通过:this.$router.go(1),参数填整数

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
<body>
<div id="app">
<router-link to="hello">点击</router-link>
<router-view></router-view>
<button v-on:click="back">后退</button>
<button v-on:click="go">前进</button>
</div>
</body>

<script>
// 组件
var Hello = {
template: `<h1>名字{{name}},年龄{{age}} </h1>`,
props:['name', 'age']
}
// 路由传参
var router = new VueRouter({
routes:[
{
path: '/hello',
component: Hello ,
props: { name:'张三', age:11 }
}
]
})
var app = new Vue({
el:"#app",
router,
methods:{
// 路由的前进和后退
go(){
this.$router.go(1)
},
back(){
this.$router.go(-1)
}
}
})
</script>

监听路由

  • 我想点击登录的时候在右上角更改信息,就是把原先的登录注册按钮改成用户名和注销
  • 然而我的登录写成了组件,在组件中更改 App.vue 的一个 data 信息,奈何学术不精,百度了好久,想到通过监听路由来出发事件
  • 主要的是watch()这个 API,只要路由变化就会触发,但是浏览器的后退是不会触发的
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
export default {
data: function () {
return {
isLogin: false,
userName: "",
};
},
methods: {
zhuxiao() {
window.sessionStorage.setItem("isLogin", false);
this.isLogin = false;
this.$router.push({
path: `/login`,
});
},
},
watch: {
$route(to) {
if (to.path === "/view") {
this.isLogin = true;
this.userName = window.sessionStorage.getItem("userName");
}
},
},
};