Vue.js入门(6)组件间通信
序言
如果你了解过JavaScript的设计模式一一观察者模式,一定知道dispatchEvent和addEventListener这两个方法。
Vue组件也有与之类似的一套模式,子组件用$emit()来触发事件,父组件用$on()来监昕子组件的事件。
父传子
通过prop实现通信
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title></title> <meta charset="utf-8" /> <script type="text/javascript" src="../../assets/js/vue.js"></script> </head> <body> <div id="app"> <div>{{pmsg}}</div> <child title='来自父组件的值'></child> </div> <template id="childtmpl"> <div> <div>{{msg}}-{{title}}</div> </div> </template> <script type="text/javascript"> //子组件 Vue.component('child', { props: ['title'], data: function() { return { msg: '子组件本身的数据' } }, template: '#childtmpl' }); //父组件 var vm = new Vue({ el: '#app', data: { pmsg: '父组件', } }); </script> </body> </html>
子传父
$emit()
第一个参数为自定义的事件名称第二个参数为需要传递的数据
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title></title> <meta charset="utf-8" /> <script type="text/javascript" src="../../assets/js/vue.js"></script> </head> <body> <!--父组件 --> <div id="app"> <div :style='{fontSize: fontSize + "px"}'>{{pmsg}}</div> <child @father-enlarge-text='fatherfunction($event)'></child> </div> <!-- 子组件 --> <template id="childtmpl"> <div> <button @click='sonfunction'>子传父</button> </div> </template> <script type="text/javascript"> Vue.component('child', { template: '#childtmpl' , methods: { sonfunction: function(val){ this.$emit("father-enlarge-text", 5) } } }); var vm = new Vue({ el: '#app', data: { pmsg: '父组件', fontSize: 10 }, methods: { fatherfunction: function(val){ this.fontSize += val; } } }); </script> </body> </html>
兄弟传
在Vue.2.x中,推荐使用一个空的Vue实例作为中央事件总线(bus),也就是一个中介。
这种方法巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级,而且Vue1.x和Vue2.x都适用。
提供事件中心 var hub = new Vue()
传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据)
接收数据方,通过mounted(){} 钩子中 触发hub.$on()方法名
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title></title> <meta charset="utf-8" /> <script type="text/javascript" src="../../assets/js/vue.js"></script> </head> <body> <div id="app"> <div>父组件</div> <div> <button @click='handle'>销毁事件</button> </div> <borther-a></borther-a> <borther-b></borther-b> </div> <template id="Atmpl"> <div> <div>a:{{num}}</div> <div> <button @click='handle'>点击</button> </div> </div> </template> <template id="Btmpl"> <div> <div>b:{{num}}</div> <div> <button @click='handle'>点击</button> </div> </div> </template> <script type="text/javascript"> //1、 中央事件总线 var bus = new Vue(); Vue.component('borther-a', { data: function(){ return { num: 0 } }, template: '#Atmpl', methods: { handle: function(){ //2、传递数据方,通过一个事件触发bus.$emit(方法名,传递的数据) 触发兄弟组件的事件 bus.$emit('b-event', 2); } }, mounted: function() { // 3、接收数据方,通过mounted(){}钩子中触发bus.$on(方法名 bus.$on('a-event', (val) => { this.num += val; }); } }); Vue.component('borther-b', { data: function(){ return { num: 0 } }, template: '#Btmpl', methods: { handle: function(){ //2、传递数据方,通过一个事件触发bus.$emit(方法名,传递的数据)触发兄弟组件的事件 bus.$emit('a-event', 1); } }, mounted: function() { // 3、接收数据方,通过mounted(){}钩子中触发bus.$on()方法名 bus.$on('b-event', (val) => { this.num += val; }); } }); var vm = new Vue({ el: '#app', data: {}, methods: { handle: function(){ //4、销毁事件 通过bus.$off()方法名销毁之后无法进行传递数据 bus.$off('a-event'); bus.$off('b-event'); } } }); </script> </body> </html>
当你的项目比较大,有更多的小伙伴参与开发时,也可以选择更好的状态管理解决方案vuex。
除了中央事件总线bus外,还有两种方法可以实现组件间通信:父链和子组件索引。
父链
在子组件中,使用this.$parent可以直接访问该组件的父实例或组件,父组件也可以通过this.$children访问它所有的子组件,而且可以递归向上或向下无线访问,直到根实例或最内层的组件。
尽管Vue允许这样操作,但在业务中,子组件应该尽可能地避免依赖父组件的数据,更不应该去主动修改它的数据,因为这样使得父子组件紧藕合,只看父组件,很难理解父组件的状态,因为它可能被任意组件修改,理想情况下,只有组件自己能修改它的状态。父子组件最好还是通过props和$emit来通信。
子组件索引
当子组件较多时,通过this.$children来一一遍历出我们需要的一个组件实例是比较困难的,尤其是组件动态渲染时,它们的序列是不固定的。Vue提供了子组件索引的方法,用特殊的属性ref来为子组件指定一个索引名称。
在父组件模板中,子组件标签上使用ref指定一个名称,井在父组件内通过this.$refs来访问指定名称的子组件。
$refs只有渲染完成后才填充,并且它是非响应式的。它仅仅作为一个直接访问子组件的应急方案,应当避免在模板或计算属性中使用础。