1、VUE3的基本功能回顾

Vue 3 的响应式数据绑定(1) Vue 3的响应式原理是其框架的核心机制之一,它使得当数据发生变化时,视图能够自动更新。以下是Vue 3响应式原理的详细解释:一、核心机制Vue 3的响应式原理主要包括两个方面:依赖收集和属性代理。二、实现原理:Vue 3使用了ES6的Proxy对象作为其响应式系统的核心。Proxy可以拦截对象的读取和设置操作,从而实现对数据的监听和响应。相比于Vue 2中的Object.defineProperty,Proxy提供了更强大的功能和更好的性能。三、相关API四、注意事项。 阅读详情

一、VUE3基础

1、数据绑定

在vue中数据绑定存在两种形式,一种是数据单向绑定,另一种是数据双向绑定。其中数据单向绑定可以使用插值表达式 {{ … }} 和 v-bind 这两种形式,而数据双向绑定可以使用 v-model 示例如下:

// 1、数据单向绑定
// 插值表达式
<span>Message: {{ msg }}</span>

// v-bind 单向绑定
<span v-bind:class="back">Message</span>
// 可以简写成 :
<span :class="back">Message</span>

// 2、数据双向绑定
<input v-model="message">

总结:数据单向绑定适用于数据渲染型场景,即根据绑定的数据进行渲染。而数据双向绑定一般适用于表单、搜索框等具有页面输入性质的场景。

2、条件渲染

在vue中条件渲染涉及到的指令有 v-if 、v-else、v-else-if、v-show 这四个。其中前三个一般可以组合使用,而 v-show 和其他三个性质不同,是单独使用。示例如下:

<div v-if="type === 'A'">
  A
</div>
<div v-else-if="type === 'B'">
  B
</div>
<div v-else-if="type === 'C'">
  C
</div>
<div v-else>
  Not A/B/C
</div>
<h1 v-show="isTrue">Hello!</h1>

通过上面的示例,我们可以看出,条件渲染都是通过判断指定的数据是否满足条件来进行渲染判断,例如上面的 type 和 isTrue 如果条件为 true 则渲染,反之则不渲染。此外,v-if 是基于DOM元素的销毁和创建来实现显示和隐藏的,对性能消耗较高。而 v-show 则是通过对 DOM 元素的 display 属性进行动态修改来实现元素的显示和隐藏,对性能影响小。因此,如果是频繁的切换显示和隐藏,使用 v-show 较好,反之则使用 v-if

3、列表渲染

在vue中列表渲染使用的指令是 v-for ,使用示例如下:

// 普通用法
<li v-for="item in items">
  {{ item.message }}
</li>

// 带元素下表用法
<li v-for="(item, index) in items">
  {{ parentMessage }} - {{ index }} - {{ item.message }}
</li>

// 通过 key 管理状态用法,推荐使用
<div v-for="item in items" :key="item.id">
  <!-- 内容 -->
</div>

除了列表渲染之外,我们对列表数据最常用的还有列表数据进行变更操作:常见的有如下操作:

  • push() 往列表中添加一个元素
  • pop() 从列表中移除一个元素
  • sort() 对列表元素进行排序
  • reverse() 对列表元素进行倒排序
  • filter() 对列表元素进行过滤
const numbers = ref([1, 2, 3, 4, 5])

const evenNumbers = computed(() => {
  return numbers.value.filter((n) => n % 2 === 0)
})

具体可参考官网:v-for指令详解

4、DOM元素事件处理

在vue中事件指的是用户交互或系统触发的操作,例如,点击按钮、刷新页面、滚动页面等等。对于这些事件,我们需要监听处理。因此,事件处理是一个非常重要的环节。事件处理可分为事件监听和处理实现两个部分,事件监听可以使用指令 v-on 或者其简写 @ 来实现。通过 @ 可以绑定一个事件处理函数,用来处理事件。具体示例如下:

// 不带参数
<span @click="handleTest"></span>

const handleTest = () => {
    console.log('hello world')
}

// 带参数
<span @click="handleTest('hello world')"></span>

const handleTest = (param: string) => {
    console.log(param)
}

从上面的示例中,我们了解了事件处理的方式,但这还不够,我们必须对事件本身有足够的了解,在vue中存在多种事件,不同事件对应不同的操作。常用的事件有如下:
1、click 点击事件
2、submit 表单提交事件
3、元素获得焦点事件
4、keydown 键盘按下事件
5、keyup 键盘松开事件
除了以上的常用事件之外,还有另一个事件修饰符也同样重要,通过事件修饰符,我们可以有效地控制事件的行为。常见的时间修饰符有:

  • .prevent: 阻止事件的默认行为
  • .stop: 阻止事件冒泡传播
  • .capture: 在捕获阶段触发事件处理程序
  • .self: 只有当事件发生在该元素本身而不是子元素时才会触发事件处理程序
  • .once: 事件只会触发一次

