目录
引言
我们知道了子组件可以通过$emit,来触发父组件上给子组件绑定的自定义事件,并在$emit时附上参数的形式来实现子组件给父组件数据。今天我们来带上v-for生成子组件的情况,探讨一下各种情况吧。
总结
老样子 先给总结。吉吉国王先看总结,能用直接拿去。
v-for生成的子组件
当v-for的item和index需要传入函数中,且函数是由v-for中的子组件触发时,使用$event即可拿到第一个参数(仅第一个参数)
//父组件
<test-component @sayHi="say(index,item.name,$event)"></test-component>
//子组件
this.$emit("sayHi",this.data)
事件参数问题
1、函数名带括号时候,父组件传参优先级更高,此时子组件参数不能通过$emit传入父组件
/*父组件*/
//写法一 正常传参
<test-component ref="component" @sayHi="say"></test-component>
/*只有父传入的参数生效*/
//写法二
<test-component ref="component" @sayHi="say()"></test-component>
//写法三
<test-component ref="component" @sayHi="say(index,item.name)"></test-component>
2、无论是父组件还是子组件触发事件,都会按照事件函数定义时的方式传参。比如定义时有加入父组件的参数,则子组件触发时,无须子组件来定义参数,也会传入父组件参数。
- 如果在父组件上触发事件,$event拿到的是该dom元素
- 自定义子组件里$emit触发父组件绑定在子组件的事件,$event拿到的组件$emit时返回的第一个参数
//父组件
<button @click="say(index,item.name,$event)">父组件来触发</button>
<test-component ref="component" @sayHi="say(index,item.name,$event)"></test-
PS:提一嘴,v-for生成的ref,this.$refs.xx是一个数组,可以搭配index,this.$refs.xx[index]拿到对应的dom元素
子传父数据
父组件代码
<template>
<div>
<div class="container" v-for="(item,index) of list" :key="index">
我是{
{index}}号{
{item.name}}
<test-component
ref="component"
@sayHi="say">
</test-component>
</div>
</div>
</template>
<script>
import testComponent from '@/components/testComponent.vue';
export default {
components:{testComponent},
data(){
return{
list:[
{
name:'爸爸'
},
{
name:'爷爷'
},
]
}
},
methods:{
say(){
console.log(...arguments)
}
}
}
</script>
子组件
<template>
<div class="component">
<div>我是组件 {
{data}}</div>
<button @click="back">返回组件数据</button>
</div>
</template>
<script>
export default {
props:[],
data(){
return{
data:'我是帅哥',
sex:'男'
}
},
methods:{
//一顿对Data操作后
back(){
this.$emit("sayHi",this.data)
}
}
}
</script>
这是正常情况下的子组件向父组件传参。
但是新的需求下来了,有时候需要把v-for生成的index或者item传进来,进行进一步的数据的处理。
于是我们把代码改成这样
<test-component ref="component" @sayHi="say(index,item)"></test-component>
我们来看看结果
index和item是拿到了,但是子组件向父组件的值弄丢了,那我们有什么办法可以解决这种冲突的情况呢。
- 方法一 暴力解决 把数据传入子组件再打包返回
//父组件内
<test-component ref="component" :myIndex="index" :myItem="'item'" @sayHi="say"></test-component>
....
//子组件内
....
props:['myItem','myIndex'],
data(){
return{
data:'我是帅哥',
sex:'男'
}
},
methods:{
//一顿对Data操作后
back(){
let data={
data:this.data,
item:this.myItem,
index:this.myIndex,
}
this.$emit("sayHi",data)
}
}
结果如下
我们确实成功拿到了,但是这种办法非常的笨,于是有了第二种办法
- 方法二 使用$event获取自定义组件的返回参数(仅限第一个参数)
我们只要定义事件时参数加上了$event即可,该方法在普通元素中会拿到相应的dom元素,但在子组件的自定义事件上使用能拿到子组件传入的参数。(有些组件依然可以拿到相应的dom元素,如uniapp的官方组件,此时可以使用detail.value或target.value拿到参数)
<test-component ref="component" @sayHi="say(index,item.name,$event)"></test-component>
注意:此方法只能获取传入的第一个参数,若这样写
this.$emit("sayHi",'美女',this.data)
data是拿不到的
事件函数传参问题
此时,可能有人疑虑了,这vue的事件函数带括号写法和不带写法 在本文的情况下有什么区别吗?我们逐个来验证。
/*父组件*/
//写法一
<test-component ref="component" @sayHi="say"></test-component>
//写法二
<test-component ref="component" @sayHi="say()"></test-component>
//写法三
<test-component ref="component" @sayHi="say(index,item.name)"></test-component>
//子组件
this.$emit("sayHi",'美女',this.data)
- 第一种情况便是最普通的,我们能顺利拿到子传父的参数,并且不限参数数量。
- 第二种带括号但是不传入参数,子组件传入的参数一个都没拿到
say(){
console.log(...arguments)
if(arguments.length==0)console.log('哦噢,没有参数哦')
}
- 第三种带括号且传入参数,能拿到父组件参数,但拿不到子组件参数。
到这,大家应该应该心领神会了。有括号时候,父组件传参优先级更高,能正常拿到index和item。
我们再来两种情况来强化一下认识。讨论父组件触发事件和子组件触发事件的区别。
//父组件
<button @click="say(index,item.name,$event)">父组件来触发</button>
<test-component ref="component" @sayHi="say(index,item.name,$event)"></test-
- 如果在父组件上触发事件,$event拿到的是该dom元素
- 自定义子组件里$emit触发父组件绑定在子组件的事件,$event拿到的组件$emit时返回的第一个参数
无论是父组件还是子组件触发事件,都会按照事件函数定义时的方式传参。比如定义时有加入父组件的参数,则子组件触发时,无须子组件来定义参数,也会传入父组件参数。与在哪触发,是否传入参数,传入参数个数无关。
最终测试代码
父组件
<template>
<div>
<div class="container" v-for="(item,index) of list" :key="index">
我是{
{index}}号{
{item.name}}
<button @click="say(index,item.name,$event)">父组件来触发</button>
<test-component ref="component" @sayHi="say(index,item.name,$event)"></test-component>
</div>
</div>
</template>
<script>
import testComponent from '@/components/testComponent.vue';
export default {
components:{testComponent},
data(){
return{
list:[
{
name:'爸爸'
},
{
name:'爷爷'
},
]
}
},
methods:{
say(){
console.log(...arguments)
if(arguments.length==0)console.log('哦噢,没有参数哦')
}
}
}
</script>
子组件
<template>
<div>
<div>我是组件 {
{data}}</div>
<button @click="back">返回组件数据</button>
</div>
</template>
<script>
export default {
props:[],
data(){
return{
data:'我是帅哥'
}
},
methods:{
//一顿对Data操作后
back(){
this.$emit("sayHi",this.data)
}
}
}
</script>