- 父子组件通信指的就是父组件数据需要传递给子组件,那么就需要某种方式传递
- 父传子通过props属性传递,子传父需要通过自定义事件$emit传递
一、父传子props
1.传递简单数据
- 如果传递的数据内容比较少或者类型简单,那么可以声明一个prop数组来接收即可
- 在注册组件时,可以在对象中声明一个props数组来定义子组件变量名,这个变量名不仅是为了区分父组件传来的变量名,而且还是通过v-bind命令绑定父组件数据的重要依据。
- 注意在用到template模板的地方使用v-bind绑定数据,然后模板内部双花括号取的变量名就是props里声明的值
- 注意props属性尽量不要使用驼峰命名法,因为不受支持,如果变量真的声明了驼峰命名,那么在v-bind绑定数据的时候要加-,例如cMoives 要写为c-movies
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>父组件传子组件</title>
<script src="https://cdn.bootcss.com/vue/2.4.2/vue.js"></script>
</head>
<body>
<template id="tmp">
<div>
<p>{{cmessage}}</p>
<p>{{cmovies}}</p>
<p>遍历Array</p>
<ul>
<li v-for="(item,index) in cmovies">{{index}} - {{item}}</li>
</ul>
</div>
</template>
<div id="app">
<tmp :cmessage="message" :cmovies="movies"></tmp>
</div>
<script>
Vue.component('tmp',{
template: '#tmp',
data(){
return {}
},
props:['cmessage','cmovies']
});
const vm = new Vue({
el: '#app',
data: {
message: '电影列表',
movies: ['钢铁侠','美国往事','火星救援','阿甘正传']
}
})
</script>
</body>
</html>
2.传递复杂数据
- props属性不仅可以声明为数组,还可以声明为对象类型,这样可以进一步对变量作一些属性要求,并且每个变量还可以声明为一个对象,赋予更多的属性
- 可以对props里声明的变量做类型要求,是否有默认值等,其中type可传的类型有[String,Array,Object,Number,Boolean,Date]等等
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>父组件传子组件</title>
<script src="https://cdn.bootcss.com/vue/2.4.2/vue.js"></script>
</head>
<body>
<template id="tmp">
<div>
<p>获取到父组件的message是:{{cmessage}}</p>
<p>获取到父组件的movies是:{{cmovies}}</p>
<p>获取到父组件的cperson是:{{cperson}}</p>
</div>
</template>
<div id="app">
<tmp :cmessage="message" :cmovies="movies" :cperson="person"></tmp>
</div>
<script>
Vue.component('tmp',{
template: '#tmp',
data(){
return {}
},
props:{
cmessage: {
type: String,
default: ''
// 可以声明required属性,如果父组件没传值将会报错
// required:true
},
cmovies: {
type: Array,
default: function () {
return []
}
},
cperson: {
type: Object,
default: function () {
return {}
}
}
}
});
const vm = new Vue({
el: '#app',
data: {
message: '文本内容',
movies: ['钢铁侠','美国往事','火星救援','阿甘正传'],
person: {
name: '小明',
age: '18',
sex: '男',
hobbies: ['篮球','羽毛球','乒乓球']
}
}
})
</script>
</body>
</html>
二、子传父this.$emit
- 子传父是需要自定义事件,并通过$emit发送给父组件
- 首先注册局部组件,并声明了一组分类数据通过data()函数返回
- 模板里遍历整个Array并创建对应按钮,每个按钮通过v-on绑定了函数,组件可以声明自己的函数,点击按钮时会触发该函数
- 函数里通过 this.$emit(‘itemclick’,category)发出自己信号,第一个参数表示发射给父组件的变量名,第二参数为对应的值
- 然后父组件使用子组件<tmp>,通过v-on监听刚才子组件发射的变量名,并绑定父组件的方法事件pbtnclick,点击按钮则会出发该事件,至此完成了子传父
- 注意@itemclick="pbtnclick"中参数可以省略,也可以声明@itemclick=“pbtnclick(category)”
- 注意方法名尽量不要使用驼峰命名
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>子传父</title>
<script src="https://cdn.bootcss.com/vue/2.4.2/vue.js"></script>
</head>
<body>
<!-- 子组件模板 -->
<template id="tmp">
<div>
<p>{{cmessage}}</p>
<button v-for="category in categoryList" @click="btnclick(category)">{{category.name}}</button>
</div>
</template>
<!-- 父组件模板 -->
<div id="app">
<tmp @itemclick="pbtnclick" :cmessage="message"></tmp>
</div>
<script>
const app = new Vue({
el: '#app',
data:{
message: '提示信息'
},
// 注册局部组件
components:{
tmp:{
template: '#tmp',
data: function () {
return {
categoryList:[
{id:1,name:'手机数码'},
{id:2,name:'家用电器'},
{id:3,name:'生活用品'},
{id:4,name:'时尚穿搭'}]
}
},
props:['cmessage'],
methods:{
btnclick:function (category) {
console.log(category);
// 向父组件发射事件
this.$emit('itemclick',category);
}
}
}
},
methods: {
pbtnclick:function (category) {
console.log("父组件接收到的内容:"+JSON.stringify(category));
}
}
});
</script>
</body>
</html>
三、父子组件引用对象访问
- 父子组件之间既可以进行通信,也可以通过获得当前引用对象来访问其中内容(注意只能进行访问,不能修改内容,例如修改变量值等)
1.父组件通过引用对象访问子组件
- 通过this.$children可以获得当前子组件对象内容
- 访问其中内容必须通过下标指定访问哪一个children,因为可能有多个子组件被声明使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>父访问子-引用对象形式访问</title>
<script src="https://cdn.bootcss.com/vue/2.4.2/vue.js"></script>
</head>
<body>
<template id="tmp">
<div>
<p>{{msg}}</p>
</div>
</template>
<div id="app">
<tmp></tmp>
<button @click="getRefContent">获取子组件引用对象</button>
</div>
<script>
const app = new Vue({
el: '#app',
data:{},
methods:{
getRefContent(){
console.log("子组件对象内容:");
console.log(this.$children);
console.log("调用子组件方法: "+this.$children[0].showMsg());
console.log("获取子组件变量: "+this.$children[0].msg)
}
},
components:{
tmp:{
template: '#tmp',
data(){
return {
msg: '这是子组件消息'
}
},
methods: {
showMsg(){
console.log("子组件showMsg方法");
}
}
}
}
});
</script>
</body>
</html>
**注意:**通过下标来访问某一个组件内容是不可靠的,因为可能会有新的组件被声明使用,例如:
<div id="app">
<!-- 新增了一个子组件cpn,这时放在两个tmp中间,那么通过this.children[1]去访问第二个tmp就不可靠了,因为它变成了cpn -->
<tmp></tmp>
<cpn></cpn>
<tmp></tmp>
<button @click="getRefContent">获取子组件引用对象</button>
</div>
那么,其实这种情况也是有解决办法的,那就是通过$refs进行访问,修改一些代码:
<div id="app">
<tmp></tmp>
<!-- 这里通过ref指定了引用名 -->
<tmp ref="aaa"></tmp>
<button @click="getRefContent">获取子组件引用对象</button>
</div>
<script>
const app = new Vue({
el: '#app',
data:{},
methods:{
getRefContent(){
console.log("子组件对象内容:");
console.log(this.$children);
// console.log("调用子组件方法: "+this.$children[0].showMsg());
// console.log("获取子组件变量: "+this.$children[0].msg)
console.log(this.$refs.aaa.msg);
}
},
components:{
tmp:{
template: '#tmp',
data(){
return {
msg: '这是子组件消息'
}
},
methods: {
showMsg(){
console.log("子组件showMsg方法");
}
}
}
}
});
</script>
通过ref给组件指定引用名,然后方法里通过this. r e f s 引 用 调 用 指 定 的 组 件 名 就 可 以 获 取 其 中 内 容 , 这 样 , 无 论 组 件 发 生 怎 样 的 变 化 , 都 可 以 确 保 访 问 正 确 的 子 组 件 。 refs引用调用指定的组件名就可以获取其中内容,这样,无论组件发生怎样的变化,都可以确保访问正确的子组件。 refs引用调用指定的组件名就可以获取其中内容,这样,无论组件发生怎样的变化,都可以确保访问正确的子组件。refs默认是个空的引用,必须配合组件上指定引用的名称方可使用
2.子组件通过引用对象访问父组件
- 子组件通过this.$parent获取父组件对象内容
- 子组件childrentmp通过this.$parent获取的是它的上一级父组件parenttmp内容
- 而父组件parenttmp通过this.$parent获取的是它的上一级父组件内容是vue实例(new Vue这个实例对象)
- 如果childrentmp想获取根属性,可以通过this.$root引用获取
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>子访问父-引用对象形式访问</title>
<script src="https://cdn.bootcss.com/vue/2.4.2/vue.js"></script>
</head>
<body>
<template id="childrentmp">
<div style="width: 500px;height: 300px;background-color: #CCE8EB">
<h2>{{ccontent}}</h2>
<button @click="getParent">获取父组件内容</button>
</div>
</template>
<template id="parenttmp">
<div style="width: 600px;height: 500px;background-color: #cad9ea">
<h1>{{pcontent}}</h1>
<button @click="getOriginParent">获取父组件内容</button>
<!-- 引用子组件 -->
<childrentmp></childrentmp>
</div>
</template>
<div id="app">
<parenttmp></parenttmp>
</div>
<script>
const app = new Vue({
el:'#app',
data:{
message: 'vue实例'
},
components:{
parenttmp:{
template:'#parenttmp',
data(){
return {
pcontent: '这是父组件内容',
name: '父组件'
}
},
methods: {
getOriginParent(){
console.log(this.$parent);
}
},
components: {
childrentmp:{
template:'#childrentmp',
data(){
return {
ccontent: '这是子组件内容',
}
},
methods:{
getParent(){
console.log(this.$parent);
// 获取根对象,即vue实例
console.log(this.$root);
}
}
}
}
}
}
});
</script>
</body>
</html>