通过事件修饰符,我们可以了解到每个DOM元素都存在默认的行为,例如 a 标签的默认行为链接跳转,表单元素提交的默认行为是刷新页面。这些DOM元素的默认行为都可以通过事件修饰符来控制。

5、监听器watch

watch 监听器本身是一个 API 函数,所有的函数在使用时都是以 函数名 + ()的形式组成。例如:watch()、useRoute()等等。在 setup 组合式API中,我们可以使用 watch 监听器来实现对某个响应式数据动态变化的感知处理。只要目标数据发生改变,watch 监听器就能够实时的捕捉到并做出相应处理。watch 监听器的默认基本结构如下:

watch(data, (newData, oldData) => {
  // 函数处理内容
})

从 watch 监听器的基本结构可以看出,watch 监听器内部由两部分组成:
1、监听目标对象
2、回调处理函数
对于监听目标有硬性要求:
1、必须是响应式对象
2、不能直接监听响应式对象内的属性值,可间接监听,使用 getter 函数实现
示例如下:

const x = ref(0)
const y = ref(0)

// getter 函数
watch(
  () => x.value + y.value,
  (sum) => {
    console.log(`sum of x + y is: ${sum}`)
  }
)


const obj = reactive({ count: 0 })

// 提供一个 getter 函数
watch(
  () => obj.count,
  (count) => {
    console.log(`count is: ${count}`)
  }
)

对于回调处理函数,我们可以通过添加参数来观察监听对象变化前后的内容,回调处理函数的第一个参数默认是变化后数据,第二个参数是变化前数据。这些参数我们可加也可不加,这取决于我们是否要观察监听对象。
此外,由于 watch 监听器是被动触发的,它是在感知到监听对象数据变化时才执行的,因此,可以说默认的 watch 监听器是懒执行的。但在某些场景下,我们希望它能够在当前组件初始化时就主动执行一次该怎么办呢?我们可以通过在 watch 监听器中传入 immediate:true 选项来实现。示例如下:

watch(source, (newValue, oldValue) => {
  // 立即执行,且当 `source` 改变时再次执行
}, { immediate: true })

6、组件生命周期

每一个vue组件实例在创建时都会经历一系列的初始化步骤,这些初始化步骤共同构成了组件生命周期。下面是比较重要的组件生命周期阶段:
1、创建阶段
涉及两个过程 beforeCreate 组件创建之前、created 组件创建之后
2、挂载阶段
涉及两个过程 beforeMount 组件数据挂载渲染之前、mounted 组件数据挂载渲染之后
3、更新阶段
涉及两个过程 beforeUpdate 组件数据更新之前、updated 组件数据更新之后
4、销毁阶段
涉及两个过程 beforeUnmount 组件销毁之前、unmounted 组件销毁之后
一般来说,我们使用最多的就是 mounted 和 updated 这两个过程,可以重点掌握,示例如下:

onMounted(() => {
  console.log('页面挂载完成,触发了onMounted钩子函数');
  timer = setInterval(() => {
    console.log('定时器正在运行中', count++)
  }, 1000)
})

onUpdated(() => {
  console.log('数据发生了更新,触发了onUpdated钩子函数')
})

7、响应式基础

在vue中响应式是一个核心点,当我们使用vue时,必定会涉及响应式数据,我们无时不刻都在和响应式数据打交道,那么,我们该如何创建或者说定义响应式数据呢?vue为我们提供了多种创建响应式数据的 API 函数,其中最常用的有如下两种:
1、ref() 函数
ref 函数的特点是适用于所有类型的响应式数据创建,它既可以创建基本类型的响应式数据,也可以创建复杂类型的响应式数据。此外,另一个特点是,通过 ref 创建的响应式数据对象是一个包装对象,真正的源数据需要通过 **.value **来获取。示例如下:

import { ref } from 'vue'

const count = ref(0)
count.value++

const objectRef = ref({ count: 0 })

// 这是响应式的替换
objectRef.value = { count: 1 }

因此,想要获取 ref 函数括号中的数据,需要使用 .value
2、reactive() 函数
reactive 函数不可以用来创建基本数据类型的响应式数据,只能创建对象和集合类型的响应式数据。并且不用像 ref 那样,需要使用 **.value **来获取源数据,可以直接使用。示例如下:

const raw = { count: 0 }
const proxy = reactive(raw)

