Vue.js的组件化开发与组件通信

补上昨晚咕的部分hhhh

组件化开发

在组件内的代码是不能访问vue实例里面的数据的,如果不想要写死的数据,可能会想到用mustache语法输出变量,但其实这样不可以。组件是一个单独功能模块的封装,这个模块有属于自己的HTML模板,也应该有属性自己的数据data。即使组件可以访问vue实例中的data,这就要把所有的数据都放在vue实例中,实例就会很臃肿。vue组件应该有自己保存数据的地方。

那么就在component里面加入一个data对象就可以了?仍然不可以。vue规定,组件的data必须是一个函数,不写成函数形式会报错。可以让这个data直接返回一个对象,在里面写各种数据,这样就可以了。

1
2
3
4
5
data() {
return {
title: 'test',
}
}

那么,组件中的data为什么必须是一个函数?考虑到组件的复用问题,一个组件可能要被用在多个位置,如果组件中保存的数据为同一份,那么就相当于无形中把这些所有组件的数据都共享了,多个组件使用同一块数据可能会导致隐性的bug,会产生一些“连锁反应”,明明我动的是这个组件,结果全部的组件都跟着动了,这有时候不是我们想要的。把data封装成函数有一个好处,每次申请组件实例的时候都会去调用这个data函数,每次调用都会返回一个对象,多次调用就会返回多个对象,多个对象之间是不同的,本质上是地址不相同(JavaScript不能取地址,这一点比较遗憾),所以这样做就能保证每一个组件的数据都是独立的。而method就不需要封装成函数,该怎么写就怎么写就可以,方法是可以共享的,这个没什么问题。(据说这是一个面试题)

父子组件的通信

子组件是不能引用父组件或者Vue实例的数据的,但是在开发中,往往经常用到数据在层级之间传递的操作。比如显示商品列表,服务器的请求是在最上层发出的,返回的数据也会在最上层,但是一般来讲商品列表并不会在最上层(想一想,li会在最顶上那一层,挨着body吗,应该是不会的),这个时候并不需要让子组件再发送一次网络请求,而是让父组件将数据传递给子组件。vue中有两种方式提供父子组件的通信,比如通过props向子组件传递数据,或者通过事件向父组件发送消息。两种方式对应的传递顺序不一样,刚才举的例子是父组件向子组件传递信息,但是子组件也可以向父组件传递信息,这种时候就要用后者的传递方式。

tips:vue实例可以看作是一个根组件,或者说叫root组件。

props基本用法:在组件中,使用选项props来声明需要从父级接收到的数据。值有两种方式,一种叫字符串数组,数组中的字符串就是传递时的名称。一种叫对象,对象可以设置传递时的类型,也可以设置默认值等。

注册组件后,使用props属性,值是一个数组,传入想要通信的数据的名字,在html代码里面的对应组件内,使用v-bind绑定属性,就可以实现父组件到子组件的通信。

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
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue测试</title>
<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div id="app">
<cpn :cclass7="newclass7" :cmessage="message"></cpn>
</div>
<template id="cpn">
<div>
<p>{{cclass7.toString()}}</p>
<h2>{{cmessage}}</h2>
</div>
</template>
<script type="text/javascript">
const cpn = {
template: '#cpn',
props: ['cclass7', 'cmessage'],
data() {
return {};
},
};
const app = new Vue({
el: '#app',
data: {
message: 'hello',
newclass7: ['尤娜', '库尔特', '亚尔缇娜', '亚修', '缪洁'],
},
components: {
cpn,
}
});
</script>
</body>
</html>

props还可以是一个对象,需要类型验证时就有用了。可以限定类型,设定默认值,以及设定是否必须传值。在一些较低的版本中,类型是对象或者数组时,默认值必须是一个函数,就像上面说的组件的data必须是一个函数那样,不过我使用的这个版本中这个限制貌似去掉了。在开发中对象形式使用的多一些。验证时使用自定义类型也是可以的,可以不用JavaScript的自带类型。

1
2
3
4
5
6
7
8
9
10
11
12
props: {
cmessage: {
type: String,
default: 'aaaaaa',
required: true,
},
cclass7: {
type: Array,
default: [],
required: true,
},
},

需要注意:这里不能使用驼峰式命名法,因为html部分全都会被认作小写,而js对大小写敏感,会导致报错,但是可以使用连字符。

子组件向父组件传递的一般是事件,比如我在菜单栏中有很多物品分类选项,点击某个就会在页面上显示相关的推荐商品,这种时候就要子组件向父组件传递一个点击事件,告诉父组件用户点击了什么。此时需要用$emit发射事件。在父组件使用v-on接收事件后,再去父组件的method里面用一个方法处理事件即可。即子传父用的是自定义事件。

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
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue测试</title>
<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div id="app">
<cpn @itemclick="cpnclick"></cpn>
</div>
<template id="cpn">
<div>
<button type="button"
v-for="item in categories"
@click="btnclick(item)">
{{item.name}}
</button>
</div>
</template>
<script type="text/javascript">
const cpn = {
template: '#cpn',
data() {
return {
categories: [
{id: 'a', name: '热门推荐'},
{id: 'b', name: '手机数码'},
{id: 'c', name: '家用家电'},
{id: 'd', name: '电脑办公'},
]
}
},
methods: {
btnclick(item) {
this.$emit('itemclick', item);
}
}
};
const app = new Vue({
el: '#app',
data: {},
methods: {
cpnclick(item) {
console.log('cpnclick', item);
}
},
components: {
cpn,
}
});
</script>
</body>
</html>

大体分三步,子组件发射,父组件读取,父组件处理。

打赏
  • 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!
  • © 2018-2020 Shawn Zhou
  • Powered by Hexo Theme Ayer
  • PV: UV:

感谢打赏~

支付宝
微信