Vue2.5去哪儿实战课程(2)

本文详细探讨了Vue组件的各个方面,包括使用组件的细节(如is、data和ref属性),父子组件间的数据传递(强调单向数据流),组件参数校验,以及如何给组件绑定原生事件。此外,还介绍了非父子组件间的通信(如Vuex和总线机制)和插槽的使用,特别是作用域插槽的概念和应用场景。最后,讨论了动态组件与v-once指令的优化,以及Vue中的动画效果,如CSS和Js动画的实现,以及Animate.css和Velocity.js的结合使用。
摘要由CSDN通过智能技术生成

4.深入理解Vue组件

4.1使用组件的细节点(is、自定义组件中data、ref)

在这里插入图片描述

vue中is的属性引入是为了解决dom结构中对放入html的元素有限制的问题,例如上图不正确渲染就会出现上图所示结果,譬如ul里面要接上li的标签或者tbody下要接上tr时,引入is的属性后,你完全可以写成这样

//我这个是li标签,同时使用row组件
<div class="language-html">
    <ul>  
        <li is="row"></li>  
    </ul>
</div> 

这样会保证dom结构在浏览器的正常渲染,也保证可以使用组件,尽量避免在不正确的结构中直接使用组件

<script>
    Vue.component('row', {
        template: '<li>this is a row</li>'
    })
</script>

Vue.component中得data:

//不是New vue里面data,组件data后面要是函数,New Vue中data可以定义是对象
Vue.component('row',{
	data:function(){
		return {
			content:'this is content'
		}
	},
	template:'<tr><td>{{content}}</td></tr>'
})

ref:引用,获取dom节点,同样也可以获取一个组件

<!--//ref引用-->
//此时this.$refs获取的是dom元素
<div
	ref='hello'
	@click='handleClick'>
	hello world
</div>

handleClick:function(){
	console.log(this.$refs.hello.innerHTML) //this.$refs.hello获取所有引用中的hello这个,获取dom节点, 输出结果是hello world
}

//此时this.$refs是对组件的引用,然后再获取组件的dom节点/数据/methods等
<div>
	<counter ref="one" @change="handleChange"></counter>
	<counter ref="two" @change="handleChange"></counter>
	<div>{{total}}</div>
</div>

Vue.component('counter',{
			template:'<div @click="handleClick01">{{number}}</div>',
			data:function(){
				return {
					number:0
				}
			},
			methods:{
				handleClick01:function(){
					this.number ++
					this.$emit('change')
				}
			}
})

methods:{
				handleChange:function(){
					//针对每次得变化进行求和
					this.total = this.$refs.one.number + this.$refs.two.number
				}

}

ref调用组件完整代码:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<style type="text/css">

	</style>
	<script type="text/javascript" src="https://cdn.bootcss.com/vue/2.6.3/vue.min.js"></script>
</head>
<body>
	<!--Vue中is/ref的示例-->
	<div id="app">

		<ul>
			<li is="row"></li>
		</ul>

		<counter ref="one" @change="handleClick"></counter>
		<counter ref="two" @change="handleClick"></counter>
		<div>{{total}}</div>
		
	</div>
	<script>

		Vue.component('row',{
			data:function(){
				return {
					content:'this is content'
				}
			},
			template:'<li>{{content}}</li>'
		})

		Vue.component('counter',{
			data:function(){
				return {
					number:0
				}
			},
			template:'<div @click="handleClick01">{{number}}</div>',
			methods:{
				handleClick01:function(){
					this.number++;
					this.$emit('change')
				}
			}
		})

		var app = new Vue({
			el:"#app",
			data:{
				total:''
			},
			methods:{
				handleClick:function(){
					this.total = this.$refs.one.number + this.$refs.two.number;
				}
			}
		})

	</script>
</body>
</html>

4.2父子组件间的数据传递(建议是子组件不直接修改父组件传来的值,可以通过拷贝进行修改)

父组件通过属性的方式向子组件传递数据。

props和emit