// 代理对象和原始对象不是全等的
console.log(proxy === raw) // false
proxy.count++

8、计算属性

计算属性的作用是将复杂的处理逻辑封装起来,然后以响应式的方式提供给模板使用,避免直接在模板中处理逻辑。当计算的源数据发生变化时,计算属性会重新计算处理并给出最新的处理结果,因此,计算属性是响应式的。使用示例如下:

<template>
  <p>Has published books:</p>
  <span>{{ publishedBooksMessage }}</span>
</template>

<script setup>
import { reactive, computed } from 'vue'

const author = reactive({
  name: 'John Doe',
  books: [
    'Vue 2 - Advanced Guide',
    'Vue 3 - Basic Guide',
    'Vue 4 - The Mystery'
  ]
})

// 一个计算属性 ref
const publishedBooksMessage = computed(() => {
  return author.books.length > 0 ? 'Yes' : 'No'
})
</script>

从计算属性 computed 的函数内容可以看出,其内使用了 getter 函数,那么什么是 getter 函数呢?
在vue中,具有返回数据的函数函数都可以称 getter 函数。 同样,所有设置数据的函数都可以称 setter 函数。示例如下:

const obj = ref({
  count: 0
})

// getter 函数中的 return 可以省略,并且可以带参数
const getter1 = () => return obj.count
const getter2 = () => obj.count
const getter3 = (param:number) => obj.count + number

// setter 函数也可以带参数
const setter1 = () => obj.count = 100
const setter2 = (param:number) => obj.count = number

9、模板元素获取

模板元素指的是 template 标签域中的所有元素,一般来说,这些元素是一些原生DOM元素、UI框架封装的组件、自定义组件。因此,当我们需要获取这些元素并做一些使用时,我们该怎么办呢?在原生的 JS 中,获取DOM元素一般是如下方式:

<div id="example">Hello, world!</div>
const elem = document.getElementById('example')

<div class="item">Hello, world!</div>
const elems = document.getElementsByClassName('item')

主要常用的是通过 DOM 元素上的一些标识,常用的有类选择器、id选择器这两种。但在vue框架下,它为我们提供了一种更加方便的获取元素的方式,就是通过 ref 元素属性来获取,其实也是一种为模板元素设置选择标识的一种方式。使用方式如下:

<template>
  <input ref="input" />
</template>

<script setup>
import { ref, onMounted } from 'vue'

// 声明一个 ref 来存放该元素的引用
// 必须和模板里的 ref 同名
const input = ref(null)

onMounted(() => {
  input.value.focus()
})
</script>

此外,需要特别注意的一点是,当我们获取模板元素并试图去使用的时候,我们必须选择合适的组件生命阶段,我们只能在组件挂载渲染完成之后的阶段使用获取的模板元素,这是因为在当前组件都还没有渲染完成时,其内的元素都还不存在呢!因此,在 onMounted、onUpdated等阶段,我们可以放心使用。

10、组件基础

在vue语言框架下,任何的** .vue** 单文件都可以是组件,每个组件都是独立存在的,整个应用的构建就像搭积木一样,每一个组件都将是构成整个应用的元素。
image.png
除了了解单文件组件之外,另一个经常使用到的是:动态组件,动态组件指的是可以在不同的组件之间来回切换,示例如下:

<template>
  <div>
    <button @click="toggleComponent">Toggle Component</button>
    <component :is="currentComponent"></component>
  </div>
</template>

<script>
import { defineAsyncComponent, ref } from 'vue'

<script setup>
const currentComponent = ref('ComponentA')
const toggleComponent = () => {
  currentComponent.value =
    currentComponent.value === 'ComponentA' ? 'ComponentB' : 'ComponentA'
}
const ComponentA = defineAsyncComponent(() =>
  import(/* webpackChunkName: "ComponentA" */ './ComponentA.vue')
)
const ComponentB = defineAsyncComponent(() =>
  import(/* webpackChunkName: "ComponentB" */ './ComponentB.vue')
)
return {
  currentComponent,
  toggleComponent,
  ComponentA,
  ComponentB,
}
</script>

在上面的实例中,传递给 :is 的值有两种情况,
1、全局注册的组件名
2、当前文件引入的组件对象

二、VUE3进阶

1、注册组件

在vue3中组件注册一共有两种方式:
1、全局注册:
全局注册指的是在主应用配置文件中进行的注册,其是在 main.ts 中完成的。

// 先引入,后注册
import ComponentA from './App.vue'

app.component('ComponentA', ComponentA)

像这样全局注册的组件,可以在此应用的任意其他组件模板域中使用。使用如下:

