在vue项目中了解组件间通讯很重要,也是最基础的面试题,可以大致总结为以下几种情况和方法:

父 to 子 :通过动态绑定属性的方式,子组件在props中去接收,占为已用

  1. // father.vue
  2. <child :name="name" />
  3. data() {
  4. return {
  5. name:'Echoyya',
  6. }
  7. }
  1. // child.vue
  2. export default {
  3. props: ['name'],
  4. mounted() {
  5. console.log(this.name) // Echoyya
  6. }
  7. }

子 to 父 :通过.emit('事件名称', 传递的参数)事件分发的方式, 父组件当中,调用子组件标签上绑定自定义事件,其中包含一个参数,即子组件传递过来的数据

  1. emit 只能接受两个参数,其余不生效,第一个参数:事件名称,第二个: 传递的数据

  2. 事件分发,不一定要通过点击事件,也可使用钩子函数等

  3. 需要传递多个参数时,emit第二个参数可选择数组或是对象

  1. // father.vue
  2. <child @send="getChildData" />
  3. methods: {
  4. getChildData(data){
  5. console.log('子组件传递过来的数据:' + data);
  6. }
  7. }
  1. // child.vue
  2. <button @click="send">发送数据</button>
  3. export default {
  4. data() {
  5. return {
  6. year:2021
  7. };
  8. },
  9. methods:{
  10. send(){
  11. this.$emit('send',this.year)
  12. }
  13. }
  14. };
  1. 除了平时常见的emit 事件分发的方式实现子向父传值,还有一种不太常见的方式

  2. 在前面有提到过,父向子传值可以通过在标签上动态绑定属性的方式,同样也可以动态绑定方法,在子组件props中接收

  3. 在根据实际情况,在子组件中调用该方法,并传值,此时父组件执行对应的方法及参数的处理,该参数便是子向父传递的数据

  1. // father.vue
  2. <child :fn="getChildData" />
  3. methods: {
  4. getChildData(data){ // 子组件传递过来的数据
  5. console.log(data); // 子组件 - 2021
  6. }
  7. }
  1. // child.vue
  2. export default {
  3. props: ['fn'],
  4. mounted(){
  5. this.fn('子组件 - 2021')
  6. }
  7. };
  1. 父组件中调用子组件,绑定一个ref属性

  2. 主动获取属性:this.$refs.ref名称.属性

  3. 主动调用方法:this.$refs.ref名称.方法()

  1. // father.vue
  2. <child :name="name" ref="child" />
  3. mounted() {
  4. console.log(this.$refs.child.year); // 2021
  5. this.$refs.child.showName() // Echoyya:2021
  6. }
  1. // child.vue
  2. export default {
  3. data(){
  4. return {
  5. year:2021
  6. }
  7. },
  8. methods:{
  9. showName(){
  10. console.log('Echoyya:'+ this.year);
  11. }
  12. }
  13. };
  1. 子组件中调用父组件,使用 this.$parent

  2. 主动获取属性:this.$parent.属性

  3. 主动调用方法:this.$parent.方法(),还可以向父组件传递参数

  1. // father.vue
  2. <div id="app">
  3. <child />
  4. </div>
  5. <script>
  6. import child from "./components/child";
  7. export default {
  8. components: {
  9. child
  10. },
  11. data() {
  12. return {
  13. name:'Echoyya',
  14. };
  15. },
  16. methods: {
  17. parentMethod(data){
  18. // 可以接收子组件调用时传递的参数
  19. console.log(data); // 2021
  20. }
  21. }
  22. };
  23. </script>
  1. // child.vue
  2. export default {
  3. mounted(){
  4. console.log(this.$parent.name); // Echoyya
  5. this.$parent.parentMethod(2021); // 2021
  6. }
  7. };

多层级组件之间相互传值,或兄弟组件之间传值,通常使用EventBus,实际上就是 vm,即 Vue 的实例,通过发布订阅者模式,实现任意两组件之间的通讯,父子亦可。

  1. 首先创建EventBus文件,导出vm 实例

  2. 在需要通讯的两组件中分别引入该文件

  3. 通过发布订阅 .$emit.$on 实现传值 ,操作的事件名需相同。

  1. // EventBus.js
  2. import Vue from 'vue'
  3. var vm = new Vue()
  4. export default vm
  1. // App.vue
  2. <template>
  3. <div id="app">
  4. <bus-one />
  5. <bus-two />
  6. </div>
  7. </template>
  8. <script>
  9. import busOne from "./components/bus1";
  10. import busTwo from "./components/bus2";
  11. export default {
  12. components: {
  13. busOne,
  14. busTwo
  15. }
  16. }
  17. </script>
  1. // bus1.vue
  2. <template>
  3. <div>
  4. 组件 1 发布
  5. <button @click="sendNum">通讯发送数据</button>
  6. </div>
  7. </template>
  8. <script>
  9. import eb from "../EventBus";
  10. export default {
  11. methods:{
  12. sendNum(){
  13. eb.$emit('sendData',111)
  14. }
  15. }
  16. };
  17. </script>
  1. // bus2.vue
  2. <template>
  3. <div>
  4. 组件 2 订阅
  5. </div>
  6. </template>
  7. <script>
  8. import eb from "../EventBus";
  9. export default {
  10. created(){
  11. eb.$on('sendData',function(data){
  12. console.log(data); // 111
  13. })
  14. }
  15. };
  16. </script>
  1. 手动模拟一个 EventBus,实现发布订阅的效果,创建一个 myEventBus 文件

  2. 创建一个对象,分别设置发布、订阅的事件,以及事件存储的对象,通过事件订阅将事件存储至事件对象中,

  3. 事件发布时,自动调用事件对象中相同 key 的所有事件

