让多个组件使用同一个挂载点,并动态切换,这就是动态组件
1. 实现
动态组件借助component
标签和它的is
属性实现:
<div id="app">
<!-- 原来的写法 -->
<test></test>
<!-- 改为动态组件 -->
<component is="test"></component>
</div>
2. 组件缓存
把组件的引入改为动态引入后,每次加载组件都会重新渲染,出于性能考虑,为避免多次重复渲染降低性能。Vue 提供了keep-alive
标签用于组件缓存,切换组件后,原来的组件不会被清除,而是维持当前的状态。
官网解释:<keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 <transition> 相似,<keep-alive> 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在父组件链中。 当组件在 <keep-alive> 内被切换,它的 activated 和 deactivated 这两个生命周期钩子函数将会被对应执行。 在 2.2.0 及其更高版本中,activated 和 deactivated 将会在 <keep-alive> 树内的所有嵌套组件中触发。 主要用于保留组件状态或避免重新渲染。
示例:
<div id="app">
<keep-alive>
<!-- 被包裹的动态组件可以被缓存 -->
<component :is="view"></component>
</keep-alive>
</div>
应用场景
如果未使用keep-alive组件,则在页面回退时仍然会重新渲染页面,触发created钩子,使用体验不好。 在以下场景中使用keep-alive组件会显著提高用户体验,菜单存在多级关系,多见于列表页+详情页的场景如:
- 商品列表页点击商品跳转到商品详情,返回后仍显示原有信息
- 订单列表跳转到订单详情,返回,等等场景。
2.1 keep-alive的生命周期
- 初次进入时:created > mounted > activated;退出后触发 deactivated
- 再次进入:会触发 activated;
事件挂载的方法等,只执行一次的放在 mounted 中;组件每次进去执行的方法放在 activated 中
1. 简介
Vue 实现了一套内容分发的 API,将 元素作为承载分发内容的出口。
Slot 通俗的理解就是“占坑”,在组件模板中占好了位置,当使用该组件标签时候,组件标签里面的内容就会自动填坑(替换组件模板中slot
位置)
并且可以作为承载分发内容的出口 .
插槽(Slot)是Vue提出来的一个概念,正如名字一样,插槽用于决定将所携带的内容,插入到模板template指定的某个位置,从而使模板分块,具有模块化的特质和更大的重用性。插槽显不显示、怎样显示是由父组件来控制的,而插槽在哪里显示就由子组件来进行控制。
插槽含义:引入子组件后,在插入子组件元素中添加信息或者标签,使得子组件的指定位置插入信息或者标签
插槽有三种:默认插槽、具名插槽、作用域插槽.
插槽,其实就相当于占位符。它在组件中给你的HTML模板占了一个位置,让你来传入一些东西。插槽又分为匿名插槽、具名插槽以及作用域插槽。
没有插槽的情况下在组件标签内些一些内容是不起任何作用的,当我在组件中声明了slot元素后,在组件元素内写的内容就会跑到它这里了!
插槽是父组件与子组件新的通讯的方式,可以将父组件里面的内容显示到子组件中(包括标签)
2. 插槽使用
创建 product.vue 组件,然后引入 productDetail.vue 组件
<template>
<div>
<h3>product.vue组件</h3>
<!-- 标签内的内容 会出现在 子组件写的插槽位置 -->
<productDetail>hello productDetail </productDetail>
</div>
</template>
<script>
import productDetail from "./produceDetail"
</script>
创建 productDetail.vue 组件
<template>
<div>
<h3>productDetail.vue组件</h3>
<!-- slot 标签就是插槽标签,它会被替换为 父组件使用子组件标签时写在标签内的内容 如:hello productDetail -->
<slot></slot>
</div>
</template>
当组件渲染的时候,<slot></slot>
会被替换为hello productDetail
插槽内也可以包含任何模板代码,包括HTML
<h3>product.vue组件</h3>
<productDetail>
<!-- 添加标签 -->
<h3>hello productDetail</h3>
</productDetail>
如果<productDetail>
中没有包含一个<slot>
元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃。
在插槽中使用数据
插槽跟模板其他地方一样都可以访问相同的实例属性而不能访问<productDetail>
的实例属性
<template >
<div>
<h3>product.vue组件</h3>
<productDetail>
<!-- 可以访问到 product 的数据 -->
<h3>hello productDetail--{{count}}</h3>
</productDetail>
</div>
</template>
<script>
import productDetail from "./ProductDetail"
export default {
data() {
return {
count:100
}
},
components:{
productDetail
}
}
</script>
规则:
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
3. 默认插槽 (匿名)
有时候我们需要给插槽设置一个具体的默认内容,当别的组件没有给你内容的时候,那么默认的内容就会被渲染
<template>
<div>
<h3>productDetail.vue组件</h3>
<!-- 在slot插槽里设置默认内容 hello -->
<slot>hello</slot>
</div>
</template>
在 product 组件中直接使用,默认内容 hello 将会被渲染。
4. 具名插槽
有时候我们一个组件里需要多个插槽,对于这样的情况,slot
标签通过name
属性区分不同的插槽。
默认插槽的name
值为default
。父组件通过在template
标签上写v-slot:name
属性来给对应的插槽插入内容。
注意:v-slot
属性只能添加在template
标签上。
<template>
<div>
<h3>productDetail.vue组件</h3>
<!-- 插槽默认名字为 default -->
<slot></slot>
<!-- 给插槽起名字 -->
<slot name="title"></slot>
<slot name="price"></slot>
</div>
</template>
product.vue 组件
<template >
<div>
<h3>product.vue组件</h3>
<productDetail>
<!-- 通过 template 标签添加 v-slot:插槽名 给对应的插槽赋值 -->
<template v-slot:title>
<h3>OPPO Reno6 Pro+ 12+256GB</h3>
</template>
<template v-slot:price>
<h3>¥ 3499.00</h3>
</template>
</productDetail>
</div>
</template>
缩写:
跟v-on
和v-bind
一样,v-slot
也有缩写,可以将v-slot:
替换为字符 #
<productDetail>
<template #title>
<h3>OPPO Reno6 Pro+ 12+256GB</h3>
</template>
<template #price>
<h3>¥ 3499.00</h3>
</template>
</productDetail>
5. 作用域插槽
如果我们想要在插槽内使用子组件的数据,可以使用作用域插槽。
- 在子组件中将父组件使用的数据绑到
<slot>
上 - 在父组件中用
v-slot
设置一个值来接收子组件传递的属性
<template>
<div>
<h3>productDetail.vue组件</h3>
<!-- 在子组件中添加对应的属性,将值传递给插槽 -->
<slot name="student" :student="people"></slot>
<slot name="goods" :goods="goodsList"></slot>
</div>
</template>
<script setup>
export default {
data(){
return {
id:'100026761908',
people:{
name:'lucky',
age:12
},
goodsList:[
{
id:1102,
name:'戴尔(DELL)游匣G15 15.6英寸',
price:'¥ 5199.00'
},
{
id:'1106',
name:'美的(Midea)S8+自动集尘扫拖机器人',
price:'¥ 2499.00'
},
{
id:'11056',
name:'Apple Watch SE',
price:'¥ 2119.00'
}
]
}
}
}
</script>
product.vue 组件:
<productDetail>
<!-- 通过命名组件="变量" 接收子组件传递的值 -->
<template #student="studentDetail">
<!-- 变量的值是一个对象,属性由子组件传递的属性组成 -->
{{studentDetail}}
<p>
当前学生的名字是:{{studentDetail.student.name}},
当前学生的姓名是:{{studentDetail.student.age}}
</p>
</template>
</productDetail>
5.1 解构传值
我们可以使用解构的形式获取到子组件传递给插槽的值:
product.vue 组件:
<productDetail>
<!-- 解构获取 -->
<template #student={student}>
{{student}}
<p>
当前学生的名字是:{{student.name}},
当前学生的姓名是:{{student.age}}
</p>
</template>
</productDetail>