<!-- 这在当前应用的任意组件中都可用 -->
<ComponentA/>

  // 或者如下
<component-a></component-a>

虽然全局注册能够使组件可以在应用任意其他组件模板域中使用,但其缺点也很明显:主要有以下两点:
(1)可能存在无用或者不必要的注册,这些注册了但没有被使用的组件是无法自动删除的。导致无用代码增多。
(2)使用全局注册的组件将导致组件之间的依赖关系变得不那么明确,不能够直观的看出引入组件的位置,使得代码的维护性变差。因为,一旦我们需要修改引入的组件,还需要去 main.ts 中查找其具体位置。
2、局部注册:
局部注册指的是直接在需要引入使用的组件中显示 import 导入,如果我们使用的是 setup 组合式API,那么就可以直接在该组件的模板域中使用,而不需要再像选项式API中那样显示的注册引入的组件。示例如下:

// template 模板域中
<template>
<component-a></component-a>

</template>
  
// setup 组合式API中
<script setup lang="ts">
import ComponentA from './App.vue'

</script>

相比于全局注册,局部注册使得组件之间的依赖关系更加清晰,使用起来也很方便,推荐使用。
总结:对于那些全剧共用的组件,例如,components 文件夹下的组件,建议使用全局注册,其他的则采用局部注册。

2、组件通信

**(1)使用 props **
在 setup 组合式API中,我们可以在单文件组件中显式使用宏命令 defineProps 声明 props ,这样Vue才能够知道外部传入的哪些是 props 。使用方式如下:

// MenuItem 单文件组件中,源组件中定义
const props = defineProps({
  data: {
    type: Object,
    required: true,
  },
});


// Menu 单文件组件中,引入方组件绑定传值
<menu-item :data="item"></menu-item>

在上面的 defineProps 定义中,只定义了一个绑定属性 data 并对该属性进行了一些约束,例如,数据类型约束、是否为必须绑定属性约束等等。因此,当我们使用 MenuItem 源组件时,就必须强制性满足这些约束条件。当然,我们也可以同时定义多个绑定属性,例如下方示例:

// MenuItem 单文件组件中,源组件中定义
const props = defineProps({
  data: {
    type: Object,
    required: true,
  },
  value: Integer
});


// Menu 单文件组件中,引入方组件绑定传值,必传 data
<menu-item :data="item"></menu-item>
// value 属性为可选绑定属性,可绑亦可不绑
<menu-item :data="item" :value="2023"></menu-item>

在上面的示例中,我们定义了两个组件绑定属性 data 和 value 其中 data 属性为必选绑定属性,只要其他组件引入使用该源组件,就必须绑定 data 属性。对于哪些没有显示声明 **required: true **的属性,都默认为可选绑定属性。
此外,我们约定如下:任何对象类型的数据即用大括号包裹的数据,其中的所有属性的类型均使用包装类型,不要使用基本数据类型。示例如下:

// 可以这样使用,都是用包装类型
export type UserInfo = {
  	id: String;
    name: String;
		age: Integer;
    avatar: String;
}

// 不建议如下使用
export type UserInfo = {
  	id: string;
    name: string;
		age: int;
    avatar: string;
}

(2)使用 pinia
pinia是vue的存储库,它允许你跨组件实现共享状态,这个状态就是数据。因此,我们也可以借助 pinia 来实现组件通信。其在 setup 组合式API中的使用方式如下:

// 从pinia中引入defineStore函数来定义store
import { createPinia } from 'pinia'

const pinia = createPinia()

export default pinia
import {defineStore} from 'pinia'

// 一个 Store 就是一个实体,类比Java中的实体类
// 头部区域状态管理
export const useHeader = defineStore('useHeader', {
  	// 类比Java中实体类的成员属性
    state: () => {
        return {
            collapse: false
        }
    },
    // 类比Java中的 Getter 方法。推荐使用箭头函数,依赖state
    getters: {
        getCollapse: (state) => {
            return state.collapse
        }
    },
    // 类比Java中的 Setter 方法
    actions: {
        setCollapse() {
            this.collapse = !this.collapse
        }
    }
})
const headerStore = useHeader()
const { collapse } = storeToRefs(headerStore)

3、插槽slots

image.png

4、什么是插件

插件是一种可复用的功能模块,可以为Vue应用程序提供额外的功能或添加全局级别的组件或指令。
如何创建自定义插件呢?下面是一个用于将公共组件注册成全局组件的插件。我们单独使用一个插件来完成这项功能,从而避免将注册过程全部写在 main.ts 程序入口文件中。这样使得我们可以像使用 pinia、vue-router等插件一样。

