携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情
如果我们一个应用程序将所有的逻辑都放在一个组件中,那么这个组件就会变成非常的臃 肿和难以维护
所以我们必然会进行组件的拆分,最后再将这些组件组合嵌套在一起,最终形成我们的应用程序
此时必然就需要进行组件和组件间的数据通信, 也就是数据传递
父组件将对应的数据以props的方式传递给子组件
子组件向父组件传递事件,以在父组件中统一对数据进行处理
父传子
在开发中很常见的就是父子组件之间通信,比如父组件有一些数据,需要子组件来进行展示
这个时候我们可以通过props来完成组件之间的通信
Props是你可以在组件上注册一些自定义的attribute
父组件给这些attribute赋值,子组件通过attribute的名称获取到对应的值
使用方式一 - 数组形式
字符串数组,数组中的字符串就是attribute的名称
父组件
<!-- 组件没有插槽的时候,可以写成单标签, 但必须严格闭合 -->
<Child name="Klaus" age="23" />
子组件
<template>
<div>
<div>name: {{ name }}</div>
<div>age: {{ age }}</div>
</div>
</template>
<script>
export default {
// 使用props接收父组件传递过来的props
props: ['name', 'age']
}
</script>
使用方式二 - 对象方式
对象类型,对象类型我们可以在指定attribute名称的同时,指定它需要传递的类型、是否是必须的、默认值等等
数组用法中我们只能说明传入的attribute的名称,并不能对其进行任何形式的限制
当使用对象语法的时候,我们可以对传入的内容限制更多
- 指定传入的attribute的类型
- 指定传入的attribute是否是必传的
- 指定没有传入时,attribute的默认值
父组件
<!--
当属性前面加上v-bind的时候
属性的值即可以是任何的JS变量或合法的js表达式
所以可以使用:age="23"的形式 为prop传入一个number类型的值
-->
<Child name="Klaus" :age="23" height="1.88" />
子组件
export default {
props: {
// name必须传入一个字符串类型的值
name: String,
// age必须是number类型的是,且是必传值
// 但age属性为必传值的时候,就没有必要在设置props的默认值了
age: {
type: Number,
required: true
},
height: {
type: String,
// 设置默认值 - 当且仅当属性值为undefined的时候,就会使用对应的默认值
default: '1.98'
}
}
}
prop可以选取的值
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbol
prop的其它写法
export default {
props: {
// propA的值即可是Number类型值,也可以是String类型值
propA: [Number, String]
},
// 如果prop的类型是除Function外的引用类型, 如Array,Object等
// 那么为了避免多个组件在使用默认prop值的时候
// 因为是同一个引用对象而产生冲突问题
// 所以对于这类prop,在设置默认值的时候
// 值必须是一个返回默认值的函数
propB: {
type: Array,
default: () => [1, 2, 3]
},
// 因为函数类型更多的是用于执行而不是使用值
// 所以对于Function类型的prop,在设置默认值的时候并不需要使用函数
propC: {
type: Function,
default: () => {}
},
// 自定义校验规则
propD: {
// 会将值以参数的形式传入validator函数中
// 该函数需要返回一个boolean类型的值
// 当该函数的返回值为true的时候校验通过,否则校验失败
validator(v) {
return v % 2
}
}
}
大小写
<!--
如果属性值为小驼峰形式的时候
在传递props的时候
和组件名一样, prop名称 既可以使用小驼峰写法,也可以使用中划线形式
-->
<Child userName="Klaus" />
<Child user-name="Klaus" />
非prop的attribute
当我们传递给一个组件某个属性,但是该属性并没有定义对应的props或者emits时,就称之为 非Prop的Attribute,常见的包括class、style、id属性等
当组件有单个根节点时,非Prop的Attribute将自动继承到根节点上
父组件
<Child name="Klaus" age="32" />
子组件
<div>Child</div>
渲染后
<div name="Klaus" age="32"> Child </div>
如果我们不希望组件的根元素继承attribute,可以在组件中设置 inheritAttrs: false
此时可以通过 $attrs来访问所有的 非props的attribute
并将所有非props的attribute应用于根元素之外的其他元素
<template>
<div class="child">
<div v-bind="$attrs">Child</div>
</div>
</template>
<script>
export default {
inheritAttrs: false
}
</script>
如果存在子元素存在多个根节点的时候,如果没有显示的绑定,那么会报警告,必须手动的指定要绑定到哪一个属性上
<template>
<div v-bind="$attrs" class="child1">Child1</div>
<div class="child2">child2</div>
</template>
子传父
当子组件有一些事件发生的时候,比如在组件中发生了点击,父组件需要切换内容
子组件有一些内容想要传递给父组件的时候,我们都需要将数据从子组件传递给父组件
父组件
<template>
<div>
<!-- 在组件上监听自定义事件,并绑定对应的事件处理函数 -->
<Child @childEvent="handleEvent" />
</div>
</template>
<script>
import Child from './components/Child'
export default {
components: {
Child
},
methods: {
handleEvent(...payload) {
console.log(payload)
}
}
}
</script>
子组件
<template>
<!-- 在子组件中监听对应的方法,响应对应的事件 -->
<button @click="handleClick">click me</button>
</template>
<script>
export default {
methods: {
handleClick() {
// 通过$emit触发一个事件
// 参数一 - 自定义事件名
// 后续参数 - 参数列表
this.$emit('childEvent', 10, 20)
}
}
}
</script>
参数验证
export default {
// 自vue3开始,所有的事件都必须在emits中进行声明
// 不声明,也不会存在对应的问题
// 但是不声明的事件,会存在于$attrs中
// 所以推荐对使用到的自定义事件在emits中进行声明
emits: ['childEvent'],
methods: {
handleClick() {
this.$emit('childEvent', 10, 20)
}
}
}
emits选项除了上述的数组写法,还存在对象的写法
通过对象语法,可以对自定义事件中所传入的参数进行自定义校验
emits: {
// 当属性值为null的时候,表示不进行任何的校验
// 此时的写法和数组写法是一致的
childEvent: null
}
emits: {
// 自定义事件传入了几个参数,在自定义校验函数中,就可以获取到几个对应参数
// 该自定义事件需要返回一个boolean类型值,如果为true表示校验通过
// 如果为false,校验失败,事件依旧会成功执行,只是会在控制台中出现警告
childEvent(num1, num2) {
return num1 >= num2
}
}