单向数据流:父组件可以向子组件直接传值,子组件不能直接修改父组件传递过来的数据,但是可以通过在子组件中拷贝一个副本处理props:['count'] number:this.count进行处理,类似下面代码。

<counter :count="2" @count="countNum"></counter>

Vue.component('counter',{
			props:['count'],
			template:'<div @click="handleClick01">{{number}} | {{count}}</div>',
			data:function(){
				return {
					number:this.count //从父组件传递过来的值拷贝一个副本进行处理
				}
			},
			methods:{
				handleClick01:function(){
					this.number ++
					this.$emit('count')
				}
			}
})

methods:{
	countNum:function(){
		
	}
}

可以看上面ref对组件引用的完整代码,了解$emit。

上面这种子组件不直接修改父组件传过来的值,通过拷贝进行修改,就是Vue中的单向数据流,但是Vue中数据是双向绑定的。

参考文章:
https://juejin.im/entry/59e8b8a8518825579d131e51
https://www.jianshu.com/p/1ebc15645abe
https://blog.csdn.net/LeonBec/article/details/78019486

附注:父组件向子组件传值,count前加不加:区别

//加:表示后面是JavaScript表达式,此时是number类型,不加:此时"1"是字符串类型
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
	<runoob :count="0"></runoob>
</div>

<script>
// 注册
Vue.component('runoob', {
  props:["count"],
  template: '<h1 @click="handleClick">{{count}}自定义组件!</h1>',
  methods:{
	  handleClick:function(){
	  	console.log(typeof this.count);
	  }
  }
})
// 创建根实例
new Vue({
  el: '#app'
})
</script>
</body>
</html>

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

参数校验,是子组件对父组件传递的值进行一些约束。

<!--f父组件向子组件传递content-->
<child content="hello world"></child>

Vue.component('child',{
	props:{		//上面不约束时候props后面是数组,此时props是对象
		content:String //规定content必须是字符串类型,这就是参数校验
		// content:[Number,String] 字符串或者数字类型,还可以更复杂
	},
	template: '<div>Child | {{content}}</div>'
})

//更复杂形式
content:{
	type:String,
	required:false,
	default:'default value',
	validator:function(value){ //检验器,长度大于5
		return (value.length > 5)
	}
}

props特性:
父组件传递的值,如果子组件有对应接收,就是props特性,传递的值(content=“hell”)不会显示在源代码中,不会显示在DOM树中。

<child content=''hell'></child>

Vue.component('child',{
	props:{
		content:String //规定content必须是字符串类型,这就是参数校验
	},
	template: '<div>Child | {{content}}</div>'
})

在这里插入图片描述

非props特性:
父组件传,子组件不接,要传递的属性值(content=“hell”)会显示在DOM树中。

重点记住:
Vue中的加的时候代表是一个变量,不加代表是一个字符串。

4.4给组件绑定原生事件

本来点击组件触发事件要通过组件内部,然后$emit传递到父组件,然后在new Vue中书写函数,但是我们能不能直接在组件中书写函数,而不是通过两次事件传递。

可以这么理解,在组件上的监听是自定义事件,在元素上绑定是原生事件。

// 通过添加修饰符.native
<child @click.native = "handleClick"></child>

Vue.component('child',{
	template:'<div>Child</div>'
})

new Vue({
	el:'#root',
	methods:{
		handleClick:function(){
			alert('click')
		}
	}
})

//之前的写法
<child></child>

Vue.component('child',{
	template:"<div @click="handleClick">Child</div>",
	methods:{
		handle:function(){
			
		}
	}
})

4.5非父子组件间的传值

非父子组件间的传值:

  1. Vuex
  2. 总线机制(Bus / 发布订阅模式 / 观察者模式):
    Vue.prototype.bus = new Vue();
    让原型属性 bus 指向一个 Vue 实例,让其充当非父子组件之间传值的桥梁,相当于计算机各种功能部件之间传送信息的公共通信干线(总线 Bus)
    1. 给 Vue 类加上原型属性 bus, 这样每个 Vue 实例都能访问到原型属性 bus
    2. 利用 bus 的实例方法 $emit 触发事件
    3. 再利用生命周期方法(钩子) mounted 给 bus 绑定监听函数, 在事件触发时执行