// 自定义插件
import SvgIcon from "@/components/SvgIcon/SvgIcon.vue";
import SimplePage from "@/components/Pagination/SimplePage.vue";
import SimpleUpload from "@/components/Upload/SimpleUpload.vue";

const components = {SvgIcon, SimplePage, SimpleUpload}

export default {
    // 必须是install方法
    install(app: any) {
        // 注册方式一
        app
            .component('SvgIcon', SvgIcon)
            .component('SimplePage', SimplePage)
            .component('SimpleUpload', SimpleUpload)

        // 注册方式二
        for (const [key, component] of Object.entries(components)) {
            app.component(key, component);
        }

    }
}
import globalPlugin from "@/components/index"

const app = createApp(App);

// 注册插件
app.use(router);
app.use(pinia);

app.use(globalPlugin);
app.mount("#app");

一个插件最重要的组成就是 install 函数,只要当前插件被注册,那么,当应用启动时,install 方法会自动执行一次,已完成一些初始化功能。因此,创建自定义插件的关键就在于如何实现 install 函数中的内容。

5、KeepAlive的用法

是一个内置组件,它的功能是在多个组件间动态切换时缓存被移除的组件实例。
在组件基础章节中,我们已经介绍了通过特殊的 元素来实现动态组件的用法:

<component :is="activeComponent" />

默认情况下,一个组件实例在被替换掉后会被销毁。这会导致它丢失其中所有已变化的状态,当这个组件再一次被显示时,会创建一个只带有初始状态的新实例。如果我们希望在组件切换显示时,依旧能够保留 切走 时的状态,那么,我们可以用 内置组件将这些动态组件包装起来:

<!-- 非活跃的组件将会被缓存! -->
<KeepAlive>
  <component :is="activeComponent" />
</KeepAlive>

6、Transition过渡动画

如果我们想为一个元素添加动画效果,我们可以使用 vue 提供的内置组件 Transition,我们只需要将需要挂载动画的元素插入到 Transition 组件中即可。
是一个内置组件,这意味着它在任意别的组件中都可以被使用,无需注册。它可以将进入和离开动画应用到通过默认插槽传递给它的元素或组件上。进入或离开可以由以下的条件之一触发:

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

以下是最基本用法的示例:

<button @click="show = !show">Toggle</button>
<Transition>
  <p v-if="show">hello</p>
</Transition>
/* 下面我们会解释这些 class 是做什么的 */
.v-enter-active,
.v-leave-active {
  transition: opacity 0.5s ease;
}

.v-enter-from,
.v-leave-to {
  opacity: 0;
}

基于CSS的过渡动画
image.png
我们只需要关注,动画开始和结束两个状态,以及两个进入和离开两个过程。分别如下:

// 动画过程
.v-enter-active,
.v-leave-active {
  transition: opacity 0.5s ease;
}

// 前后状态
.v-enter-from,
.v-leave-to {
  opacity: 0;
}

为过渡动画取名
我们可以给 组件传一个 name prop 来声明一个过渡效果名:

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

对于一个有名字的过渡效果,对它起作用的过渡 class 会以其名字而不是 v 作为前缀。比如,上方例子中被应用的 class 将会是 fade-enter-active 而不是 v-enter-active。这个“fade”过渡的 class 应该是这样:

.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.5s ease;
}

.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}

配合 CSS 的 transition 使用
一般都会搭配原生 CSS 过渡一起使用,正如你在上面的例子中所看到的那样。这个 transition CSS 属性是一个简写形式,使我们可以一次定义一个过渡的各个方面,包括需要执行动画的属性、持续时间和速度曲线。
下面是一个更高级的例子,它使用了不同的持续时间和速度曲线来过渡多个属性:

<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;
}

配合 CSS 的 animation 使用
原生 CSS 动画和 CSS transition 的应用方式基本上是相同的,只有一点不同,那就是 *-enter-from 不是在元素插入后立即移除,而是在一个 animationend 事件触发时被移除。
对于大多数的 CSS 动画,我们可以简单地在 *-enter-active 和 *-leave-active class 下声明它们。下面是一个示例:

<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);
  }
}

自定义过渡 class
你也可以向 传递以下的 props 来指定自定义的过渡 class:

  • enter-from-class
  • enter-active-class
  • enter-to-class
  • leave-from-class
  • leave-active-class
  • leave-to-class

你传入的这些 class 会覆盖相应阶段的默认 class 名。这个功能在你想要在 Vue 的动画机制下集成其他的第三方 CSS 动画库时非常有用,比如 Animate.css:

<!-- 假设你已经在页面中引入了 Animate.css -->
<Transition
  name="custom-classes"
  enter-active-class="animate__animated animate__tada"
  leave-active-class="animate__animated animate__bounceOutRight"
