一、为什么要学习组件的通信?
- 1、组件实例的作用域是相互独立的。本身组件之间数据和方法是不能交流的
- 2、但是对vue来说,组件之间的消息传递是非常重要的
- 3、组件可以有一下几种关系
如上图所示,A 和 B、B 和 C、B 和 D 都是父子关系,C 和 D 是兄弟关系,A 和 C 是隔代关系(可能隔多代)。
- 所以组件之间通信就有:父子之间的通信、兄弟之间的通信、……
- 针对不同的使用场景。如何选择有效的通信方式?
二、通信方式总结
2.1、方式一:props/$emit
props
- 父组件向子组件传递数据是通过prop传递 A—B
- 实现有三步:在代码中有解释
- 代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="js/vue-2.4.0.js"></script>
<style>
.parent{
width: 500px;
height: 500px;
background: skyblue;
}
.child{
width: 300px;
height: 300px;
background: pink;
}
</style>
</head>
<body>
<div id="parent" :class="['parent']">
<h1>我是父亲</h1>
{{msg}}
<!-- 调用组件-->
<!-- 继承家产第一步先:通过属性添加有个继承接口-->
<child :parent="msg"></child>
</div>
<template id="child">
<div :class="['child']" >
<h4>我是儿子</h4>
{{msg}}
<br><br><br>
<!-- //第三步可以使用继承下的数据了-->
我继承的家产: {{parent}}
</div>
</template>
<script>
//定义组件
Vue.component('child',{
template:'#child',
data:function () {
return {
msg:'我是儿子,这是我的数据'
}
},
//继承家产第二步:继承后存到自己名下
props:['parent']
})
new Vue({
el:'#parent',
data:{
msg:'我是老爹,这是我的家产'
},
methods:{
}
})
</script>
</body>
</html>
$emit
- 子组件向父组件传递数据是通过$emit触发事件来实现的B----A
- 也是三步代码中有解释
- 代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="js/vue-2.4.0.js"></script>
<style>
.parent{
width: 500px;
height: 500px;
background: skyblue;
}
.child{
width: 300px;
height: 300px;
background: pink;
}
</style>
</head>
<body>
<div id="parent" :class="['parent']">
<h1>我是父亲</h1>
{{msg}}
<input v-model="son">
<!-- 调用组件-->
<!-- 老子要用小子的家产第二步:-->
<child :parent="msg" @toparent="getSonData"></child>
</div>
<template id="child">
<div :class="['child']" >
<h4>我是儿子</h4>
{{msg}}
<button @click="toparent1" >孝敬长辈</button>
</div>
</template>
<script>
//定义组件
Vue.component('child',{
template:'#child',
data:function () {
return {
msg:'我是儿子,这是我的数据',
parent1:'这是我孝敬长辈的'
}
},
//继承家产第二步:继承后存到自己名下
props:['parent'],
methods: {
// 老子要用小子的家产第三步
toparent1(){
this.$emit('toparent',this.parent1);
}
}
})
new Vue({
el:'#parent',
data:{
msg:'我是老爹,这是我的家产',
son:'123'
},
methods:{
//老子要用小子的家产第一步:需要给父组件定义一个方法
getSonData(son){
this.son=son;
console.log('继承的:'+this.son);
}
}
})
</script>
</body>
</html>
总结:子组件继承是通过添加属性而父子间继承子组件的数据是通过事件events,实际上就是子组件把自己的数据发送到父组件
2.2、方式2-emit/emit/emit/on中央事件总线
- 上面两种方式处理的都是父子组件之间的数据传递,而如果两个组件不是父子关系呢?
- 种情况下可以使用中央事件总线的方式。
- 这种方法通过一个空的 Vue 实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级。当我们的项目比较大时,可以选择更好的状态管理解决方案 vuex。
- 具体实现
1、var Event=new Vue();
2、 Event.emit(事件名,数据);起自定义事件3、Event.emit(事件名,数据); 起自定义事件 3、 Event.emit(事件名,数据);起自定义事件3、Event.on(事件名,data => {
console.log(data)//上面传值
});接收自定义数据
- 代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="js/vue-2.4.0.js"></script>
</head>
<body>
<div id="itany">
<my-a></my-a>
<my-b></my-b>
<my-c></my-c>
</div>
<!--定义几个组件-->
<template id="a">
<div>
<h3>A组件:{{name}}</h3>
<button @click="send">将数据发送给C组件</button>
</div>
</template>
<template id="b">
<div>
<h3>B组件:{{age}}</h3>
<button @click="send">将数组发送给C组件</button>
</div>
</template>
<template id="c">
<div>
<h3>C组件:{{name}},{{age}}</h3>
</div>
</template>
<script>
//第一步:定义一个空的Vue实例,
var Event=new Vue();
//第二步:定义组件
var A = {
template: '#a',
data() {
return {
name: 'tom'
}
},
methods: {
send() {
Event.$emit('data-a', this.name);
}
}
}
var B = {
template: '#b',
data() {
return {
age: 20
}
},
methods: {
send() {
Event.$emit('data-b', this.age);
}
}
}
var C = {
template: '#c',
data() {
return {
name: '',
age: ""
}
},
mounted() {//在模板编译完成后执行
Event.$on('data-a', name => {
this.name = name;//箭头函数内部不会产生新的this,这边如果不用=>,this指代Event
})
Event.$on('data-b', age => {
this.age = age;
})
}
}
var vm = new Vue({
el: '#itany',
components: {
'my-a': A,
'my-b': B,
'my-c': C
}
});
</script>
</body>
</html>
- 案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="js/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">
<my-addbtn :count="count" ></my-addbtn>
{{count}}
<my-removebtn :count="count" ></my-removebtn>
</div>
<script>
var busVm=new Vue();
var vm=new Vue({
el:'#app',
data:{
count:0
},
mounted:function(){
busVm.$on('change',function (num) {
this.count=num;
}.bind(this));
},
components:{
'my-addbtn':{
template:'<button @click=" setCount">+</button>',
props:['count'],
methods:{
setCount:function () {
busVm.$emit('change',this.count+1);
}
}
},
'my-removebtn':{
template:'<button @click=" setCount">-</button>',
props:['count'],
methods:{
setCount:function () {
busVm.$emit('change',this.count- 1);
}
}
}
}
})
</script>
</body>
</html>
2.3、方式三、attrs/attrs/attrs/listeners实现跨多级的组件嵌套关系
- attrs是存放父组件数据但不包括在props中的容器
- listeners是存放父组件方法的容器
- 主要用途:用在父组件传递数据给子组件或者孙组件
- A 组件与 C 组件之间的通信
- A-----B----C
A的组件:中有A的数据和方法,并且A组件中嵌套B的组件,B的组件继承了A组件的数据和方法通过v-bind和v-on
Vue.component('A',{
template:`
<div :class="['A']">
<B :messagec="messagec" :message="message" v-on:getCData="getCData" v-on:getChildData="getChildData(message)"></B>
</div>
`,//B中有A的数据和事件:数据通过v-bind传递 事件通过v-on传递
data(){
return {
message:'A',
messagec:'A---C' //传递给c组件的数据
}
},
methods:{
getChildData(val){
console.log('这是来自B组件的数据')
},
//执行C子组件触发的事件
getCData(val){
console.log(val);
console.log("这是来自C组件的数据:"+val)
}
}
})
B组件
- B组件中包含C组件 C组件有一个参数通过v-bind把B接收A的数据传过去
- 和v-on把事件传递过去
Vue.component('B',{
data(){
return {
mymessage:this.message
}
},
template:`
<div :class="['B']">
<p>我是B组件</p>
<input type="text" v-model="mymessage" @input="passData(mymessage)">
<C v-bind="$attrs" v-on="$listeners"></C>
</div>
`,//通过刚B获取了A的数据和方法,并没有给B的props中添加---
props:['message'],//得到父组件传递过来的数据
methods:{
passData(val){
//触发父组件中的事件
this.$emit('getChildData',val)
console.log(this.$attrs);
}
},
mounted(){
console.log(this.$attrs);//这是装入数据的容器
console.log(this.$listeners);//这是装入方法的容器
}
});
这个是在B组件打印的数据计算mounted中的–这个是把props给注释了
C组件
- 由B组件传递过来的attrs和attrs和attrs和listeners
- C就可以用了
Vue.component('C',{
template:`
<div :class="['C']">
<p>我是C组件</p>
<input type="text" v-model="$attrs.messagec" @input="passCData($attrs.messagec)">
<p>{{$attrs.messagec}}</p>
</div>
`,
methods:{
passCData(val){
//触发父组件A中的事件
this.$emit('getCData',val)
}
},
mounted(){
console.log(this.$attrs);//这是装入数据的容器
console.log(this.$listeners);//这是装入方法的容器
}
});
var app=new Vue({
el:'#app',
template:`
<div>
<A></A>
</div>
`
});
2.4、方式四、Vuex
2.5、方式五、provide/inject
- 父组件中通过provider来提供变量,然后在子组件中通过inject来注入变量。
- 不论子组件有多深,只要调用了inject那么就可以注入provider中的数据。
- 而不是局限于只能从当前父组件的prop属性来获取数据,只要在父组件的生命周期内,子组件都可以调用。
- 代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="js/vue-2.4.0.js"></script>
</head>
<body>
<div id="app"></div>
<script>
Vue.component('child',{
inject:['for'],//得到父组件传递过来的数据---数组的形式
data(){
return {
mymessage:this.for
}
},
template:`
<div>
<input type="text" v-model="mymessage">
</div>`
})
Vue.component('parent',{
template:`
<div>
<p>this is parent compoent!</p>
<child></child>
</div>
`,
//第一步:父组件来注册变量
provide:{
for:'test'
},
data(){
return {
message:'hello'
}
}
})
var app=new Vue({
el:'#app',
template:`
<div>
<parent></parent>
</div>
`
})
</script>
</body>
</html>
2.6、方式六、$ parent/$ children/$refs
-
refs:首先你的给子组件做标记。demo:<firstchildref="one"></firstchild>然后在父组件中,通过this.refs:首先你的给子组件做标记。demo :<firstchild ref="one"></firstchild> 然后在父组件中,通过this.refs:首先你的给子组件做标记。demo:<firstchildref="one"></firstchild>然后在父组件中,通过this.refs.one就可以访问了这个自组件了,包括访问自组件的data里面的数据,调用它的函数
-
$parent为当前组件树的根实例
-
$children为当前组件的直接子组件,是一个无序的数组
1)组件只能一个根节点
2)可以在自组件中使用this.$parent.属性值,或者函数
3)在父组件中可以使用this.refs.组件的标记访问子组件,或者this.refs.组件的标记 访问子组件,或者this.refs.组件的标记访问子组件,或者this.children[i].属性,,访问子组件的
4)你需要注意this的指向
2.7、方式七、v-model
- 父组件通过v-model传递值给子组件时,会自动传递一个value的prop属性,在子组件中通过this.$emit(‘input’,val)自动修改v-model绑定的值
Vue.component('child',{
props:{
value:String, //v-model会自动传递一个字段为value的prop属性
},
data(){
return {
mymessage:this.value
}
},
methods:{
changeValue(){
this.$emit('input',this.mymessage);//通过如此调用可以改变父组件上v-model绑定的值
}
},
mounted(){
console.log(this.value);//hello
},
template:`
<div>
<input type="text" v-model="mymessage" @change="changeValue">
</div>`
})
Vue.component('parent',{
template:`
<div>
<p>this is parent compoent!</p>
<p>{{message}}</p>
<child v-model="message"></child>
</div>
`,
data(){
return {
message:'hello'
}
}
})
var app=new Vue({
el:'#app',
template:`
<div>
<parent></parent>
</div>
`
})