# 组件

<RouterLink />组件默认支持激活样式显示的类名,只需要给active-class属性设置对应的类名即可


<RouterLink active-class="active" :to="`/category/${item.id}`"></RouterLink>

# 组件的全局注册(插件方式)

//把computed中的组件都进行全局注册
//通过插件的方式
import ImageView from "@/components/ImageView/index.vue";
import XtxSku from "@/components/XtxSku/index.vue";

export const componentsPlugin = {
    install(app) {
        // app.component('组件名字',组件对象)
        app.component("XtxImageView", ImageView);
        app.component("XtxSku", XtxSku);
    },
};

main.js

//引入全局组件
import {componentsPlugin} from "@/components/index";

app.use(componentsPlugin);

# Teleport组件

<Teleport> 是一个内置组件,它可以将一个组件内部的一部分模板“传送”到该组件的 DOM 结构外层的位置去。

有时我们可能会遇到这样的场景:一个组件模板的一部分在逻辑上从属于该组件,但从整个应用视图的角度来看,它在 DOM 中应该被渲染在整个 Vue 应用外部的其他地方。

这类场景最常见的例子就是全屏的模态框。理想情况下,我们希望触发模态框的按钮和模态框本身是在同一个组件中,因为它们都与组件的开关状态有关。但这意味着该模态框将与按钮一起渲染在应用 DOM 结构里很深的地方。这会导致该模态框的 CSS 布局代码很难写。

# 接下来我们来看看 <MyModal> 的实现:


<script setup>
  import {ref} from "vue";

  const open = ref(false);
</script>

<template>
  <button @click="open = true">Open Modal</button>

  <div v-if="open" class="modal">
    <p>Hello from the modal!</p>
    <button @click="open = false">Close</button>
  </div>
</template>

<style scoped>
  .modal {
    position: fixed;
    z-index: 999;
    top: 20%;
    left: 50%;
    width: 300px;
    margin-left: -150px;
  }
</style>

这个组件中有一个 <button> 按钮来触发打开模态框,和一个 class 名为 .modal<div>,它包含了模态框的内容和一个用来关闭的按钮。

当在初始HTML 结构中使用这个组件时,会有一些潜在的问题:

position: fixed 能够相对于浏览器窗口放置有一个条件,那就是不能有任何祖先元素设置了 transformperspective 或者 filter 样式属性。也就是说如果我们想要用 CSS transform 为祖先节点 <div class="outer"> 设置动画,就会不小心破坏模态框的布局!

这个模态框的 z-index 受限于它的容器元素。如果有其他元素与 <div class="outer"> 重叠并有更高的 z-index,则它会覆盖住我们的模态框。

<Teleport> 提供了一个更简单的方式来解决此类问题,让我们不需要再顾虑 DOM 结构的问题。

# 让我们用 <Teleport>改写一下 <MyModal>


<button @click="open = true">Open Modal</button>

<Teleport to="body">
  <div v-if="open" class="modal">
    <p>Hello from the modal!</p>
    <button @click="open = false">Close</button>
  </div>
</Teleport>

<Teleport> 接收一个 to prop 来指定传送的目标。to 的值可以是一个 CSS 选择器字符串,也可以是一个 DOM 元素对象。这段代码的作用就是告诉 Vue“把以下模板片段传送到 body 标签下”。

# 搭配组件使用

<Teleport> 只改变了渲染的 DOM 结构,它不会影响组件间的逻辑关系。也就是说,如果 <Teleport> 包含了一个组件,那么该组件始终和这个使用了 <teleport> 的组件保持逻辑上的父子关系。传入的 props 和触发的事件也会照常工作。

这也意味着来自父组件的注入也会按预期工作,子组件将在 Vue Devtools 中嵌套在父级组件下面,而不是放在实际内容移动到的地方。

# 禁用 Teleport

在某些场景下可能需要视情况禁用 <Teleport> 。举例来说,我们想要在桌面端将一个组件当做浮层来渲染,但在移动端则当作行内组件。我们可以通过对 <Teleport> 动态地传入一个 disabled prop 来处理这两种不同情况。


<Teleport :disabled="isMobile">
  ...
</Teleport>

这里的 isMobile 状态可以根据 CSS media query 的不同结果动态地更新。

# 多个 Teleport 共享目标

一个可重用的模态框组件可能同时存在多个实例。对于此类场景,多个 <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>

# 循环注在自定义组件

# 方法一 全局注册

  1. 在自定义组件根目录创建公共导入文件index.js
SensenUi / index.js
//导入组件
import TransFer from './Transfer/Transfer.vue'
//声明组件数组
const components = {
    TransFer,
}
export default {
    install(app) {//循环导入
        Object.keys(components).forEach((key) => {
            app.component(key, components[key])
        })
    },
}
  1. 在main.js中导入
//导入自定义组件库
import SenseUi from '../modules/sensen-ui'
//使用
createApp(App).use(SenseUi).mount('#app')

# 方法二 局部注册 按需引入

跟方法一不同的是 方法一的公共组件文件是引入所用组件
而方法二是引入单个组件 因此我们要把index.js文件新建在单个组件中

SensenUi / Transfer / index.js

import Transfer from './Transfer'

export default {
    install(app) {
        app.component('Transfer', Transfer)
    },
}

