一、Vue组件的嵌套关系
认识组件的嵌套
如果我们一个应用程序将所有的逻辑都放在一个组件中 那么这个组件就会变得非常臃肿难以维护
所以组件化的核心思想是对组件进行拆分 拆分成 一个个小的组件
再将这些组件组合嵌套在一起 最终形成 我们的应用程序
组件的拆分
我们可以按照如下的方式进行拆分:
按照上面的拆分方式后,我们开发对应的逻辑只需要去对应的组件编写即可.
组件的通信
上面的嵌套逻辑如下:存在如下关系:
App组件是Header Main Footer组件的父组件
Main组件是Banner ProductList组件的父组件
在开发过程中 经常遇到需要组件之间相互进行通信:
如 App可能使用了多个 Header 每个地方的Header展示的内容不同 就需要使用者传递给Header一些数据来进行展示
我们在Main中一次性 请求了Banner数据和ProductList数据,那么就需要传递给它们来进行展示
也可能是子组件中发生了事件 需要由父组件来完成某些操作 就需要子组件向父组件传递事件
二、父组件传递子组件
父子组件之间通信的方式
父子组件之间如何进行通信呢?
父组件传递给子组件:通过props属性
子组件传递给父组件:通过$emit触发事件
父组件传递给子组件
在开发中常见的就是 父子组件之间通信 如 父组件有一些数据 需要子组件展示:
通过props 来完成组件之间的通信
什么是Props呢?
Props是你可以在组件上注册一些自定义的attribute
父组件给这些attribute赋值,子组件通过attribute的名称获取到对应的值
Props有两种常见的用法:
方式一:字符串数组,数组中的字符串就是attribute的名称
方式二:对象类型 对象类型我们可以在指定attribute 名称的同时,指定他需要传递的类型 是否是必须的 默认值等
Props数组写法
Props的对象用法(掌握)
数组用法我们只能说明传入的attribute的名称,并不能对其进行任何形式的限制.
使用对象语法时 可以对传入都额内容限制更多
如制定传入的attribute的类型 , 是否必传 , 没有传入时 attribute为默认值
细节一: type的类型都可以是哪些?
String
Number
Boolean
Array
Object
Date
Function
Symbol
细节二:对象类型的其他写法
细节三:Prop的大小写命名
Prop的大小写命名
HTML中的attribute名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符
使用DOM中的模版时 camelCase(驼峰命名法)的prop名需要使用其等价的kebab-case(短横线分割命名)命名;
三、非props的attribute
非Prop的Attribute
什么是非Prop的attribute?
当我们传递给一个组件某个属性 但是该属性并没有定义对应的props或emits时 就称为非Prop的Attribute
常见的包括class style id属性等
Attribute继承
当组件有单个根节点时 非Prop的Attribute将自动添加到根节点的Attribute中
禁用Attribute继承和多根节点
不希望组件的根元素继承attribute,可以在组件中设置inheritAttrs:false:
禁用attribute继承的常见情况是需要将attribute应用于根元素之外的其他元素
我们可以通过$attrs来访问所有的 非props
多个根节点的attribute
多个根节点的attribute如果没有显示的绑定 那么会警告,我们必须手动的指定要绑定到哪一个属性
四、子组件传递父组件
子组件传递给父组件
什么情况下 子组件需要传递内容到父组件呢?
当 子组件有一些事件发生 的时候 如 在组件中发生了点击,父组件需要切换内容
子组件 有一些内容想要传递给父组件的时候
如何完成上面的操作?
1 我们需要在 子组件中定义好在某些情况下触发的事件名称
2 在 父组件中以v-on的方式传入要监听的事件名称, 并且绑定到对应的方法中
3 在子组件中发生某个事件的时候,根据事件名称触发对应的事件
自定义事件的流程
封装一个CounterOperation.vue的组件
内部其实是监听两个按钮的点击,点击之后通过this.$emit的方式发出去事件
JavaScript export default { name: "AddCounter", methods:{ btnAdd(count){ // console.log(count) // 改变App.vue文件中的counter的数值 // 让子组件 发出一个自定义事件 // 第一个参数 是自定义的事件名称,第二个参数是传递的参数 this.$emit("add",count) } } }
自定义事件的参数和验证
自定义事件的时候 我们也可以传递一些参数给父组件
JavaScript btnSub(count){ // this.$emit(参数名称,传参值) this.$emit('sub',count) }
在Vue3中 我们可以对传递的参数进行验证:
JavaScript // 2 emits 对象语法 好处:可以给参数进行验证 emits:{ // null 表示 不做任何验证 add:function(count){ if(count <= 10){ return true } return false } },
五、组件通信案例练习
实现效果:
App.vue文件代码
JavaScript <template> <div class="app"> <!-- 1 tab-control--> <tab-control/> <!-- <tab-control :names="['电脑','手机','平板']" />--> <!-- 2 展示内容--> <h1>{{products[currentIndex]}}</h1> </div> </template> <script> import tabControl from "@/04_组件通信_案例练习/TabControl"; export default { name: "App", components: { tabControl }, data(){ return { products:["衣服列表","鞋子列表","裤子列表"], currentIndex:0 } }, methods:{ tabItemClick(index){ console.log("app",index) this.currentIndex = index } } } </script> <style scoped> </style>
TabControl.vue文件代码
JavaScript <template> <div class="tab-control"> <template v-for="(item,index) in names" :key="item"> <!-- :class="{active:index===currentIndex} 如果 索引号全等于 下面的索引号 就为true--> <div class="tab-control-item" :class="{active:index===currentIndex}" @click="itemClick(index)">{{item}}</div> </template> </div> </template> <script> export default { name: "TabControl", // 让别人告诉我展示数据 props:{ names:{ type:Array, //如果没有传值 进来 就为 默认值 default:()=>['衣服','鞋子','裤子'] } }, data(){ return { // 记住 索引很重要 点击谁 就赋值给它 currentIndex:0 } }, methods:{ //这里发生点击 外部不知道 要穿出去 emits:["tabItemClick"], itemClick(index){ this.currentIndex = index // console.log(this.currentIndex) this.$emit("tabItemClick",index) } } } </script> <style scoped> .tab-control { display: flex; height: 44px; line-height: 44px; text-align: center; background-color: pink; } .tab-control-item { flex: 1; } .tab-control-item.active { color:skyblue; border-bottom: 3px solid red; font-weight: 700; } </style>