这是个人处女做品,也是构思了好久,不知如何下手🙄。那就先在引言部分说说为何要开始写文章吧。javascript
我看掘金也有较长时间了,刷到过不少优秀的文章,不单单补充了本身的知识盲区,并且还掌握了不少知识细节,可是咱们这样子获取的知识都是碎片化的,实际场景中去解释的时候,会发现碎片拼凑有些困难,甚至没法顺畅的解释一些细节知识,这点我我的深有体会,因而决定开始经过写文章的方式来锻炼一下,写一篇文章很花时间,写一篇好的文章更是要花时间,写一篇文章会逼迫本身去了解这个知识的细节点,只有本身先了然于心,理解消化后,写出来的东西才会有读者接受。html
废话很少说,下面直接开始。html5
react-transition-groupjava
window hashchange事件react
做为开发人员,咱们代码逻辑里面跳转到下一个页面或者回退到上一个页面的时候,会有好几种方式,而做为用户,其实只有浏览器的前进后退(不管是左右滑屏仍是安卓物理键,最终表现为浏览器的前进后退),总结了如下表格git
动做 | 方式 | 动画 |
---|---|---|
前进 | react-router结合history.js的原生api:props.history.push\props.history.replace; window.location.href; window.location.replace;go(n); browser forward; | 自右向左 |
后退 | go(n)/goback(); browser back; | 自左向右 |
接下来是重点,如何在用户点击或者程序执行的时候,提早知道页面正确的过场动画呢?首先,咱们要知道react-router的router render方法,另外咱们要了解window hashchange,以下表格统计了俩个事件与页面组件执行顺序。github
动做 | 先 | 后 |
---|---|---|
props.history.push\props.history.replace | router render | hashchange |
window.location.href; window.location.replace;go(n)/goback(); browser back; browser forward; | hashchange | router render |
对于咱们代码层面,props.history.push\replace 彻底能够作到自控,统一书写规范,所有使用props.history的api,可是若是在一个旧的项目里面增长过场动画,你会发现,页面跳转基本经过 window.location 的api。注意表格的第二行,先触发hashchange事件的动做,既有前进的方法,也有后退的方法,咱们怎么区别呢?api
本地维护一个historyStack,只记录hash值,由于若是业务状况复杂的话,不少参数会经过url上动态变化来控制,因此若是直接存所有url的话,达不到咱们的目的。既然提到业务复杂会动态改变url的参数,咱们能够经过props.history.replace的方式完成,这种方式会触发router render,因为react组件没有任何改变,因此不会引发dom更新,这个方法是可行的。可是我的更加推荐使用 html5 history的 replaceState、pushState的方式去动态改变url的参数,俩个方法不会引发任何render或者hashchange事件,可是由于咱们本地维护了historyStack,因此咱们须要对这俩个api进行改造。具体看如下代码:浏览器
import React from 'react'
import ReactDOM from 'react-dom'
import { HashRouter, Switch, Route } from 'react-router-dom'
import { TransitionGroup, CSSTransition } from 'react-transition-group'
import { createBrowserHistory } from 'history'
import Page1 from 'containers/Page1'
import Page2 from 'containers/Page2'
import Page3 from 'containers/Page3'
import Page4 from 'containers/Page4'
// 一个不许确的 appHistoryStack,不对外暴露接口,不能当作历史记录参考
const setStorage = true
const appHistoryStack = setStorage && sessionStorage.getItem('appHistoryStack') && JSON.parse(sessionStorage.getItem('appHistoryStack')) || [getPathname(window.location.href)]
const replaceState = history.replaceState
const pushState = history.pushState
let appAction = 'FORWARD'
let onAnimation = false
let animationClassName = ''
function getPathname(url) {
if (url.indexOf('#/') !== -1) {
const hash = url.split('#/')[1]
return hash.split('?')[0]
}
return window.location.pathname
}
history.replaceState = function() {
setTimeout(() => {
const newPathname = getPathname(window.location.href)
appHistoryStack.splice(appHistoryStack.indexOf(newPathname), 1, newPathname)
}, 0)
replaceState.apply(history, arguments)
}
history.pushState = function() {
setTimeout(() => appHistoryStack.push(getPathname(window.location.href)), 0)
pushState.apply(history, arguments)
}
window.addEventListener('hashchange', (HashChangeEvent) => {
const { newURL, oldURL } = HashChangeEvent
const newURLPathname = getPathname(newURL)
const oldURLPathname = getPathname(oldURL)
if (newURLPathname !== oldURLPathname) {
const newURLIndex = appHistoryStack.indexOf(newURLPathname)
const oldURLIndex = appHistoryStack.indexOf(oldURLPathname)
if (newURLIndex === -1) {
appHistoryStack.push(newURLPathname)
}
if (newURLIndex === -1 || newURLIndex - oldURLIndex > 0) {
appAction = 'FORWARD'
} else {
appAction = 'GOBACK'
}
} else {
appHistoryStack.splice(newURLPathname, 1, newURLPathname)
}
})
ReactDOM.render((
<HashRouter history={createBrowserHistory()}>
<Route render={({ location, history }) => {
if (['PUSH', 'REPLACE'].includes(history.action)) {
animationClassName = onAnimation && animationClassName ? animationClassName : 'slide-left'
} else {
animationClassName = appAction === 'FORWARD' ? 'slide-left' : 'slide-right'
}
return (
<TransitionGroup className={animationClassName}>
<CSSTransition
key={location.pathname}
classNames="animation"
timeout={304}
onEnter={() => {
onAnimation = true
}}
onEntered={() => {
onAnimation = false
sessionStorage.setItem('appHistoryStack', JSON.stringify(appHistoryStack))
}} >
<Switch location={location}>
<Route exact path="/page1" component={Page1}/>
<Route exact path="/page2" component={Page2}/>
<Route exact path="/page3" component={Page3}/>
<Route exact path="/page4" component={Page4}/>
</Switch>
</CSSTransition>
</TransitionGroup>
)
}}/>
</HashRouter>
), document.getElementById('app'))
复制代码
具体源码地址:git地址session