>
  <p v-if="show">hello</p>
</Transition>

此外,关于在 Vue 3 的 transition 组件中,过渡动画分为几个阶段,以下是一个旋转动画的开始和结束过程的分析:

  1. 开始过程:当元素插入 DOM 时,Vue 会自动应用一系列 CSS 类名。假设我们的 transition 组件的 name 属性为 rotate,那么以下类名会按顺序应用到元素上:在动画开始时,Vue 会先应用 rotate-enter-from 和 rotate-enter-active 类名。在下一帧,Vue 会移除 rotate-enter-from 类名并应用 rotate-enter-to 类名。这样,浏览器会在元素上执行旋转动画。
    • rotate-enter-from:定义动画开始时的样式。通常在这个类名中设置旋转的初始状态,例如 transform: rotate(0deg)。
    • rotate-enter-active:定义动画过程中的样式。通常在这个类名中设置动画的持续时间、延迟和缓动效果,例如 transition: transform 1s ease。
    • rotate-enter-to:定义动画结束时的样式。通常在这个类名中设置旋转的最终状态,例如 transform: rotate(360deg)。
  2. 结束过程:当动画完成后,Vue 会移除 rotate-enter-active 和 rotate-enter-to 类名。这样,元素会保持在旋转动画的最终状态。

类似地,在元素从 DOM 中移除时,Vue 会自动应用一系列 CSS 类名,例如 rotate-leave-from、rotate-leave-active 和 rotate-leave-to。这些类名用于定义元素移除时的旋转动画。
以下是一个简单的 Vue 3 旋转动画示例:

<template>
  <div>
    <button @click="show = !show">Toggle</button>
    <transition name="rotate">
      <div v-if="show">I am a rotating element!</div>
    </transition>
  </div>
</template>

<script>
import { ref } from "vue";
export default {
  setup() {
    const show = ref(true);
    return { show };
  },
};
</script>

<style>
.rotate-enter-from,
.rotate-leave-to {
  transform: rotate(0deg);
}
.rotate-enter-active,
.rotate-leave-active {
  transition: transform 1s ease;
}
.rotate-enter-to,
.rotate-leave-from {
  transform: rotate(360deg);
}
</style>

在这个示例中,当 show 的值为 true 时,元素会执行旋转动画并插入到 DOM 中。当 show 的值为 false 时,元素会执行旋转动画并从 DOM 中移除。在任何情况下,transition 组件都会确保在元素插入或移除之前应用过渡动画。
总结:为true,插入动画;为false,移除动画。不论是插入还是移除,两个过程都是一个完整的循环,也就是说,在上面的例子中,插入过程是旋转一周,而移除过程同样是旋转一周,效果是一样的,除非我们将两个过程分别单独设置。如果是如下一样并列设置
image.png
那么,两个过程动画效果将一模一样。此外,插入的开始状态至插入的结束状态为一个完整旋转一周的循环,同理,移除的开始至结束两个状态也是一样,并且,插入的结束状态和移除的开始状态是同一个值,插入的开始状态和移除的结束状态也是同一个值。

Vue3属性绑定方式分析 最近还整理一份JavaScript与ES的笔记,一共25个重要的知识点,对每个知识点都进行了讲解和分析。能帮你快速掌握JavaScript与ES的相关知识,提升工作效率。 阅读详情

相关推荐

vue1使用教程

放大文字</button>})// 父组件中使用数据驱动视图组件化开发指令系统响应式数据绑定了解Vue的发展历程维护遗留项目深入理解Vue的核心思想对于新项目,强烈建议使用Vue 3以获得更好的性能、开发体验和长期支持。

a181001_的博客 1265

Vue3中数据的绑定

本文主要介绍在Vue3中几种数据绑定的方式。

一起学习 1805

【Vue3 知识第四讲】数据双向绑定、事件绑定、事件修饰符详解

数据双向绑定、事件绑定、事件修饰符详解

前端知识分享喵 1487

初入vue3之--->关于vue3的数据绑定(主要是对象和数组)

vue3的双向绑定

m0_55336994的博客 3193

Vue3通透教程【三】Vue3代码初体验找不同

初体验Vite构建Vue3项目工程入口文件的不同

商务合作欢迎私信我 5万+

TMC22系列步进电机驱动设计:从静音原理到硬件实战

