Vue组件传值(父子传值、兄弟传值、爷孙传值)整理


  Vue组件之间传值无论面试还是正常开发中,都很常用,这里主要整理一下父子传值、兄弟传值、爷孙传值的几种方法。(我尽量整理的全一点,各位大哥们多指教指教,别踩别踩!!!哎嘛。或者您们有什么新的方式,欢迎评论,我给加上,尽量尽善尽美嘛!!!)

  先补充下简写:’ : ‘ 是 v-bind的简写,

                            ’ @ ‘是v-on的简写

一、父子传值

  父子组件的关系可以总结为 prop 向下传递,事件 向上传递。父组件通过prop给子组件下发数据,子组件通过 事件给父组件发送信息。

                                                                             

1.1 使用 props 传值【父传子】

  props可以是数组或对象,用来接收父组件传来的数据,在接收的数据中也可以配置一些属性(type、default、required、validator【验证器】函数)

<!-- 父组件 --> 
<first-child :fatherMethod="fatherMethod" :sendNumber="number" :sendObj="obj"></first-child>

<!-- 子组件 -->
props: {
    /* 函数 */
    fatherMethod: {
      type: Function, //String、Object、Array等原生构造函数中的一种 、 自定义构造函数 、 这些组成数组
      default: null, //默认值,如果 prop 没有被传入,则换做用这个值
      required: false //定义该 prop 是否是必填项(如果是true,但没有传,控制台会报警告)
    },
    /* 数字 */
    sendNumber: {
      type: Number,
      default: 0
    },
      /* 字符串 */
    sendObj: {
      type: Object,
      default: {}
    }
},

1.2 使用 this.$emit(eventName,[...args]) 传值【子传父】

  eventName:事件名称,子组件传递给父组件的事件名,上面说的 @ ( v-on )【官网对其用法描述:绑定事件的监听器】

【...args】:附加参数,它会传给监听器回调

<!-- 子组件 -->
this.$emit('isUseFatherMet', true)

<!-- 父组件 -->
<first-child @isUseFatherMet="isUseFatherMet"></first-child>

//子组件调用父组件的方法
isUseFatherMet(val) {
  console.log('子组件传值过来了?', val)
},

1.3 调用子组件中的方法(使用this.$refs.子组件注册名.子组件方法)

  官方文档对 vm.$refs 的定义为:一个对象,持有注册过 refattribute 的所有 DOM 元素和组件实例。我们将引用信息注册在父组件的 $refs 对象上,对象就会指向子组件实例,以此我们可访问子组件的方法,进行传值调用。

<!-- 父组件 -->
<first-child ref="child1"></first-child>

//调用子组件里的方法
runChildMet() {
  this.$refs.child1.showIsUse()
}

<!-- 子组件 -->
//父组件会调用这个方法
showIsUse() {
  alert(true)
}

 1.4 使用 vm.$on 和 vm.$emit【也可应用于兄弟组件传值】

 首先就官方文档描述:

  vm.$on(event,callback) :监听当前实例上的自定义事件。事件可以由  vm.$emit 触发。回调函数会接收所有传入事件触发函数的额外参数。

  vm.$emit(eventName,[...args]):触发当前实例上的事件。附加参数都会传给监听器回调。

  也就是说,子组件通过 vm.$emit() 把事件名和参数传递给父组件,父组件使用 vm.$on() 对这个自定义事件监听,回调函数接收到值,进行下一步操作。

  用法:我们可以新建一个Bus.js文件,文件暴露一个Vue实例,之后分别在父子组件中导入进来,然后调用。

// Bus.js (把它想象成运送数据的交通工具)
// 没有源码,在别的项目里找的
import Vue from 'vue'
export default new Vue()
<!-- 父组件 -->
import Bus from'../utils/Bus'
Bus.$on('backDmDetail', val => {
  console.log(val);
})

beforeDestroy(){
    //最后不要忘了删除传输事件
    Bus.$off("backDmDetail")
}

<!-- 子组件 -->
import Bus from'../utils/Bus'
Bus.$emit('backDmDetail',"dsdd")

1.5 使用 $parent 和 $children 方法

  $parent :在子组件中直接访问该组件的父实例或组件,

  $children:当前实例的子组件,需要注意 $children 并不保证顺序,也不是响应式的。考虑使用 v-for 来生成子组件

    

/* 使用$parent */

<!-- 子组件 -->
useParent() {
  this.$parent.isUseFatherMet('是的,传过来了,而且我用了$parent')
}

<!-- 父组件 -->
//子组件调用父组件的方法
isUseFatherMet(val) {
  console.log('子组件传值过来了?', val)
},

 


二、兄弟传值

  2.1 使用 vm.$on 和 vm.$emit

   就如 标题1.4 所说,导入一个公共的 Bus.js 文件,暴露一个 Vue 实例,在兄弟组件传递时使用 Bus.$on  和 Bus.$emit

// Bus.js (把它想象成运送数据的交通工具)
// 没有源码,在别的项目里找的
import Vue from 'vue'
export default new Vue()
<!-- 父组件 -->
import Bus from'../utils/Bus'
Bus.$on('backDmDetail', val => {
  this.investigationIndex = this.flag
})

