动态组件与keepAlive+插槽
动态组件(component配合 :is属性)
提出原因:
v-show/ v-if 因为参数形式是boolean属性不能满足动态选择子组件的显示
样例:(实现点击子组件的名字显示子组件内容)
<!-- 父组件 -->
<template>
<!-- 动态组件, is: 需要渲染的组件名称-->
<!-- 这里的显示相当于v-show和v-if -->
<!-- <component is="tabMonday"></component> -->
<!-- 实现子组件的切换 定义数组(遍历数组呈现数组名称,通过选择数组中元素的名称作为唯一键显示该子组件的内容)-->
<!-- 再定义一个点击事件,当选中的标签赋给当前元素item,当遍历的数组元素等于目前的选中的元素就加上一个class属性:active:true|false 显示当前的样式 -->
<button v-for="item in tabArr" :key="item" @click="currentTab = item" :class="{ active: currentTab === item }">
{{ item }}
</button>
</template>
<script>
import tabMonday from "./components/tabMonday.vue";
import tabTuesday from "./components/tabTuesday.vue";
import tabWednesday from "./components/tabWednesday.vue";
export default {
data() {
return {
currentTab: "tabMonday", // 当前需要渲染的组件名称
tabArr: ["tabMonday", "tabTuesday", "tabWednesday"], // 需要动态切换组件的名称
};
},
components: {
tabMonday,
tabTuesday,
tabWednesday,
},
};
</script>
<style scoped>
.active {
background-color: #f00;
}
</style>
<!-- 子组件1 -->
<template>
<!-- <div>周一:逆天战纪</div> -->
<div>
周一
</div>
</template>
<!-- 子组件2 -->
<template>
<!-- <div>周二:我是不白吃</div> -->
<div>
周二
</div>
</template>
<template>
<!-- <div>周三:长剑风云</div> -->
<div>
周三
</div>
</template>
KeepAlive
运用场景:
当子组件存在复选框等需要被记录的数据,原本component组件不能满足,因为component组件本质上是讲子组件中的标签进行一个注册和删除,当切换时会自动移除上一个被选中的子组件
本质:
缓存原本应该被移除的组件实例, 在组件第一次被创建(渲染)时缓存起来
代码:
<KeepAlive >
<component :is="currentTab"></component>
</KeepAlive>
拓展:条件性的选择哪些组件被缓存
前提:相关的组件(子组件)必须指定name选项来做区分,不然是没有办法根据组件名字匹配
<!-- 父组件 -->
<!-- 注意直接匹配不用加属性绑定(:)正则和数组需要加 -->
<!-- include: 包含的就缓存 -->
<KeepAlive include="tabMonday,tabTuesday">
<component :is="currentTab"></component>
</KeepAlive>
<!-- 正则 -->
<KeepAlive :include="/tabMonday|tabTuesday/">
<component :is="currentTab"></component>
</KeepAlive>
<!-- 数组 -->
<KeepAlive :include="['tabMonday', 'tabTuesday']">
<component :is="currentTab"></component>
</KeepAlive>
<!-- exclude: 匹配名字的组件都不缓存, 未匹配到名字的组件就缓存 -->
<KeepAlive exclude="tabMonday,tabTuesday">
<component :is="currentTab"></component>
</KeepAlive>
<KeepAlive :exclude="/tabMonday|tabTuesday/">
<component :is="currentTab"></component>
</KeepAlive>
<KeepAlive :exclude="['tabMonday', 'tabTuesday']">
<component :is="currentTab"></component>
</KeepAlive>
<!-- max:最大缓存的实例数量。如果超出最大数,会将缓存里最早最久没有被访问的实例销毁掉,为新组件实例腾空间 -->
<KeepAlive :max="10">
<component :is="currentTab"></component>
</KeepAlive>
<!-- 子组件 -->
<template>
<!-- <div>周二:我是不白吃</div> -->
<div>
周二
</div>
</template>
<script>
export default{
name: "tabTuesday" //其他同理需要定义自己的name
}
</script>
总结:
1.不使用缓存时,组件的切换是: mounted挂载 / unmounted销毁
2.缓存的组件实例的生命周期. 组件的切换是:激活/不活跃的状态变化
****缓存时切换组件状态会自动调用子组件的的两个钩子函数(activated和deactivated)
代码:
<script>
export default{
name: "tabMonday", // 选项式API要加, 组合式API不用加
activated(){
// 激活时
console.log("%c 激活时---activated", "color: #f00");
},
deactivated(){
// 不活跃时
console.log("%c 不活跃时---deactivated", "color: #f00");
}
}
</script>
插槽
作用:
设计封装时, 可以接收外部传入的内容
构成:
1.入口:父组件运用子组件双标签中间的内容(内容包含父组件data数据[插值函数]、其他组件)
2.出口:子组件添加组件显示【预留的内容占位符(内容指的是写在组件标签之间的 内容)】
代码:
<!-- 父组件 -->
<template>
<p>App.vue根组件</p>
{{ age }}
<hr>
<Child>
这里是父组件向插槽提供的内容 <!-- 普通元素 -->
<p>这是父组件传过来的内容--{{ name }}</p> <!-- 父元素插值函数 -->
<p>这是父组件传过来的内容--{{ age }}</p>
<tabMonday/> <!-- 导入的其他组件 -->
</Child>
</template>
<script>
import Child from "./components/Child.vue";
import tabMonday from "./components/tabMonday.vue";
export default {
data() {
return {
name: "丸子",
age: 16
};
},
components: {
Child,
tabMonday
},
};
</script>
<!-- 子组件 -->
<template>
<div>Child</div>
<slot>
<p>插槽的默认内容</p>
... <!-- <slot>标签中可以定义插值的默认内容(当父元素没有给值显示) -->
</slot>
</template>
上述代码是子组件只有一个插槽函数时例子,当子组件有多个插槽怎么办呢?
子类多插槽:
针对多个插槽出口时,v-slot属性搭配template元素(v-slot:子组件name属性)
代码:
<!-- 父组件 -->
<Child>
22222-----这里的内容是没有指定给哪个name插槽的 <!-- 会传递给默认插槽 -->
<template v-slot:wanzi>
<p>这里写的都是给wanzi的</p> <!-- 会传递给名为wanzi插槽 -->
</template>
<template v-slot:cherry>
<p>这里写的都是给cherry的</p>
</template>
<template v-slot:afei>
<p>这里写的都是给afei的</p>
</template>
1111-----这里的内容是没有指定给哪个name插槽的 <!-- 会传递给默认插槽 -->
</Child>
<!-- v-slot 简化写法 #-->
<!-- <Child>
22222-----这里的内容是没有指定给哪个name插槽的
<template #wanzi>
<p>这里写的都是给wanzi的</p>
</template>
<template #cherry>
<p>这里写的都是给cherry的</p>
</template>
<template #afei>
<p>这里写的都是给afei的</p>
</template>
1111-----这里的内容是没有指定给哪个name插槽的
</Child> -->
<!-- 子组件 -->
<!-- 默认插槽。 它的name名为default 。接收的是没人要的内容(默认内容)-->
<slot name="default"></slot> <!-- 这里的name="default"可以不加 -->
<header>
<slot name="wanzi"></slot>
</header>
<main>
<slot name="cherry"></slot>
</main>
<footer>
<slot name="afei"></slot>
</footer>
子类向父类传递数据
v-slot属性点子类传向父类属性别名
代码
<!-- 父组件 -->
<Child v-slot="slotProps">
<p>这是父组件传过来的内容--{{ name }}</p>
<p>这是父组件传过来的内容--{{ age }}</p>
<!-- 在书写内容的位置,访问当前Child组件的数据 -->
<p>{{ slotProps.username }}---{{ slotProps.usertag }}</p>
</Child>
<!-- 子组件 -->
<!-- 作为props传递时, 书写名不能与当前组件的data数据同名 -->
<template>
<slot :username="name" :usertag="tag"></slot>
</template>
<script>
export default{
data(){
return {
name: "周四",
tag: "再坚持1天~"
}
}
}
</script>
插槽赋值也可以使用结构
代码:
<!-- 父组件 -->
<Child>
<template #wanzi="{username, usertag}">
<p>{{ username }}---{{ usertag }}</p>
</template>
<template v-slot="slotProps2">
<p>{{ slotProps2.xxname }}</p>
</template>
</Child>
<!-- 子组件 -->
<template>
<div>Child</div>
<!-- 作用域插槽 -->
<!-- 作为props传递时, 书写名不能与当前组件的data数据同名 -->
<slot name="wanzi" :username="name" :usertag="tag"></slot>
<slot :xxname="name"></slot>
</template>