一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情。
动态组件
1. 什么是动态组件
动态组件指的是动态切换组件的显示与隐藏。
2. 如何实现动态组件渲染
vue 提供了一个内置的 compoent 组件(我们也可以将该组件理解为我们要展示的组件的占位符),专门用来实现动态组件的渲染。通过该 component 组件的 is 属性来指定要显示的组件的名称。
示例代码如下;
<template>
<div class="app-container">
<!-- 渲染 Left 组件和 Right 组件 -->
<component :is="comName"></component>
</div>
</template>
<script>
import Left from './components/Left.vue'
import Right from './components/Right.vue'
export default {
data(){
return{
// 表示要进行展示的组件的名字
comName:'Left'
}
},
components:{
Left,Right
}
}
</script>
我们来实现以下点击响应的按钮,实现展示组件的切换:
<button @click="comName='Left'">展示 Left</button>
<button @click="comName='Right'">展示 Right</button>
<div class="box">
<!-- 渲染 Left 组件和 Right 组件 -->
<component :is="comName"></component>
</div>
<script>
export default{
data(){
return{
// 表示要进行展示的组件的名字
comName:'Left'
}
},
}
</script>
3. 使用 keep-alive 保持状态
默认情况下,切换动态组件时无法保持组件的状态(就相当于每一次返回到对应的组件,组件中的内容就会被重置====销毁,重新创建)。此时可以使用 vue 内置的 keep-alive 组件保持动态组件的状态。可以使组件被切换为其他组件的之后,被切换的组件不会被销毁(被缓存,状态是 inactive )
示例代码如下:
<keep-alive>
<component :is="comName"></component>
</keep-alive>
4. keep-alive 对应的生命周期函数
当组件被缓存时,会自动触发组件的 deactivated 生命周期函数。
当组件被激活时,会自动触发组件的 activated 生命周期函数。
<script>
export default {
activated(){
console.log('组件被激活了!')
},
deactivated(){
console.log('组件被缓存了!')
}
}
</script>
5. keep-alive 的 include 属性
默认情况下,keep-alive 标签里面包含的内容都是会被缓存的;但是某些情况下我们并不需要相应的内容都被缓存,这就可以使用 include 属性来解决。
include 属性用来指定:只有名称匹配的组件会被缓存。多个组件名之间使用英文的逗号分隔;同时和他对应的还有一个属性就是 exclude ,这个属性的属性值包含的内容是指定其不被缓存,但是两者不能同时使用。
<keep-alive include="Left,Right">
<component :is="comName"></component>
</keep-alive>
叭叭小知识:
-
当我们不给我们调用的组件给 name 属性的话,默认的就是定义的时候的标签名;当我们提供了 name 属性之后,组件的名称就是 name 属性的值(include 和 exclude 属性里面对应的名称也要和我们定义的 name 的属性名一致)
-
组件的注册名称的主要应用场景是:以标签的形式,把注册好的组件,渲染和使用到页面结构之中
-
组件声明时候的 name 属性名称的主要应用场景:结合 keep-alive 标签实现组件的缓存功能(include 和 exclude 里面内容的指定),以及在调试工具中看到组件的 name 名称
下面是具体的示例代码:
APP.vue 中的代码
<template>
<div class="app-container">
<keep-alive include="Left">
<component is="MyLeft"></component>
</keep-alive>
</div>
</template>
<script>
import Left from './components/Left.vue'
export default {
components:{
Left
},
}
</script>
Left.vue 中的代码:
<script>
export default {
name:"MyLeft"
}
</script>
插槽
1. 什么是插槽
插槽(Slot)是 vue 为组件的封装者提供的能力。允许开发者在封装组件时,把不确定的、希望由用户指定的部分定义为插槽。
可以把插槽认为是组件封装期间,为用户预留的内容的占位符,然后在调用组件的时候在组件的标签里面使用的标签来占取之前定义标签组件的占位符的位置。
2. 体验插槽的基础用法
在封装组件时,可以通过 slot 元素定义插槽,从而为用户预留内容占位符。示例代码如下:
//Left 组件
<template>
<div class="left-container">
<h3>Left 组件</h3>
<!-- 声明一个插槽 -->
<!-- vue 官方规定:每一个 solt 插槽,都要有一个 name 名称,如果省略了该属性,则会有一个默认的名称 default -->
<slot name="default"></slot>
</div>
</template>
//App 组件
<template>
<div class="app-container">
<h1>App 根组件</h1>
<hr />
<div class="box">
<!-- 渲染 Left 组件和 Right 组件 -->
<Left>
<!-- 默认情况下:在使用组件的时候,提供的内容都会被填充到名字为 default 的插槽中 -->
<p>插槽体验</p>
<!-- 指定要显示相应内容的插槽的做法--使用 v-slot 指令(简写为 # )指定插槽名(要注意的是:该属性只能用在组件 component 标签和 template 标签上面,不能直接使用在其他元素上上面) -->
<!-- template 标签只是起到一个包裹器的作用,并不会渲染成真正的元素(或者说是 其他实质性的 html 元素) -->
<template v-slot:default>
<p>指定要显示相应内容的插槽 </p>
</template>
</Left>
</div>
</div>
</template>
<script>
import Left from './components/Left.vue'
export default {
components:{
Left
}
}
</script>
2.1 没有预留插槽的内容会被丢弃
如果在封装组件时没有预留任何 slot 插槽,则用户提供的任何自定义内容都会被丢弃。即在封装的组件里面没有使用 slot 的话,在使用该组件的时候,该组件的标签中间的内容会直接被系统默认丢弃(删除)
当然,如果在组件中定义了插槽,但是在使用的时候,在这个组件标签里面,并没有填放内容,那么会默认什么也不显示。
2.2 后备内容
封装组件时,可以为预留的 slot 插槽提供后备内容(默认内容)。如果组件的使用者没有为插槽提供任何内容,则后备内容会生效。示例代码如下:
<template>
<div class="left-container">
<h3>Left 组件</h3>
<!-- 声明一个插槽 -->
<slot>
<p> 后备内容--默认内容 </p>
</slot>
</div>
</template>
3. 具名插槽
如果在封装组件时需要预留多个插槽节点,则需要为每个 slot 插槽指定具体的 name 名称。这种带有具体名称的插槽叫做“具名插槽”。
注意:
-
一个组件里面可以有多个具有相同的 name 属性的插槽,在调用组件的时候,指定 name 的插槽里面显示的内容,会在具有该 name 名称的所有插槽的位置显示该指定的内容。
-
没有指定 name 名称的插槽,会有隐含的名称叫做 “default”。
-
在向具名插槽提供内容的时候,我们可以在一个 template 元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称。
-
跟 v-on 和 v-bind 一样,v-slot 也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符 #。例如 v-slot:header
可以被重写为 #header
//组件代码 Artilce.vue
<template>
<div class="article-container">
<h3 v-color="'red'">Article 组件</h3>
<!-- 文章的标题 -->
<div class="header-box">
<slot name="title"></slot>
</div>
<!-- 文章的内容 -->
<div class="content-box">
<!-- 在封装组件时,为预留的 <slot> 提供属性对应的值,这种用法,叫做 “作用域插槽” -->
<!-- 在定义插槽的时候使用一些自定义属性,可以对象的形式传递给调用该组件的组件 -->
<slot name="content" msg="hello vue.js" :user="userinfo"></slot>
</div>
<!-- 文章的作者 -->
<div class="footer-box">
<slot name="author"></slot>
</div>
</div>
</template>
<script>
export default {
// 首字母要大写
name: 'Article',
data() {
return {
// 用户的信息对象
userinfo: {
name: 'zs',
age: 20
}
}
}
}
</script>
//组件调用
<Article>
<template #title>
<h3>一首诗</h3>
</template>
<!-- 我们接收作用于插槽的自定义属性对象的时候,接收对象的名称,我们一般使用 scope -->
<!-- <template #content="scope"> -->
<!-- 这样我们后面使用的时候就可以直接使用该对象得到其属性===比如:scope.msg -->
<!-- 下面的这种写法是对对象进行结构接收(可以用几个接收几个)--格式为 { 要接收对象的属性名(中间使用 ',' 分隔) } -->
<template #content="{ msg, user }">
<div>
<p>啊,大海,全是水。</p>
<p>{{ msg }}</p>
<p>{{ user.name }}</p>
</div>
</template>
<template #author>
<div>作者:彬果锅</div>
</template>
</Article>
小练手:
把之前的那个购物车案例进行一下重构(优化):对其计数器( Counter )部分进行优化