<!-- 子组件 -->
import Bus from'../utils/Bus'
Bus.$emit('backDmDetail',"dsdd")

  注意:暴露一个 Vue 实例,在Vue2.x版本是可以这么使用,但是在Vue3.x中,没有全局的vue,会报警告:"export 'default' (imported as 'Vue') was not found in 'vue'"

  强势大哥:呵呵,说了这么多,什么什么跟上面一样,有啥用,那我就想用Vue3.x,不想降版本,那咋整?

  卑微小弟:别急,哥,有下面的方法:使用第三方库Mitt

npm install -S mitt //先引入依赖

//新建一个Bus.js(这车有空调,手动滑稽)

<!-- 兄弟组件1 -->
import Bus from '@/utils/Bus.js'

//给兄弟组件传值
sendToBrother() {
  Bus.emit('toBrotherVal', '我是传向兄弟组件的值')
}


<!-- 兄弟组件2 -->
import Bus from '@/utils/Bus.js'
Bus.on('toBrotherVal', (val) => {
  alert(val)
})

//使用完别忘了移除(重复发车不回收,性能耗不起啊。。。)
unmounted() {
  //移除toBrotherVal
  Bus.off('toBrotherVal')
}

  2.2 子传父,父再传子

     一种比较常规的使用方法,通常都是this.$emit、props等方式一起使用,不做赘述。

 


三、爷孙传值

  3.1 使用 provide 和 inject

     provide/inject: 这对选项要一起使用, 以允许一个祖先组件向其子孙后台注入一个依赖,不管组件层次有多深,并在其上下有关系成立的时间里始终生效(Vue官方文档)。

     也就是说他们允许父子组件传值、爷孙组件传值。

// 爷爷组件通过provide将自己的数据以对象形式传出去
provide() {
  return {
    parentValue: '我是儿他爹,也是孙他爷'
  }
},
  
//儿子组件接收
inject: {
  // 使用一个默认值使其变成可选项
  parentValue: {
    // 健名
    from: 'parentValue', // 来源
      default: '000' // 默认值( vue2.5.0++ )
  }
},

//孙子组件接收
inject: {
  // 使用一个默认值使其变成可选项
  parentValue: {
    // 健名
    from: 'parentValue', // 来源
      default: '000' // 默认值( vue2.5.0++ )
  }
},

源代码(ps:框架是Vue3.x/vue-cli4.54):

父组件:

点击查看代码

<template>
  <div class="Father">
    <h5>这里是父组件</h5>
    <el-button @click="runChildMet">使用$refs</el-button>
    <el-button @click="useChildren">使用$children</el-button>
    <!-- 子组件1 -->
    <first-child
      :fatherMethod="fatherMethod"
      :sendNumber="number"
      :sendObj="obj"
      @isUseFatherMet="isUseFatherMet"
      ref="child1"
    ></first-child>
    <!-- 子组件2 -->
    <second-child></second-child>
  </div>
</template>
<script>
import child1 from './Child1'
import child2 from './Child2'
export default {
  name: 'Father',
  props: {},
  components: {
    'first-child': child1,
    'second-child': child2
  },
  // 父组件通过provide将自己的数据以对象形式传出去
  provide() {
    return {
      parentValue: '我是儿他爹,也是孙他爷'
    }
  },
  data() {
    //这里存放数据
    return {
      number: 12,
      obj: { name: '小明' }
    }
  },
  methods: {
    fatherMethod() {
      alert('这里是父组件')
    },
    //子组件调用父组件的方法
    isUseFatherMet(val) {
      alert('子组件传值过来了?' + val)
    },
    //调用子组件里的方法
    runChildMet() {
      this.$refs.child1.showIsUse('Father')
    },
    useChildren() {
      console.log('dsdsd', this.$children)
      //返回的是一个组件集合,如果你能清楚的知道子组件的顺序,你也可以使用下标来操作
      for (let i = 0; i < this.$children.length; i++) {
        this.$children[i].showIsUse()
      }
    }
  }
}
</script>
<style lang="less">
.Father {
  font-size: 18px;
  .el-divider--horizontal {
    margin: 10px 0;
  }
}
</style>

两个儿子组件:

点击查看代码

// Child1
<template>
  <div class="Child1">
    <el-divider></el-divider>
    <h5>这里是Child1</h5>

    <el-button @click="runProps">使用Props</el-button>
    <el-button @click="runFather">使用this.$emit方法</el-button>

    <div style="margin:12px 0 ">
      <span>Props传的值:</span>
      <div v-if="showProps">
        <span v-html="sendObj.name"></span>
        <span v-html="sendNumber"></span>
      </div>
    </div>
    <el-button @click="useParent">使用 $parent </el-button>
    <el-button @click="sendToBrother">使用Bus给兄弟组件传值</el-button>
    <h5 v-html="'我是Child1,我爹传来的值:' + parentValue"></h5>
  </div>
