vue基础(2):深入理解 Vue 组件、Vue中的动画特效

本文深入探讨Vue组件的使用技巧,包括组件坑的解决、父子组件数据传递、参数校验、自定义事件及非父子组件通信。此外,还详细讲解了Vue中的动画原理,如何结合第三方库实现复杂动画效果,以及列表过渡等实战技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章目录

一、深入理解 Vue 组件

1.1 使用组件容易遇到的坑

总结一些组件再使用时容易遇到的坑:

1.1.1 使用is='组件名'解决html表签嵌套规范造成的bug

html嵌套规范,会造成bug的标签有table、ul(有的浏览器会)、select等

	<div id="app">
		<table>
			<tbody>
				<row></row>
				<row></row>
				<row></row>
				<row></row>
			</tbody>
		</table>		
	</div>
	<script type="text/javascript">
		Vue.component('row',{
			template: '<tr><td>这是表格</td></tr>',
		});
		var vm = new Vue({
			el: '#app',
		});
	</script>

<tr>标签被渲染到<table>外面了。
在这里插入图片描述

解决方案
	<div id="app">
		<table>
			<tbody>
                <tr is="row"></tr>
                <tr is="row"></tr>
                <tr is="row"></tr>
                <tr is="row"></tr>
			</tbody>
		</table>		
	</div>
	<script type="text/javascript">
		Vue.component('row',{
			template: '<tr><td>这是表格</td></tr>',
		});
		var vm = new Vue({
			el: '#app',
		});
	</script>



1.1.2 非根组件(子组件)中data必须是函数,返回一个对象。

子组件不像是根组件只会有一个,既然注册了子组件就是想要复用,为了保证每一个子组件数据的唯一性,避免多个子组件数据共享,通过函数将数据存在到独立的函数作用域中。

    <div id="app">
        <table>
            <tbody>
                <tr is="row"></tr>
                <tr is="row"></tr>
                <tr is="row"></tr>
                <tr is="row"></tr>
            </tbody>
        </table>
    </div>
    <script type="text/javascript">
    Vue.component('row', {
        template: "<tr><td @click='addNum'>{{num}}</td></tr>",
        data: function() {
            return {
                num: 0,
            };
        },
        methods: {
            addNum: function() {
                this.num++;
            },
        }
    });
    var vm = new Vue({
        el: '#app',
    });
    </script>



1.1.3 使用引用ref="引用名"获取dom元素和组件引用

虽然使用vue我们只需要关心数据,不用操作dom。但是复杂的动画就不要指望vue,还是要使用dom。

1.使用ref获取dom元素

  • 通过在html标签上加ref="xxx"属性,可通过this.$refs.xxx取得该dom节点
    <div id="app">
		<div ref="hello" @click="getDom">{{message}}</div>
    </div>
    <script type="text/javascript">
    var vm = new Vue({
        el: '#app',
        data: {
        	message: ' hello poorpenguin',
        },
        methods: {
        	getDom: function(){
        		console.log(this.$refs.hello);
        	}
        }
    });
    </script>

在这里插入图片描述

2.使用ref获取组件引用

  • 组件上添加了ref属性,可以取得的是该组件的引用(也可以理解为该组件对象),因此可以访问该组件上的属性
这只是举个例子,并不推荐这样写
	<div id="app">
		<row ref="numOne" @numchange="changeTotal"></row>
		&nbsp;&nbsp;+&nbsp;&nbsp;
		<row ref="numTwo" @numchange="changeTotal"></row>
		&nbsp;&nbsp;=&nbsp;&nbsp;{{total}}
    </div>
    <script type="text/javascript">
   		Vue.component('row',{
   			template: '<span @click="addNum">{{number}}</span>',
   			data: function(){
   				return {
   					number: 0,
   				};
   			},
   			methods: {
   				addNum: function(){
   					this.number++;
   					this.$emit('numchange');
   				}
   			}
   		});

	    var vm = new Vue({
	        el: '#app',
	        data: {
	        	total: 0,
	        },
	        methods: {
	        	changeTotal: function(){
	        		this.total = this.$refs.numOne.number + this.$refs.numTwo.number;
	        	}
	        }

	    });
    </script>

两个组件对象。
在这里插入图片描述
在这里插入图片描述


1.1.4 子组件的单个根元素

当子组件的模板template中有多个dom元素的时候,要使用一个元素将他们包裹起来。每个组件必须只有一个根元素,所有要将模板的内容包裹在一个父元素内。

		模板中这样写是会报错的。
		var counter = {
			props: ['count'],
			template: '<span>123123</span><h1>poorpenguin</h1><div>{{count}}</div>',
		};
		
		这样式正确的写法
		var counter = {
			props: ['count'],
			template: '<div><span>123123</span><h1>poorpenguin</h1><div>{{count}}</div></div>',
		};