在main.js中注册单个组件

import Transfer from '../modules/sensen-ui/Transfer'

createApp(App).use(Transfer).mount('#app')

# Transition组件

它可以将进入和离开动画应用到通过默认插槽传递给它的元素或组件上。进入或离开可以由以下的条件之一触发:

  • 由 v-if 所触发的切换
  • 由 v-show 所触发的切换
  • 由特殊元素 <component> 切换的动态组件
  • 改变特殊的 key 属性

# 离开过渡效果的 CSS class

  • v-enter-from - 元素显示时动画开始的样式
  • v-enter-active - 元素显示进入时的动画效果
  • v-enter-to - 组件显示时动画结束时的样式
  • v-leave-from - 元素隐藏时动画开始的样式
  • v-leave-active - 元素隐藏进入时的动画效果
  • v-leave-to - 组件隐藏时动画结束时的样式

# 组件中的name属性

我们可以给 <Transition> 组件传一个 name prop 来声明一个过渡效果名


<Transition name="t">
  ...
</Transition>

上述代码会将过渡效果名设置为 t ,也就是当前动画的class应该为 :

.t-enter-from {
/ / 元素显示时动画开始 opacity:0;
}

.t-enter-active {
/ / 这个 class 可以被用来定义进入动画的持续时间、延迟与速度曲线类型。 transition:all 3 s;
}

.t-enter-to {
/ / 元素显示时动画结束 opacity:1;
}

.t-leave-from {
/ / 元素隐藏时动画开始 opacity:1;
}

.t-leave-active {
/ / 这个 class 可以被用来定义进入动画的持续时间、延迟与速度曲线类型。 transition:all 3 s;
}

.t-leave-to {
/ / 元素隐藏时动画结束 opacity:0;
}

# 元素class变化:

  1. 元素显示时加入动画开始样式类(元素开始显示)
  2. 移除动画开始样式类(元素显示 应用元素原始样式类)
  3. 加入动画结束样式类(元素开始隐藏)
  4. 移除动画结束样式类(元素隐藏 应用元素原始样式类) 因此上述样式合并可以写为
.t-enter-from,
.t-leave-to {
  //元素显示时动画开始
  opacity: 0;
  transform: translateY(-20px);
}

.t-enter-active,
.t-leave-active {
  //这个 class 可以被用来定义进入动画的持续时间、延迟与速度曲线类型。
  transition: all 1s;
}

# 组件中的apper属性

如果你想在某个节点初次渲染时应用一个过渡效果,你可以添加 appear prop:


<Transition appear>
  ...
</Transition>

# CSS 的 animation

对于大多数的 CSS 动画,我们可以简单地在 *-enter-active 和 *-leave-active class 下声明它们。下面是一个示例:

.t-enter-active {
  animation: t-in 0.5s;
}

.t-leave-active {
  animation: t-in 0.5s reverse;
}

@keyframes t-in {
  0% {
    transform: scale(0);
  }
  50% {
    transform: scale(1.25);
  }
  100% {
    transform: scale(1);
  }
}

# 自定义过渡 class类名

安装Animate.css动画库

npm install animate.css --save

main.js中引入

import 'animate.css'

使用 官网 (opens new window)


<Transition
    enter-active-class="animate__animated animate__backInDown"
    leave-active-class="animate__animated animate__backOutUp"
    appear>
  <h1
      v-if="show"
      class="t">
    {{ msg }}
  </h1>
</Transition>

# defineComponent

defineComponent 是一个函数,它接收一个对象作为参数,并返回一个新的 Vue 组件构造器。

const MyComponent = defineComponent({
    props: {
        msg: String,
    },
    setup(props) {
        return {
            msg: props.msg,
        };
    },
    template: `<div>{{ msg }}</div>`,
});

# 异步组件

可以使用 defineAsyncComponent 创建一个异步组件。

const AsyncComponent = defineAsyncComponent(() => import('./AsyncComponent.vue'))

异步组件是一种特殊的组件,它会在组件第一次被渲染的时候,通过一个函数来异步地加载组件定义。


<template>
  <div>
    <Suspense>
      <AsyncComponent/>
    </Suspense>
  </div>
</template>

<script>
  import {defineAsyncComponent} from 'vue';

  const AsyncComponent = defineAsyncComponent(() => import('./AsyncComponent.vue'))

# Suspense组件

当一个组件在其子树中包含一个 <Suspense> 组件时,只有在这个子树的所有组件都被加载和解析之后,才会渲染 <Suspense> 组件本身。


<template>
  <div>
    <Suspense>
      <AsyncComponent/>
    </Suspense>
  </div>
</template>

<script setup>
  import {defineAsyncComponent} from 'vue';
</script>

# Suspense配合Transition使用 实现加载元素时显示动画效果


<template>
  <div>
    <Suspense>
      <Transition
          enter-active-class="animate__animated animate__backInDown"
          leave-active-class="animate__animated animate__backOutUp"
          appear>
        <asyncImgComponent/>
      </Transition>
      <template #fallback>
        <div>loading...</div>
      </template>
    </Suspense>
  </div>
</template>

<script setup>
  import {defineAsyncComponent} from 'vue';
  import {Transition} from 'vue';
</script>
上次更新: 3/1/2024, 3:03:16 PM