vue3中内置组件

286 阅读12分钟

一、关于过渡和动画内置组件

1、transition过渡

1. 是什么?

transition过渡主要是一个组件或者元素进入离开时的过渡效果,

2. 什么时候用?

  • v-ifv-show切换时使用
  • <component> 切换的动态组件
  • 改变特殊的 key 属性

<Transition> 仅支持单个元素或组件作为其插槽内容。如果内容是一个组件,这个组件必须仅有一个根元素。

当一个 <Transition> 组件中的元素被插入或移除时,会发生下面这些事情:

  1. Vue 会自动检测目标元素是否应用了 CSS 过渡或动画。如果是,则一些 CSS 过渡 class会在适当的时机被添加和移除。
  2. 如果有作为监听器的 JavaScript 钩子,这些钩子函数会在适当时机被调用。
  3. 如果两个都没有,那么 DOM 的插入、删除操作将在浏览器的下一个动画帧后执行。

3. CSS过渡class

1. 使用vue的class名,有6种情况
  1. v-enter-from:进入动画的起始状态。在元素插入之前添加,在元素插入完成后的下一帧移除
  2. v-enter-active:进入动画的生效状态。应用于整个进入动画阶段。在元素被插入之前添加,在过渡或动画完成之后移除。这个 class 可以被用来定义进入动画的持续时间、延迟与速度曲线类型
  3. v-enter-to:进入动画的结束状态。在元素插入完成后的下一帧被添加 (也就是 v-enter-from 被移除的同时),在过渡或动画完成之后移除
  4. v-leave-from:离开动画的起始状态。在离开过渡效果被触发时立即添加,在一帧后被移除。
  5. v-leave-active:离开动画的生效状态。应用于整个离开动画阶段。在离开过渡效果被触发时立即添加,在过渡或动画完成之后移除。这个 class 可以被用来定义离开动画的持续时间、延迟与速度曲线类型
  6. 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的区别?

  • 默认情况下,不会渲染一个容器元素。要传入 tag prop 来指定一个元素作为容器元素来渲染。
  • 过渡模式mode="out-in"在这里不可用,因为不再是在互斥的元素之间进行切换。
  • 列表中的每个元素都必须有一个独一无二的 key attribute。
  • 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

image.png

  • 在cache中寻找判断有无缓存,有的话,将缓存的vnode返回,并将key从keys中删除再添加到最后,方便清除不常用组件;没有的话,将key存储到keys中,在这会判断是否超过最大缓存实例数
  • 判断max属性,超过最大缓存实例数,删除keys中第一个key

image.png

  • 判断includes、excludes是否变化,调用pruneCache函数:
    • 遍历cache,判断name与新缓存的规则是否匹配,没有的话删除

image.png

三、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. 是什么?

image.png 在一个组件嵌套子组件,子组件包含异步依赖,这时候加载父组件dashboard,可能会产生3个loading的加载,影响页面。这时候可以使用suspense来控制,所有的依赖加载完后再进行显示默认内容。

<Suspense> 可以等待的异步依赖有两种:

  1. 带有异步 setup() 钩子的组件。这也包含了使用 <script setup> 时有顶层 await 表达式的组件。
<script setup>
const res = await fetch(...)
const posts = await res.json()
</script>

<template>
  {{ posts }}
</template>
  1. 异步组件:使用defineAsyncComponent来注册组件
  • 异步组件与suspense一起使用,加载状态是由 <Suspense> 控制,而该组件自己的加载、报错、延时和超时等选项都将被忽略
  • 异步组件也可以通过在选项中指定 suspensible: false 表明不用 Suspense 控制,并让组件始终自己控制其加载状态。

2. 属性有哪些,加载状态?

有两个插槽:

  • #default:默认显示的内容,也就是插件中的内容;在异步依赖加载完后进行显示
  • #fallback:后备内容,在加载异步依赖时,会进行挂起,显示后备内容

suspense加载过程:

  • 初始渲染时,显示默认的插槽内容。过程中遇到任何异步依赖,则会进入挂起状态。在挂起状态期间,展示的是后备内容。当所有的异步依赖完成后,<Suspense> 会进入完成状态,显示默认插槽的内容。在初次渲染时没有遇到异步依赖,<Suspense> 会直接进入完成状态。

  • 进入完成状态后,只有当默认插槽的根节点被替换时,<Suspense> 才会回到挂起状态。组件树中新的更深层次的异步依赖不会造成 <Suspense> 回退到挂起状态。

  • 发生回退时,后备内容不会立即展示出来。相反,<Suspense> 在等待新内容和异步依赖完成时,会展示之前 #default 插槽的内容。

3. 事件有哪些?

<Suspense> 组件会触发三个事件:pendingresolve 和 fallbackpending 事件是在进入挂起状态时触发。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>