1.2 父子组件间的数据传递

1.2.1 父组件传值给子组件

  1. 父组件通过 属性形式 传值给子组件(不一定要使用v-bind将属性和根组件中的变量绑定)

不使用指令v-bind,直接使用属性进行传值。但是这样" "中的就不是js表达式,子组件接收到的就是字符串

	<div id="app">
		<counter count="3"></counter>
	</div>
	<script type="text/javascript">
		var counter = {
			props: ['count'],
			template: '<div>{{count}}</div>',
		};
		var vm = new Vue({
			el: '#app',
			components: {
				'counter': counter,
			}
		});
	</script>

使用指令v-bind,就可以将属性和根组件中的变量进行绑定,这时" "中就是js表达式,也可以直接传递数字。

	<div id="app">
		<counter :count="num1"></counter>	这里使用v-bind命令
		或者<counter :count="3"></counter>	这样传递的也是数字,而不是字符串
	</div>
	<script type="text/javascript">
		var counter = {
			props: ['count'],
			template: '<div>{{count}}</div>',
		};
		var vm = new Vue({
			el: '#app',
			data: {
				num1: 3,
			},
			components: {
				'counter': counter,
			}
		});
	</script>
  1. 子组件通过props[]接收传递过来的属性,但是不能直接操作接收的属性

子组件通过props[]接收传递过来的属性,并将传递过来的属性赋值给在data中定义新属性。
因为单项数据流,修改Object的值会导致别的引用了该对象的子组件内数据的变化,用在子组件内复制一份该对象,修改子组件内自己的data来代替。

	<div id="app">
		<counter :count="num1"></counter>
	</div>
	<script type="text/javascript">
		var counter = {
			props: ['count'],
			data: function(){
				return {
					number: this.count,
				};
			},
			template: '<div @click="addNum">{{number}}</div>',
			methods: {
				addNum: function(){
					this.number++
				},
			}
		};
		var vm = new Vue({
			el: '#app',
			data: {
				num1: 3,
			},
			components: {
				'counter': counter,
			}
		});
	</script>

错误示范:

	子组件
		var counter = {
			props: ['count'],
			template: '<div @click="addNum">{{count}}</div>',
			methods: {
				addNum: function(){
					this.count++
				},
			}
		};



1.2.2 子组件传值给父组件

  1. 子组件事件触发的形式this.$emit("自定义事件名","传给父组件的数据"),向父组件传值
		var counter = {
			props: ['count'],
			data: function(){
				return {
					number: this.count,
				};
			},
			template: '<div @click="addNum">{{number}}</div>',
			methods: {
				addNum: function(){
					this.number++
					this.$emit('numchange',this.number);
				},
			}
		};
  1. 父组件通过@自定义事件名="父组件的方法“监听子组件传值
	<div id="app">
		<counter :count="num1" @numchange="show"></counter>	监听子组件的事件
	</div>
	<script type="text/javascript">
		var counter = {
			props: ['count'],
			data: function(){
				return {
					number: this.count,
				};
			},
			template: '<div @click="addNum">{{number}}</div>',
			methods: {
				addNum: function(){
					this.number++
					this.$emit('numchange',this.number,'阿西吧');
				},
			}
		};
		var vm = new Vue({
			el: '#app',
			data: {
				num1: 3,
			},
			components: {
				'counter': counter,
			},
			methods: {
				show: function(value,str){	接收子组件的传值
					console.log(value,str);
				},
			}
		});
	</script>



1.3 组件参数校验与非 props 特性

1.3.1 组件参数校验

1.3.2 非props特性

props 特性:父组件向子组件传递一个属性,子组件通过 props 声明了相应属性进行接收,在 DOM 中不会显示该属性。

	<div id="app">
		<my-component title="标题1" content="hello poorpenguin"></my-component>
	</div>
	<script type="text/javascript">
		Vue.component('my-component',{
			props: ['content','title'],
			template: `<div>
							<h1>{{title}}</h1>
							<div>{{content}}</div>
						</div>`,
		});
		var vm = new Vue({
			el: '#app',
		});
	</script>

在这里插入图片描述

