最近做的一个 React
项目,因为频繁使用动画,考虑到快速开发和更好的可维护性,决定使用其他库来辅助,期间看到 React官网 提供的两个 API
,决定尝试一下。
其中高级 API
使用起来没什么难度,用过 Angular
或者 Vue
动画的人基本上扫一眼文档就能上手,但是 另外一个 底层API
: ReactTransitionGroup
,官网上几乎没什么介绍,只将几个生命周期的钩子函数罗列了一下,网上也没有什么关于这方面的讲解或者 Demo
之类的东西,基本上都是围绕高级API
ReactCSSTransitionGroup
的。
因为需要对动画进行细粒度的控制,所以必须要用底层API
才行,网上找不到使用的例子,没办法,只好自己亲自上手一个个实验,好在最后差不多算是弄清楚是怎么用的了。
React
官网上对底层 API ReactTransitionGroup
的解释:
ReactTransitionGroup
是动画的基础。它通过require('react-addons-transition-group)
访问。当子级被声`明式的从其中添加或移除(就像上面的例子)时,特殊的生命周期挂钩会在它们上面被调用。
六个特殊的生命周期钩子函数如下:
componentWillAppear()
ReactTransitionGroup
是动画的基础。它通过require('react-addons-transition-group')
访问。当子级被声明式的从其中添加或移除(就像上面的例子)时,特殊的生命周期挂钩会在它们上面被调用。componentDidAppear()
在 传给componentWillAppear
的 回调 函数被调用后调用。componentWillEnter(callback)
对于被添加到已存在的TransitionGroup
的组件,它和componentDidMount()
在相同时间被调用 。它将会阻塞其它动画发生,直到callback被调用。它不会在TransitionGroup
初始化渲染时被调用。componentDidEnter()
在传给componentWillEnter
的回调函数被调用之后调用。componentWillLeave(callback)
在子级从ReactTransitionGroup
中移除时调用。虽然子级被移除了,ReactTransitionGroup
将会保持它在DOM
中,直到callback
被调用。componentDidLeave()
在willLeave
callback
被调用的时候调用(与componentWillUnmount
同一时间)
相信应该有不少人在看了上述官网讲解后依然是一头雾水,虽然六个钩子函数每一个都说的很仔细,但到底怎么用,可能还是不清楚,至于钩子函数中存在的回调函数 callback
到底是什么东西,就更不明白了。
对于这两点我也是很疑惑,不过实验了一番后,至于弄明白是怎么回事了,先上代码:
// Example.js 运动组件
class Example extends React.Component {
componentWillAppear() {
console.log('componentWillAppear');
}
componentDidAppear() {
console.log('componentDidAppear');
}
componentWillEnter(callback) {
console.log('componentWillEnter');
document.querySelector('.flyBall').className += ' changeTop'
callback()
}
componentDidEnter() {
console.log('componentDidEnter');
}
componentWillLeave(callback) {
callback()
}
componentDidLeave() {
console.log('componentDidLeave');
}
render() {
return (
<div className="flyBall">div>
)
}
}
其中 changeTop
样式为:
.changeTop {
animation: move 5s;
}
@keyframes move
{
from {left:0px;}
to {left:200px;}
}
// 引入运动组件
import Example from 'Example'
class Main extends React.Component {
constructor(props) {
super(props)
this.state = {
show: false
}
}
render() {
return (
<div className="box1">
"div">
{
this.state.show &&
}
div>
)
}
}
效果如下:
可以看到,动画的钩子函数,其实也就算是一种特殊的生命周期。
其中, 在本例中, componentWillAppear
和 componentDidAppear
这两个钩子函数没有执行,这是因为这两个钩子函数与 componentWillEnter
和 componentDidEnter
这两个钩子函数是 互斥
的,什么意思呢?
componentWillAppear
和 componentDidAppear
这两个钩子函数,只能在ReactTransitionGroup
初始化的时候,被在刚开始就存在于ReactTransitionGroup
中的子元素所触发,并且只能触发一次,后来追加进来的子元素只会触发 componentWillEnter
和 componentDidEnter
,而不会触发 Enter
。
在初始化挂载,所有的
ReactCSSTransitionGroup
子级将会appear
但不enter
。然而,所有后来添加到已存在的ReactCSSTransitionGroup
的子级将enter
但不appear
。
说的简单点,同一个生命周期中,触发了 componentWillEnter
和 componentDidEnter
,那么就不会触发 componentWillAppear
和 componentDidAppear
,反之亦然。
关于 callback
需要说明的是,这东西我开始以为是自己定义的函数,然后再钩子函数中回调调用,但是却一直不明白该怎么将自己写的函数传进去,直到后来实验了几次才明白,原来这东西是内置的参数,根本不用管,如果你把这callback
打印出来,就能看到其实它是这样的:
console.log(callback, callback.name)
// => "bound" function(){[native code]}
是一个函数名为 bound
的内置函数(native code
)。
这个 callback
的作用就是让特殊的生命周期继续往下执行,如果你不调用的话,那么就会阻滞动画的执行。
例如,如果你显式声明了:
componentWillLeave(callback) {
// 如果想要动画继续执行,必须调用下面代码,否则元素将被阻滞
callback()
}
然后如果不在这个函数中执行 callback()
的话,那么元素就会被暂停
住,这个时候,就算你的组件正常的生命周期中的 componentWillUnMount
已经执行完了,组件依旧不会被卸载掉。
官网上关于如何使用此 API
的说法很模糊,我稍微说一下。
"ul" className="animated-list">
...
官网上给了这种使用方法,如果不指定 component
,则默认在页面上渲染出一个 span
元素,否则渲染出一个指定的元素,什么意思呢?
例如上述代码渲染出一个 ul
元素,并且指定了其类名为 animated-list
,其实是说上述代码会被完全替换为
这段代码。
你同样可以在其中添加子元素,子元素在渲染的时候会被保留。
<ReactTransitionGroup component="ul" className="animated-list">
<li>a li Element<li>
ReactTransitionGroup>
上述会被替换为:
<ul class="animated-list">
<li>a li Element<li>
ul>