运用的场合
我们已经知道,从父组件向子组件传值可以通过在子组件中添加props实现,而子组件向父组件传值主要通过向外触发函数。
当我们的页面采用三层嵌套,颜色由浅到深并对应图中1,2,3。那么如果我们想沿着蓝色的线传递值,是否需要将值从1-> 2-> 3这样;如果我们想沿着紫色的线传递值,是否要将值从3 ->2 ->1 ->2 -> 3呢。这些过于麻烦,都不需要!
我们需要使用: Bus/总线/发布订阅模式/观察者模式(反正都是一个意思) ,便可以实现非父子关系的传值。
基本格式:
<body>
<div id="root">
<child></child>
<child></child>
</div>
<script type="text/javascript">
Vue.component('child',{
template:'<div>child</div>'
})
var vm=new Vue({
el:'#root'
})
</script>
</body>
这里我们先写入一个最简单的父子组件传值。
<div id="root">
<child content='Aston'></child>
<child content='Martin'></child>
</div>
<script type="text/javascript">
Vue.component('child',{
props:{
content:String
},
template:'<div>{{content}}</div>'
})
var vm=new Vue({
el:'#root'
})
</script>
用一个例子实现传值
我现在想实现如果点击任何一个文字,另一个文字也会变成我点击的文字,其实就是改变未点击文字的content值。这就属于非父子组件的兄弟组件传值。
1. 在组件中添加基本的点击事件
Vue.component('child',{
props:{
content:String
},
template:'<div @click="handleclick">{{content}}</div>',
methods:{
handleclick:function(){
alert(this.content)
}
}
})
2. 添加总线属性:
Vue.prototype.bus = new Vue()
这里是给Vue的每个prototype上挂载一个bus属性,然后将bus属性指向vue实例。由于我们的每一个实例都是通过Vue创建的,因此我们每个组件上都会有bus属性。
3. 修改点击事件
handleclick:function(){
this.bus.$emit('change',this.content)
}
由于第一步已经给所有实例添加了bus属性,这里调用bus以 $emit 方法向外(总线)触发事件“change”,并携带参数“this.content”。
这个bus顾名思义,可以理解为“公交车/总线”,作为一个公共区域被多方监听并触发多个事件。
这里举个栗子来理解:将子组件相当于病房,父组件相当于护士台,但是不方便的是,病房之间传递东西(参数)需要先呼叫护士台,拿到护士台后再送去别的病房。
现在我们使用总线,类似使用医院走廊,一个病房需要传给隔壁病房一个东西时候,在走廊嚎一嗓子(事件触发),隔壁病房监听(事件监听)到走廊的声音,出去取东西即可。
4. 使用生命周期钩子
mounted:function(){
this.bus.$on{this.bus.$on('change',function(msg){
this.content = msg;
})
}
mounted就是实例被挂载的时候执行的函数,并在这个函数中实现监听bus上触发的事件。
但注意,这个方法加入后期望效果仍不能实现!
原因是:我们使用了两个子组件,并且都具有“点击向总线触发”和“监听总线”这两个方法。所以如果我点击第一个组件,他向外触发事件的同时进行监听,这就会导致this分别指向各自本身,第一个组件的this都指向自己,第二个组件同样。这样就没法做到把第一个this指向的值传递给第二个组件。
5. 记录下点击的this
我们在mounted函数中进行修改:
mounted:function(){
this.bus.$on{this.bus.$on('change',function(msg){
var loc=this
loc.content = msg;
})
}
我们在mounted(已挂载函数)函数中声明一个变量“loc”用以保存我点击的那个this。
这样我们的效果就实现了,但是当我们打开控制台,会发现报错:避免子组件改变父组件的值。也就是说我们一直在改变父组件所传递的content,这是不应该的。6. 保存一份父组件的值
由于上面的报错,我们在组件中添加data来保存父组件的值:
data:function(){
return {
selfcontent:this.content
}
}
并将其他部分的content进行替换。
7. 全部代码
<body>
<div id="root">
<child content='Aston'></child>
<child content='Martin'></child>
</div>
<script type="text/javascript">
Vue.prototype.bus = new Vue()
Vue.component('child',{
data:function(){
return {
selfcontent:this.content
}
},
props:{
content:String
},
template:'<div @click="handleclick">{{selfcontent}}</div>',
methods:{
handleclick:function(){
this.bus.$emit('change',this.selfcontent)
}
},
mounted:function(){
var loc=this;
this.bus.$on('change',function(msg){
loc.selfcontent=msg;
})
}
})
var vm=new Vue({
el:'#root'
})
</script>
</body>
总结
我们最后再捋一遍整个过程:我们点击第一个文字盒子,触发组件内“handleclick”事件,此事件向总线触发“change”事件并将第一个盒子中的内容作为参数传递,这时两个盒子都执行mounted函数,将指向各自组件的this保存在变量loc中,然后监听的“change”事件被触发,将事件所携带的参数(指向第一个盒子的内容)赋给指向各自的内容。第一个盒子赋给自己不变,第二个盒子实现第一个盒子的值赋给自己。