非props特性:父组件向子组件传递一个属性,子组件没有通过 props 声明相应属性进行接收:
1、子组件中是无法使用父组件传递过来的值
2、该属性会显示在子组件模板中的根元素上

	<div id="app">
		<my-component title="标题1" content="hello poorpenguin"></my-component>
	</div>
	<script type="text/javascript">
		Vue.component('my-component',{
			template: `<div>
							<h1>123</h1>
							<div>123</div>
						</div>`,
		});
		var vm = new Vue({
			el: '#app',
		});
	</script>

在这里插入图片描述




1.4 自定义组件绑定原生事件

什么是原生事件

  • 在dom元素上绑定的事件就是原生事件
  • 在子组件(自定义组件)的模板template中dom元素绑定的也是原生事件。
	<div id="app">
		<my-component title="标题1" content="hello poorpenguin"></my-component>
		<div @click="clickDiv"></div>
	</div>
	<script type="text/javascript">
		Vue.component('my-component',{
			template: `<div>
							<h1>123</h1>
							<div @click="clickDiv">123</div>
						</div>`,
			methods: {
				clickDiv: function(){
					alert('这是子组件中原生事件');
				}
			}
		});
		var vm = new Vue({
			el: '#app',
			methods: {
				clickDiv: function(){
					alert('这是原生事件');
				}
			}
		});
	</script>

什么是自定义事件

  • 在自定义的组件上使用@click是无法直接触发click事件的,vue会将其当作自定义的事件,需要在组件内去触发这个自定义的事件才会执行。

  • 在自定义组件里通过 this.$emit(‘事件名’) 触发绑定在该组件上对应的自定义事件

	<div id="app">
		<my-component title="标题1" content="hello poorpenguin" @click="show"></my-component>
		<div @click="clickDiv"></div>
	</div>
	....
  • 如果需要在自定义的组件上直接实现@click事件需要使用@click.native监听原生点击事件。
		<my-component @click.native="handleClick"></my-component>
	</div>
	<script type="text/javascript">
		Vue.component('my-component',{
			template: `<div>
							<h1>123</h1>
							<div>123</div>
						</div>`,
		});
		var vm = new Vue({
			el: '#app',
			methods: {
				handleClick: function(){
					alert('自定义事件');
				}
			}
		});
	</script>




1.4.1 在路由<router-link>上绑定原生事件

在router-link上直接绑定点击事件是不起作用的,需要使用.native修饰符才能绑定原生事件

<router-link to="/" tag="div" class="header-abs" v-show="isShow" @click.native="removeScrollListener">
	<i class="iconfont">&#xe624;</i>
</router-link>




1.5 非父子组件间的传值

一个网页可以拆分成多个组件,根组件下有多个子组件,每一个子组件下也有许多子组件。

  • 父子组件,就是具备父子关系的组件(如下图1和2就是父子组件)
  • 非父子组件,就是两个组件传值,但是两个组件不具备父子关系。(如下图的1和33和3就是分父子组件)
    在这里插入图片描述

解决非父子组件间的传值,有两个解决方案

  1. 使用Vuex
  2. 利用vue的总线机制。

1.5.1 利用bus(总线机制/发布订阅模式/观察者模式)解决非父子组件间传值问题

前面强调过,每一个组件都是一个Vue实例,如果在Vue的原型prototype上定义一个属性,那后续实例化出来的根组件还是子组件都会有这个属性。

利用bus解决的三步骤:

  1. 在Vue的prototype上定义bus属性 Vue.prototype.bus = new Vue();
  2. 在组件的mounted生命周期钩子里使用this.bus.$on(‘事件名’, function(value){});来监听所定义的bus属性上对应的事件被触发,然后在回调函数里进行操作。
  3. 在组件的methods定义的方法里使用 this.bus.$emit(‘事件名’, value); 触发事件并传值。

兄弟组件间的值传递:

	<div id="app">
		<child content="poor"></child>
		<child content="penguin"></child>
	</div>
	<script type="text/javascript">
		//在Vue的原型上定义一个bus属性,并传值一个Vue实例
		Vue.prototype.bus = new Vue();

		Vue.component('child',{
			props: {
				content: String,
			},
			template: '<div @click="handleClick">{{selfContent}}</div>',
			data: function(){
				return {
					selfContent: this.content,
				};
			},
			mounted: function(){
				var this_ = this;	//这里保存一下this,
				this.bus.$on('childchange',function(value){
					//这里的this指向bus,所以要在外面保存this
					this_.selfContent = value;
				});
			},
			methods: {
				handleClick: function(){
					this.bus.$emit('childchange',this.selfContent);
				},
			}
		});
		var vm = new Vue({
			el: '#app',
		});
	</script>



1.6 Vue中使用插槽<slot>

1.6.1 插槽<slot>的作用

