0%

第十四天

第十四天

父组件向子组件传值

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>父组件向子组件传值</title>
<script src="./js/vue.js"></script>
</head>

<body>
<div id="app">
<!-- 父组件可以在引用子组件时,通过属性绑定的形式,把需要传递的值数据,以属性绑定的形式传递到子组件内,供子组件使用 -->
<com1 :parentmsg="msg"></com1>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
msg: '父组件中数据'
},
components: {
com1: {
data() { // 子组件中的data数据,并不是通过父组件传递过来的,而是子组件自身私有的,比如:子组件通过ajax请求回来的数据,都可以放到data身上
// data中的数据,都是可读可写的
return {
title: '123'
}
},
// 结论:子组件中默认无法访问到父组件中的data上的数据和methods中的方法
// template: '<h1>这是子组件---{{msg}}</h1>'
template: '<h1 @click="change">这是子组件---{{parentmsg}}</h1>',
// props中的数据都是只读的,无法重新赋值(强行赋值不报错,有警告)
props: ['parentmsg'], // 把父组件传递过来的 parentmsg属性,先在props数组中定义,才能使用这个数据
methods: {
change() {
this.parentmsg = "被修改了";
}
}
}
}
})
</script>
</body>

</html>

父组件把方法传递给子组件,子组件通过方法向父组件传值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>父组件把方法传递给子组件</title>
<script src="./js/vue.js"></script>
</head>

<body>
<div id="app">
<!-- 父组件向子组件传递方法,使用的是事件绑定机制,自定义一个事件func -->
<com2 @func="show"></com2>
</div>
<template id="tmp1">
<div>
<h1>这是子组件</h1>
<input type="button" value="子组件按钮,点击触发父组件" @click="myclick">
</div>
</template>
<script>
// 定义了一个字面量类型的组件模板对象
var com2 = {
data() {
return {
user: {
name: 'zs',
age: 22
}
}
},
template: '#tmp1',
methods: {
myclick() {
// 当点击子组件按钮时, 如何拿到func方法, 并调用?
// emit:触发
this.$emit('func', this.user);
}
}
}

var vm = new Vue({
el: '#app',
data: {
msg: ''
},
methods: {
show(data) {
console.log("调用了父组件的show方法");
console.log(data);
// 通过函数,子组件向父组件传值
this.msg = data;

}
},
components: {
// com2: com2
com2
}
})
</script>
</body>

</html>

实例-实现评论的发表与自动刷新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>案例-评论列表</title>
<script src="./js/vue.js"></script>
<style>
ul {
margin: 0;
padding: 0;
list-style: none;
}

li {
border: 1px dashed #000;
}

li span {
float: right;
}
</style>
</head>

<body>
<div id="app">
<cmt-box @func="loadComments"></cmt-box>
<ul>
<li v-for="item in list" :key="item.id">
<span>评论人:{{item.user}}</span>
{{item.content}}
</li>
</ul>
</div>

<template id="tmpl">
<div>
<div>
<label for="">评论人</label>
<input type="text" v-model="user">
</div>
<div>
<label for="">评论内容</label>
<textarea v-model="content"></textarea>
</div>
<div>
<input type="button" value="发表评论" @click="postComment">
</div>
</div>
</template>
<script>
var commentBox = {
data() {
return {
user: '',
content: ''
}
},
template: "#tmpl",
methods: {
// 发表评论的方法
// 分析:发表评论的业务逻辑
// 1. 评论数据存放到本地存储中
// 2. 先组织出一个最新的评论数据对象
// 3. 把2中得到的对象保存到本地存储,
// 3.1 localStoreage只支持存放字符串,要先调用JSON.stringify
// 3.2 在保存最新的评论数据前,先从localStorege获取到之前的评论数据(string),转为数组对象,然后将最新评论push进数组
// 3.3 如果获取到的评论字符串为空,则可以返回一个'[]',让JSON.parse转换
// 3.4 将最新的评论列表数组,再次调用JSON.stringify转为数组字符串,保存
postComment() {
var comment = {
id: Date.now(),
user: this.user,
content: this.content
};
// 从localStoreage 中获取所有评论
var list = JSON.parse(localStorage.getItem('cmts') || '[]');
list.unshift(comment);
// 重新保存最新的评论数据
localStorage.setItem('cmts', JSON.stringify(list));
this.user = this.content = "";
this.$emit('func');
}
}
}