在这里插入图片描述
懂原型的自然就懂bus了,所有的Vue实例都有一些共用的属性和方法,存放这些属性和方法的对象叫原型。相当于Vue实例1把数据传入这个共用的属性bus上,其他Vue实例就自然可以享用这个数据,因为它们的原型地址指向同一块堆内存。

相当于一辆公交车,一些数据从某一个站通过bus传到另一个站,完成信息传输。

通俗的说整个过程:
在vue的prototype上创建一个bus,用于数据运输想要获取什么数据,就在该子组件创建点击事件,通过触发实现数据传输,以emit将属性上传给bus,实现属性共有,再在另一个子组件,通过this.bus.$on的方式在mounted的时候获取到数据,并新建一个值去接受它。(传输的数据通过emit传输到bus,需要的组件通过on在mounted时候获取到数据。)

子组件的的this指的时子组件本身,通过proptype把所有的vue都有了bus属性,所有的vue都可以调用这个属性,同时这个bus属性是个new Vue(),所以有$emit()事件,通过此次挂载,实现非父子组件之间的事件触发,实现传值的目的。

父组件通过props向子组件传值,子组件通过事件触发向父组件传值。

比较复杂的传值:
在这里插入图片描述
两种解决方法:
1.vuex
2.发布订阅模式

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document(Bus/总线/发布订阅模式/观察者模式)</title>
	<script type="text/javascript" src="https://cdn.bootcss.com/vue/2.6.3/vue.min.js"></script>
</head>
<body>
	<div id="root">
		<child content="Dell"></child>
		<child content="Lee"></child>
	</div>


	<script type="text/javascript">
		//js代码中先将prototype的bus挂载在vue实例中
		//然后每一次的this(vue实例)都将挂载有这个bus属性
		//就是让这个vue实例被所有vue实例所共享
		Vue.prototype.bus = new Vue()

		Vue.component('child',{
			data:function(){
				return {
					selfContent: this.content
				}
			},
			props:{
				content:String
			},
			template:'<div @click=handleClick"">{{content}}</div>',
			methods:{
				handleClick:function(){
					this.bus.$emit('change',this.selfContent)
				}
			},
			mounted:function(){ //this.bus.$on() 里面的函数中的 this 指的是属性 bus 指向的 vue 实例。而函数外部的 this 指的是 vue 组件。所以使用var this_ = this 处理下
				var this_ = this
				this.bus.$on('change',function(msg){ //change事件这个函数,不一定自什么时候被谁调用,所以this是说不准会指向哪里的,所以要想指向外部的this,就要提前保存着。
					this_.selfContent = msg
				})
			}
		})

		var vm = new Vue({
			el:'#root'
		})
	</script>
</body>
</html>

this.bus.$on() 里面的函数中的 this 指的是属性 bus 指向的 vue 实例。而函数外部的 this 指的是 vue 组件。

关于上面代码中var _this = this;中this的指向问题可以看:https://coding.imooc.com/learn/questiondetail/52358.html

还可以查看文章:
https://cloud.tencent.com/developer/article/1352108
https://juejin.im/post/5c778631f265da2ddf789d69
https://blog.csdn.net/xiasohuai/article/details/80664605
https://segmentfault.com/a/1190000012555128
https://noobakong.cn/2018/12/06/vue%E4%B8%AD%E7%88%B6%E5%AD%90%E7%BB%84%E4%BB%B6%E4%BC%A0%E5%80%BC%E5%92%8CBus%E6%80%BB%E7%BA%BF%E4%BC%A0%E5%80%BC/

4.6在Vue中使用插槽

一句话:插槽内可以是任意内容

<div id="app">
    <child-component>你好</child-component>
