vue组件通信方式小结

vue项目中,组件间的通信是最常用到的,也是面试必问的问题之一。

组件通信可以分为几种类型:

1、父子通信

   1.1 父传子

   1.2 子传父

2、跨级传递

   2.1祖父传孙

   3.1孙传祖父

3、同级组件间通信

首先说一下通用的方式,即不管哪种场景都在功能上可以实现,撇开具体场景的适合程度,其实也就是全局的通信方式。

一、vue bus  以vue实例为总线,传递数据

新建bus.js和busA.vue、busB.vue:

bus.js

import Vue from "vue"
const Bus = new Vue();
export default Bus;

在main.js引入,挂载到全局上去:

import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import Bus from "@/utils/bus";

Vue.config.productionTip = false;
Vue.prototype.bus = Bus;

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount("#app");

busA.vue:

<template>
    <div class="input">
        <button @click="handleClick">click</button>
    </div>
</template>

<script>
export default {
    data() {
        return {
        };
    },
    methods: {
      handleClick() {
        this.bus.$emit("change", 12333) // 触发事件
      }
    }
};
</script>
<style>
.input{
  display: flex;
}
</style>

busB.vue:

<template>
    <div class="input">
    </div>
</template>

<script>
export default {
    data() {
        return {
        };
    },
    created() {
      this.bus.$on('change', this.handleClick) // 监听事件
    },
    methods: {
      handleClick(val) {
        console.log(val)
      }
    }
};
</script>
<style>
.input{
  display: flex;
}
</style>

这样就实现了兄弟组件之间的数据传递,当然也适合所有类型的组件通信。

二、vuex  官方提供的状态管理器,统一管理状态可参考官方文档

接下来就是比较定制化的通信方式了:

一、父子通信

1、父传子:props

子传父:$emit

这两种方式都是最常用也是最简单的,可参考官方文档

2、通过实例通信:

$children

$parent

获取节点实例,然后访问实例下的数据或方法

3、父组件里给子组件设置ref属性,通过this.$refs.name访问子组件实例

二、隔代传递

利用组件的name属性作为标识,通过递归、遍历找到name对应的实例并返回,只要拿到了实例,那就可以随意访问实例下的数据和方法了。

1、向上查找最近指定节点

export const findComponentUpword = (context,componentName) => {
    let parent = context.$parent;
    let name = parent.$options.name;
    while(parent && (!name || [componentName].indexOf(name) < 0) {
        parent = parent.$parent;
        if(parent) {
            name = parent.$options.name;
        }
    }
    return parent || null;
}

组件中调用(父组件一定要有具体的name属性才行,不然找不到):

import {findComponentUpward} from "@/utils"
、、、、
、、、、
let fatherComponent = findComponentUpward(this,"FatherName"); // 父组件名称

2、向上查找所有指定节点

export const findComponentsUpward = (context,componentName) => {
    let parents = [];
    let parent = context.$parent;
    let name = parent.$options.name;
    if(parent) {
        if(name === componentName) {
            parents.push(parent);
        }
        return parents.concat(findComponentsUpward(parent,componentName));
    }else {
        return [];
    }
}

组件中调用:

import {findComponentsUpward} from "@/utils"
、、、、
、、、、
let fatherComponent = findComponentsUpward(this,"FatherName"); // 父组件名称, 返回匹配组件的实例数组

3、向下查找最近的指定节点

export const findComponentDownward = (context,componentName) => {
        let children = context.$children;
        let child = null;
        if(children.length) {
            for(const childItem in children) {
                const name = childItem.$options.name;
                if(name === componentName) {
                    child = childItem;
                    break;
                }else {
                    child = findComponentDownward(childItem,componentName);
                    if(child) {break}
                }
            }
        }
        return child;
    }

4、向下查找所有的指定节点

export const findComponentsDownwards = (context,componentName) => {
    return context.$children.reduce((components, child) => {
        if(child.$options.name === componentName) {
            components.push(child);
        }
        const findChildren = findComponentsDownwards(child,componentName);
        return [...components,...findChildren];
    },[])
}

5、provide、inject方式

vue的api原生提供的方法,在父组件向下注入一个provide,不管子组件嵌套有多深,都可以通过inject获取到

父组件:

<template>
  <div class="wrapper">
  </div>
</template>

<script>
export default {
  props: {
  },
  // provide() {  // 如果父组件本身是一个组件
  //  return {
  //   father:this
  // }
  provide: {
        father: this
    }
  },
  data() {
    return {
        testData: "哈哈哈哈"
    };
  },
  created() {
  },
  methods: {
  }
};
</script>

子组件:

<template>
  <div class="son">
   
  </div>
</template>

<script>
export default {
  inject: ['father'],
  props: {
  },
  data() {
    return {
    };
  },
  mounted() {
    console.log(this.father.testData) // 哈哈哈哈
  },
  methods: {}
};
</script>
<style lang="css">
</style>

有了这些组件通信方式,再复杂的业务需求也能得到满足了

========================================================

2079.10.6

父子组件的通信方式还有一种,就是同过props的Function类型传递:

父组件a.js

<template>
  <div class="app-container">
    <upload-excel-component :on-success="handleSuccess" :before-upload="beforeUpload" />
    <el-table :data="tableData" border highlight-current-row>
      <el-table-column v-for="item of tableHeader" :key="item" :prop="item" :label="item" />
    </el-table>
  </div>
</template>

<script>
import UploadExcelComponent from '@/components/UploadExcel/index.vue'

export default {
  name: 'UploadExcel',
  components: { UploadExcelComponent },
  data() {
    return {
      tableData: [],
      tableHeader: []
    }
  },
  methods: {
    beforeUpload(file) {
      const isLt1M = file.size / 1024 / 1024 < 2

      if (isLt1M) {
        return true
      }

      this.$message({
        message: '上传文件不得大于2M。',
        type: 'warning'
      })
      return false
    },
    handleSuccess({ results, header }) {
      this.tableData = results
      this.tableHeader = header
    }
  }
}
</script>

传递两个函数给UploadExcel组件

UploadExcel/index.vue

<template>
  <div>
    <input ref="excel-upload-input" class="excel-upload-input" type="file" accept=".xlsx, .xls" @change="handleClick">
  </div>
</template>

<script>

export default {
  props: {
    beforeUpload: Function,
    onSuccess: Function
  },
  data() {
    return {
      loading: false,
      excelData: {
        header: null,
        results: null
      }
    }
  },
  methods: {
    generateData({ header, results }) {
      this.excelData.header = header
      this.excelData.results = results
      this.onSuccess && this.onSuccess(this.excelData) // 子传父
    },
    handleClick(e) {
      const files = e.target.files
      const rawFile = files[0] // only use files[0]
      if (!rawFile) return
      this.upload(rawFile)
    },
    upload(rawFile) {
      this.$refs['excel-upload-input'].value = null
      const before = this.beforeUpload(rawFile) // 父传子
      if (before) {
        this.readerData(rawFile)
      }
    },
    readerData(rawFile) {
      return new Promise((resolve, reject) => {
        const reader = new FileReader()
        reader.onload = e => {
          、、、
          this.generateData({ header, results })
          resolve()
        }
        reader.readAsArrayBuffer(rawFile)
      })
    }
  }
}
</script>

通过Function类型,父子组件就可以轻松传递值了

发布了59 篇原创文章 · 获赞 29 · 访问量 15万+

猜你喜欢

转载自blog.csdn.net/dongguan_123/article/details/88935470