插槽在Vue中很重要,如果想要父组件向子组件传递dom元素,子组件在模板template展现传递过来的dom元素,就需要用来插槽。

父组件使用属性传递dom元素

有人会说可以使用属性传递dom元素啊

	<div id="app">
		<child content="<h1>poorpenguin</h1>"></child>
	</div>
	<script type="text/javascript">
		var child = {
			props: ['content'],
			template: `<div>
							<p>属性传递</p>
							{{content}}
						</div>`,
		};
		var vm = new Vue({
			el: '#app',
			components: {
				'child': child,
			}
		});
	</script>

在这里插入图片描述
可以发现HTML标签并没有被解析。
那使用v-html命令

	template: `<div>
				<p>属性传递</p>
				{{content}}
			</div>`,

虽然HTML标签被解析了,但是外面又包裹了一层<div><template>别想了没用。>

使用插槽就可以很好的解决问题。


1.6.2 插槽<slot>基本用法

  • 在父组件中的自定义组件中直接插入dom元素
  • 在子组件的template中使用<slot></slot>占位,在渲染后占位的<slot></slot>会被替换成dom元素。
	<div id="app">
		<child>
			<h1>poorpenguin</h1>		**插槽的内容
		</child>
	</div>
	<script type="text/javascript">
		var child = {
			props: ['content'],
			template: `<div>
							<p>使用插槽</p>
							<slot></slot>		占位
						</div>`,
		};
		var vm = new Vue({
			el: '#app',
			components: {
				'child': child,
			}
		});
	</script>

成功插入
在这里插入图片描述


多个<slot>重复渲染的问题

这样说并不准确,下面例子说明问题。

	<div id="app">
		<child>
			<div>这是头headler</div>
			<div>这是脚footer</div>
		</child>
	</div>
	<script type="text/javascript">
		var child = {
			props: ['content'],
			template: `<div>
							<slot></slot>
							<div>这是内容content</div>
							<slot></slot>
						</div>`,
		};
		var vm = new Vue({
			el: '#app',
			components: {
				'child': child,
			}
		});	
	</script>

在这里插入图片描述

使用具名插槽解决<slot>重复渲染的问题


1.6.3 具名插槽

具名插槽就是给插槽取名<slot name="名字1">,给要要传递的内容指定插槽<XXX slot="名字1">

		<div id="app">
		<child>
			<div slot="header">这是头headler</div>
			<div slot="footer">这是脚footer</div>
		</child>
	</div>
	<script type="text/javascript">
		var child = {
			props: ['content'],
			template: `<div>
							<slot name="header"></slot>
							<div>这是内容content</div>
							<slot name="footer"></slot>
						</div>`,
		};
		var vm = new Vue({
			el: '#app',
			components: {
				'child': child,
			}
		});	
	</script>

在这里插入图片描述


1.6.4 插槽定义默认值

用法

	<slot name="header">
		插槽默认内容
	</slot>

例子:

	<div id="app">
		<child>
			<div slot="footer">这是脚footer</div>
		</child>
	</div>
	<script type="text/javascript">
		var child = {
			props: ['content'],
			template: `<div>
							<slot name="header">
								<h1>这是默认头</h1>
							</slot>
							<div>这是内容content</div>
							<slot name="footer"></slot>
						</div>`,
		};
		var vm = new Vue({
			el: '#app',
			components: {
				'child': child,
			}
		});	
	</script>

在这里插入图片描述


1.6.5 作用域插槽

作用域插槽的使用场景:

子组件循环中要循环的模板是由父组件决定的时候。(也就是同一个组件在不同地方使用,需要展现不同的样子)

  • 作用域插槽必须用包裹,同时 必须定义slot-scope=‘props’来接受子组件传来插槽的值
	<div id="app">
		<child>
			<template slot="content" slot-scope="props">
				<h2>{{props.item}}</h2>
			</template>
		</child>
	</div>
	<script type="text/javascript">
		var child = {
			props: ['content'],
			data: function(){
				return {
					list :['某一发','某宣告','某本伟','某天佑','某糯米'],
				};
			},
			template: `<div class="component">
							<h1>以下是被和谐名单</h1>
							<slot name="content" v-for="item of list" :item="item"></slot>
						</div>`,
		};
		var vm = new Vue({
			el: '#app',
			components: {
				'child': child,
			}
		});	
	</script>

在这里插入图片描述




1.7 动态组件<component>