</div>
<script>
    Vue.component('child-component',{
        template:`
            <div>Hello,World!</div>
        `
    })
    let vm = new Vue({
        el:'#app',
        data:{

        }
    })
</script>

输出内容还是在组件中的内容,在<child-component>内写的内容没起作用。

这时候就需有插槽了。

Vue.component('child-component',{
        template:`
            <div>
            Hello,World!
            <slot></slot> //我们还可以在slot中写默认值,当上面调用组件时候没有写入值,会填入默认值
            </div>
        `
})

我们现在给组件增加一个<slot></slot>插槽

我们在<child-component></child-component>内写的"你好"起作用了!!!

到现在,我们知道了什么是插槽:

插槽就是Vue实现的一套内容分发的API,将<slot></slot>元素作为承载分发内容的出口。

具名插槽,就是给这个插槽起个名字

在组件中,我给插槽起个名字,一个名字叫"girl",一个名字叫"boy",还有一个不起名字。

然后再<child-component></child-component>内,slot属性对应的内容都会和组件中name一一对应。

而没有名字的,就是默认插槽!!

<div id="app">
    <child-component>
        <div slot="girl">
            漂亮、美丽、购物、逛街
        </idv>
        <div slot="boy">
            帅气、才实
        </div>
        <div>
            我是一类人,
            我是默认的插槽
        </div>
    </child-component>
</div>
<script>
    Vue.component('child-component',{
        template:`
            <div>
            <h4>这个世界不仅有男人和女人</h4>

            <slot name="girl"></slot>

            <div style="height:1px;background-color:red;"></div>

            <slot name="boy"></slot>

            <div style="height:1px;background-color:red;"></div>

            <slot></slot>
            </div>
        `
    })
    let vm = new Vue({
        el:'#app',
        data:{

        }
    })
</script>

4.7作用域插槽

作用域插槽:说白了就是我在组件上的属性,可以在组件元素内使用!

作用域插槽的使用场景:
子组件循环并且是从父组件传入HTML元素的时候,
或者说,同一个组件在不同地方使用,需要展现不同的样子,这时候就需要作用域插槽。

先看一个最简单的例子!!

我们给<slot></slot>元素上定义一个属性say(随便定义的!),接下来在使用组件child,然后在template元素上添加属性slot-scope!!随便起个名字a

我们把a打印一下发现是 {“say” : “你好”},也就是slot上面的属性和值组成的键值对!!!(也就是a是slot上面的键值对(属性和值))

这就是作用域插槽!

<div id="app">
    <child :lists="nameList">
        <template slot-scope="a">
            {{a}}
        </template>
    </child>
</div>
<script>
    Vue.component('child',{
        props:['lists'],
        template:`
            <div>
                <ul>
                    <li v-for="list in lists">
                        <slot :bbbbb="list"></slot>
                    </li>
                </ul>
            </div>
        `
    })

    let vm = new Vue({
        el:'#app',
        data:{
            nameList:[
            {id:1,name:'孙悟空'},
            {id:2,name:'猪八戒'},
            {id:3,name:'沙和尚'},
            {id:4,name:'唐僧'},
            {id:5,name:'小白龙'},
            ]
        }
    })
</script>

在这里插入图片描述
更多可以参考文章:https://www.cnblogs.com/chinabin1993/p/9115396.html

子组件循环遍历的item,父组件得到放在props里,然后props.item进行获取。这样每一项显示什么,是由子组件决定,子组件从父组件获取属性,下面的格式是固定的。

<template slot-scope=" ">

</template>

在这里插入图片描述
也就是相对默认插槽,可以传递值和在子组件修改样式。

作用域插槽进一步理解

参考文章:https://juejin.im/post/5d5b9b2fe51d4561f64a080c

为什么要有作用域插槽?我们调用插槽时候,想在父组件中的插槽中调用子组件的数据,就可以使用作用域插槽。