myEventBus.html

  1. <script>
  2. var eventbus = {
  3. fnObj:{
  4. //abc:[fn1,fn2], 可以多次订阅同一个事件,因此每个 key 对应一个数组
  5. },
  6. $on: function (id, fn) {
  7. if(!this.fnObj[id]){
  8. this.fnObj[id] = [fn]
  9. }else{
  10. this.fnObj[id].push(fn)
  11. }
  12. },
  13. $emit: function (id, data) {
  14. if(this.fnObj[id]){
  15. var fnArr = this.fnObj[id]
  16. // 当事件触发时,循环执行每个 key 对应的 所有function
  17. for(var i = 0;i<fnArr.length;i++){
  18. var fn = fnArr[i]
  19. fn(data)
  20. }
  21. }else{
  22. console.log('异常,函数不存在')
  23. }
  24. }
  25. }
  26. eventbus.$on('abc',function(data){
  27. console.log(1)
  28. })
  29. eventbus.$on('abc',function(data){
  30. console.log(2)
  31. })
  32. eventbus.$on('abc',function(data){
  33. console.log(3)
  34. })
  35. eventbus.$on('abc',function(data){
  36. console.log(4)
  37. })
  38. eventbus.$emit('abc','123')
  39. </script>

  1. Vuex 应用程序开发的 状态管理模式,集中式管理应用所有组件的状态,进行组件通讯

  2. 安装插件,引入 Vuex,创建 store 实例,配置到 Vue 实例中

  3. 为防止状态,方法声明等过分重复和冗余,Vuex 提供了一些辅助函数,mapState,mapGetters,mapMutations,mapActions,可使用扩展运算符展开,其中:

    • mapState,mapGetters声明在 computed 中

    • mapMutations,mapActions 声明在 methods 中

  4. 创建 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,甚至可以嵌套子模块

  1. // main.js
  2. import Vue from 'vue'
  3. import App from './App.vue'
  4. import Vuex from 'vuex'
  5. import numModule from './numModule.js'
  6. Vue.use(Vuex)
  7. var store = new Vuex.Store({
  8. modules:{
  9. numModule
  10. }
  11. })
  12. new Vue({
  13. store,
  14. render: h => h(App)
  15. }).$mount('#app')
  1. // numModule.js
  2. export default {
  3. state: {
  4. num: 1
  5. },
  6. getters: {
  7. getNum(state) { // 可以返回一些派生状态
  8. return state.num
  9. }
  10. },
  11. mutations: {
  12. // 同步修改状态的函数
  13. changeNum(state, payload) {
  14. state.num += payload.num
  15. },
  16. // 异步时,开发工具会遗漏快照,不便于调试(不推荐)
  17. testAsync(state, data) {
  18. // 模拟服务器获取数据
  19. setTimeout(function () {
  20. state.num += data
  21. }, 0)
  22. }
  23. },
  24. actions: {
  25. // 异步获取数据,提交给 mutation 进行修改
  26. incNumByService(store) {
  27. setTimeout(function () {
  28. var serviceData = 0.1
  29. store.commit('changeNum', {
  30. num: serviceData
  31. })
  32. }, 0)
  33. }
  34. }
  35. }
  1. // App.vue
  2. <template>
  3. <div id="app">
  4. 第一组件
  5. <p>state:{{$store.state.numModule.num}}</p>
  6. <!-- getters:方式为只读,数据更安全 -->
  7. <p>getters:{{$store.getters.getNum}}</p>
  8. <p>MapGetters:{{gn}}</p>
  9. <button @click="changeNum">mutation同步</button>
  10. <button @click="testAsync">mutation异步</button>
  11. <button @click="testActions">action异步</button>
  12. <Son />
  13. </div>
  14. </template>
  15. <script>
  16. import { mapGetters } from "vuex";
  17. import Son from "./components/son";
  18. export default {
  19. components: {
  20. Son
  21. },
  22. methods: {
  23. changeNum() {
  24. this.$store.commit({
  25. type: "changeNum",
  26. num: 1
  27. });
  28. //效果同上
  29. // this.$store.commit("changeNum",{
  30. // num: 1
  31. // });
  32. },
  33. testAsync() {
  34. this.$store.commit("testAsync", 10);
  35. },
  36. testActions() {
  37. // dispatch 异步执行完毕,返回 封装的promise 对象
  38. this.$store.dispatch("incNumByService").then(res => {
  39. alert("dispatch执行完毕");
  40. });
  41. }
  42. },
  43. computed: {
  44. //使用对象展开运算符将 getter 混入 computed 对象中
  45. //如果想将一个 getter 属性另取一个名字,使用对象形式:
  46. ...mapGetters({
  47. gn: "getNum" // 把 `this.gn` 映射为 `this.$store.getters.getNum`
  48. })
  49. }
  50. };
  51. </script>
  1. // Son.vue
  2. <template>
  3. <div class="hello">
  4. 第二组件
  5. <button>state:{{$store.state.numModule.num}}</button>
  6. <!-- getters:方式为只读,数据更安全 -->
  7. <button>getters:{{$store.getters.getNum}}</button>
  8. <button @click="clickMe">MapGetters:{{gn}}</button>
  9. </div>
  10. </template>
  11. <script>
  12. import { mapGetters } from "vuex";
  13. export default {
  14. computed: {
  15. ...mapGetters({
  16. gn: "getNum"
  17. })
  18. }
  19. };
  20. </script>

版权声明:本文为echoyya原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/echoyya/p/14404397.html