动态组件<component :is="">使用is属性指定加载哪一个组件使用。

  • v-if类似,没有被加载的组件是会被销毁的。
  • 每一次组件的切换都是创建销毁
	<div id="app">
		<component :is="type"></component>
		<button @click="handleBtnClick">change</button>
	</div>
	<script type="text/javascript">
		Vue.component('child-one',{
			template: '<h1>child-one</h1>',
		});
		Vue.component('child-two',{
			template: '<h1>child-two</h1>',
		});

		var vm = new Vue({
			el: '#app',
			data: {
				type:'child-one',
			},
			methods: {
				handleBtnClick: function(){
					this.type = this.type === 'child-one'? 'child-two' : 'child-one';
				}
			}
		});
	</script>

在这里插入图片描述


在子组件的根元素上使用v-once指令,可以提升效率

		Vue.component('child-one',{
			template: '<h1 v-once>child-one</h1>',
		});
		Vue.component('child-two',{
			template: '<h1 v-once>child-two</h1>',
		});

这样每一次在切换的时候,之前的内容会被保存在内存中,而不会被销毁。




二、Vue中的动画特效

在以下的情况可以使用<transition>动画特效

  • 条件渲染 (使用 v-if)
  • 条件展示 (使用 v-show)
  • 动态组件
  • 组件根节点

2.1 Vue动画或过渡原理

vue中的css动画是通过在某一时刻自动的给元素标签上增加相应css样式来实现的。

设置过渡或动画的步骤

  1. 将要实现动画效果的 DOM 节点用 transition 标签包裹起来,可添加 name 属性;若没有 name 属性,则默认的 class 前缀为 v。
	<transition name="fade">
		<h1 v-if="show">Hello poorpenguin</h1>
	</transition>
  1. 定义过渡类名

元素进入是的执行过程
在这里插入图片描述
在元素显示的瞬间(第1帧的时候),会向目标元素上添加两个classfade-enterfade-enter-active;执行到 第2帧 的时候,会去掉fade-enter,添加fade-enter-to;到动画 结束前1帧 的时候,去掉fade-enter-activefade-enter-to,元素保持原始状态。


1 fade-enter:定义元素进入过渡第1帧的状态,在第2帧被移除。
2 fadel-enter-active:定义元素整个过渡的过渡时间,延时和曲线函数,其实就是transition: all .5s ease;这个样子,在动画结束前1帧被移除。
3 fade-enter-to:定义元素在动画结束前1帧的状态,在第2帧添加,在动画结束前1帧被移除。一般都不写,让动画默认过渡到元素的原始状态。


元素离开时的执行过程
在这里插入图片描述
1 fade-leave:定义元素离开时第1帧的状态,一般都不写,默认从元素初始状态过渡。
2 fade-leave-active:…
3 fade-leave-to:定义动画结束前1帧的状态,这个是必须要写的。

	<style>
		.fade-enter{
			transform: translateX(50px);
			opacity: 0;
		}
		.fade-enter-active,
		.fade-leave-active{
			transition: all 3s ease;
		}
		.fade-leave-to{
			transform: translateX(50px);
			opacity: 0;	
		}
	</style>

如果没有给<transition>添加name属性,则css样式名可以v-的前缀,不过不建议这样做。

	<style>
		.v-enter{
			transform: translateX(50px);
			opacity: 0;
		}
		.v-enter-active,
		.v-leave-active{
			transition: all 3s ease;
		}
		.v-leave-to{
			transform: translateX(50px);
			opacity: 0;	
		}
	</style>
	....
	<transition>
		<h1 v-if="show">Hello poorpenguin</h1>
	</transition>

案例1:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<script type="text/javascript" src="./vue.js"></script>
	<style>
		.fade-enter{
			transform: translateX(50px);
			opacity: 0;
		}
		.fade-enter-active,
		.fade-leave-active{
			transition: all 3s ease;
		}
		.fade-leave-to{
			transform: translateX(50px);
			opacity: 0;	
		}
	</style>
</head>
<body>
	<div id="app">
		<transition name="fade">
			<h1 v-if="show">Hello poorpenguin</h1>
		</transition>
		<button @click="handleBtnClick">{{btnText}}</button>
	</div>
	<script type="text/javascript">
		var vm = new Vue({
			el: '#app',
			data: {
				show: true,
				btnText: '显示',
			},
			methods: {
				handleBtnClick: function(){
					this.show = !this.show;
					this.btnText = this.btnText === '显示'? '隐藏' : '显示';
				},
			}
		});
	</script>
</body>
</html)>



2.2 在vue中使用动画 关键帧@keyframes

对css3动画不是很了解的请看
CSS3 基础(7)—— CSS3动画(animation)