<script src="https://unpkg.com/vue"></script>
<div id="app">
    //然后就可以接收传过来的值,绑定在 <slot> 元素上的特性被称为插槽 prop。在父组件中,我们可以用 v-slot 设置一个值来定义我们提供的插槽 prop 的名字,然后直接使用就好了
	<child v-slot:default="slotProps">
		{{slotProps.usertext.firstName}}
	</child>

</div>
<script>

Vue.component('child',{
		data(){
			return{
				user:{
					firstName:"Fan",
					lastName:"Jun"
				}
			}
		},
		template:`
			<div>
			   	<!-- 设置默认值:{{user.lastName}}获取 Jun -->
				<!-- 如果home.vue中给这个插槽值的话,则不显示 Jun -->
	<!-- 设置一个 usertext 然后把user绑到设置的 usertext 上 -->
				<slot v-bind:usertext="user">{{user.lastName}}</slot>
			</div>

		`
})

var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
})
</script>

4.8动态组件与v-once指令

<div id="app">

	//1.第一种方法,利用v-if判断切换
	<child-one v-if = "type === 'child-one' "></child-one>
	<child-two v-if = "type === 'child-two' "></child-two>
	
	//2.第二种方法,利用动态组件切换,component 是Vue自带的标签,表示是一个动态组件
	//根据type的值加载对应的组件
	<component :is="type"></component>
		
	<button @click="handleBtnClick">Change</button>

</div>
<script>

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

	Vue.component('child-two',{
		template:'<div v-once>chile-two</div>'
	})

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

</script>

加入v-once后,从内存读取,所以可以利用v-once指令可以提高一些静态内容的展示效率。

v-once:只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。

5.Vue中的动画特效

本章将讲解 Vue 中的 Css 及 Js 动画原理,以及在 Vue 中如使用 Animate.css 及Velocity.js 动画库,在理解了基础动画原理后,本章还扩展了 Vue 中多元素及列表过渡效果实现的知识,并会带同学们学习如何对通用动画效果进行代码封装。

关于本节动画笔记还可以参考:https://blog.csdn.net/cckevincyh/article/details/87903552

5.1Vue动画-Vue中CSS动画原理

动画开始前有fade-enter、fade-enter-active两个class,当动画执行一帧后,fade-enter消失,fade-enter-to添加。

下面为什么是fade-enter-active呢?因为fade-enter消失后,fade-enter-active会一直存在整个动画过程,也就是下面6s之间,opacity从0到1.当运行第一帧的时候,fade-enter、fade-enter-active同时存在,处于隐藏状态,第二帧fade-enter消失,整个6s动画过程都是fade-enter-active在起作用。
在这里插入图片描述
不同过程中会添加/删除不同样式。

在这里插入图片描述

	<style type="text/css">
		.fade-enter{
			opacity: 0
		}
		.fade-enter-active{
			transition: opacity 6s;
		}
		/*
		*刚刚开始,两个都存在,动画开始时fade-enter消失,所以是从消失到显示
		*fade是因为name是fade,默认情况是v(没有name时候),其他名字也可以
		*下面是从显示到消失(注意是fade-leave-to)
		*/
		.fade-leave-to{
			opacity: 0;
		}
		.fade-leave-active{
			transition:opacity 6s;
		}
	</style>
	<div id="root">
		<transition name="fade">
			<div v-if="show">hello world</div>
		</transition>
		<button @click="handleClick">点击</button>
	</div>
	
	<script type="text/javascript">
		var vm = new Vue({
			el:'#root',
			data:{
				show:true
			},
			methods:{
				handleClick:function(){
					this.show = ! this.show
				}
			}
		})
	</script>

Vue中一般称上面为过渡动画。

注意:v-leave和v-leave-to相当于动画开始和最终的状态,v-leave-active用来实现两个状态过渡的方式,比如设置时间什么的,enter同理

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello World</title>
    <script type="text/javascript" src="./vue.js"></script>
    <style>
    	/*隐藏到现实*/
        .fade-enter{ /*第一帧存在,第二帧就移除了,所以是opacity从0到1的过程*/
            opacity: 0
        }
        .fade-enter-active{
            transition: opacity 1s;
        }
        .fade-enter-to{

        }
        /*显示到隐藏*/
        .fade-leave-to{/*opacity从1到0,fade-leave-active一直在监听*/
            opacity:0;
        }
        .fade-leave-active{
            transition:opacity 1s;
        }
    </style>
