- 父组件通过 props 属性向子组件传递数据
props: ["子组件的属性"] //可以是数组(不常用),也可以是对象(常用)
我们可以设置 prop 是否有默认值,是否是必传值,值得类型
子组件用 {{prop名}} 在 html 里来接值即可 - 子组件通过自定义事件向父组件发送消息:
- 在子组件中,通过 $emit 来触发事件
子组件里的某元素事件 @click="clickbutton('aaa')"
子组件的方法: clickbutton(value) { this.$emit('myevent', value); } - 在父组件中,通过 v-on 来监听子组件事件
父组件调用子组件的时候 需要绑定的方法 @myevent="dosomething"
父组件的方法:dosomething(value) {alert(value);}
- 在子组件中,通过 $emit 来触发事件
- 父组件访问子组件$refs 子组件里设置 ref="aaa" 父组件调用的时候用 $refs.aaa
- 不常用的访问方式
Tips:
如果父组件传给子组件的值是不同的,如
父组件1传递给子组件的image 的值是 p1.image
父组件2传递给子组件的image 的值是 p2.show.img
那么子组件接值利用下计算机属性 <img src="showImg" />
props:[product],
computed:{
showImg(){ return this.product.image || this.product.show.img; }
}
多层组件通讯问题(如爷孙组件)
多层组件通讯有三种方式
1.一层一层的传递
2.通过 Vuex 来改变一个值,然后监听这个值是否发生变化
3.事件总线
案例:用 Better scroll 的时候想要监听很底层的组件 GoodListItem 的图片是否全部加载完毕。
孙组件 GoodListItem.vue code:
<img @load="imageLoad" />
imageload(){
this.$bus.$emit('itemImageLoad') //向全局发送了一个 aaa 事件
}
爷组件 Home.vue code:
data(){
return {
itemImgListener: null //将函数存储在一个变量中方便取消事件总线
}
},
mounted(){
//监听孙组件发来的图片加载完成的事件,注意,这里由于每个图片加载完成之后都要调用一遍 load 喊出,因此要进行防抖操作,具体操作方式参考我的“插件”->"防抖函数"文章
this.itemImgListener = () =>{
console.log('接收到了该时间,这里执行');
//this.$refs.scroll.refresh
};
this.$bus.$on('itemImageLoad', this.itemImgListener)
},
deactivated(){ //如果是不销毁的组件离开之后调用这个函数 deactivated()
//取消全局事件监听的方法
//this.$bus.off('itemImageLoad', 函数) //这里函数不传的话,就会取消所有组件中的事假总线 “itemImageLoad” 的监听
this.$bus.off('itemImageLoad', this.itemImgListener)
}
注:
直接调用 this.$bus 是 undefind。想要调用需要在 main.js 文件中加一句话:
Vue.prototype.$bus = new Vue();
Demo1, 点击子组件2,改变父组件的值和子组件1的值
<div id="app">
<h2>我是父组件</h2>
<p>{{message}}</p>
<h2>父传子</h2>
<mycpn :title="message" :subtitle="'sdsd'"></mycpn>
<h2>子传父</h2>
<mycpn2 @myevent="dosomething"></mycpn2>
</div>
<template id="childcpn">
<div>
<p>我是子组件 1</p>
<p>父组件传过来的 title: {{title}} <br>
父组件传过来的 subtitle: {{subtitle}}</p>
</div>
</template>
<template id="childcpn2">
<div>
<p>我是子组件 2</p>
<button v-for="item in categories" @click="clickbutton(item)">{{item}}</button>
</div>
</template>
<script>
const vue = new Vue({
el: '#app',
data: {
message: "我是父组件的值"
},
methods: {
dosomething(value) {
this.message = value;
}
},
components: {
'mycpn': {
template: '#childcpn',
props: {
title: {
type: String,
required: true //必须传值
},
subtitle: {
type: String,
default: '没有传值就有一个默认值'
}
}
},
'mycpn2': {
template: '#childcpn2',
data() {
return { categories: ['111', '222', '333'] }
},
methods: {
clickbutton(value) { this.$emit('myevent', value); }
}
}
}
});
</script>
Demo 2
父子组件想要实现数据双向绑定,不能采用 v-model 的形式。
需要采用本案例的方法。
本案例需求:
父组件的 input 框发生变化后 子组件变成 父组件的 100倍
子组件的 input 框发生变化后 父组件变成 子组件的 1/100
<div id="app">
<h2>我是父组件</h2>
<p>fatherdata {{fatherdata}}</p>
<input type="text" :value="fatherdata" @input.enter="fatherchange">
<mycpn :childdataprop="fatherdata" @childevent="dosomething"></mycpn>
</div>
<template id="childcpn">
<div>
<p>我是子组件</p>
<p>childdataprop {{childdataprop}} <br>
childdata {{childdata}}
</p>
<input type="text" :value="childdata" @input.enter="childchange">
</div>
</template>
<script>
const vue = new Vue({
el: '#app',
data: {
fatherdata: "0",
},
methods: {
dosomething(value) {
this.fatherdata = value / 100;
},
fatherchange(e) { this.fatherdata = e.target.value }
},
components: {
'mycpn': {
template: '#childcpn',
data() { return { childdata: "0" } },
props: ['childdataprop'],
methods: {
childchange(e) {
this.childdata = e.target.value;
this.$emit('childevent', e.target.value);
}
},
watch: {
childdataprop(newvalue, oldvalue) {
this.childdata = newvalue *100;
}
}
}
}
});
</script>