</template>
<script>
import Bus from '@/utils/Bus.js'
export default {
  name: 'Child1',
  props: {
    /* 函数 */
    fatherMethod: {
      type: Function, //String、Object、Array等原生构造函数中的一种 、 自定义构造函数 、 这些组成数组
      default: null, //默认值,如果 prop 没有被传入,则换做用这个值
      required: false //定义该 prop 是否是必填项(如果是true,但没有传,控制台会报警告)
    },
    /* 数字 */
    sendNumber: {
      type: Number,
      default: 0
    },
    /* 字符串 */
    sendObj: {
      type: Object,
      default: {}
    }
  },
  components: {},
  provide() {
    return {
      brotherVal: '我是Child1'
    }
  },

  inject: {
    // 使用一个默认值使其变成可选项
    parentValue: {
      // 健名
      from: 'parentValue', // 来源
      default: '000' // 默认值( vue2.5.0++ )
    }
  },
  data() {
    //这里存放数据
    return {
      showProps: false //是否显示父组件传的值
    }
  },
  methods: {
    //使用props传值
    runProps() {
      this.showProps = true
      this.fatherMethod()
    },

    //运行父组件的方法
    runFather() {
      this.$emit('isUseFatherMet', true)
    },

    //父组件会调用这个方法
    showIsUse(val) {
      alert('这里是Child1,是' + val + '调用的这个方法')
    },
    useParent() {
      this.$parent.isUseFatherMet('是的,传过来了,而且我用了$parent')
    },

    //给兄弟组件传值
    sendToBrother() {
      Bus.emit('toBrotherVal', '我是传向兄弟组件的值')
    }
  }
}
</script>


// Child2
<template>
  <div class="Child2">
    <el-divider></el-divider>
    <h5>这里是Child2</h5>
    <h5 v-html="'我是Child2,我爹传来的值:' + parentValue"></h5>
    <grond-son></grond-son>
    <div>----------------------------</div>
    <h5 v-html="brotherVal"></h5>
    <div style="font-size:15px;padding:5px;margin-bottom:20px">
      很显然,上面兄弟组件使用 provide/inject
      传值没有传过来,因为官方说的是在组件之间存在上下有关系才会生效
    </div>
  </div>
</template>
<script>
import Bus from '@/utils/Bus.js'
import grondSon from './GrandSon.vue'
export default {
  name: 'Child2',
  props: {},
  components: {
    'grond-son': grondSon
  },
  data() {
    //这里存放数据
    return {}
  },
  inject: {
    // 使用一个默认值使其变成可选项
    parentValue: {
      // 健名
      from: 'parentValue', // 来源
      default: '000' // 默认值( vue2.5.0++ )
    },
    brotherVal: {
      from: 'brotherVal',
      default: 'Child222'
    }
  },
  methods: {},
  mounted() {
    Bus.on('toBrotherVal', (val) => {
      alert('这里是Child2,传来的值:' + val)
    })
  },

  unmounted() {
    //移除toBrotherVal
    Bus.off('toBrotherVal')
  }
}
</script>

孙子组件:

点击查看代码

<!-- 孙子组件 -->
<template>
  <div class="GrandSon">
    <div style="text-align:center">这里是孙子的地界,我在Child2里</div>
    <h5 v-html="'我爷爷传来的值:' + parentValue"></h5>
  </div>
</template>

<script>
//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
//例如:import 《组件名称》 from '《组件路径》';

export default {
  name: 'GrandSon',
  props: {},
  //import引入的组件需要注入到对象中才能使用
  components: {},
  inject: {
    // 使用一个默认值使其变成可选项
    parentValue: {
      // 健名
      from: 'parentValue', // 来源
      default: '000' // 默认值( vue2.5.0++ )
    }
  },
  data() {
    //这里存放数据
    return {}
  },
  //计算属性
  computed: {},
  //监控data中的数据变化
  watch: {},
  methods: {},
  mounted() {}
}
</script>

四、总结

     组件传值的方式:

       1、使用 props 传值

       2、使用 this.emit();

       3、使用 this.$refs

       4、使用 $parent 和 $children 方法

       5、定义一个js文件,暴露一个vue实例,使用Bus.$on() 和 Bus.$emit()【别忘了使用Bus.$off 删除传值事件,ps:Vue2.x】

       6、定义一个js文件,引入mitt依赖,然后使用Bus.on() 和 Bus.emit()【别忘了Bus.off 删除传值事件,ps:Vue3.x、Vue2.x】

       7、使用 provide 和 inject

       Vue组件传值的方法有很多很多,我这边总结的肯定也是不全的,当然无论是使用propsthis.$emit还是别的什么方法,高性能、方便易懂,我认为是最重要的(我就是这么认为的!!耶稣来了也不好使),而且就技术更新迭代速度来说,本文说不定过几年就不好用了呢,哈哈哈。安啦安啦,有更新的话,会更的......

 

借鉴各位大佬:

vue事件之vm.$on事件和v-on事件:https://blog.csdn.net/jaquechen/article/details/102557718

VUE3前端笔记 Mitt事务总线使用方法:https://blog.csdn.net/fuweipeng2012/article/details/113812794

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM