1. 组件的嵌套和拆分
之前我们创建了一个App.vue组件,如将逻辑全放一个组件这太过臃肿
组件化的核心就是拆分一个个小组件,然后进行组合嵌套形成我们的应用程序
如果App.vue组件内容如下:
我们可以按照如下的方式进行拆分:
拆分后App组件是Header、Main、Footer组件的父组件,Main组件是Banner、ProductList组件的父组件
但是拆分后就要涉及到每个组件的通信,即相互交换数据
2.组件样式的scoped属性
- 作用:让样式在局部生效,防止冲突。
- 写法:
<style scoped>
3.父子组件之间通信的方式
父组件传递给子组件:通过props属性
子组件传递给父组件:通过$emit触发事件
通过props属性
- 功能:让组件接收外部传过来的数据
- 传递数据:
<Demo name="xxx"/>
接收数据:
-
第一种方式(只接收):
props: ['name']
-
第二种方式(限制类型):
props: {name: String}
或props: [String, Number]
-
第三种方式(限制类型、限制必要性、指定默认值):
备注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据
type的属性可以为String、Number、Boolean、Array、Object、Date、Function、Symbol
TML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符
使用传值时候建议kebab-case (短横线分隔命名) ,接受的时候可以为camelCase (驼峰命名法)
非Prop的Attribute
当我们传递给一个组件某个属性,但是该属性并没有定义对应的props或者emits时,就称之为 非Prop的 Attribute
常见的包括class、style、id属性等
当组件由单个根节点的时候,非Prop的Attribute将自动添加到根节点的Attribute中
如果我们不希望组件的根元素继承attribute,可以在组件中设置 inheritAttrs: false
根元素不继承了,但是我们依然可以通过 $attrs来访问所有的 非props的attribute赋值给其他元素
多个根节点的attribute如果没有显示的绑定,那么会报警告,我们必须手动的指定要绑定到哪一个属性上
通过$emit触发自定义事件
一种组件间通信的方式,适用于:子组件 ===> 父组件
我们需要在子组件中定义好在某些情况下触发的事件名称
然后在父组件中以v-on的方式传入要监听的事件名称,并且绑定到对应的方法中
最后,在子组件中触发事件,根据事件名触发对应父组件绑定的事件
绑定自定义事件:
-
在父组件中:
<Demo @handlerYun="test"/>
或<Demo v-on:handlerYun="test"/>
-
在在父组件中:
<Demo ref="demo"/>
......
mounted(){
// 若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法。
// 回调要么配置在methods中,要么用箭头函数,否则this指向会出问题
this.$refs.xxx.$on('handlerYun',this.test)
}
子组件触发自定义事件:
this.$emit('handlerYun',数据)
解绑自定义事件:
this.$off('handlerYun'), // 解绑自定义事件
this.$off(['name1','name2']) // 解绑多个自定义事件
this.$off() // 解绑全部的自定义事件
this.destroy() // 销毁当前组件的实例,销毁后的全部组件实例的自定义事件全都不奏效。
注意:组件上也可以绑定原生DOM事件,需要使用native
修饰符。
我们封装一个CounterOperation.vue的组件,内部其实是监听两个按钮的点击,点击之后通过 this.$emit的方式发出去事件
我们也可以传递参数给自定义事件
在vue3当中,我们可以对传递的参数进行验证,emits变为对象写法
4.商品页面的切换
App.vue
<template>
<div>
<tab-control :titles="titles" @titleClick="titleClick"></tab-control>
<h2>{{ contents[currentIndex] }}</h2>
</div>
</template>
<script>
import TabControl from "./TabControl.vue";
export default {
components: {
TabControl,
},
data() {
return {
titles: ["衣服", "鞋子", "裤子"],
contents: ["衣服页面", "鞋子页面", "裤子页面"],
currentIndex: 0,
};
},
methods: {
titleClick(index) {
this.currentIndex = index;
},
},
};
</script>
<style scoped></style>
TabControl.vue
<template>
<div class="tab-control">
<div
class="tab-control-item"
:class="{ active: currentIndex === index }"
v-for="(title, index) in titles"
:key="title"
@click="itemClick(index)"
>
<span>{{ title }}</span>
</div>
</div>
</template>
<script>
export default {
emits: ["titleClick"],
props: {
titles: {
type: Array,
default() {
return [];
},
},
},
data() {
return {
currentIndex: 0,
};
},
methods: {
itemClick(index) {
this.currentIndex = index;
this.$emit("titleClick", index);
},
},
};
</script>
<style scoped>
.tab-control {
display: flex;
}
.tab-control-item {
flex: 1;
text-align: center;
}
.tab-control-item.active {
color: red;
}
.tab-control-item.active span {
border-bottom: 3px solid red;
padding: 5px 10px;
}
</style>