通过2.1的原理我们知道在过渡类名中v-enter-activev-enter-active这两个class在元素执行过渡或动画的过程中一直存在。所以我们可以省略除了v-enter-activev-enter-active外其他class,使用关键帧@keyframes来控制元素动画的初始和结束状态

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<script type="text/javascript" src="./vue.js"></script>
	<style>
		@keyframes fade{
			0%{
				opacity: 0;
				transform: translateX(50px);
			}
			100%{
				opacity: 1;
			}
		}
		.fade-enter-active{
			animation: fade 3s;
		}
		.fade-leave-active{
			animation: fade 3s reverse;
		}
	</style>
</head>
<body>
	<div id="app">
		<transition name="fade">
			<h1 v-if="show">Hello poorpenguin</h1>
		</transition>
		<button @click="handleBtnClick">{{btnText}}</button>
	</div>
	<script type="text/javascript">
		var vm = new Vue({
			el: '#app',
			data: {
				show: true,
				btnText: '显示',
			},
			methods: {
				handleBtnClick: function(){
					this.show = !this.show;
					this.btnText = this.btnText === '显示'? '隐藏' : '显示';
				},
			}
		});
	</script>
</body>
</html>



2.3 利用自定义过渡类名使用第三方动画库animate.css

2.3.1 自定义过渡类名

通过在transition标签中加上

<transition
    enter-class = '..'
    enter-active-class = '..'
    enter-to-class = '..'
    leave-class = '..'
    leave-active-class= '..'
    leave-to-class>='..' 

</transition>

来使用自定义的过渡类名。
例子:

	<style>
		@keyframes fade{
			0%{
				opacity: 0;
				transform: translateX(50px);
			}
			100%{
				opacity: 1;
			}
		}
		.show{	
			animation: fade 3s;
		}
		.hide{
			animation: fade 3s reverse;
		}
	</style>

	<div id="app">
	//使用自定义的过渡类名
		<transition
			enter-active-class="show"	
			leave-active-class="hide"
		>
			<h1 v-if="show">Hello poorpenguin</h1>
		</transition>
		<button @click="handleBtnClick">{{btnText}}</button>
	</div>
	
	<script type="text/javascript">
		var vm = new Vue({
			el: '#app',
			data: {
				show: true,
				btnText: '显示',
			},
			methods: {
				handleBtnClick: function(){
					this.show = !this.show;
					this.btnText = this.btnText === '显示'? '隐藏' : '显示';
				},
			}
		});
	</script>

2.3.2 使用第三方动画库animate.css

animate.css官网

使用步骤

  1. 使用 link 来引用animate.css
  2. 只需要在<transition enter-active-class/leave-active-class="animated XXX(某库里的动画名)"></transition>就行

ps:注意这里使用的是动画,只需要使用enter-active-classleave-active-class就行,如果是使用过渡那就需要其他的过渡类名了

	<link rel="stylesheet" type="text/css" href="./animate.css">

	<div id="app">
		<transition
			enter-active-class="animated bounceInDown"
			leave-active-class="animated bounceOutDown"
		>
			<h1 v-if="show">Hello poorpenguin</h1>
		</transition>
		<button @click="handleBtnClick">{{btnText}}</button>
	</div>
	
	<script type="text/javascript">
		var vm = new Vue({
			el: '#app',
			data: {
				show: true,
				btnText: '显示',
			},
			methods: {
				handleBtnClick: function(){
					this.show = !this.show;
					this.btnText = this.btnText === '显示'? '隐藏' : '显示';
				},
			}
		});
	</script>

在这里插入图片描述


2.4 解决第一次刷新页面没有动画的问题

使用appearappear-active解决。
使用2.3的例子

		<transition
			appear
			appear-active-class="animated bounceInDown"
			enter-active-class="animated bounceInDown"
			leave-active-class="animated bounceOutDown"
		>
			<h1 v-if="show">Hello poorpenguin</h1>
		</transition>

这样第一次进入(刷新页面)就有动画效果了。
在这里插入图片描述


2.5 在vue中同时使用过渡和动画

想要同时使用过渡和动画,只需要过渡和动画的两种方法相结合就行。

	<style>
		.fade-enter,
		.fade-leave-to{
			opacity: 0;
		}
		.fade-enter-active,
		.fade-leave-active{
			transition: opacity 3s;
		}
	</style>
	
	<transition
		name="fade"
		appear
		appear-active-class="animated swing fade-enter-active"
		enter-active-class="animated swing fade-enter-active"
		leave-active-class="animated shake fade-leave-active"
	>
		<h1 v-if="show">Hello poorpenguin</h1>
	</transition>

