我们已经知道,从父组件向子组件传值可以通过在子组件中添加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值。这就属于非父子组件的兄弟组件传值。
Vue.component('child',{ props:{ content:String }, template:'<div @click="handleclick">{{content}}</div>', methods:{ handleclick:function(){ alert(this.content) } } }) 复制代码
Vue.prototype.bus = new Vue() 复制代码
这里是给Vue的每个prototype上挂载一个bus属性,然后将bus属性指向vue实例。由于我们的每一个实例都是通过Vue创建的,因此我们每个组件上都会有bus属性。
handleclick:function(){ this.bus.$emit('change',this.content) } 复制代码
由于第一步已经给所有实例添加了bus属性,这里调用bus以 $emit 方法向外(总线)触发事件“change”,并携带参数“this.content”。
这个bus顾名思义,可以理解为“公交车/总线”,作为一个公共区域被多方监听并触发多个事件。
mounted:function(){ this.bus.$on{this.bus.$on('change',function(msg){ this.content = msg; }) } 复制代码
mounted就是实例被挂载的时候执行的函数,并在这个函数中实现监听bus上触发的事件。
但注意,这个方法加入后期望效果仍不能实现!
原因是:我们使用了两个子组件,并且都具有“点击向总线触发”和“监听总线”这两个方法。所以如果我点击第一个组件,他向外触发事件的同时进行监听,这就会导致this分别指向各自本身,第一个组件的this都指向自己,第二个组件同样。这样就没法做到把第一个this指向的值传递给第二个组件。
我们在mounted函数中进行修改:
mounted:function(){ this.bus.$on{this.bus.$on('change',function(msg){ var loc=this loc.content = msg; }) } 复制代码
我们在mounted(已挂载函数)函数中声明一个变量“loc”用以保存我点击的那个this。
这样我们的效果就实现了,但是当我们打开控制台,会发现报错:避免子组件改变父组件的值。也就是说我们一直在改变父组件所传递的content,这是不应该的。由于上面的报错,我们在组件中添加data来保存父组件的值:
data:function(){ return { selfcontent:this.content } } 复制代码
并将其他部分的content进行替换。
<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”事件被触发,将事件所携带的参数(指向第一个盒子的内容)赋给指向各自的内容。第一个盒子赋给自己不变,第二个盒子实现第一个盒子的值赋给自己。