组件的定义:
组件(Component)是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素, Vue.js 的编译器为它添加特殊功能。vuejs最大的特点就是采用组件来组合成页面,他将任何类型的应用的界面都抽象为一棵组件树。
说到底,组件就是我们封装的自定义的html元素,vue会根据模板编译成浏览器能够识别的html标签元素。所以vue的组件也跟我们标准的元素一样,在层级上,有父子组件,兄弟(平行)组件之分。不同层级的组件可以相互通信。下面我们就看不同组件之间的通信方式
1、子组件与父组件通信
当子组件想访问父组件的值的时候,我们可以通过子组件的props属性,将父组件的值传递给子组件
DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta charset="utf-8"> <!-- Always force latest IE rendering engine or request Chrome Frame --> <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible"> <script type="text/javascript" src="https://unpkg.com/vue@2.2.1/dist/vue.min.js"></script> <title>Cart</title> </head> <body class="checkout"> <div id="container"> <p>父组件</p> <p>父组件obj.a的值是:{{obj.a}}</p> <p>----------</p> <my-component :obj="obj"></my-component> </div> </body> <script type="text/javascript"> //定义子组件 //父组件给子组件传值 var Child = { template: '<div><span>子组件得到父组件obj.a的值是:{{obj.a}}</span></div>', props : ['obj'] } var vm = new Vue({ el: "#container", data: { obj : { a : 1, b : 2 } }, components: { 'my-component': Child } }); </script> </html>
运行结果:
2、父组件与子组件通信
当子组件想修改父组件的值的时候,我们同样可以通过props属性来修改,但是注意的是,在这里父组件传递给子组件的数据对象必须是对象的引用,而不能是字面量
使用对象的引用来传递数据
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta charset="utf-8"> <!-- Always force latest IE rendering engine or request Chrome Frame --> <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible"> <script type="text/javascript" src="https://unpkg.com/vue@2.2.1/dist/vue.min.js"></script> <title>Cart</title> </head> <body class="checkout"> <div id="container"> <p>父组件</p> <p>父组件obj.a的值是:{{obj.a}}</p> <p>----------</p> <my-component :obj="obj"></my-component> </div> </body> <script type="text/javascript"> //父组件给子组件传值,通过object var Child = { template: '<div><p>子组件A</p><span>子组件得到父组件obj.a的值是:{{obj.a}}</span><input type="button" @click="changeValue()" value="改变obj.a的值为3"></input></div>', props : ['obj'], methods : { changeValue: function(){ this.obj.a = '3'; } } } var vm = new Vue({ el: "#container", data: { obj : { a : 1, b : 2 } }, components: { 'my-component': Child } }); </script> </html>
运行结果
这种方法是通过利用对象的引用指向同一个存储区域的方式来实现的。需要注意的是,这种方式虽然能够实现子组件修改父组件,但是我们在开发的过程中最好不要采用这种方式。因为我们总是希望的组件系统相对独立,这有利于组件之间的解耦,随意地在子组件这样的修改父组件的值,会破坏其独立性,导致项目难易维护。
官网推荐的方法是在子组件中通过v-on绑定自定义事件来实现
每个 Vue 实例都实现了事件接口,即:
使用 $on(eventName) 监听事件
使用 $emit(eventName) 触发事件
这个的运行跟我们常用的dom原生的事件机制是一样的。父组件注册好监听事件,当子组件需要对父组件进行操作的时候,调用触发函数,触发事件。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta charset="utf-8"> <!-- Always force latest IE rendering engine or request Chrome Frame --> <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible"> <script type="text/javascript" src="https://unpkg.com/vue@2.2.1/dist/vue.min.js"></script> <title>Cart</title> </head> <body class="checkout"> <div id="container"> <p>父组件</p> <p>父组件obj.a的值是:{{obj.a}}</p> <p>----------</p> <my-component :obj="obj" v-on:change="fatherChange"></my-component> </div> </body> <script type="text/javascript"> //通过vm实例的on,emit方法 var Child = { template: '<div><p>子组件A</p><span>子组件得到父组件obj.a的值是:{{obj.a}}</span></br><input type="button" @click="changeValue()" value="改变obj.a的值"></input></div>', props : ['obj'], methods : { changeValue: function(){ console.log('aaa'); this.$emit('change',3) } } } var vm = new Vue({ el: "#container", data: { obj : { a : 1, b : 2 } }, components: { 'my-component': Child }, methods: { fatherChange: function(data){ this.obj.a = data; } } }); </script> </html>
这样做的好处是,我们的事件监听是在我们的父组件上面的,相对父组件做任何修改的逻辑都在父组件上实现,子组件只负责通知,这样就最大范围的解耦了两个组件之间的联系,增加了子组件的可复用性。
3、非父子组件的相互通信
在简单的场景下,使用一个空的 Vue 实例作为中央事件总线
这个空的vue实例作为一个媒介,来处理双方的通信
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta charset="utf-8"> <!-- Always force latest IE rendering engine or request Chrome Frame --> <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible"> <script type="text/javascript" src="https://unpkg.com/vue@2.2.1/dist/vue.min.js"></script> <title>Cart</title> </head> <body class="checkout"> <div id="container"> <p>父组件</p> <p>----------</p> <child1></child1> <child2></child2> </div> </body> <script type="text/javascript"> //兄弟组件间的传递 var bus = new Vue(); var Child1 = { template: '<div><p>子组件A的值为{{a}}</p><button @click="changeNum">改变值</button></div>', data : function(){ return { a : 1 } }, mounted : function(){ var _that = this; bus.$emit('calculateB',_that.a); }, watch:{ a :function(){ var _that = this; bus.$emit('calculateB',_that.a); } }, methods: { changeNum : function(){ ++ this.a; } } } var Child2 = { template: '<div><p>子组件B的值总是比组件A大3为{{a}}</p></div>', data : function(){ return { a : 0 } }, created : function(){ var _that = this; bus.$on('calculateB',function(data){ _that.a = data + 3; }) } } var vm = new Vue({ el: "#container", components: { Child1 : Child1, Child2 : Child2 }, methods: { } }); </script> </html>
运行结果
以上的这些通讯方式可以实现我们任何的应用开发。但是有一个问题,当我们的项目越来越大,组件层级关系越来越复杂的时候,比如说我们的子组件又有子组件需要用到父组件的值,那么我们必须将父组件的值通过props一层一层传递下去,当层级只有2、3层的时候还好,如果我们层级有5层以上,这样一层一层传递导致我们的代码难以维护,同时出错的概率很大。为了解决这个问题,vue2.0提供了一个状态管理的方案vuex,具体可查看状态管理(http://cn.vuejs.org/v2/guide/state-management.html)