子组件接受父组件传递的事件
1.子组件使用@事件名="$emit('父组件中传递的事件名',想给父组件传递的参数(可选))"
@click="$emit('click')"
2.子组件使用
v-on="$listeners"
父组件:
<template>
<div id="app">
<myComponents :msg="form.msg" @click="a"/>
</div>
</template>
<script>
import myComponents from "./components/myComponents";
export default {
name: 'App',
components: {
myComponents
},
data(){
return {
form:{
msg: "10"
}
}
},
methods:{
a(){
console.log('哈哈哈')
},
}
}
</script>
<style>
</style>
子组件:
<template>
<div>
<!-- 子组件接收父组件传来的参数-->
<!-- v-on="$listeners" 和 @click="$emit('click')" 效果一样 两者选一即可-->
<h1 v-on="$listeners" @click="$emit('click')">{{ msg }}</h1>
</div>
</template>
<script>
export default {
name: 'my-Components',
props: {
msg: {
type: String,
default: "18"
}
},
created() {
console.log(this.$listeners) //一个对象,里面包含父组件传递过来的所有事件
},
methods: {}
}
</script>
<style scoped>
</style>
父组件传值给子组件
1.子组件使用props声明想要的属性,再将该属性动态绑定给子组件
props: { msg: { type: String, default: "18" }, },
然后子组件中绑定数据即可
父组件使用prop声明,就可以传递给子组件
:prop="msg"
<Head1 :name="form.name"/>
2.子组件使用$attrs,这个组件包含了被传入,但没有声明的prop
子组件:
<template>
<div>
<!-- 子组件接收父组件传来的参数-->
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
name: 'my-Components',
//不声明msg
// props: {
// msg: {
// type: String,
// default: "18"
// },
// },
created() {
console.log(this.$attrs) //一个对象,包含父组件中被传入,但没有声明的prop
},
methods: {}
}
</script>
<style scoped>
</style>
打印结果:
此时h1标签上就有msg属性:
<template>
<div>
<!-- 子组件接收父组件传来的参数-->
<h1 v-bind="$attrs"> {{this.$attrs.msg}}</h1>
</div>
</template>
页面显示结果:
需要注意的一点是:Vue会将被传入,但未声明的prop作为html属性,绑定到组件的根元素上:
可以设置属性 inheritattrs为false,将这些默认行为去掉,来解决这个问题。
<template>
<div>
<!-- 子组件接收父组件传来的参数-->
<h1 v-bind="$attrs"> {{this.$attrs.msg}}</h1>
</div>
</template>
<script>
export default {
name: 'my-Components',
inheritAttrs:false,
}
</script>
<style scoped>
</style>
对外暴露子组件插槽
1.直接向子组件转发插槽,使用父组件的 $slots
该组件包含了传递给父组件的非作用域插槽
使用$scopeSlots可传递给父组件的作用域插槽,这个组件包含了组件接受的所有插槽
子组件:
<template>
<el-input>
v-model="innerVal"
v-bind="$attrs
@input="$emit('input", $event)"
v-on="$listeners"
//子组件中写两个插槽
<template #append>
<slot name="append"></slot></template>
<template #prepend>
<slot name="prepend"></slot></template>
</el-input>
</template>
打印this.$slots
props validator
一般我们会用对象的形式声明prop,可以在对象中指定prop的默认值,也可以指定类型,对prop进行验证。
一个更灵活的的方式是,传入并编写一个验证函数,prop会作为参数传入该函数,函数返回fals时,会抛出控制台警告,这种方式特别适合验证枚举值,
子组件:
<template>
<div>
<!-- 子组件接收父组件传来的参数-->
<h1 v-on="$listeners" @click="$emit('click')" v-bind="$attrs"> {{this.$attrs.msg}}</h1>
</div>
</template>
<script>
export default {
name: 'my-Components',
inheritAttrs:false,
props:{
numberIs:{
default: '1',
//当传入的prop值不是1,2,3时,控制台会抛出警告
validator: prop => ['1', '2', '3'].includes(prop)
}
},
}
</script>
<style scoped>
</style>
父组件:
<template>
<div id="app">
<!-- 传入numberIs的值为4 -->
<myComponents :msg="form.msg" @click="a" numberIs="4"/>
</div>
</template>
<script>
import myComponents from "./components/myComponents";
export default {
name: 'App',
components: {
myComponents
},
data() {
return {
form: {
msg: "10"
},
}
},
methods: {
a() {
console.log('哈哈哈')
},
}
}
</script>
<style>
</style>
$refs用于父组件获取整个子组件实例,然后可以使用子组件的方法和属性(暴露子组件方法)
父组件:
<template>
<div id="app">
<!-- ref相当于给当前子组件设置了一个id,可以使用refs根据ref的值获取该组件 -->
<myComponents :msg="form.msg" @click="a" numberIs="1" ref="bb"/>
</div>
</template>
<script>
import myComponents from "./components/myComponents";
export default {
name: 'App',
components: {
myComponents
},
data() {
return {
form: {
msg: "10"
},
}
},
methods: {
a() {
//获取ref值为bb的子组件,并调用该子组件上的show方法
this.$refs.bb.show()
},
}
}
</script>
<style>
</style>
子组件:
<template>
<div>
<!-- 子组件接收父组件传来的参数-->
<h1 v-on="$listeners" v-bind="$attrs"> {{this.$attrs.msg}}</h1>
</div>
</template>
<script>
export default {
name: 'my-Components',
inheritAttrs:false,
props:{
numberIs:{
default: '1',
//当传入的prop值不是1,2,3时,控制台会抛出警告
validator: prop => ['1', '2', '3'].includes(prop)
}
},
created() {
},
methods: {
show(){
console.log('1111')
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
打印 this.$refs 显示为:
点击之后,控制台输出:
但在封装子组件后,如果不对子组件的方法进行暴露,调用时,我们往往需要通过ref先获取父组件实例,再获取子组件实例,这种方式导致组件代码难以维护:
//获取父组件bb之后,再获取子组件aa,最后使用aa中的show方法
this.$refs.bb.$refs.aa.show()
我们可以再父组件中提供与子组件同名的方法, 对外暴露,让使用者只通过父组件就可以进行调用:
子组件中:
methods:{
show() {
this.$refs.aa.show()
}
}
父组件中:
methos: {
show() {
this.$refs.bb.show()
}
}
总结:
1. $attrs 简化多层组件之间props传值;
2. $listeners 简化多层组件之间事件传递;
3. $Slots 更多拓展自定义组件传值,包括自定义html元素,及对象;
4. props validator 增强组件传值稳健性,可自定义业务代码效验参数;
5. $refs 对外提供API 增强组件灵活度和可控性;