# 4. Vue 组件模块
# 什么是Vue组件和模块?
组件的出现就是为了拆分Vue实例的代码量,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能,就可以去调用对应的组件即可。
组件化和模块化的不同:
- 模块化:是从代码逻辑的角度进行划分的;方便代码分层开发,保证每个功能模块的职能单一。
- 组件化:是从UI界面的角度进行划分的;前端的组件化,方便UI组件的重用。
# 组件基础
# 注册全局组件
# 第一种方式
基础版
<!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">
<script src="./lib/vue/vue.js"></script>
<title>Vue-组件</title>
</head>
<body>
<div id="app">
<!-- 如果要使用组件,直接把组件的名称以HTML标签的形式,引入到页面中。 -->
<my-com1></my-com1>
</div>
</body>
<script>
// 1.1 使用Vue.extend 来创建全局的Vue组件
var com1 = Vue.extend({
template: '<h3>这是使用 Vue.extend 创建的组件 </h3>' // 通过 template 属性,指定了组件要展示的HTML结构。
})
// 1.2 使用 Vue.component('组件的名称', 创建出来的组件模板对象)
// 注意: 组件的名称如果是驼峰,调用时需要变为以-分割。
// 建议不使用驼峰命名组件名称。
Vue.component('myCom1', com1)
var vm = new Vue({
el: '#app',
data: {
},
methods: {
}
})
</script>
</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
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
简写版
<!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">
<script src="./lib/vue/vue.js"></script>
<title>Vue-组件</title>
</head>
<body>
<div id="app">
<my-com></my-com>
</div>
</body>
<script>
Vue.component('my-com', Vue.extend({
template: '<h3>这是使用 Vue.extend 创建的组件 </h3>' // 通过 template 属性,指定了组件要展示的HTML结构。
}))
var vm = new Vue({
el: '#app',
data: {},
methods: {}
})
</script>
</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
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 name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script src="./lib/vue/vue.js"></script>
<title>Vue-组件</title>
</head>
<body>
<div id="app">
<my-com></my-com>
</div>
</body>
<script>
Vue.component('my-com',{
// 注意:必须有且只能有唯一的一个根元素。
// 比如: template: '<h3>123</h3><span>456</span>' 是不允许的。
template: '<h3>直接使用Vue.component 注册出来的组件</h3>'
})
var vm = new Vue({
el: '#app',
data: {},
methods: {}
})
</script>
</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
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
# 第二种方式(推荐)
使用绑定id的方式创建组件
<!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">
<script src="./lib/vue/vue.js"></script>
<title>Vue-组件</title>
</head>
<body>
<div id="app">
<my-com></my-com>
</div>
<!-- 注意:定义template组件模板标签需要在被vue控制的 #app 外面定义 -->
<template id="my-com">
<div>
<h3>这是通过template标签注册的组件</h3>
<h4>这种方式是推荐使用的,因为它可以有代码提示和代码高亮</h4>
</div>
</template>
</body>
<script>
// 通过 绑定外部template标签中的id注册组件
Vue.component('my-com', { template: '#my-com' })
var vm = new Vue({
el: '#app',
data: {},
methods: {}
})
</script>
</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
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
# 注册私有组件
在Vue实例创建的时候定义
components
var vm = new Vue({
el: '#app',
data: {}.
methods: {},
components: {
// 定义内部的私有组件,key为组件名,value为对象,设置里面的template属性
login: {
template: '#login'
}
}
})
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 组件的data数据
使用组件可以给组件定义data数据。
语法如下:
Vue.component('my-com',{
template: '#my-com',
data: function (){
return {
msg: '这是组件中的data定义的数据!'
}
}
})
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
简写如下:
Vue.component('my-com',{
template: '#my-com',
data: () => ({
msg: '这是组件中的data定义的数据!'
})
})
1
2
3
4
5
6
2
3
4
5
6
# 组件的其他定义
- 组件拥有Vue实例的全部配置。
- 配置方法除了data有点区别外其余的基本相同。
比如下面代码所示:
- 定义了data、methods、filters。
Vue.component('my-com',{
template: '#my-com',
data: () => ({
msg: '这是组件中的data定义的数据!'
}),
methods: {
add (){
console.info('这是组件中定义的methods方法函数!')
}
},
filters: {
nameFormat: (name) => {
return "姓名:" + name;
}
}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# component组件标签
Vue 提供了一个 component 的标签,其中:is
属性可以用来指定要展示的组件名称。
注意:带冒号是属性绑定,那么值会当做一个表达式来解析,所以在直接指定组件名称时,应该把组件名称当做字符串做解析。
<component :is="'my-com'"></component>
1
实战情况:
需求:登录和注册切换显示
<!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">
<script src="./lib/vue/vue.js"></script>
<title>Vue-组件切换</title>
</head>
<body>
<div id="app">
<a href="" @click.prevent='comName = "login"'>用户登录</a>
<a href="" @click.prevent='comName = "register"'>用户注册</a>
<hr />
<component :is="comName"></component>
</div>
<template id="login">
<div>登录组件</div>
</template>
<template id="register">
<div>注册组件</div>
</template>
</body>
<script>
Vue.component('login', {
template: '#login',
})
Vue.component('register', {
template: '#register',
})
var vm = new Vue({
el: '#app',
data: {
comName: 'login'
}
})
</script>
</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
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
# 实际开发常见组件引用方式
比如现在有一个公共组件,我们调用它
<!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">
<script src="./lib/vue/vue.js"></script>
<title>Vue-业务组件</title>
</head>
<body>
<!-- 业务组件 -->
<template id="serviceTemplate">
<div>业务组件</div>
</template>
<!-- 比如上方是我们的公共组件库 -->
<div id="app">
<!-- 下面调用这个组件 -->
<service-template></service-template>
</div>
</body>
<script>
var serviceTemplate = {
template: '#serviceTemplate'
}
var vm = new Vue({
el: '#app',
data: {},
methods: {},
components: {
serviceTemplate // 直接简写,把对象直接扔进去。
}
})
</script>
</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
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
# 向组件传递属性
当我们需要在一些页面中向某个组件传递某个字段或属性时,可以通过内部组件绑定属性来绑定外部的属性。
下面的示例中,展示的是将appMsg和appNum传递给组件,组件显示消息。
关键属性:
props
用于装载父级属性,和data
一样可定义属性。不同的是,data
可读可写,props
只读。
<!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>Document</title>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<input type="button" value="传递属性给私有组件中" @click='send'>
<br />
<!-- 将app控制的作用域中的属性绑定到组件中,使组件内部可以动态获得外层的属性。 -->
<my-com :msg='appMsg' :num='appNum'></my-com>
</div>
<template id="my-com">
<!-- 直接输出组件中的msg。 -->
<p>{{ msg }}</p>
</template>
</body>
<script>
var vm = new Vue({
el: '#app',
data: {
appMsg: '',
appNum: 0
},
methods: {
send() {
// 添加按钮触发该方法,并使其消息改变。
this.appNum++
this.appMsg = '这是app的消息!' + this.appNum
}
},
components: {
'my-com': {
template: '#my-com',
// 通过props定义组件绑定的变量。
props: ['msg', 'num']
}
}
})
</script>
</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
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
# 组件调用父级组件方法
当需要在组件中使用父级方法时,利用事件绑定将父级方法绑定到事件中,在组件内部调用
$emit
触发该事件即可。
当我们需要传参时,
$emit
的第二个参数开始就是对应的参数列表。
<!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="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!-- 使用事件绑定机制传递方法 -->
<my-com @parent-click='parentClick'></my-com>
</div>
<template id="my-com">
<!-- 直接输出组件中的msg。 -->
<input type="button" value="调用父类的parentClick方法" @click='comClick'>
</template>
</body>
<script>
var vm = new Vue({
el: '#app',
methods: {
parentClick(value) {
console.info('这是父类的方法,被调用了,传递的参数是: --- ' + value)
}
},
components: {
'my-com': {
template: '#my-com',
methods: {
comClick() {
this.$emit('parent-click', '病毒')
}
}
}
}
})
</script>
</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
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
# 子组件向父级组件传递数据
在上述例子中,可以将父级组件的方法传递给子组件使用,并且可以在子组件中传递参数,那么我们可以利用这一个方法,让子组件向父级组件传递数据。
<!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="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!-- 使用事件绑定机制传递方法 -->
<my-com @set-msg='setMsg'></my-com>
<!-- 父级显示msg属性 -->
<p>{{ msg }}</p>
</div>
<template id="my-com">
<div>
<input type="text" v-model='msg' />
<input type="button" value="在子组件中设置父级组件的msg属性" @click='setMsg'>
</div>
</template>
</body>
<script>
var vm = new Vue({
el: '#app',
data: {
msg: ''
},
methods: {
setMsg(msg) {
this.msg = msg
}
},
components: {
'my-com': {
template: '#my-com',
data: () => ({ msg: '' }),
methods: {
setMsg() {
this.$emit('set-msg', this.msg)
}
}
}
}
})
</script>
</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
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
# 组件案例-评论列表
需求:
- 实现评论列表功能。
- 评论保存到localStorage中。
- 发表评论后列表刷新
- 重新进入该页面时,加载localStorage中的评论数据。
- 最新发表的评论要显示在第一条。
- 作者和评论内容不能为空。
- 如果当前没有评论,加载默认的列表数据。
<!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>评论列表示例Demo</title>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" />
</head>
<body>
<div id="app">
<comment-box @default_list='defaultList' @update_list='updateList'></comment-box>
<ul class='list-group'>
<li class="list-group-item" v-for='item in list' :key='item.id'>
<span class='badge'>作者:{{ item.userName }}</span>
{{ item.content }}
</li>
</ul>
</div>
<template id='commentBox'>
<div>
<div class="form-group">
<label for="">作者:</label>
<input type="text" class='form-control' v-model='userName' />
</div>
<div class="form-group">
<label for="">评论内容:</label>
<textarea class='form-control' v-model='content'></textarea>
</div>
<div class="form-group">
<input type="button" class="btn btn-primary" value="发表评论" @click='sendComment' />
<input type="button" class="btn btn-primary" value="清空localStorage" @click='clearLocalStorage' />
<span style='color: red;'>{{ errorMsg }}</span>
</div>
</div>
</template>
</body>
<script>
var commentBox = {
template: '#commentBox',
data: () => ({
userName: '',
content: '',
errorMsg: ''
}),
methods: {
sendComment() {
console.info()
if (this.userName === undefined || this.userName === '') {
this.errorMsg = '作者不能为空!'
return
}
if (this.content === undefined || this.content === '') {
this.errorMsg = '评论内容不能为空!'
return
}
this.errorMsg = ''
// 发表评论
var obj = {
id: Date.now(),
userName: this.userName,
content: this.content
}
var list = JSON.parse(localStorage.getItem('cmts') || '[]')
list.unshift(obj)
localStorage.setItem('cmts', JSON.stringify(list))
this.content = ''
this.$emit('update_list')
},
// 清空
clearLocalStorage() {
localStorage.setItem('cmts', '[]')
this.$emit('update_list')
this.$emit('default_list')
}
}
}
var vm = new Vue({
el: '#app',
data: {
list: []
},
created() {
this.updateList()
this.defaultList()
},
methods: {
// 来一组默认值
defaultList() {
if (this.list.length == 0) {
let listTemp = [
{ id: Date.now(), userName: '李白', content: '模板数据:天生我材必有用' },
{ id: Date.now() + 1, userName: '江小白', content: '模板数据:劝君更尽一杯酒' },
{ id: Date.now() + 2, userName: '小马', content: '模板数据:我姓马,风吹草低见牛羊的马' }
]
this.list = listTemp
}
},
// 更新列表
updateList() {
this.list = JSON.parse(localStorage.getItem('cmts') || '[]')
}
},
components: {
commentBox
}
})
</script>
</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
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