</head>
<body>

    <div id="app">

        <transition name="fade">
            <div v-if="show">hello world</div>
        </transition>
        <button @click="handleClick">切换</button>
        
    </div>

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


</body>
</html>

Vue中css动画:
在需要设置动画标签外部用<transition></transition>包裹,可以添加name属性
1.没有name属性,css 默认前缀是v,类名:
隐藏到显示:第一帧加载v-enter;v-enter-cative,第二帧将v-enter清除,添加v-enter-to,最后一帧将所有类名删掉
显示到隐藏:第一帧加载v-leave,v-leace-active,第二帧将v-leave清除,添加v-leave-to,最后一帧将所有类名删掉
2.有name属性时,css前缀为name的属性值
通过在某一时刻,向div元素自动添加class的底层原理,vue实现动画

5.2在Vue中使用animate.css库

我们可以通过以下特性来自定义过渡类名:
1.enter-class
2.enter-active-class
3.enter-to-class (2.1.8+)
4.leave-class
5.leave-active-class
6.leave-to-class (2.1.8+)

使用@keyframes形式动画:

	<style type="text/css">

		@keyframes bounce-in {
			0%{
				transform: scale(0); /*2D/3D转换transform*/
			}
			50%{
				transform: scale(1.5);
			}
			100%{
				transform: scale(1);
			}
		}

		.active{
			transform-origin:left center; 
			animation: bounce-in 1s;
		}

		.active{
			transform-origin:left center; 
			animation: bounce-in 1s reverse;
		}
	</style>

	<div id="root">
		<!--可以自定义class-->
		<transition name="fade" 
		enter-active-class="active"
		leave-active-class="leave">
			<div v-if="show">hello world</div>
		</transition>
		<button @click="handleClick">点击</button>
	</div>
	
	<script type="text/javascript">
		var vm = new Vue({
			el:'#root',
			data:{
				show:true
			},
			methods:{
				handleClick:function(){
					this.show = ! this.show
				}
			}
		})
	</script>

使用animate.css:

<body>
	<div id="root">
		<!--可以自定义class-->
		<transition name="fade" 
		enter-active-class="animated swing"
		leave-active-class="animated shake">
			<div v-if="show">hello world</div>
		</transition>
		<button @click="handleClick">点击</button>
	</div>
	
	<script type="text/javascript">
		var vm = new Vue({
			el:'#root',
			data:{
				show:true
			},
			methods:{
				handleClick:function(){
					this.show = !this.show
				}
			}
		})
	</script>

</body>

animate.css中提供的动画效果和CSS3中的@keyframes动画效果一样。

还可以参考链接:https://www.jianshu.com/p/fd861af247e5

5.3在Vue中同时使用过渡和动画

可以通过 appear 特性设置节点的在初始渲染的过渡。

<transition
  appear
  appear-class="custom-appear-class"
  appear-active-class="custom-appear-active-class"
>
</transition>

.custom-appear-class{
    font-size: 40px;
    color: red;
    background: green;
}

.custom-appear-active-class{
    background: green;
}

也就是当前transition元素第一次渲染时的过渡动画,它也提供了两个css动画钩子:
appear:表示是否开启此特性
appear-class:表示元素渲染完毕后应用的css样式,它里面的css样式会参与整个动画的过渡
appear-active-class:也表示元素渲染完毕后应用的css样式,它不参与整个动画的过渡

同时使用过渡和动画(添加fade-enter-active和fade-leave-active):



	<style type="text/css">
		.fade-enter, .fade-leave-to{
			opacity: 0;
		}
		.fade-enter-active, .fade-leave-active{
			transition: opacity 3s;
		}
	</style>