步进电机驱动是运动控制系统的核心,其性能直接影响设备的精度、噪音和效率。传统驱动方案如A4988采用方波控制,存在转矩脉动大、运行噪音高等问题。而高性能驱动通过优化电流波形与切换策略,从根本上提升了运动质量。其中,**StealthChop2**技术通过同步MOSFET开关与电机反电动势过零点,实现了近乎无声的低速运行;**SpreadCycle**模式则通过逼近正弦波的电流调节,优化了中高速动态性能。这些技术为3D打印机、CNC机床、自动化设备等对静音和精度有要求的场景提供了关键解决方案。本文以广泛应用的

weixin_31858171的博客 353

回顾Vue3组件通信的形式

总结Vue3中各种组件通信的方式,包括Props、Emits、v-model、provide/inject等

Recording code life 1779

(UE5 5.2)HISM Mobile DrawInstance在渲染层的实现浅分析

在 (UE4 4.27) UHierarchicalInstancedStaticMesh(HISM)原理分析 这篇博客大致介绍HISM组件从游戏线程到渲染线程的重建KD-Tree和剔除并提交DrawCall逻辑,但是没有分析渲染层的大致数据结构和实现. 可以看出FHierarchicalStaticMeshSceneProxy 实际存储Instance数组数据来自于FInstancedStaticMeshSceneProxy FHierarchicalStaticMeshSceneProxy只是以FClu

qq_29523119的博客 2724

【Vue3】回顾watch,学习watchEffect

合理的使用watch和watchEffect可以让我们的开发变得更加高效挑战与创造都是很痛苦的,但是很充实。

null18的博客 2373

手把手教你用EGM96模型校正DEM数据(含在线工具推荐)

本文详细介绍了如何使用EGM96地球重力场模型校正DEM数据中的高程偏差。通过对比WGS84椭球面与大地水准面的差异,解释了校正原理,并提供了从在线工具快速验证到Python脚本批量处理的全流程实战指南,帮助GIS开发者和数据分析师确保高程数据基准一致,提升空间分析精度。

ttt77的博客 1007

交流异步机VVVF变频控制simulink/matlab仿真分析

VVVF,是Variable Voltage and Variable Frequency的缩写, 意为:可变电压、可变频率,也就是变频调速系统。 VVVF控制的逆变器连接电机,通过同时改变频率和电压,达到磁通恒定(可以用反电势/频率近似表征)和控制电机转速(和频率成正比)的目的,所以多应用在变频器中,属于工业自动化领域。

Ezekiel_Mok的博客 4207

Vue.js入门系列(三十四):Vue2与Vue3的响应式原理回顾与比较

Reflect是 ES6 中新增的一个内置对象,它提供了与Proxy操作对象相同的反射方法。Reflect的存在使得Proxy的实现更加简洁,并且提供了一些默认行为。Vue2 响应式原理:了解了 Vue2 中基于的响应式系统及其局限性。Vue3 响应式原理:通过Proxy和Reflect取代 Vue2 的,解决了 Vue2 的局限性,并大大提升了性能和灵活性。reactive和ref的对比:学会了如何在 Vue3 中使用ref和reactive处理不同类型的数据,并理解了它们各自的应用场景。

私聊前往站内信:https://i.youkuaiyun.com/#/msg/chat/weixin_44976692 2万+

YOLOv8 | 损失函数改进,最新提出的Shape-IoU,有效涨点

作为检测器定位分支的重要组成,边框回归损失在目标检测任务中发挥巨大作用。现有的边框回归方法,通常考虑了GT框与预测框之间的几何关系,通过使用边框间的相对位置与相对形状等计算损失,而忽略了边框其自身的形状与尺度等固有属性对边框回归的影响。为了弥补现有研究的不足,本文提出聚焦边框自身形状与尺度的边框回归方法。首先我们对边框回归特性进行分析,得出边框自身形状因素与尺度因素会对回归结果产生影响。

kay_545 1913

Vue的路由配置(Vue2和Vue3的路由配置)

系列文章目录 一、路由是什么? 二、Vue2中路由的创建步骤 1.安装正确的路由版本(这里我踩坑了) 2.vue2中配置路由的步骤 步骤如下: 代码如下图所示 如下图所示:

weixin_46022934的博客 2万+

深入理解 ABAP 中的 Fast Data Access:从 @itab 到 FOR ALL ENTRIES 的性能关键路径

ABAP性能优化的关键在于理解Fast Data Access(FDA)机制,它连接了应用服务器的内表与数据库运算。FDA允许将内表数据推送到数据库进行筛选、聚合等操作,是SELECT...FROM@itab语法的基础。开发者需注意:1)该语法可能在内置引擎或数据库执行;2)仅当SQL能力超出ABAP范围时才建议使用;3)需通过CL_ABAP_DBFEATURES检查数据库支持情况。合理使用FDA可优化批量数据处理,但滥用可能导致性能下降。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1269