var vm = new Vue({
el: '#app',
data: {
list: [{
id: Date.now(),
user: '李白',
content: '天生我材必有用'
},
{
id: Date.now(),
user: '杜甫',
content: '会当凌绝顶'
}
]
},
created() {
this.loadComments();
},
methods: {
// 从本地localStorege中加载评论列表
loadComments() {
var list = JSON.parse(localStorage.getItem('cmts') || '[]');
this.list = list;
}
},
components: {
'cmt-box': commentBox
}
})
</script>
</body>

</html>

使用$refs获取DOM的组件

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>使用ref获取DOM元素和组件</title>
<script src="./js/vue.js"></script>
</head>

<body>
<div id="app">
<input type="button" value="获取元素" @click="getElement" ref="mybtn">
<h3 ref="myh3">这是一个h3</h3>
<login ref="mylogin"></login>
</div>
<script>
var login = {
template: '<h1>登录组件</h1>',
data() {
return {
msg: 'son msg'
}
},
methods: {
show() {
console.log('调用了子组件的方法');
}
}
}

var vm = new Vue({
el: "#app",
methods: {
getElement() {
// console.log(document.getElementById("").innerText);
// ref(reference) 获取DOM节点
console.log(this.$refs.myh3.innerText);
console.log(this.$refs);
// console.log(this.$refs.mylogin.msg);
console.log(this.$refs.mylogin.show());
}
},
components: {
login
}
})
</script>
</body>

</html>

路由

