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 的定义为:一个对象,持有注册过 ref、attribute 的所有 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组件传值的方法有很多很多,我这边总结的肯定也是不全的,当然无论是使用props、this.$emit还是别的什么方法,高性能、方便易懂,我认为是最重要的(我就是这么认为的!!耶稣来了也不好使),而且就技术更新迭代速度来说,本文说不定过几年就不好用了呢,哈哈哈。安啦安啦,有更新的话,会更的......
借鉴各位大佬:
vue事件之vm.$on事件和v-on事件:https://blog.csdn.net/jaquechen/article/details/102557718
VUE3前端笔记 Mitt事务总线使用方法:https://blog.csdn.net/fuweipeng2012/article/details/113812794