在vue项目中了解组件间通信很重要,也是最基础的面试题,能够大体总结为如下几种状况和方法:html
父 to 子
:经过动态绑定属性
的方式,子组件在props
中去接收,占为已用vue
// father.vue <child :name="name" /> data() { return { name:'Echoyya', } }
// child.vue export default { props: ['name'], mounted() { console.log(this.name) // Echoyya } }
子 to 父
:经过.emit('事件名称', 传递的参数)事件分发
的方式, 父组件当中,调用子组件标签上绑定自定义事件,其中包含一个参数
,即子组件传递过来的数据面试
emit 只能接受两个参数,其他不生效,第一个参数:事件名称,第二个: 传递的数据vuex
事件分发,不必定要经过点击事件,也可以使用钩子函数等数组
须要传递多个参数时,emit第二个参数可选择数组或是对象promise
// father.vue <child @send="getChildData" /> methods: { getChildData(data){ console.log('子组件传递过来的数据:' + data); } }
// child.vue <button @click="send">发送数据</button> export default { data() { return { year:2021 }; }, methods:{ send(){ this.$emit('send',this.year) } } };
除了平时常见的emit 事件分发的方式实现子向父传值,还有一种不太常见的方式缓存
在前面有提到过,父向子传值能够经过在标签上动态绑定属性的方式,一样也能够动态绑定方法
,在子组件props
中接收安全
在根据实际状况,在子组件中调用该方法,并传值,此时父组件执行对应的方法及参数的处理,该参数即是子向父传递的数据服务器
// father.vue <child :fn="getChildData" /> methods: { getChildData(data){ // 子组件传递过来的数据 console.log(data); // 子组件 - 2021 } }
// child.vue export default { props: ['fn'], mounted(){ this.fn('子组件 - 2021') } };
父组件中调用子组件,绑定一个ref
属性app
主动获取属性:this.$refs.ref名称.属性
主动调用方法:this.$refs.ref名称.方法()
// father.vue <child :name="name" ref="child" /> mounted() { console.log(this.$refs.child.year); // 2021 this.$refs.child.showName() // Echoyya:2021 }
// child.vue export default { data(){ return { year:2021 } }, methods:{ showName(){ console.log('Echoyya:'+ this.year); } } };
子组件中调用父组件,使用 this.$parent
主动获取属性:this.$parent.属性
主动调用方法:this.$parent.方法()
,还能够向父组件传递参数
// father.vue <div id="app"> <child /> </div> <script> import child from "./components/child"; export default { components: { child }, data() { return { name:'Echoyya', }; }, methods: { parentMethod(data){ // 能够接收子组件调用时传递的参数 console.log(data); // 2021 } } }; </script>
// child.vue export default { mounted(){ console.log(this.$parent.name); // Echoyya this.$parent.parentMethod(2021); // 2021 } };
多层级组件之间相互传值,或兄弟组件之间传值,一般使用EventBus
,实际上就是 vm,即 Vue 的实例,经过发布订阅者模式,实现任意两组件之间的通信,父子亦可。
首先建立EventBus文件,导出vm 实例
在须要通信的两组件中分别引入该文件
经过发布订阅 .$emit
和 .$on
实现传值 ,操做的事件名需相同。
// EventBus.js import Vue from 'vue' var vm = new Vue() export default vm
// App.vue <template> <div id="app"> <bus-one /> <bus-two /> </div> </template> <script> import busOne from "./components/bus1"; import busTwo from "./components/bus2"; export default { components: { busOne, busTwo } } </script>
// bus1.vue <template> <div> 组件 1 发布 <button @click="sendNum">通信发送数据</button> </div> </template> <script> import eb from "../EventBus"; export default { methods:{ sendNum(){ eb.$emit('sendData',111) } } }; </script>
// bus2.vue <template> <div> 组件 2 订阅 </div> </template> <script> import eb from "../EventBus"; export default { created(){ eb.$on('sendData',function(data){ console.log(data); // 111 }) } }; </script>
手动模拟一个 EventBus,实现发布订阅的效果,建立一个 myEventBus 文件
建立一个对象,分别设置发布、订阅的事件,以及事件存储的对象,经过事件订阅将事件存储至事件对象中,
事件发布时,自动调用事件对象中相同 key 的全部事件
myEventBus.html
<script> var eventbus = { fnObj:{ //abc:[fn1,fn2], 能够屡次订阅同一个事件,所以每一个 key 对应一个数组 }, $on: function (id, fn) { if(!this.fnObj[id]){ this.fnObj[id] = [fn] }else{ this.fnObj[id].push(fn) } }, $emit: function (id, data) { if(this.fnObj[id]){ var fnArr = this.fnObj[id] // 当事件触发时,循环执行每一个 key 对应的 全部function for(var i = 0;i<fnArr.length;i++){ var fn = fnArr[i] fn(data) } }else{ console.log('异常,函数不存在') } } } eventbus.$on('abc',function(data){ console.log(1) }) eventbus.$on('abc',function(data){ console.log(2) }) eventbus.$on('abc',function(data){ console.log(3) }) eventbus.$on('abc',function(data){ console.log(4) }) eventbus.$emit('abc','123') </script>
Vuex 应用程序开发的 状态管理模式,集中式管理应用全部组件的状态,进行组件通信
安装插件,引入 Vuex,建立 store 实例,配置到 Vue 实例中
为防止状态,方法声明等过度重复和冗余,Vuex 提供了一些辅助函数,mapState
,mapGetters
,mapMutations
,mapActions
,可以使用扩展运算符展开,其中:
mapState
,mapGetters
声明在 computed 中
mapMutations
,mapActions
声明在 methods 中
建立 store 实例,核心属性:
State
:一个对象,包含所有的应用状态。做为惟一数据源
存在。store实例会注入到根组件下的全部子组件
Getters
:state 中数据派生出一些状态相似计算属性,返回值会根据它的依赖被缓存起来,只有依赖发生改变时才会被从新计算,state 做为其第一个参数.
Mutations
:更改 store 中的状态,store.commit('事件名',payload参数可选)
触发,只作同步操做,
Actions
:相似于Mutations, 提交的是 mutation,而不是直接变动状态,能够包含任意异步操做。this.$store.dispatch
分发,异步执行完毕,返回封装的promise 对象。接受一个与 store 实例具备相同方法和属性的对象
Modules
:为防止 store 对象过于复杂,可将其分割成模块,每一个模块都有本身的State,Getters,Mutations,Actions,甚至能够嵌套子模块
// main.js import Vue from 'vue' import App from './App.vue' import Vuex from 'vuex' import numModule from './numModule.js' Vue.use(Vuex) var store = new Vuex.Store({ modules:{ numModule } }) new Vue({ store, render: h => h(App) }).$mount('#app')
// numModule.js export default { state: { num: 1 }, getters: { getNum(state) { // 能够返回一些派生状态 return state.num } }, mutations: { // 同步修改状态的函数 changeNum(state, payload) { state.num += payload.num }, // 异步时,开发工具会遗漏快照,不便于调试(不推荐) testAsync(state, data) { // 模拟服务器获取数据 setTimeout(function () { state.num += data }, 0) } }, actions: { // 异步获取数据,提交给 mutation 进行修改 incNumByService(store) { setTimeout(function () { var serviceData = 0.1 store.commit('changeNum', { num: serviceData }) }, 0) } } }
// App.vue <template> <div id="app"> 第一组件 <p>state:{{$store.state.numModule.num}}</p> <!-- getters:方式为只读,数据更安全 --> <p>getters:{{$store.getters.getNum}}</p> <p>MapGetters:{{gn}}</p> <button @click="changeNum">mutation同步</button> <button @click="testAsync">mutation异步</button> <button @click="testActions">action异步</button> <Son /> </div> </template> <script> import { mapGetters } from "vuex"; import Son from "./components/son"; export default { components: { Son }, methods: { changeNum() { this.$store.commit({ type: "changeNum", num: 1 }); //效果同上 // this.$store.commit("changeNum",{ // num: 1 // }); }, testAsync() { this.$store.commit("testAsync", 10); }, testActions() { // dispatch 异步执行完毕,返回 封装的promise 对象 this.$store.dispatch("incNumByService").then(res => { alert("dispatch执行完毕"); }); } }, computed: { //使用对象展开运算符将 getter 混入 computed 对象中 //若是想将一个 getter 属性另取一个名字,使用对象形式: ...mapGetters({ gn: "getNum" // 把 `this.gn` 映射为 `this.$store.getters.getNum` }) } }; </script>
// Son.vue <template> <div class="hello"> 第二组件 <button>state:{{$store.state.numModule.num}}</button> <!-- getters:方式为只读,数据更安全 --> <button>getters:{{$store.getters.getNum}}</button> <button @click="clickMe">MapGetters:{{gn}}</button> </div> </template> <script> import { mapGetters } from "vuex"; export default { computed: { ...mapGetters({ gn: "getNum" }) } }; </script>