2.5.1 使用type使过渡和动画同时结束

.同时存在过渡动画和其他动画,但时长不一样时,可以设置type属性来决定以谁为准,如: type="transition"则以过渡的时长为准,type="animation"以动画时长为准

		<transition
			type="transition"
			name="fade"
			appear
			appear-active-class="animated swing fade-enter-active"
			enter-active-class="animated swing fade-enter-active"
			leave-active-class="animated shake fade-leave-active"
		>
			<h1 v-if="show">Hello poorpenguin</h1>
		</transition>

2.5.2 自定义时长:duration

当要自定义时长时可以这样, :duration=“3000” 以毫秒计,这样的话不会以动画时长为准,而是自己定义的时长;更复杂点可以设置入场和出场动画时长,:duration="{enter:5000, leave:10000}"


2.6 Vue中的 Js 动画和使用第三方js动画库Velocity.js

2.6.1 Vue中的JS动画

js动画是通过绑定在<transition>上的钩子函数完成的。

<transition
	@before-appear     					都是第一次打开页面(刷新)
	@appear     
	@after-appear

	@before-enter="beforeEnter"			进入动画前执行
	@enter="enter"						进入动画时执行
	@after-enter="afterEnter"		    进入动画完成后,在enter中调用done()告诉vue,js动画结束才会触发

    @before-leave="beforeLeave"
    @leave="leave"
    @after-leave="afterLeave"
>
  <!-- ... -->
</transition>

在定义的方法中都会接收到一个参数

			methods: {
				beforeEnter: function(el){	//这里接收到的el就是transition包裹这的dom元素
					el.style.color='red';
				}
				enter: function(el,done){
					done();		只有调用这个回调函数,Vue才知道动画执行结束
				}
				afterEnter: function(el){

				}
			}

例子1:

	<div id="app">
		<transition
			@before-enter="beforeEnter"
			@enter="enter";
			@after-enter="afterEnter"
		>
			<h1 v-if="show">Hello poorpenguin</h1>
		</transition>

		<button @click="handleBtnClick">{{btnText}}</button>
	</div>
	<script type="text/javascript">
		var vm = new Vue({
			el: '#app',
			data: {
				show: true,
				btnText: '显示',
			},
			methods: {
				beforeEnter: function(el){	//这里接收到的el就是transition包裹这的dom元素
					el.style.color='red';
				},
				enter: function(el,done){
					setTimeout(function(){
						el.style.color='blue';
					},2000);

					setTimeout(function(){
						done();
					},4000);

				},
				afterEnter: function(el){
					el.style.color='black';
				},
				handleBtnClick: function(){
					this.show = !this.show;
					this.btnText = this.btnText === '显示'? '隐藏' : '显示';
				},
			}
		});
	</script>

2.6.2 使用第三方js动画库Velocity.js

Velocity.js自称比jQuery动画和CSS-transitions以及CSS3 animation性能更好的JavaScript动画库。
velocity官网
velocity中文文档
使用Velocity.js可以实现参数和Vue中变量双向绑定,可以实现许多复杂的动画




2.7 Vue中的多个元素或组件过渡

  • vue的dom元素复用机制,可能会导致动画无法执行。元素添加唯一的key值来使vue对该元素不进行复用
  • 先进入再隐藏和先隐藏再进入,可在元素上设置: mode=“in-out” 或mode=“out-in”

2.7.1 Vue中的多个元素过渡

例子1:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<script type="text/javascript" src="./vue.js"></script>
	<style type="text/css">
		.fade-enter,
		.fade-leave-to{
			opacity: 0;
		}
		.fade-enter{
			transform: translateX(-50px);
		}
		.fade-leave-to{
			transform: translateX(50px);
		}
		.fade-enter-active,
		.fade-leave-active{
			transition: all 3s ease;
		}
	</style>
</head>
<body>
	<div id="app">
		<transition name="fade">
			<div v-if="show" key="hello">Hello porpenguin</div>
			<div v-else key="bye">bye poorpenguin</div>
		</transition>
		<button @click="changeDiv">change</button>
	</div>
	<script type="text/javascript">
		var vm = new Vue({
			el: '#app',
			data: {
				show: true,
			},
			methods: {
				changeDiv: function(){
					this.show = !this.show;
				},
			}
		});
	</script>
</body>
</html>

例子2:

		<transition name="fade" mode="out-in">
			<div v-if="show" key="hello">Hello porpenguin</div>
			<div v-else key="bye">bye poorpenguin</div>
		</transition>