<body>
	<div id="root">
		<!--可以自定义class-->
		<transition
		type="transition" 
		//:duration="10000"
		name="fade"
		appear 
		enter-active-class="animated swing fade-enter-active"
		leave-active-class="animated shake fade-leave-active"
		appear-active-class="animated swing"
		>
			<div v-if="show">hello world</div>
		</transition>
		<button @click="handleClick">点击</button>
	</div>
	
	<script type="text/javascript">
		var vm = new Vue({
			el:'#root',
			data:{
				show:true
			},
			methods:{
				handleClick:function(){
					this.show = !this.show
				}
			}
		})
	</script>

</body>

还有一个问题:animate.css动画默认是1秒,过渡动画我们设置是3秒,那么时间是怎么样?
有两种解决方法:
1.我们可以设置type==“transition”
2.设置:duration=“10000” / duration="{enter:10000,leave:10000}"

5.4Vue中Js动画与Velocity.js的结和

JavaScript钩子:
beforeEnter、enter、afterEnter、enterCancelled
beforeLeave、leave、afterLeave、leaveCancelled

JS动画:

	<!--JS动画效果-->
	<!--handleBeforeEnter在显示之前触发该事件-->
	<div id="root">
		<transition 
			name="fade"
			@before-enter="handleBeforeEnter"
			@enter="handleEnter"
			@after-enter="handleAfterEnter"
		>
			<div v-if="show">hello world</div>
		</transition>
		<button @click="handleClick">点击</button>
	</div>

	<script type="text/javascript">
		var vm = new Vue({
			el:'#root',
			data:{
				show:true
			},
			methods:{
				handleClick:function(){
					this.show = ! this.show
				},
				handleBeforeEnter: function(el){
					//el事transition包裹的标签div
					el.style.color = 'red'
				},
				handleEnter:function(el,done){
					setTimeout(()=>{
						el.style.color = 'green'
					},2000)
					setTimeout(()=>{
						done()
					},40000)
				},
				handleAfterEnter:function(el){
					el.style.color = '#000'
				}
			}
		})
	</script>

Velocity.js:

<body>
	<!--JS动画效果-->
	<!--handleBeforeEnter在显示之前触发该事件-->
	<div id="root">
		<transition 
			name="fade"
			@before-enter="handleBeforeEnter"
			@enter="handleEnter"
			@after-enter="handleAfterEnter"
		>
			<div v-if="show">hello world</div>
		</transition>
		<button @click="handleClick">点击</button>
	</div>
	
	<script type="text/javascript">
		var vm = new Vue({
			el:'#root',
			data:{
				show:true
			},
			methods:{
				handleClick:function(){
					this.show = ! this.show
				},
				handleBeforeEnter: function(el){
					el.style.opacity = 0;
				},
				handleEnter:function(el,done){
					Velocity(el,{opacity:1},
						{duration:1000,
						 complete:done
					})
				},
				handleAfterEnter:function(el){
					
				}
			}
		})
	</script>

</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello World</title>
    <script type="text/javascript" src="./vue.js"></script>
    <link href="./animate.css" rel="stylesheet">
    <style>

        /*
            transition 补间动画
            type="transition" 指定以transition动画时长为准
            :duration="10000" 指定自定义动画时长
            :duration="{enter:5000,leave:10000}" 更复杂的自定义;
        */
        .fade-enter {
            opacity: 0;
        }
        .fade-enter-active{
            transition: opacity 3s;
        }

        .fade-leave-to{
            opacity: 0;
        }
        .fade-enter-active{
            transition: opacity 3s;
        }

    </style>
</head>
<body>

    <div id="app">
            <!--type="transition" -->
        <transition 
            :duration="10000"
            name="fade"
            appear
            enter-active-class="animated swing fade-enter-active"
            leave-active-class="animated shake fade-leave-active"
            appear-active-class="animated swing">
            <div v-if="show">hello world</div>
        </transition>
        <button @click="handleClick">切换</button>
        
    </div>

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


</body>

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

