# 一、动态组件
动态组件是指 动态的切换组件的显示和隐藏 。先来一个案例体验一些吧:
这个部分的需求应用非常广泛,几乎所有的网站应用都会使用到。
# 效果思考
<template>
<div>
<div class="tabs">
<button
v-for="item in tabs"
:key="item"
:class="{ active: currentTab === item }"
@click="tabClick(item)"
>
{{ item }}
</button>
</div>
<div class="main">
<template v-if="currentTab == 'home'">
<home-view></home-view>
</template>
<template v-else-if="currentTab == 'category'">
<category></category>
</template>
<template v-else>
<home-view></home-view>
</template>
</div>
</div>
</template>
<script>
import Home from './view/HomeView'
import Category from './view/Category'
import About from './view/AboutWorld'
export default {
name: 'App',
components: {
Home,
Category,
About
},
data() {
return {
tabs: ['home','category','about']
currentTab: 'Home'
}
},
methods: {
tabClick(item) {
this.currentTab = item
}
}
}
</script>
# 1、使用示例
那如果实现动态组件呢?我们可以使用 vue 提供的<component>
标签,它和 slot
标签一样,是作为占位符存在。不同的是,<component>
标签是为要所以的子组件占位。
在 component 标签中使用 is 属性可以决定最终使用/显示的是那个子组件。
<template>
<div>
<div class="main">
<!-- 动态组件 🤔 -->
<component :is="currentTab"></component>
</div>
</div>
</template>
当然 component 标签可以和子组件标签一样,利用 props 属性和自定义事件进行数据交互。
<component
active-color="#4fc08d"
:dot="isShow"
@click-tab="tabClickFn"
:is="currentTab"
></component>
# 2、组件状态 ✨
上面的小案例貌似没有什么问题,但在其他应用场景却可能存在 bug:
问题
若像京东 App 这样的,在下面在加一个购物车(Cart.vue),来回切换时,发现购物车中原先选好的商品不见了 —— 回到了购物车的原始界面。
默认情况下组件之间切换的时候是无法保持组件的状态,并且每次切换新标签的时候,Vue 都创建了一个新的 currentTabComponent 实例,这样容易反复重新渲染导致的性能问题。
验证
<script>
export default {
// 离开Cart组件时,组件被销毁
destroyed() {
console.log('组件被销毁')
},
// 回到Cart组件时,重新创建组件
created() {
console.log('组件被创建')
}
}
</script>
此时我们可以使用 vue 内置的 <keep-alive> 组件可以保持动态组件的状态。
注意
<keep-alive>
要求被切换到的组件都有自己的名字,不论是通过组件的 name 选项,还是局部/全局注册。
<template>
<div>
<div class="main">
<!-- 保持组件状态 -->
<keep-alive>
<component :is="currentTab"></component>
</keep-alive>
</div>
</div>
</template>
原理解析
通过 vue 调试工具可以发现,切换页面时,对<Cart>标签
进行了 inactive 标记。
<script>
export default {
// 离开Cart组件时,它会被缓存(同时自动触发组件的 deactivated生命周期函数)
deactivated() {
console.log('Cart组件被缓存')
},
// 回到Cart组件时,它会被激活(同时自动触发组件的 activated生命周期函数)
activated() {
console.log('Cart组件被激活')
}
}
</script>
# 3、include 属性 🤔
在上面的案例中我们发现,若使用了 keep-alive,那么所以的组件都会被缓存,而对于 Ower 组件,显然是没有必要的。
而 keep-alive (opens new window) 中的 include 属性 可以指定哪些组件会被缓存(多个组件名之间用英文逗号隔开)。对应的,也有一个指定哪些组件不用被缓存的 exclude 属性,但二者不能同时使用。
<template>
<div>
<!-- 指定组件保持状态 -->
<div class="main">
<keep-alive exclude="home, about">
<component :is="currentTab"></component>
</keep-alive>
</div>
</div>
</template>
如果在子组件中有可用的 name 选项,但其选项值和父组件中 include 属性值不同的话,include 属性是不会生效的。
原理及案例演示
若使用了include
/ exinclude
属性,它会首先检查子组件的 name 选项,如果该 name 值不可用,则匹配父组件 components 选项的键值)。匿名组件不能被匹配。
- 父组件
<template>
<div>
<div class="main">
<!-- 这里的exclude使用值为:👀about -->
<keep-alive include="category">
<component :is="currentTab"></component>
</keep-alive>
</div>
</div>
</template>
- 子组件
<script>
export default {
// name值为: CategoryView, 和 exclude 属性值: category 不一致
name: 'CategoryView'
}
</script>
所以,我们引入子组件时,✍ 注册的组件名字尽量和原子组件的 name 选项值相同。
# 二、其他
# 1、vue 路由
对于大多数单页面应用,都推荐使用官方支持的 vue-router 库 (opens new window)。更多细节可以移步 vue-router 文档 (opens new window) 。
<template>
<div>
<!-- 1、以前 -->
<div class="box">
<keep-alive>
<component :is="comp_Name"></component>
</keep-alive>
</div>
<!-- 2、现在 -->
<div class="box">
<keep-alive>
<router-view></router-view>
</keep-alive>
</div>
</div>
</template>
# 2、异步组件
<script>
// import Home from './view/HomeView'
export default {
name: 'App',
components: {
// Home
home: () => import('./view/HomeView')
}
}
</script>