這是個人處女做品,也是構思了好久,不知如何下手🙄。那就先在引言部分說說爲何要開始寫文章吧。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