在vue更新DOM时,如果key值相同则会对原有组件进行复用,如果不同,则会重新生成。

可以参考文章:
https://blog.csdn.net/qq_41861679/article/details/80659278
https://www.jianshu.com/p/7838e1e765ae

多个元素的过渡:

	<style type="text/css">
		.v-enter, .v-leave-to{
			opacity: 0;
		}
		.v-enter-active, .v-leave-active{
			transition: opacity 1s;
		}
	</style>

<body>
	<div id="root">
	<!--在vue更新DOM时,如果key值相同则会对原有组件进行复用,如果不同,则会重新生成。
-->
		<transition mode="in-out">
			<div v-if="show" key="hello">hello world</div>
			<div v-else key="bye">Bye World</div>
		</transition>
		<button @click="handleClick">点击</button>
	</div>
	
	<script type="text/javascript">
		var vm = new Vue({
			el:'#root',
			data:{
				show:true
			},
			methods:{
				handleClick:function(){
					this.show = !this.show
				}
			}
		})
	</script>

</body>

我们还要了解一个概念:过渡模式

Vue 提供了 过渡模式:

1.in-out:新元素先进行过渡,完成之后当前元素过渡离开。

2.out-in:当前元素先进行过渡,完成之后新元素过渡进入。

用 out-in 重写之前的开关按钮过渡:

<transition name="fade" mode="out-in">
  <!-- ... the buttons ... -->
</transition>

多个组件的过渡:

	<style type="text/css">
		.v-enter, .v-leave-to{
			opacity: 0;
		}
		.v-enter-active, .v-leave-active{
			transition: opacity 1s;
		}
	</style>

	<transition mode="out-in">
		<child></child>
		<child-one></child-one>
	</transition>

//通过动态组件实现过渡
	<style type="text/css">
		.v-enter, .v-leave-to{
			opacity: 0;
		}
		.v-enter-active, .v-leave-active{
			transition: opacity 1s;
		}
	</style>

	<transition>
		<component :is="type"></component>
	</transition>
	<button @click="handleClick">toggle</button>

	data:{
		type:'child'
	}
	methods:{
		handleClick:function(){
			this.type=
				this.type === 'child' ? 'child-one' : 'child';
		}
	}

5.6Vue中的列表过渡

	<style type="text/css">
		.v-enter, .v-leave-to{
			opacity: 0;
		}
		.v-enter-active, .v-leave-active{
			transition: opacity 1s;
		}		
	</style>

<body>
	<div id="root">
		<transition-group>
			<div v-for="item of list" :key="item.id">
				{{item.title}}
			</div>
		</transition-group>
		<button @click="handleBtnClick">Add</button>
	</div>
	
	<script type="text/javascript">

		var count=0;

		var vm = new Vue({
			el:'#root',
			data:{
				list:[]
			},
			methods:{
				handleBtnClick:function(){
					this.list.push({
						id:count++,
						title:"hello world"
					})
				}
			}
		})
	</script>

</body>

5.7Vue中的动画封装

利用组件封装,把一个动画封装在一个组件里面

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<script type="text/javascript" src="https://cdn.bootcss.com/vue/2.6.3/vue.min.js"></script>
	<style type="text/css">

	</style>
</head>
<body>
	<div id="root">
		<fade :show="show">
			<div>hello world</div>
		</fade>
		<button @click="handleClick">点击</button>
	</div>
	
	<script type="text/javascript">

		Vue.component('fade',{
			props:['show'],
			template:`
				<transition @before-enter="handleBeforeEnter"
					@enter="handleEnter">
					<slot v-show="show"></slot>
				</transition>`,
			methods:{
				handleBefore:function(el){
					el.style.color = 'red'
				},
				handleEnter:function(el,done){
					setTimeout(()=>{
						el.style.color = 'green'
						done()
					},2000)
				}
			}
		})

		var vm = new Vue({
			el:'#root',
			data:{
				show:true
			}
		})
	</script>

</body>
</html>

5.8本章小结

更多动画学习可以去官网查看资料。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值