2.7.2 Vue中多个组件过渡

  • 多个普通组件的过渡,和多个元素过渡一样。
		<transition name="fade" mode="out-in">
			<child-one v-if="show">Hello porpenguin</child-one>
			<child-two v-else>bye poorpenguin</child-two>
		</transition>
  • 动态组件的过渡
	<style type="text/css">
		.fade-enter,
		.fade-leave-to{
			opacity: 0;
		}
		.fade-enter{
			transform: translateX(-50px);
		}
		.fade-leave-to{
			transform: translateX(50px);
		}
		.fade-enter-active,
		.fade-leave-active{
			transition: all 1s ease;
		}
	</style>
	<div id="app">
		<transition name="fade" mode="out-in">
			<component :is="type"></component>
		</transition>
		<button @click="changeDiv">change</button>
	</div>
	<script type="text/javascript">
		Vue.component('child-one',{
			template: '<div v-once>hello poorpenguin</div>',
		});
		Vue.component('child-two',{
			template: '<div v-once>bye poorpenguin</div>',
		});
		var vm = new Vue({
			el: '#app',
			data: {
				type: 'child-one',
			},
			methods: {
				changeDiv: function(){
					this.type = this.type === 'child-one'? 'child-two' : 'child-one';
				},
			}
		});
	</script>



2.8 Vue中的列表过渡<transition-group>

  • 循环列表建议给每一项都添加key这个特征,不建议key的值为index,建议值为每个数据的id。
  • <transition-group>的原理
<transition-group>
	<div v-for"item of list" :key="item.id">
		{{item.content}}
	</div>
</transition-group>
相当于
<transition>
	<div>.....</div>
</transition>
<transition>
	<div>.....</div>
</transition>
<transition>
	<div>.....</div>
</transition>
每添加一项,都会单独执行一次过渡

例子:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<script type="text/javascript" src="./vue.js"></script>
	<style type="text/css">
		.fade-enter,
		.fade-leave-to{
			opacity: 0;
		}
		.fade-enter{
			transform: translateX(-50px);
		}
		.fade-leave-to{
			transform: translateX(50px);
		}
		.fade-enter-active,
		.fade-leave-active{
			transition: all 1s ease;
		}
	</style>
</head>
<body>
	<div id="app">
		<transition-group name="fade">
			<div v-for="(item,index) of list" :key="item.id" @click="deleteItem(index)">
				{{item.content}}
			</div>
		</transition-group>
		<button @click="addItem">添加</button>
	</div>
	<script type="text/javascript">
		var count = 0;
		var vm = new Vue({
			el: '#app',
			data: {
				list: [],
			},
			methods: {
				addItem: function(){
					this.list.push({
						id: ++count,
						content: 'hello poorpenguin',
					});
				},
				deleteItem: function(index){
					this.list.splice(index,1);
				},
			}
		});
	</script>
</body>
</html>

在这里插入图片描述



2.9 使用componentslot封装动画

写一个组件,里面的模版是
<transition>包裹的里面带一个插槽
用这个组件包裹需要动画的节点就可以了
比较推荐动画用js而不是css写,这样可以把动画全部封装在一个组件中。

在 slot 标签上使用 v-show,程序功能是无法实现的,必须要改成v-if。

slot 实际上是一个抽象元素,有点类似 template,本质上并不是一个元素。
而 v-show 是通过控制元素的 display 来进行显示隐藏的,slot 本质上并不是元素,所以压根也就不会有display 这个css属性。
所以,slot 的显示隐藏,得使用 v-if。

带css样式的普通动画封装

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<script type="text/javascript" src="./vue.js"></script>
	<style type="text/css">
		.fade-enter,
		.fade-leave-to{
			opacity: 0;
		}
		.fade-enter{
			transform: translateX(-50px);
		}
		.fade-leave-to{
			transform: translateX(50px);
		}
		.fade-enter-active,
		.fade-leave-active{
			transition: all 1s ease;
		}
	</style>
</head>
<body>
	<div id="app">
		<fade :state="show">
			<h1>poorpenguin</h1>
		</fade>
		<button @click="changeState">change</button>
	</div>
	<script type="text/javascript">
		Vue.component('fade',{
			props: ['state'],
			template: `<transition name="fade">
							<slot v-if="state"></slot>
						</transition>`,
		});

		var vm = new Vue({
			el: '#app',
			data: {
				show: true,
			},
			methods: {
				changeState: function(){
					this.show = !this.show;
				},
			}
		});
	</script>
</body>
</html>

推荐使用Vue中的js动画,这样我们可以把动画的js代码都封装进组件中,省略css样式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值