动态组件
语法
有些场景会需要在两个组件间来回切换,比如 Tab 界面, 可通过<component>动态组件实现
HTML
<!-- Home为组件名 -->
<component is="Home"></component>
<component :is="'Home'"></component>
<!-- currentTab 改变时组件也改变 -->
<component :is="currentTab"></component>
案例
Home.vue
HTML
<template>
<div class="tab">
Home component
</div>
</template>
Posts.vue
HTML
<template>
<div class="tab">
Posts component
</div>
</template>
Archive.vue
HTML
<template>
<div class="tab">
Archive component
</div>
</template>
App.vue
HTML
<script>
import Home from './Home.vue'
import Posts from './Posts.vue'
import Archive from './Archive.vue'
export default {
components: {
Home,
Posts,
Archive
},
data() {
return {
currentTab: 'Home',
tabs: ['Home', 'Posts', 'Archive']
}
}
}
</script>
<template>
<div class="demo">
<button
v-for="tab in tabs"
:key="tab"
:class="['tab-button', { active: currentTab === tab }]"
@click="currentTab = tab"
>
{{ tab }}
</button>
<component :is="currentTab" class="tab"></component>
</div>
</template>
<style>
.demo {
font-family: sans-serif;
border: 1px solid #eee;
border-radius: 2px;
padding: 20px 30px;
margin-top: 1em;
margin-bottom: 40px;
user-select: none;
overflow-x: auto;
}
.tab-button {
padding: 6px 10px;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
border: 1px solid #ccc;
cursor: pointer;
background: #f0f0f0;
margin-bottom: -1px;
margin-right: -1px;
}
.tab-button:hover {
background: #e0e0e0;
}
.tab-button.active {
background: #e0e0e0;
}
.tab {
border: 1px solid #ccc;
padding: 10px;
}
</style>
Transition 过渡组件
Vue 提供了两个内置组件,可以帮助你制作基于状态变化的过渡和动画:
Transition 组件
认识Transition组件
Transition 是一个内置组件,这意味着它在任意别的组件中都可以被使用,无需注册。它可以将进入和离开动画应用到通过默认插槽传递给它的元素或组件上。进入或离开可以由以下的条件之一触发:
-
由 v-if 所触发的切换
-
由 v-show 所触发的切换
-
由特殊元素
<component>切换的动态组件 -
改变特殊的 key 属性
基础用法
HTML
<button @click="show = !show">Toggle</button>
<Transition name="fade">
<p v-if="show">hello</p>
</Transition>
CSS
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
TIP<Transition> 仅支持单个元素或组件作为其插槽内容。如果内容是一个组件,这个组件必须仅有一个根元素。 当一个 <Transition> 组件中的元素被插入或移除时,会发生下面这些事情: Vue 会自动检测目标元素是否应用了 CSS 过渡或动画。如果是,则一些 CSS 过渡 class 会在适当的时机被添加和移除。
CSS****过渡class
![[e3760062-2101-4ccb-bbbe-9e978971acf1.png]]
CSS****的animation
JavaScript
<Transition name="bounce">
<p v-if="show" style="text-align: center;">
Hello here is some bouncy text!
</p>
</Transition>
CSS
.bounce-enter-active {
animation: bounce-in 0.5s;
}
.bounce-leave-active {
animation: bounce-in 0.5s reverse;
}
@keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.25);
}
100% {
transform: scale(1);
}
}
可复用过渡效果
得益于 Vue 的组件系统,过渡效果是可以被封装复用的。要创建一个可被复用的过渡,我们需要为 <Transition> 组件创建一个包装组件,并向内传入插槽内容
JavaScript
<!-- MyTransition.vue -->
<script>
</script>
<template>
<!-- 包装内置的 Transition 组件 -->
<Transition
name="my-transition">
<slot></slot> <!-- 向内传递插槽内容 -->
</Transition>
</template>
<style>
/*必要的 CSS...
注意:避免在这里使用 <style scoped>
因为那不会应用到插槽内容上
*/
</style>
使用自定义过渡组件
HTML
<MyTransition>
<div v-if="show">Hello</div>
</MyTransition>
出现时过渡
如果你想在某个节点初次渲染时应用一个过渡效果,你可以添加 appear
prop:
HTML
<Transition appear>
</Transition>
过渡模式
先执行离开动画,然后在其完成之后再执行元素的进入动画
HTML
<Transition mode="out-in">
</Transition>
组件间过渡
<Transition> 也可以作用于动态组件之间的切换
HTML
<Transition name="fade" mode="out-in">
<component :is="activeComponent"></component>
</Transition>
动态过渡
HTML
<Transition :name="transitionName">
</Transition>
TransitionGroup 组件
会在一个 v-for 列表中的元素或组件被插入,移动,或移除时应用动画。
HTML
<script>
import { shuffle } from 'lodash'
const getInitialItems = () => [1, 2, 3, 4, 5]
let id = getInitialItems().length + 1
export default {
data() {
return {
items: getInitialItems()
}
},
methods: {
insert() {
const i = Math.round(Math.random() * this.items.length)
this.items.splice(i, 0, id++)
},
reset() {
this.items = getInitialItems()
},
shuffle() {
this.items = shuffle(this.items)
},
remove(item) {
const i = this.items.indexOf(item)
if (i > -1) {
this.items.splice(i, 1)
}
}
}
}
</script>
<template>
<button @click="insert">insert at random index</button>
<button @click="reset">reset</button>
<button @click="shuffle">shuffle</button>
<TransitionGroup tag="ul" name="fade" class="container">
<div v-for="item in items" class="item" :key="item">
{{ item }}
<button @click="remove(item)">x</button>
</div>
</TransitionGroup>
</template>
<style>
.container {
position: relative;
padding: 0;
}
.item {
width: 100%;
height: 30px;
background-color: #f3f3f3;
border: 1px solid #666;
box-sizing: border-box;
}
/* 1. 声明过渡效果 */
.fade-move,
.fade-enter-active,
.fade-leave-active {
transition: all 0.5s cubic-bezier(0.55, 0, 0.1, 1);
}
/* 2. 声明进入和离开的状态 */
.fade-enter-from,
.fade-leave-to {
opacity: 0;
transform: scaleY(0.01) translate(30px, 0);
}
/* 3. 确保离开的项目被移除出了布局流
以便正确地计算移动时的动画效果。 */
.fade-leave-active {
position: absolute;
}
</style>
Vue2和Vue3的区别
Vue2
CSS
/* vue2的类名没有from和to */
.fade-enter,
.fade-leave{
opacity: 0;
}
Vue3
CSS
/* vue3需要添加from和to*/
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
KeepAlive****组件 (重要)
<KeepAlive> 是一个内置组件,它的功能是在多个组件间动态切换时缓存被移除的组件实例。
案例
HTML
<!-- 非活跃的组件将会被缓存! -->
<KeepAlive>
<component :is="activeComponent" />
</KeepAlive>
包含/排除
include和exclude中的标识符为组件定义中的name选项的值
HTML
<!-- 以英文逗号分隔的字符串 -->
<!-- 包含 -->
<KeepAlive include="a,b">
<component :is="view" />
</KeepAlive>
<!-- 排除 -->
<KeepAlive exclude="a,b">
<component :is="view" />
</KeepAlive>
<!-- 正则表达式 (需使用 v-bind) -->
<KeepAlive :include="/a|b/">
<component :is="view" />
</KeepAlive>
<!-- 数组 (需使用 v-bind) -->
<KeepAlive :include="['a', 'b']">
<component :is="view" />
</KeepAlive>
最大缓存实例数
如果缓存的实例数量即将超过指定的那个最大数量,则最久没有被访问的缓存实例将被销毁,以便为新的实例腾出空间。
HTML
<KeepAlive :max="10">
<component :is="activeComponent" />
</KeepAlive>
缓存实例的生命周期
一个持续存在的组件可以通过 activated(激活) 和 deactivated(失活) 选项来注册相应的两个状态的生命周期钩子
JavaScript
export default {
activated() {
// 在首次挂载、
// 以及每次从缓存中被重新插入的时候调用
},
deactivated() {
// 在从 DOM 上移除、进入缓存
// 以及组件卸载时调用
}
}
这两个钩子不仅适用于 <KeepAlive> 缓存的根组件,也适用于缓存树中的后代组件
Teleport组件
vue2没有Teleport组件
<Teleport> 是一个内置组件,它可以将一个组件内部的一部分模板"传送"到该组件的 DOM 结构外层的位置去。
应用场景
有时我们可能会遇到这样的场景:一个组件模板的一部分在逻辑上从属于该组件,但从整个应用视图的角度来看,它在 DOM 中应该被渲染在整个 Vue 应用外部的其他地方。
这类场景最常见的例子就是全屏的模态框。理想情况下,我们希望触发模态框的按钮和模态框本身是在同一个组件中,因为它们都与组件的开关状态有关。但这意味着该模态框将与按钮一起渲染在应用 DOM 结构里很深的地方。这会导致该模态框的 CSS 布局代码很难写。
全屏模态框案例
Parent.vue
HTML
<script>
import Modal from './Modal.vue'
export default {
components: {
Modal
},
data() {
return {
showModal: false
}
}
}
</script>
<template>
<div class="top">
<button id="show-modal" @click="showModal = true">Show Modal</button>
<Teleport to="body">
<!-- 使用这个 modal 组件,传入 prop -->
<modal :show="showModal" @close="showModal = false">
<template #header>
<h3>custom header</h3>
</template>
</modal>
</Teleport>
</div>
</template>
<style lang="scss" scoped>
.top {
/* --------------------------------注意transform属性会影响子组件 position:fixed的定位 */
transform: translateX(300px);
width: 400px;
height: 300px;
border: 1px solid #000;
}
</style>
Modal.vue
HTML
<script>
export default {
props: {
show: Boolean
}
}
</script>
<template>
<Transition name="modal">
<div v-if="show" class="modal-mask">
<div class="modal-container">
<div class="modal-header">
<slot name="header">default header</slot>
</div>
<div class="modal-body">
<slot name="body">default body</slot>
</div>
<div class="modal-footer">
<slot name="footer">
default footer
<button
class="modal-default-button"
@click="$emit('close')"
>OK</button>
</slot>
</div>
</div>
</div>
</Transition>
</template>
<style>
.modal-mask {
position: fixed;
z-index: 9998;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
transition: opacity 0.3s ease;
}
.modal-container {
width: 300px;
margin: auto;
padding: 20px 30px;
background-color: #fff;
border-radius: 2px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33);
transition: all 0.3s ease;
}
.modal-header h3 {
margin-top: 0;
color: #42b983;
}
.modal-body {
margin: 20px 0;
}
.modal-default-button {
float: right;
}
/*
对于 transition="modal" 的元素来说
当通过 Vue.js 切换它们的可见性时
以下样式会被自动应用。
*
你可以简单地通过编辑这些样式
来体验该模态框的过渡效果。
*/
.modal-enter-from {
opacity: 0;
}
.modal-leave-to {
opacity: 0;
}
.modal-enter-from .modal-container,
.modal-leave-to .modal-container {
-webkit-transform: scale(1.1);
transform: scale(1.1);
}
</style>