transition 动画与过渡的实现
在 Vue 中推荐使用 CSS3 来完成动画效果。当在插入、更新或从 DOM 中移除项时,Vue 提供了多种应用转换效果的方法。
transition 动画
Vue 中通过两个内置的组件来实现动画与过渡效果,分别是:<transition>和<transition-group>,代码如下:
<template>
<div>
<h2>hello transition</h2>
<button @click=" isShow = !isShow ">点击</button>
<transition name="slide" mode="out-in">
<div v-if="isShow" class="box"></div>
<div v-else class="box2"></div>
</transition>
</div>
</template>
<script>
export default {
data(){
return {
isShow: true
}
}
}
</script>
<style scoped>
.box{
width: 200px;
height: 200px;
background: skyblue;
}
.box2{
width: 200px;
height: 200px;
background: pink;
}
.slide-enter-from{
opacity: 0;
transform: translateX(200px);
}
.slide-enter-to{
opacity: 1;
transform: translateX(0);
}
.slide-enter-active{
transition: 1s;
}
.slide-leave-from{
opacity: 1;
transform: translateX(0);
}
.slide-leave-to{
opacity: 0;
transform: translateX(200px);
}
.slide-leave-active{
transition: 1s;
}
</style>
其中<transition>组件通过name属性去关联 CSS 中的选择器,CSS 中的选择器主要有 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被移除的同时),在过渡或动画完成之后移除。
默认情况下,进入和离开在两个元素身上是同时执行的,如果想改变其顺序,需要用到mode属性,其中out-in表示先离开再进入,而in-out表示先进入再离开。
动态组件与 keep-alive 组件缓存
动态组件
动态组件可以实现在同一个容器内动态渲染不同的组件,依一个内置组件<component>的is属性的值,来决定使用哪个组件进行渲染。
<template>
<div>
<h2>动态组件</h2>
<button @click=" nowCom = 'my-com1' ">组件1</button>
<button @click=" nowCom = 'my-com2' ">组件2</button>
<button @click=" nowCom = 'my-com3' ">组件3</button>
<component :is="nowCom"></component>
</div>
</template>
<script>
import MyCom1 from '@/13_MyCom1.vue'
import MyCom2 from '@/14_MyCom2.vue'
import MyCom3 from '@/15_MyCom3.vue'
export default {
data(){
return {
nowCom: 'my-com1'
}
},
components: {
'my-com1': MyCom1,
'my-com2': MyCom2,
'my-com3': MyCom3
}
}
</script>
keep-alive 组件
当我们点击的时候,就会进行组件的切换。在每次切换的过程中都会重新执行组件的渲染,这样组件操作的行为就会还原,而我们如何能够保证组件不变呢?可以利用<keep-alive>对组件进行缓存,这样不管如何切换,都会保持为初始的组件渲染,这样可以很好的保留之前组件的行为。
组件的切换也可以配合<transition>完成动画的切换。
<template>
<div>
<h2>动态组件</h2>
<button @click=" nowCom = 'my-com1' ">组件1</button>
<button @click=" nowCom = 'my-com2' ">组件2</button>
<button @click=" nowCom = 'my-com3' ">组件3</button>
<transition name="slide" mode="out-in">
<keep-alive>
<component :is="nowCom"></component>
</keep-alive>
</transition>
</div>
</template>
异步组件与 Suspense 一起使用
异步组件
在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块。
在上一个小节的动态组件的基础上,进行异步组件的演示。首先可以打开 chrome 浏览器的 network 网络,可以观察到在动态组件切换的时候,network 网络中没有进行任何请求的加载,这证明了在初始的时候,相关的动态组件就已经加载好了。
所以对于大型项目来说,如果能实现按需载入的话,那么势必会对性能有所提升,在 Vue 中主要就是利用 defineAsyncComponent 来实现异步组件的。
<script>
import { defineAsyncComponent } from 'vue'
export default {
data(){
return {
nowCom: 'my-com1'
}
},
components: {
'my-com1': defineAsyncComponent(() => import('@/MyCom1.vue')),
'my-com2': defineAsyncComponent(() => import('@/MyCom2.vue')),
'my-com3': defineAsyncComponent(() => import('@/MyCom3.vue'))
}
}
</script>
Suspense 组件
由于异步组件是点击切换的时候才去加载的,所以可能会造成等待的时间,那么这个时候可以配合一个 loading 效果,在 Vue 中提供了一个叫做<Suspense>的组件用来完成 loading 的处理。
<template>
<suspense>
<component :is="nowCom"></component>
<template #fallback>
<div>loading...</div>
</template>
</suspense>
</template>
跨组件间通信方案 Provide_Inject
跨组件通信方案
正常情况下,我们的组件通信是需要一级一级的进行传递,通过父子通信的形式,那么如果有多层嵌套的情况下,从最外层把数据传递给最内层的组件就非常的不方便,需要一级一级的传递下来,那么如何才能方便的做到跨组件通信呢?
可以采用 Provide 和 inject 依赖注入的方式来完成需求,代码如下:
// provide.vue
<script>
export default {
provide(){
return {
message: 'hello provide', //传递数据
count: this.count, //传递响应式数据
getInfo(data){ //获取数据
console.log(data);
}
}
}
}
</script>
// inject.vue
<template>
<div>
hello inject, {{ message }}, {{ count }}
</div>
</template>
<script>
export default {
inject: ['message', 'getInfo', 'count'],
mounted(){
this.getInfo('hello inject');
}
}
</script>
Provide 与 Inject 注意点
- 保证数据是单向流动的,从一个方向进行数据的修改
- 如果要传递响应式数据,需要把 provide 改造成工厂模式发送数据
export default {
//普通模式,无法传递响应式数据
//provide: {
// message: 'hello provide',
// getInfo(data) {
// console.log(data);
// }
//}
//工厂模式,可以传递响应式数据
provide(){
return {
message: 'hello provide', //传递数据
count: this.count, //传递响应式数据
getInfo(data){ //获取数据
console.log(data);
}
}
}
}
Teleport 实现传送门功能
Teleport 组件
Teleport 可以实现传送门功能,也就是说逻辑属于当前组件中,而结构需要在组件外进行渲染,例如:按钮模态框组件。
// 模态框.vue
<template>
<div>
<button @click=" isShow = true ">点击</button>
<teleport to="body">
<div v-if="isShow">模态框</div>
</teleport>
</div>
</template>
<script>
export default {
data(){
return {
isShow: false
}
}
}
</script>
// 调用模态框.vue
<template>
<div>
<h2>传送门</h2>
<my-modal></my-modal>
</div>
</template>
<script>
import MyModal from '@/模态框.vue'
export default {
components: {
'my-modal': MyModal
}
}
</script>
这样渲染出的模态框组件就是在 body 下,而不是在 button 按钮下面。我们经常会有这种需求,比如需要组件的位置相对于 body 偏移而不是当前组件。
逻辑组件
但是往往我们需要的并不是普通组件的调用方式,而是逻辑组件的调用方式,那么如何实现逻辑组件呢?代码如下:
// 定义逻辑组件,modal.js
import { createApp } from 'vue';
import ModalVue from '@/模态框.vue';
function modal(){
let div = document.createElement('div');
createApp(ModalVue).mount(div);
document.body.append(div);
}
export default modal;
// 调用逻辑组件
<template>
<div>
<h2>传送门</h2>
<button @click="handleClick">点击</button>
</div>
</template>
<script>
import modal from '@/modal.js'
export default {
methods: {
handleClick(){
modal();
}
}
}
</script>
Vue 是一个渐进式框架,也就是说一个网页可以用 Vue,也可以不用 Vue。也可以一个页面采用两个容器,分别创建 Vue 的实例对象。所以初始有一个<div></div>容器,可以创建一个 vue 实例。我们自己创建一个 div 作为容器,也可以再次创建一个 vue 实例,那么两个实例都可以完成 Vue 的功能,互相不影响。这里就是创建了一个新的实例作为模态框应用。