一、关于过渡和动画内置组件
1、transition过渡
1. 是什么?
transition过渡主要是一个组件或者元素进入离开时的过渡效果,
2. 什么时候用?
v-if和v-show切换时使用<component>切换的动态组件- 改变特殊的
key属性
<Transition> 仅支持单个元素或组件作为其插槽内容。如果内容是一个组件,这个组件必须仅有一个根元素。
当一个 <Transition> 组件中的元素被插入或移除时,会发生下面这些事情:
- Vue 会自动检测目标元素是否应用了 CSS 过渡或动画。如果是,则一些
CSS 过渡 class会在适当的时机被添加和移除。 - 如果有作为监听器的
JavaScript 钩子,这些钩子函数会在适当时机被调用。 - 如果两个都没有,那么
DOM 的插入、删除操作将在浏览器的下一个动画帧后执行。
3. CSS过渡class
1. 使用vue的class名,有6种情况
v-enter-from:进入动画的起始状态。在元素插入之前添加,在元素插入完成后的下一帧移除。v-enter-active:进入动画的生效状态。应用于整个进入动画阶段。在元素被插入之前添加,在过渡或动画完成之后移除。这个 class 可以被用来定义进入动画的持续时间、延迟与速度曲线类型。v-enter-to:进入动画的结束状态。在元素插入完成后的下一帧被添加 (也就是v-enter-from被移除的同时),在过渡或动画完成之后移除。v-leave-from:离开动画的起始状态。在离开过渡效果被触发时立即添加,在一帧后被移除。v-leave-active:离开动画的生效状态。应用于整个离开动画阶段。在离开过渡效果被触发时立即添加,在过渡或动画完成之后移除。这个 class 可以被用来定义离开动画的持续时间、延迟与速度曲线类型。v-leave-to:离开动画的结束状态。在一个离开动画被触发后的下一帧被添加 (也就是v-leave-from被移除的同时),在过渡或动画完成之后移除。
2. 使用自定义class名,直接将 v 替换为 name名
<Transition name="fade">
...
</Transition>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
3. 一般与原生css transition过渡或者原生 animation 动画 一起使用 写法?
默认情况下,<Transition> 组件会通过监听过渡根元素上的第一个 transitionend 或者 animationend 事件来自动判断过渡何时结束。而在嵌套的过渡中,是等待所有内部元素的过渡完成。这个时候需要使用duration prop 来显式指定过渡的持续时间 (以毫秒为单位)。总持续时间应该匹配延迟加上内部元素的过渡持续时间
<Transition name="nested" :duration="{ enter: 500, leave: 800 }">
<div v-if="show" class="outer">
<div class="inner">
Hello
</div>
</div>
</Transition>
/* 应用于嵌套元素的规则 */
.nested-enter-active .inner,
.nested-leave-active .inner {
transition: all 0.3s ease-in-out;
}
.nested-enter-from .inner,
.nested-leave-to .inner {
transform: translateX(30px);
opacity: 0;
}
/* ... 省略了其他必要的 CSS */
- transition过渡:
<Transition name="slide-fade">
<p v-if="show">hello</p>
</Transition>
/*
进入和离开动画可以使用不同
持续时间和速度曲线。
*/
.slide-fade-enter-active {
transition: all 0.3s ease-out;
}
.slide-fade-leave-active {
transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-fade-enter-from,
.slide-fade-leave-to {
transform: translateX(20px);
opacity: 0;
}
- animation动画
<Transition name="bounce">
<p v-if="show" style="text-align: center;">
Hello here is some bouncy text!
</p>
</Transition>
.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 type 更关心哪个类型
<Transition type="animation">...</Transition>
4. js钩子
js钩子可以与css一起使用,也可以单独使用,单独使用时,添加:css="false",跳过对css的检测,也可以防止意外css的干扰。
<Transition
...
:css="false"
>
...
</Transition>
js钩子可以全权负责控制什么时候过渡结束。这种情况下对于 @enter 和 @leave 钩子来说,回调函数 done 是必须的。否则,钩子将被同步调用,过渡将立即完成。
<Transition
@before-enter="onBeforeEnter"
@enter="onEnter"
@after-enter="onAfterEnter"
@enter-cancelled="onEnterCancelled"
@before-leave="onBeforeLeave"
@leave="onLeave"
@after-leave="onAfterLeave"
@leave-cancelled="onLeaveCancelled"
>
<!-- ... -->
</Transition>
// 在元素被插入到 DOM 之前被调用
// 用这个来设置元素的 "enter-from" 状态
function onBeforeEnter(el) {}
// 在元素被插入到 DOM 之后的下一帧被调用
// 用这个来开始进入动画
function onEnter(el, done) {
// 调用回调函数 done 表示过渡结束
// 如果与 CSS 结合使用,则这个回调是可选参数
done()
}
// 当进入过渡完成时调用。
function onAfterEnter(el) {}
// 当进入过渡在完成之前被取消时调用
function onEnterCancelled(el) {}
// 在 leave 钩子之前调用
// 大多数时候,你应该只会用到 leave 钩子
function onBeforeLeave(el) {}
// 在离开过渡开始时调用
// 用这个来开始离开动画
function onLeave(el, done) {
// 调用回调函数 done 表示过渡结束
// 如果与 CSS 结合使用,则这个回调是可选参数
done()
}
// 在离开过渡完成、
// 且元素已从 DOM 中移除时调用
function onAfterLeave(el) {}
// 仅在 v-show 过渡中可用
function onLeaveCancelled(el) {}
5. transition组件上的属性
1. mode="out-in"过渡模式:先离开再进入
默认的过渡模式是离开,进入同时过渡,因此我们不得不将它们设为 position: absolute 以避免二者同时存在时出现的布局问题。
<Transition mode="out-in">
...
</Transition>
2. appear 出现时过渡,某个节点初次渲染时用一个过渡效果
<Transition appear>
...
</Transition>
6. 改变特殊key属性时过渡
不使用 key attribute,只有文本节点会被更新,不会发生过渡。
<script setup>
import { ref } from 'vue';
const count = ref(0);
setInterval(() => count.value++, 1000);
</script>
<template>
<Transition>
<span :key="count">{{ count }}</span>
</Transition>
</template>
7. 在项目中使用,一般是在项目router-view时进行组件间过渡
<template>
<router-view v-slot="{ Component, route }">
<transition name="fade" mode="out-in" appear>
<component
:is="Component"
v-if="route.meta.ignoreCache"
:key="route.fullPath"
/>
<keep-alive v-else :include="cacheList">
<component :is="Component" :key="route.fullPath" />
</keep-alive>
</transition>
</router-view>
</template>
2、transitionGroup过渡
transitionGroup是v-for 列表中的元素或组件的插入、移除和顺序改变添加动画效果。与transition用法基本类似,也是css过渡class、在js钩子中使用
1. transition与transitionGroup的区别?
- 默认情况下,不会渲染一个容器元素。要传入
tagprop 来指定一个元素作为容器元素来渲染。 - 过渡模式
mode="out-in"在这里不可用,因为不再是在互斥的元素之间进行切换。 - 列表中的每个元素都必须有一个独一无二的
keyattribute。 - CSS 过渡 class 会被应用在列表内的元素上,而不是容器元素上。
2. 使用方法?
<TransitionGroup name="list" tag="ul">
<li v-for="item in items" :key="item">
{{ item }}
</li>
</TransitionGroup>
.list-move, /* 对移动中的元素应用的过渡 */
.list-enter-active,
.list-leave-active {
transition: all 0.5s ease;
}
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: translateX(30px);
}
/* 确保将离开的元素从布局流中删除
以便能够正确地计算移动的动画。 */
.list-leave-active {
position: absolute;
}
3、动画技巧
<Transition> 和 <TransitionGroup> 组件处理元素进入、离开和列表顺序变化的过渡效果。但除此之外,还有许多制作网页动画的方式在 Vue 中也适用。
1. 基于CSS class 动画
<div :class="{ shake: disabled }">
<button @click="warnDisabled">Click me</button>
<span v-if="disabled">This feature is disabled!</span>
</div>
.shake {
animation: shake 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
transform: translate3d(0, 0, 0);
}
@keyframes shake {
10%,
90% {
transform: translate3d(-1px, 0, 0);
}
20%,
80% {
transform: translate3d(2px, 0, 0);
}
30%,
50%,
70% {
transform: translate3d(-4px, 0, 0);
}
40%,
60% {
transform: translate3d(4px, 0, 0);
}
}
2. 动态绑定元素样式
<div
@mousemove="onMousemove"
:style="{ backgroundColor: `hsl(${x}, 80%, 50%)` }"
class="movearea"
>
<p>Move your mouse across this div...</p>
<p>x: {{ x }}</p>
</div>
.movearea {
transition: 0.3s background-color ease;
}
二、关于缓存内置组件
1、keepAlive
1. 是什么?
keepAlive是多个动态组件进行切换时的缓存被移除的组件。比如在A页面,切换到了第5页,切换到B页面后,再切换到A页面,仍然在第5页。
2. 具体使用?在项目中的使用?
一般使用在动态组件中,全局router-view时,也会进行动态组件的缓存
vue3中结合vue-router时变化较大,之前是keep-alive包裹router-view,现在需要反过来用router-view包裹keep-alive:
<template>
<router-view v-slot="{ Component, route }">
<transition name="fade" mode="out-in" appear>
<component
:is="Component"
v-if="route.meta.ignoreCache"
:key="route.fullPath"
/>
<keep-alive v-else :include="cacheList">
<component :is="Component" :key="route.fullPath" />
</keep-alive>
</transition>
</router-view>
</template>
3. 属性有哪些?
1. 包含或者排除的属性
<!-- 以英文逗号分隔的字符串,或者正则 -->
<KeepAlive include="a,b">
<component :is="view" />
</KeepAlive>
<!-- 正则表达式 (需使用 `v-bind`) -->
<KeepAlive :include="/a|b/">
<component :is="view" />
</KeepAlive>
<!-- 数组 (需使用 `v-bind`) -->
<KeepAlive :include="['a', 'b']" :exclude="['c']">
<component :is="view" />
</KeepAlive>
2. 最大缓存实例数
只要超过最大缓存实例数,最久没有被访问的缓存实例将被销毁,为新的实例腾出空间
<KeepAlive :max="10">
<component :is="activeComponent" />
</KeepAlive>
4. keep-alive生命周期
onActivated:组件缓存时生命周期onDeactivated:缓存组件销毁时生命周期
一般与vue-router导航守卫一起使用:juejin.cn/editor/draf…
5. 怎么缓存当前组件,缓存后怎么更新?
使用keep-alive进行缓存,缓存后更新数据可以使用
onActivated钩子函数:每次缓存进入后会调用钩子函数
<script setup>
import { onActivated, onDeactivated } from 'vue'
onActivated(() => {
// 调用时机为首次挂载
// 以及每次从缓存中被重新插入时
})
onDeactivated(() => {
// 在从 DOM 上移除、进入缓存
// 以及组件卸载时调用
})
</script>
beforeRouteEnter路由钩子:进入组件之前会调用钩子
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被验证前调用
// 不能获取组件实例 `this` !
// 因为当守卫执行时,组件实例还没被创建!
next(vm => { // 通过 `vm` 访问组件实例 })
},
6. keep-alive的原理?
keep-alive是vue内置组件,源码位置:src/compontents/keepAlive.ts
原理是通过cache来缓存所有vnode
- 定义一个map,用来存储cache,
- 定义一个set,用来存储keys
- 在cache中寻找判断有无缓存,有的话,将缓存的vnode返回,并将key从keys中删除再添加到最后,方便清除不常用组件;没有的话,将key存储到keys中,在这会判断是否超过最大缓存实例数
- 判断max属性,超过最大缓存实例数,删除keys中第一个key
- 判断includes、excludes是否变化,调用pruneCache函数:
- 遍历cache,判断name与新缓存的规则是否匹配,没有的话删除
三、vue3新增的内置组件
1、Teleport:传送门,将组件内部模板传递到组件DOM外部
1. 是什么?
teleport是vue内置组件,将组件内部模板传递到组件DOM外部,比如全屏,全屏按钮跟内容在一个组件中,点击完全屏按钮后,全屏内容应该显示在组件外,如果使用css写的话,会用到position:fixed;z-index:999,这样会有一些潜在问题。这时候就可以使用teleport
2. 怎么用?属性?
- 使用
to属性,可以是css选择器字符串,也可以是DOM元素,显示的目标位置 disabled可以根据条件来判断是否禁用teleport
<button @click="open = true">Open Modal</button>
<Teleport to="body" :disabled="isMobile">
<div v-if="open" class="modal">
<p>Hello from the modal!</p>
<button @click="open = false">Close</button>
</div>
</Teleport>
3. 跟组件一起使用时,不会影响到父子组件的逻辑;多个teleport其内容挂载在同一个目标元素上,而顺序就是简单的顺次追加,后挂载的将排在目标元素下更后面的位置上
<Teleport to="#modals">
<div>A</div>
</Teleport>
<Teleport to="#modals">
<div>B</div>
</Teleport>
<div id="modals">
<div>A</div>
<div>B</div>
</div>
2、Fragment:片段,不用引入额外的包裹元素
在vue2中,一个组件中,必须只能有一个根元素,这样我们必须写一个根元素来包裹其他子元素。在vue3中,引入fragment片段,可以直接使用<></>来包裹子元素。
<template>
<>
<h1></h1>
<p></p>
</>
</template>
好处:
- 减少了多余的包裹元素
- 不需要创建额外的DOM元素,提高渲染能力
- 减少了样式的编写与影响
3、Suspense:加载异步依赖
1. 是什么?
在一个组件嵌套子组件,子组件包含异步依赖,这时候加载父组件dashboard,可能会产生3个loading的加载,影响页面。这时候可以使用
suspense来控制,所有的依赖加载完后再进行显示默认内容。
<Suspense> 可以等待的异步依赖有两种:
- 带有异步
setup()钩子的组件。这也包含了使用<script setup>时有顶层await表达式的组件。
<script setup>
const res = await fetch(...)
const posts = await res.json()
</script>
<template>
{{ posts }}
</template>
- 异步组件:使用
defineAsyncComponent来注册组件
- 异步组件与
suspense一起使用,加载状态是由<Suspense>控制,而该组件自己的加载、报错、延时和超时等选项都将被忽略 - 异步组件也可以通过在选项中指定
suspensible: false表明不用Suspense控制,并让组件始终自己控制其加载状态。
2. 属性有哪些,加载状态?
有两个插槽:
#default:默认显示的内容,也就是插件中的内容;在异步依赖加载完后进行显示#fallback:后备内容,在加载异步依赖时,会进行挂起,显示后备内容
suspense加载过程:
-
初始渲染时,显示默认的插槽内容。过程中遇到任何异步依赖,则会进入挂起状态。在挂起状态期间,展示的是后备内容。当所有的异步依赖完成后,
<Suspense>会进入完成状态,显示默认插槽的内容。在初次渲染时没有遇到异步依赖,<Suspense>会直接进入完成状态。 -
进入完成状态后,只有当默认插槽的根节点被替换时,
<Suspense>才会回到挂起状态。组件树中新的更深层次的异步依赖不会造成<Suspense>回退到挂起状态。 -
发生回退时,后备内容不会立即展示出来。相反,
<Suspense>在等待新内容和异步依赖完成时,会展示之前#default插槽的内容。
3. 事件有哪些?
<Suspense> 组件会触发三个事件:pending、resolve 和 fallback。pending 事件是在进入挂起状态时触发。resolve 事件是在 default 插槽完成获取新内容时触发。fallback 事件则是在 fallback 插槽的内容显示时触发。
4. 与其他组件一起使用?
<RouterView v-slot="{ Component }">
<template v-if="Component">
<Transition mode="out-in">
<KeepAlive>
<Suspense>
<!-- 主要内容 -->
<component :is="Component"></component>
<!-- 加载中状态 -->
<template #fallback>
正在加载...
</template>
</Suspense>
</KeepAlive>
</Transition>
</template>
</RouterView>