文章目录
- 一、深入理解 Vue 组件
- 二、Vue中的动画特效
一、深入理解 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>
+
<row ref="numTwo" @numchange="changeTotal"></row>
= {{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 父组件传值给子组件
- 父组件通过 属性形式 传值给子组件(不一定要使用
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>
- 子组件通过
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 子组件传值给父组件
- 子组件事件触发的形式
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);
},
}
};
- 父组件通过
@自定义事件名="父组件的方法“
监听子组件传值
<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"></i>
</router-link>
1.5 非父子组件间的传值
一个网页可以拆分成多个组件,根组件下有多个子组件,每一个子组件下也有许多子组件。
- 父子组件,就是具备父子关系的组件(如下图
1和2
就是父子组件) - 非父子组件,就是两个组件传值,但是两个组件不具备父子关系。(如下图的
1和3
、3和3
就是分父子组件)
解决非父子组件间的传值,有两个解决方案
- 使用
Vuex
- 利用vue的总线机制。
1.5.1 利用bus
(总线机制/发布订阅模式/观察者模式)解决非父子组件间传值问题
前面强调过,每一个组件都是一个Vue实例,如果在Vue的原型prototype
上定义一个属性,那后续实例化出来的根组件还是子组件都会有这个属性。
利用bus解决的三步骤:
- 在Vue的prototype上定义bus属性 Vue.prototype.bus = new Vue();
- 在组件的mounted生命周期钩子里使用this.bus.$on(‘事件名’, function(value){});来监听所定义的bus属性上对应的事件被触发,然后在回调函数里进行操作。
- 在组件的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样式来实现的。
设置过渡或动画的步骤
- 将要实现动画效果的 DOM 节点用 transition 标签包裹起来,可添加 name 属性;若没有 name 属性,则默认的 class 前缀为 v。
<transition name="fade">
<h1 v-if="show">Hello poorpenguin</h1>
</transition>
- 定义过渡类名
元素进入是的执行过程
在元素显示的瞬间(第1帧的时候),会向目标元素上添加两个classfade-enter
和fade-enter-active
;执行到 第2帧 的时候,会去掉fade-enter
,添加fade-enter-to
;到动画 结束前1帧 的时候,去掉fade-enter-active
和fade-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-active
和v-enter-active
这两个class在元素执行过渡或动画的过程中一直存在。所以我们可以省略除了v-enter-active
和v-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
使用步骤
- 使用 link 来引用animate.css
- 只需要在
<transition enter-active-class/leave-active-class="animated XXX(某库里的动画名)"></transition>
就行
ps:注意这里使用的是动画,只需要使用enter-active-class
和leave-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 解决第一次刷新页面没有动画的问题
使用appear
和appear-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 使用component
和slot
封装动画
写一个组件,里面的模版是
<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样式
略