在上一个小节中,我们提到了子组件是不能引用父组件或者Vue实例的数据的。
但是,在开发中,往往一些数据确实需要从上层传递到下层:
- 比如在一个页面中,我们从服务器请求到了很多的数据。
- 其中一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示。
- 这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件)将数据传递给小组件(子组件)。
如何进行父子组件间的通信呢?Vue官方提到
- 通过props向子组件传递数据
- 通过事件向父组件发送消息
props基本用法
在组件中,使用选项props来声明需要从父级接收到的数据。
props的值有两种方式:
- 方式一:字符串数组,数组中的字符串就是传递时的名称。
- 方式二:对象,对象可以设置传递时的类型,也可以设置默认值等。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn></cpn> <!--当没有传值时 为默认值-->
<!-- <cpn :cmessage="message"></cpn> -->
</div>
<template id="cpn">
<div>
{{cmessage}}
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: '#cpn',
props: {
cmessage: {
type: String, // 类型
default: '我是子组件cmessage的默认值', // 默认值
required: false // 是否必填
}
}
}
const app = new Vue({
el: '#app',
data: {
message: '我是父组件的message'
},
components: {
cpn
}
})
</script>
</body>
</html>
子向父传递
这个时候,我们需要使用自定义事件来完成。
什么时候需要自定义事件呢?
- 当子组件需要向父组件传递数据时,就要用到自定义事件了。
- 我们之前学习的v-on不仅仅可以用于监听DOM事件,也可以用于组件间的自定义事件。
自定义事件的流程: - 先在子组件中,通过$emit()来触发事件。
- 然后在父组件中,通过v-on来监听子组件事件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<p>Total : {{total}}</p> <!--这里是父组件的total-->
<cpn @cadd="chageTotal" @csub="chageTotal"></cpn> <!--使用子组件-->
</div>
<template id="cpn">
<div>
<button @click="add">+</button>
<button @click="sub">-</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: '#cpn',
data() {
return {
count: 0
}
},
methods: {
add() {
this.count++;
this.$emit('cadd', this.count);// 向父组件发送事件与value cadd 事件的名称
},
sub() {
this.count--;
this.$emit('csub', this.count);
}
}
}
const app = new Vue({
el: '#app',
data: {
total: 0
},
methods: {
chageTotal(value) {
/*这个value为子组件传过来的值*/
this.total = value;
}
},
components: {
cpn
}
})
</script>
</body>
</html>
父子组件的访问方式
$children
this.$children是一个数组类型,它包含所有子组件对象。
这里我们定义了一个组件,并且使用了四次,当我们点击按钮时,通过vue实例,直接获取所有子组件对象
结果:
可以通过具体某一个子组件对象调用子组件的方法
$children
的缺陷:
通过$children
访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值。
但是当子组件过多,我们需要拿到其中一个时,往往不能确定它的索引值,甚至还可能会发生变化。
有时候,我们想明确获取其中一个特定的父子组件的访问方式: $refs
组件,这个时候就可以使用$refs
$refs
的使用:
$refs
和ref
指令通常是一起使用的。
首先,我们通过ref
给某一个子组件绑定一个特定的ID。
其次,通过this.$refs.ID
就可以访问到该组件了。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn></cpn>
<cpn></cpn>
<cpn ref="cpn2"></cpn>
<cpn></cpn>
<button @click="btnClick">Btn</button>
</div>
<template id="cpn">
<div>我是子组件</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {},
methods: {
btnClick() {
// 父组件访问子组件:使用$children或$refs
// $children(实际开发不常用)
console.log(this.$children);
// this.$children[0].showMessage(); // 直接调用子组件的方法
// $refs 对象类型 默认为空 需要在组件上加上 ref='xxx'属性
console.log(this.$refs);
console.log(this.$refs.cpn2); /*拿到具体的对象*/
}
},
components: {
cpn: {
template: '#cpn',
methods: {
showMessage() {
console.log('showMessage');
}
}
}
}
})
</script>
</body>
</html>
父子组件的访问方式: $parent
如果我们想在子组件中直接访问父组件,可以通过$parent
注意事项:
尽管在Vue开发中,我们允许通过$parent
来访问父组件,但是在真实开发中尽量不要这样做。
子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了。
如果我们将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题。
另外,更不好做的是通过$parent
直接修改父组件的状态,那么父组件中的状态将变得飘忽不定,很不利于我的调试和维护。