1、props:父组件向子组件传递数据;在父组件中调用子组件的时候通过v-bind将需要传递的数据绑定给一个属性,然后再在子组件的props里接收这个属性,这样就完成了父向子传递信息;注意props是单向数据流,不建议子组件直接修改,如果要修改最好还是子组件通过$emit()来触发父组件的一个事件来让父组件修改这个值,(注意如果直接修改props里的基本数据类型的值浏览器是会警告的,但是修改的数据是Object或者Array的时候是不会发出警告的,并且可以做到响应式,因为修改对象时并没有直接修改这个引用,而是修改的引用指向的地址的值,所以不会警告并能做到响应式)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
//父组件 <sontemp :tosondata='parentdata'></sontemp> /*1.在父组件中绑定要传递的数据给子组件模板的一个属性*/ import sontemp from './sontemp.vue' let mysolt = new Vue({ el:'#myvue', components:{ sontemp }, data:{ parentdata:'我是父组件的数据' } }) //子组件 <template id='sontemp'> <span>{{parentdata}}</span> /*3.在子组件中使用父组件传过来的props属性值*/ </template> let mytemp = { template:'#sontemp', props:['parentdata'],//核心:2.接收父组件传递过来的数据;他跟data一样都是存储数据的 但他的数据是只读的、单向数据流 }; |
2、$emit():子组件向父组件传递数据;子组件通过提交有数据参数的事件,父组件检测事件触发一个接收数据的方法的方式进行子组件向父组件的传值,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
//父组件 <sontemp @getsondata='parentevent'></sontemp> /*3.在父组件中监听子组件提交的事件,监听到了就执行自己的方法*/ import sontemp from './sontemp.vue' let mysolt = new Vue({ el:'#myvue', components:{ sontemp }, methods:{ parentevent(sondata){ /*4.在监听到子组件候触发该方法,子组件提交的数据通过参数传递过来*/ console.log(sondata) } } }) //子组件 <template id='sontemp'> <button @click='sonevent'>点击子组件向父组件传递信息</*1.定义一个事件来触发子组件向父组件提交方法*/ </template> let mytemp = { template:'#sontemp', data(){ return { sondata:'我是子组件的数据' } }, methods:{ sonevent(){ this.$emit('getsondata',this.sondata) /*核心:2.向父级提交事件并传递想要传递的数据*/ } } }; |
在当前组件下我们可以通过$parent来获取父级组件实例(如果有);同样的也可以通过$children来获取子组件的实例(如果有),有了实例就可以通过属性或方法访问上边的数据了;注意他们俩只是作为应急方案使用,最好还是使用props和$emit()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
//挂载vue的实例,父组件 <div id="myparandchi"> <mysontemp></mysontemp> </div> //创建子组件 <template id="mysontemp"> <div> <h2 style="color: red">我是通过$children来获取数据的子组件</h2> </div> </template> /////////////////////////////////////////////////////////////////////////////// //定义子组件 let mysontemp = { template:'#mysontemp', data(){ return { mysontempmeg:'我是子组件的信息' } }, created(){ console.log(`******我是子组件我通过$parent拿到了父组件的数据:${this.$parent.parenttempmeg}******`) //核心:使用$parent获取到父组件实例 } } //创建vue实例,父组件 let myparandchi = new Vue({ el:'#myparandchi', components:{ mysontemp }, data:{ parenttempmeg:'我是父级组件信息' }, mounted(){ console.log(`******我是父组件我通过$children拿到了子组件的数据:${this.$children[0].mysontempmeg}******`) //核心:使用$children获取子组件实例 } }) |
与$parent和$children一样都是拿到想要获得数据的实例对象,再获取数据;ref如果在普通Dom上引用指向的就是这个元素,当ref在一个vue组件上,引用就指向这个组件实例;在VUE中可以使用ref来获取当前的真是Dom元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
//挂载vue的实例,父组件 <div id="getdom" ref='domdiv'> <myson @partfun='show' ref='domson'></myson> </div> //创建子组件 <template id="domtepm"> <div> <input type="button" value="点我" @click='sonclick'> <span ref='sonspan'>我是获取元素的子组件</span> </div> </template> /////////////////////////////////////////////////////////////////////////////// //定义子组件 let mydomtemp={ template:'#domtepm', data(){ return { sonmeg:'我是子组件的一个数据' } }, props:['partfun'], methods:{ sonclick(){ this.$emit('partfun'); } } }; //创建vue实例 let domvm=new Vue({ el:"#getdom", methods:{ show(){ console.log(this.$refs.domdiv); //返回的是一个dom:<div id='getdom'>...</div> console.log(this.$refs.domson.sonmeg); //核心:返回的是子组件的数据:'我是子组件的一个数据' } }, components:{ myson:mydomtemp } }) |
在需要向隔代组件传递信息的时候我们除了能使用全局通信外就是使用props一级一级的往下传,但是这样太麻烦又不利于后期维护,于是VUE又提供了一对provide/inject来将数据传递给隔代组件;大概就是在父级组件将需要传递的数据放在provide里(类似data,父组件访问自己的provide通过this._provided),然后在子级或者隔代组件中通过inject来注入这个数据(类似props),就可以在自己或者隔代组件里使用这个数据了;注意这里边的数据不是响应式的;一般会用provide/inject来开发组件减少组件的耦合度(有点高深,以后再了解)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
//挂载vue的实例,父组件 <div id="rootvue"> <son></son> </div> //创建孙子组件 <template id="grendson"> <div> <h2 style="color: deeppink">我是跨级访问数据的provide和inject我访问到的数据是:{{rootdata}}</h2> //使用传过来的数据 </div> </template> //创建子组件 <template id="son"> <grendson></grendson> //使用孙子组件 </template> /////////////////////////////////////////////////////////////////////////////// //定义孙子组件 let grendson = { template:'#grendson' ,inject:['rootdata'] //核心:在孙子组件里使用inject将要传的数据注入进去 ,created(){ console.log(`======我是provide跨级访问的数据:${this.rootdata}======`) } } //定义子组件 let son = { template:'#son', components:{ grendson } } //创建实例 let rootvue = new Vue({ el:'#rootvue' ,components:{ son } ,data:{ add:0, meg:'我是最顶端数据' } ,provide(){//核心:在provide里放入要传送的数据 return { rootdata:this.meg } } }) |
关于数据响应官方的说法是: provide
和 inject
绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。 但是如果我们使用provide/inject传递信息的话往往还是需要数据响应的,那么该如何是好呢,这里介绍一种父级数据改变子级或者隔代inject数据也改变的方法;在我们父级provide数据中我们返回一个包含实例this的数据函数,这样我们在子级或者隔代组件中调用函数的时候都是调用当前实例下的这个属性,父组件的这一属性一发生改变,隔代调用的这个数据也会发生改变,这就做到了响应式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
//挂载vue的实例,父组件 <div id="rootvue"> <son></son> </div> //创建子级组件 <template id="son"> <grendson></grendson> //引用孙子组件 </template> //创建孙子组件 <template id="grendson"> <div> <h2 style="color: deeppink">我是跨级访问数据的provide和inject我访问到的数据是:{{rootdata()}}</h2> //展示拿到数据 </div> </template> /////////////////////////////////////////////////////////////////////////////// //定义孙子组件 let grendson = { template:'#grendson', inject:['rootdata'], computed:{ getrootdata(){ //利用计算属性将真正的数据存放在getrootdata里 return this.rootdata() } }, watch:{ 'getrootdata':val=>console.log(val) //观察getrootdata的改变已发生改变就做出相应动作 } } //定义子组件 let son = { template:'#son', components:{ grendson } } //创建vue实例 let rootvue = new Vue({ el:'#rootvue' ,components:{ son } ,data:{ meg:'我是最顶端数据' } ,provide(){ return { rootdata:()=>this.meg //核心:返回一个包含当前实例属性数据的方法 } }, mounted(){ setTimeout(()=>{ this.meg = '哇哈哈哈哈哈,我变了我能响应式'; //父组件改变数据 console.log(`如果要访问provied的话需要这样写:${this._provided}`); //注意:直接使用this.provied是访问不到provied的需要通过this._provided才能访问 },2000) } }) |
$attrs: 官方介绍是包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class
和 style
除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class
和 style
除外),并且可以通过 v-bind="$attrs"
传入内部组件——在创建高级别的组件时非常有用 ;也就是说通过$attrs可以获取除了class,style属性以及传递给prop的属性外的实例的所有属性(注意需要通过v-bind绑定在属性上,组件才能获取到);
$listeners:官方介绍是 包含了父作用域中的 (不含 .native
修饰器的) v-on
事件监听器。它可以通过 v-on="$listeners"
传入内部组件——在创建更高层次的组件时非常有用 ;也就是说通过$listeners可以获取父组件的除了.native外的所有绑定的事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
//挂载vue的实例,父组件 <div id="attrvue"> <attrtemp :name='name' :job='job' title='我是标题' @callname='callname' @dojob.native='dojob'></attrtemp> /*核心:注意:$attrs获取的是通过v-bind绑定过的非props属性以及除class和style外的其他属性;$listeners获取到的是通过v-on绑定的没有.native的所有事件;所以需要传递给子组件或者隔代组件什么数据就要在这里写清除*/ </div> //创建子组件 <template id="attrtemp"> <div> <span style="color:orange">我是$attrs获取的name数据:{{$attrs}}</span> <gaptemp v-bind='$attrs' v-on='$listeners'></gaptemp> /*核心:注意:子组件通过v-bind='$attrs'和v-on='$listeners'传递给隔代组件*/ </div> </template> //创建隔代组件 <template id="gaptemp"> <span style="color:#ccc">我是隔代组件通过$attrs获取的name数据:{{$attrs}}</span> </template> /////////////////////////////////////////////////////////////////////////////// //定义隔代组件 let gaptemp = { template:'#gaptemp' ,created(){ console.log(this.$attrs,this.$listeners) //使用:隔代组件通过$attrs获取通过v-bind绑定的没有传给props的属性和除class、style外的属性;通过$listeners获取v-on绑定的没有.nativ修饰符的事件 } } //定义子组件 let attrtemp = { template:'#attrtemp' ,components:{ gaptemp } ,props:{ job:String ,default:'noJob' } ,mounted(){ console.log(this.$listeners.callname()) //使用:子组件通过$listeners.callname触发通过v-on绑定的事件callname事件;注意:$attrs和$listeners获取的数据是可以响应式的 } } //创建vue实例 let attrvue = new Vue({ el:'#attrvue' ,components:{ attrtemp } ,data:{ name:'lele' ,age:20 ,sex:'男' ,job:'coder' } ,methods:{ callname(){ setTimeout(()=>{ console.log(666); this.name = 'wenli' },3000) }, callage(){ console.log(this.age) }, dojob(){ console.log(this.job) } } }) |
EventBus :事件总线;在组件间作为沟通桥梁的作用,就像是所有组件共用相同的事件中心,可以向该事件中心注册发送事件也可以接收事件,所以组件间都可以互相通知其他组件数据的改变;但是使用这种方式,这个组件一个$emit那个组件一个$on,数据多了就容易造成混乱,很难进行维护,所以在大型的需要共享数据较多的项目中还是使用VUEX比较好。
事件总线其实就是一个没有DOM只有方法的组件;我们可以在全局注册这个组件,然后就可以在各个组件使用它了;注册完之后的全局事件总线有三个事件方法:$emit()提交修改数据事件,$on()获取数据事件,$off()移除事件监听;注意$on监听事件要定义在$emit提交事件之前
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
/*在main.js中注册全局事件总线*/ Vue.prototype.$bus = new Vue()//注册全局事件总线 /////////////////////////////////////////////////////////////////////////////// /*在组件中使用事件总线*/ //挂载vue的实例,父组件 <div id="eventbusvue"> <bussontemp></bussontemp> <bussonbrothertemp></bussonbrothertemp> </div> //创建子组件 <template id="bussontemp"> <div> <span>我是事件总线传递信息的</span> </div> </template> //创建子组件兄弟组件的子组件 <template id="bussonbrothertemp"> <div> <span>我是子组件的兄弟组件</span> <brothersontemp></brothersontemp> </div> </template> //创建子组件的兄弟组件 <template id="brothersontemp"> <div> <span>我是兄弟组件的子组件</span> </div> </template> /////////////////////////////////////////////////////////////////////////////// //定义子组件 let bussontemp = { template:'#bussontemp', data(){ return { name:'我是兄弟组件1的' } }, created(){ this.$bus.$on('changename',message=>{ console.log(message) })//监听实例上的changename事件,并获取数据,相当于父向子传递信息 }, mounted(){ this.$bus.$emit('changeborther',this.name)//兄弟组件向eventBus提交一个事件并带上数据 } } //定义子组件的兄弟组件的子组件 let brothersontemp = { template:'#brothersontemp', data(){ return { name:"我是好兄弟组件的子组件" } }, created(){ this.$bus.$on('changename',message=>{ console.log(message) })//兄弟组件的子组件监听实例提交的事件,相当于向上跨级接收信息 }, mounted(){ this.$bus.$emit('changeborthersonname',this.name)//兄弟组件的子组件向eventBus提交事件并带上数据 } } //定义子组件的兄弟组件 let bussonbrothertemp = { template:'#bussonbrothertemp', components:{ brothersontemp }, data(){ return { name:'我是兄弟组件2的name值' } }, created(){ this.$bus.$on('changeborther',message=>{ console.log(message) });//子组件的兄弟组件监听兄弟组件提交的事件,相当于兄弟组件传递信息 } } //创建vue实例 let eventbusvue = new Vue({ el:'#eventbusvue', data:{ name:'我是vue实例的name值' }, components:{ bussontemp, bussonbrothertemp }, created(){//创建的时候就添加事件的监听 this.$bus.$on('changeborther',message=>{ console.log(message) });//监听子组件中提交的事件,相当于子向父传递信息 this.$bus.$on('changeborthersonname',message=>{ console.log(message) });//监听子组件的子组件提交的事件,相当与向下跨级接收信息 }, mounted(){//实例挂载之后提交事件 this.$bus.$emit('changename',this.name);//实例中向eventBus提交事件并带上数据 }, beforeDestroy(){//实例销毁前移除事件监听 this.$bus.$off('changeborther');//不需要的时候删除监听事件 this.$bus.$off('changeborthersonname'); } }) |
Vuex采用的是集中式存储,管理应用的所有组件的状态,在相应的规则下可以保证状态以一种可预测的方式发生变化 ;它实现了一个单向数据流,在全局拥有一个State存放数据,当组件要更改State中的数据时,必须通过commit()提交到Mutation进行修改,Mutation同时提供了订阅者模式供外部插件调用获取State数据的更新。所有异步操作需要通过dispath()提交到Action再由Action通过commit()提交到Mutation里进行修改;Vuex一般用于较大的较复杂的项目,可以很好的进行状态追踪和维护 ;Vuex有五大核心属性 State(存储数据)、getter(筛选处理数据)、Mutation(处理改变数据的事件,只能处理同步事件)、Action(处理异步改变数据的事件)、Moulde(类似命名空间)。了解详情请移步
通过缓存的方式来进行组件间的数据通信是比较简单的,就是通过 window.localStorage.setItem(key,value)
设置数据名和值,在需要的地方再通过 window.localStorage.getItem(key)
来获取需要的数据。但是需要缓存的数据和状态多了就容易造成混乱不易维护,而且缓存多了,对用户体验来讲并不是很友好
$dispatch
和 $broadcast
是一对情侣 属性,在 Vue 1.0
中主要用来实现基于组件树结构的事件流通信 —— 通过向上或向下以冒泡的形式传递事件流,以实现嵌套父子组件的通信。但是由于其显功能缺陷,在 Vue 2.0
中就被移除了,但是有很多vue的框架如 element-ui、iview 都对其进行了封装。如果想要了解可以自行搜索,这里提供一个站外链接可供参考