前面我们是将所有的逻辑放到一个App.vue中:
创建了一个组件App
;臃肿和难以维护
;对组件进行拆分
,拆分成一个个小的组件;我们来分析一下下面代码的嵌套逻辑,假如我们将所有的代码逻辑都放到一个App.vue组件中:
我们可以按照如下的方式进行拆分:
按照如上的拆分方式后,我们开发对应的逻辑只需要去对应的组件编写就可。
上面的嵌套逻辑如下,它们存在如下关系:
父组件
;父组件
;在开发过程中,我们会经常遇到需要组件之间相互进行通信:
App可能使用了多个Header
,每个地方的Header展示的内容不同,那么我们就需要使用者传递给Header一些数据,让其进行展示;子组件中发生了事件
,需要由父组件来完成某些操作,那就需要子组件向父组件传递事件;总之,在一个Vue项目中,组件之间的通信是非常重要的环节,所以接下来我们就具体学习一下组件之间是如何相互之间传递数据的;
父子组件之间如何进行通信呢?
props
属性;$emit
触发事件;在开发中很常见的就是父子组件之间通信,比如父组件有一些数据,需要子组件来进行展示:
props
来完成组件之间的通信;什么是Props呢?
注册一些自定义的attribute
;Props有两种常见的用法:
字符串数组
,数组中的字符串就是attribute的名称;对象类型
,对象类型我们可以在指定attribute名称的同时,指定它需要传递的类型、是否是必须的、默认值等等;数组用法中我们只能说明传入的attribute的名称,并不能对其进行任何形式的限制,接下来我们来看一下对象的写法是如何让 我们的props变得更加完善的。
当使用对象语法的时候,我们可以对传入的内容限制更多:
类型
;是否是必传的
;默认值
;export default {
props: {
title: {
type: String,
required: true,
default: '我是默认值'
}
}
}
那么type的类型都可以是哪些呢?
{
props: {
messageInfo: String,
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
propD: {
type: Number,
default: 100
},
// 带默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default() {
return {message: 'hello'}
}
},
// 自定义验证函数
propF: {
validator(value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].includes(value)
}
},
// 带有默认值的函数
propG: {
type: Function,
//
default() {
return 'Default function'
}
}
}
}
Prop 的大小写命名(camelCase vs kebab-case)
大小写不敏感
的,所以浏览器会把所有大写字符解释为小写字符;使用 DOM 中的模板
时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名;<div>
<show-message messageInfo="hhhh">show-message>
<show-message message-info="hhhh">show-message>
div>
什么是非Prop的Attribute呢?
Attribute继承
Attribute
中:如果我们不希望组件的根元素继承attribute,可以在组件中设置 inheritAttrs: false
:
常见情况
是需要将attribute应用于根元素之外的其他元素;$attrs
来访问所有的 非props的attribute;<div>
我是NotPropAttribute组件
<h2 :class=$attrs.class>
h2>
div>
多个根节点的attribute
多个根节点的attribute如果没有显示的绑定
,那么会报警告,我们必须手动的指定要绑定到哪一个属性上:什么情况下子组件需要传递内容到父组件呢?
我们如何完成上面的操作呢?
我们封装一个CounterOperation.vue的组件:
this.$emit
的方式发出去事件;AddCounter.vue
<template>
<div class="add">
<button @click="btnClick(1)">+1button>
<button @click="btnClick(5)">+5button>
<button @click="btnClick(10)">+10button>
div>
template>
<script>
export default {
// 1.emits数组语法
emits: ["add"],
// 2.emmits对象语法
// emits: {
// add: function(count) {
// if (count <= 10) {
// return true
// }
// return false
// }
// },
methods: {
btnClick(count) {
console.log("btnClick:", count)
// 让子组件发出去一个自定义事件
// 第一个参数自定义的事件名称
// 第二个参数是传递的参数
this.$emit("add", 100)
}
}
}
script>
<style scoped>
style>
App.vue
<template>
<div class="app">
<h2>当前计数: {{ counter }}h2>
<add-counter @add="addBtnClick">add-counter>
<add-counter @add="addBtnClick">add-counter>
<sub-counter @sub="subBtnClick">sub-counter>
div>
template>
<script>
import AddCounter from './AddCounter.vue'
import SubCounter from './SubCounter.vue'
export default {
components: {
AddCounter,
SubCounter
},
data() {
return {
counter: 0
}
},
methods: {
addBtnClick(count) {
this.counter += count
},
subBtnClick(count) {
this.counter -= count
}
}
}
script>
<style scoped>
style>
自定义事件的时候,我们也可以传递一些参数给父组件:
incrementTen() {
this.$emit("addTen", 10)
}
在vue3当中,我们可以对传递的参数进行验证:
emits: {
add: function(count) {
if (count <= 10) {
return true
}
return false
}
},
App.vue
{{ pageContents[currentIndex] }}
TabControl.vue
{{ item }}