什么是路由

  1. 后端路由:对于普通的网站,所有的超链接都是URL地址,所有的URL地址都对应服务器上对应的资源
  2. 前端路由:对于单页面应用程序,主要通过URL中的hash(#号)来实现不同页面之间切换,同时,hash有一个特点:HTTP请求中不会包含hash相关的内容;所以,单页面程序中的页面跳转主要用hash实现
  3. 在单页面应用程序中,这种通过hash改变来切换页面的方式,称作前端路由(区别于后端路由)
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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>路由的基本使用</title>
<script src="./js/vue.js"></script>
<!-- 1. 安装vue-router路由模块 -->
<script src="./js/vue-router.js"></script>
<style>
/* 实现选中路由高亮 */
/* 方式一 */
.router-link-active {
color: red;
font-weight: 800;
font-style: 80px;
}

.myactive {
color: red;
}
</style>
</head>

<body>
<div id="app">
<!-- <a href="#/login">登录</a>
<a href="#/register">注册</a> -->

<!-- router-link默认渲染了一个a标签 -->
<!-- 通过tag属性可以改变标签类型 -->
<router-link to="/login" tag="span">登录</router-link>
<router-link to="/register">注册</router-link>

<!-- 路由容器,由vue-router提供的元素,专门用来当作占位符,将来路由规则匹配到的组件,就会展示到这个router-view中 -->
<!-- 可以视作一个占位符 -->
<!-- 如果需要动画效果,可以用transition包在外面 -->
<router-view></router-view>
</div>
<script>
var login = {
template: '<h1>登录组件</h1>'
}
var register = {
template: '<h1>注册组件</h1>'
}

// 2. 创建一个路由对象, 当导入vue - router包后, 在window全局对象中就有了一个路由构造函数,叫VueRouter
// 在new路由对象时,可以为构造函数传递一个配置对象
var routerObj = new VueRouter({
// route //这个配置对象中route 表示 [路由匹配规则] 的意思
routes: [ //路由匹配规则
// 每个路由规则,都是一个对象,这个规则对象身上有两个必要属性:
// 属性1:path表示监听哪个路由链接地址
// 属性2:component表示如果路由是前面匹配的path,则展示component属性对应的那个组件
// {
// path: '/',
// component: login
// },
{
path: '/',
redirect: '/login' //重定向
},
{
path: '/login',
component: login // 注意:component的属性值,必须是一个组件模板对象,不能是组件引用名称
},
{
path: '/register',
component: register
}
],

// 路由高亮方式二
linkActiveClass: 'myactive'
})
var vm = new Vue({
el: '#app',
router: routerObj // 将路由规则对象,注册到vm实例上,用来监听URL地址的变化,然后展示对应的组件
})
</script>
</body>

</html>

路由规则传参方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>路由规则中定义参数</title>
<script src="./js/vue.js"></script>
<script src="./js/vue-router.js"></script>
</head>

<body>
<div id="app">
<!-- 第一种传参 -->
<!-- 如果在路由中,使用查询字符串给路由传递参数,则不需要修改路由规则 -->
<router-link to="/login?id=10&name=zs">登录</router-link>
<!-- 第二种传参 -->
<router-link to="/register/20/ls">注册</router-link>
<router-view></router-view>
</div>
<script>
var login = {
template: '<h1>登录{{$route.query.id}}---{{$route.query.name}}</h1>',
created() { //组件生命周期钩子函数
// 获得查询字符串
console.log(this.$route.query.id);
}
}
var register = {
template: '<h1>注册{{$route.params.id}}---{{$route.params.name}}</h1>',
created() {
// 第二种方式获取参数
console.log(this.$route.params.id);
}
}
var router = new VueRouter({
routes: [{
path: '/login',
component: login
},
{
// 第二种方式传参
path: '/register/:id/:name',
component: register
}
]
})
var vm = new Vue({
el: '#app',
router
})
</script>
</body>

</html>

路由嵌套

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>路由嵌套</title>
<script src="./js/vue.js"></script>
<script src="./js/vue-router.js"></script>
</head>

<body>
<div id="app">
<router-link to="/account">Account</router-link>
<router-view></router-view>
</div>
<template id="tmpl">
<div>
<h1>这是Account组件</h1>
<router-link to="/account/login">登录</router-link>
<router-link to="/account/register">注册</router-link>
<router-view></router-view>
</div>
</template>
<script>
var account = {
template: '#tmpl',
}
var login = {
template: '<h1>登录</h1>'
}
var register = {
template: '<h1>注册</h1>'
}
var router = new VueRouter({
routes: [{
path: '/account',
component: account,
children: [{ // children属性实现子路由
path: 'login', // 没有/,/login中/表示根目录
component: login
},
{
path: 'register',
component: register
}
]
// }, {
// path: '/account/login',
// component: login
// }, {
// path: '/account/register',
// component: register
// }
}]
});

var vm = new Vue({
el: '#app',
router
})
</script>
</body>

</html>

路由命名视图实现经典布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>路由命名视图实现经典布局</title>
<script src="./js/vue.js"></script>
<script src="./js/vue-router.js"></script>
<style>
body,
h1 {
margin: 0;
padding: 0;
}

.header {
background-color: orange;
height: 80px;
}

.container {
display: flex;
height: 620px;
}

.left {
background-color: lightgreen;
flex: 2;
}

.main {
background-color: lightpink;
flex: 8;
}
</style>
</head>

<body>
<div id="app">
<router-view></router-view>
<div class="container">
<!-- 此处name前没:,不用考虑是否加'' -->
<router-view name='left'></router-view>
<router-view name='main'></router-view>
</div>

</div>
<script>
var header = {
template: '<h1 class="header">Header头部区域</h1>'
};
var leftBox = {
template: '<h1 class="left">Left侧边栏区域</h1>'
};
var mainBox = {
template: '<h1 class="main">Main主体区域</h1>'
}
var router = new VueRouter({
routes: [{
path: '/',
components: {
default: header,
'left': leftBox,
'main': mainBox
}
}, ]
})
var vm = new Vue({
el: "#app",
router
})
</script>
</body>

</html>