论文分栏前后内容不连续?教你word如何删除分节符

问题 当我们在写论文的时候,很多期刊的模板都是两栏的格式,但有的时候我们插入的图像尺寸可能太大,两栏格式放不下,因此需要将其以一栏的格式出入,如下: 但当我们把图片删除后,再将其转换成两栏格式,前后内容就出现不连续的情况: 正常情况下应该是如下: 正确处理方式 里面有个分页符,把分页符去掉,或者把你的文字全部复制到记事本里,然后重新复制回word就可以取消原有的格式了。 删除分节符的方法(以word 2019为例) -点击菜单栏上的“视图”,点击“大纲视图”。 可以看到word文档以整列的形式展开:

左手の明天的博客 1万+

Vue 3 30天精进之旅:Day 28 - 回顾与总结

30天的学习旅程即将结束,但这只是学习 Vue 的开始。希望通过这次精进之旅,您能够在实际开发中灵活运用所学知识,不断提升自己的技术水平。继续保持学习的热情,勇于实践,将会有更多的收获在前方等待着您。祝您在前端开发的道路上越走越远!

码上飞扬的博客 1177

MATLAB极坐标伪彩色圆图绘制之polarPcolor函数

polarPcolor函数介绍 我们经常在论文或期刊上看到如下图这种以色调冷暖代表数值大小的极坐标圆图, 而function [varargout] = polarPcolor(R,theta,Z,varargin)可以很好的帮助我们快速绘制这类极坐标圆图。它非常适合雷达或激光雷达的平面位置指示器。使用polarPcolor函数时,我们只需根据手中数据构造相应的二维矩阵Z即可,十分便捷。varargin值为绘制模式选择。R值为半径坐标一维矩阵,theta值为角坐标一维矩阵。 代码示例 这里使用了海洋表面耀光

qq_19512613的博客 1万+

回顾 Vue 3 基础【Plan 1】

本文介绍了 Vue.js 组件的基础知识和模板语法。首先,Vue 组件通常定义在单文件组件(SFC)中,通过 <script setup> 语法简化了组件的定义和使用。父组件可以通过导入子组件来使用它,并且组件可以多次重用,每次使用都会创建一个新的实例。父组件可以通过 props 向子组件传递数据,子组件则可以通过 $emit 方法向父组件发送事件。插槽(<slot>)允许父组件向子组件传递内容,动态组件则可以在多个组件之间切换。在模板语法部分,文本插值使用双大括号绑定数据,v-ht

kuiini的博客 921

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

[Vue3] - 1 创建vue3工程

前言 …目标 1 vue3的优势有哪些 2 如何创建vue3工程 #render_8_1656077875-svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#render_8_1656077875-svg...

cnzzs的博客 1047

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

开源啦!一个基于 Vue 3 + Vant 3 的商城项目

之前发布过一篇文章,告诉大家我要开发一个 Vue3 的商城项目并开源到 GitHub 上,供大家练手和学习,随后也一直有收到留言和反馈,问我开发到哪里了,什么时候开源之类的问题,今天终于...

程序员有故事 1万+

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

Vue3+TypeScript 学习回顾,温故而知新(已完结)

本文章是对Vue3的学习3与回顾

starry-sea1412的弃坑之路 1046

一、VueJs 填坑日记之基础概念知识解释

概述在最开始听说vuejs这个词是在2016年,当时天真的认为自己是个后端开发工程师不需要学习太多的前端知识,不过紧接着在2017年在公司就用到了vuejs。对于初学者(尤其是干后端的初学者)来说,刚接触时,有好多思路都很难理解。 本来写这一系列博客,只是为了记录自己在这一段时间学习vuejs的成果。如果有何纰漏请大家多多理解,不喜勿喷… 再说,在目前互联网的调整发展情况下,javascrip...

I Teach You, 我教你! 144

Vuejs001——小结回顾

一、邂逅Vuejs 1.1.认识Vuejs Vue的读音 Vue的渐进式 Vue的特点 1.2.安装Vue CDN引入 下载引入 npm安装 1.3.Vue的初体验 Hello Vuejs mustache->体验Vue响应式 Vue列表展示 v-for 后面给数组追加元素的时候,新的元素可以在界面中渲染出来 Vue计数器小案例 事件监听:click->met...

just-so-so 178

回顾Vue3

/ 定义类型: string: string// 可以包含任何其他属性。

m178398家里有只小肥猫 555
上一篇: 14、配置svg图标使用环境
下一篇: Docker中安装常用的中间件全过程
龙茶清欢
博客等级 码龄7年 3720粉丝 375原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龙茶清欢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值