2020-04-15:
筆記:React-router、react-router-dom、connected-react-router 學習
https://react-guide.github.io/react-router-cn/docs/guides/basics/RouteMatching.html
https://reacttraining.com/react-router/web/api/Switch
本篇是基於一個疑問開始的:
a標簽和connected-react-router中push方法都分別通過什么方式改變路由?
從結果直接說明:
1、如果使用了connected-react-router把react-router綁定到redux上,a標簽才會跟redux出現關系,同時也才能使用上push;
2、a標簽通過主動的方式進行跳轉,react-router監聽到這個行為后一方面進行路由跳轉一方面通過connected-react-router發起一個action更新redux state
3、push是從redux出發,拋出一個action,再進行跳轉。
一、路由配置:
更應該使用絕對路徑的理由:
1、在多層嵌套路由中使用絕對路徑的能力讓我們對 URL 擁有絕對的掌控。我們無需在 URL 中添加更多的層級,從而可以使用更簡潔的 URL。
2、如果一個路由使用了相對路徑
,那么完整的路徑將由它的所有祖先節點的路徑
和自身指定的相對路徑
拼接而成。使用絕對路徑
可以使路由匹配行為忽略嵌套關系。
渲染四個URL:
import React from 'react' import { Router, Route, Link } from 'react-router' const App = React.createClass({ render() { return ( <div> <h1>App</h1> <ul> <li><Link to="/about">About</Link></li> // 用Link組件替代a標簽 <li><Link to="/inbox">Inbox</Link></li> </ul> {this.props.children} </div> ) } }) const About = React.createClass({ render() { return <h3>About</h3> } }) const Inbox = React.createClass({ render() { return ( <div> <h2>Inbox</h2> {this.props.children || "Welcome to your Inbox"} </div> ) } }) const Message = React.createClass({ render() { return <h3>Message {this.props.params.id}</h3> } }) React.render(( <Router> <Route path="/" component={App}> <Route path="about" component={About} /> <Route path="inbox" component={Inbox}> <Route path="/messages/:id" component={Message} /> {/* 跳轉 /inbox/messages/:id 到 /messages/:id */} <Redirect from="messages/:id" to="/messages/:id" /> // 利用重定向兼容新舊url </Route> </Route> </Router> ), document.body)
二、進入和離開的Hook:
Route 可以定義 onEnter
和 onLeave
兩個 hook ,這些hook會在頁面跳轉確認時觸發一次。這些 hook 對於一些情況非常的有用,例如權限驗證或者在路由跳轉前將一些數據持久化保存起來。
三、路由匹配原理:
路由擁有三個屬性來決定是否“匹配“一個 URL:
- 嵌套關系:當一個給定的 URL 被調用時,整個集合中(命中的部分)都會被渲染。嵌套路由被描述成一種樹形結構。React Router 會深度優先遍歷整個路由配置來尋找一個與給定的 URL 相匹配的路由。
路徑語法:路由路徑是匹配一個(或一部分)URL 的 一個字符串模式。
-
幾個特殊的符號:
:paramName
– 匹配一段位於/
、?
或#
之后的 URL。 命中的部分將被作為一個參數()
– 在它內部的內容被認為是可選的*
– 匹配任意字符(非貪婪的)直到命中下一個字符或者整個 URL 的末尾,並創建一個splat
參數、<Route path="/hello/:name"> // 匹配 /hello/michael 和 /hello/ryan <Route path="/hello(/:name)"> // 匹配 /hello, /hello/michael 和 /hello/ryan <Route path="/files/*.*"> // 匹配 /files/hello.jpg 和 /files/path/to/hello.jpg
-
- 優先級:路由算法會根據定義的順序自頂向下匹配路由。因此,當你擁有兩個兄弟路由節點配置時,你必須確認前一個路由不會匹配后一個路由中的
路徑
。例如,千萬不要這么做:
// not do this <Route path="/comments" ... /> <Redirect from="/comments" ... />
四、History:
React Router 是建立在 history 之上的。 簡而言之,一個 history 知道如何去監聽瀏覽器地址欄的變化, 並解析這個 URL 轉化為 location
對象, 然后 router 使用它匹配到路由,最后正確地渲染對應的組件。
history.js: 一個無刷新就可改變瀏覽器欄地址的插件:
提供了window.history一系列api,目的是幫助構建單頁面無刷新網站。地址修改了,而頁面只有局部dom被修改,這樣能省去每次都重新渲染降低性能損耗,而且能看到動態效果。
react-router中history的常見形式:
1、browserHistory(更好兼容瀏覽器): React Router 的應用推薦的 history。它使用瀏覽器中的 History API 用於處理 URL,創建一個像
example.com/some/path
這樣真實的 URL 。
對舊版瀏覽器的兼容 :如果我們能使用瀏覽器自帶的 window.history
API,那么我們的特性就可以被瀏覽器所檢測到。如果不能,那么任何調用跳轉的應用就會導致 全頁面刷新。
它不需要被實例化,直接 import { browserHistory } from 'react-router',< Router history={browserHistory}>
2、createHashHistory(方便調試) : 使用 URL 中的 hash(#
)部分去創建形如 example.com/#/some/path
的路由。
五、默認路由與IndexLink:
這是關於默認路由(首頁)的配置:
1、假如這個路由Home想使用如 onEnter hook這些路由機制時,由於其沒有渲染的位置,而無法使用,此時,需要用到IndexRoute渲染Home:
<Router>
<Route path="/" component={App}>
<IndexRoute component={Home}/>
<Route path="accounts" component={Accounts}/>
<Route path="statements" component={Statements}/>
</Route>
</Router>
2、如果你在這個 app 中使用 <Link to="/">Home</Link>
, 它會一直處於激活狀態,因為所有的 URL 的開頭都是 /
。 這確實是個問題,因為我們僅僅希望在 Home
被渲染后,激活並鏈接到它。
如果需要在 Home
路由被渲染后才激活的指向 /
的鏈接,請使用 <IndexLink to="/">Home</IndexLink>
(基礎部分到此,高級部分可能后補)
React-router-dom:
基於react-router(z)
,加入了在瀏覽器運行環境下的一些功能,如Switch
1、<Switch>:
<Route> 的機制是只要匹配到的view都全部重新渲染,這樣浪費性能,<Switch>做到只渲染匹配到的第一個路由的頁面。
let routes = ( <Switch> <Route exact path="/"> <Home /> </Route> <Route path="/about"> <About /> </Route> <Route path="/:user"> <User /> </Route> <Route> <NoMatch /> </Route> </Switch> );
connected-react-router:
是一個綁定react-router到redux的組件,來實現雙向綁定router的數據到redux store中,這么做的好處就是讓應用更Redux化,可以在action中實現對路由的操作。
// app.ts import * as React from 'react'; import * as ReactDOM from 'react-dom'; import {Store} from 'redux'; import {Provider} from 'react-redux'; import {Persistor} from 'redux-persist'; import {PersistGate} from 'redux-persist/integration/react'; import {ConnectedRouter} from 'connected-react-router'; import {History} from 'history'; export const render = ( store : Store, history : History, persistor : Persistor, ) => ReactDOM.render(( <Provider store={store}> <ConnectedRouter history={history}> <PersistGate loading={null} persistor={persistor}> <App /> </PersistGate> </ConnectedRouter> </Provider> ), document.getElementById('root')); ===================================== // index.ts import {createHashHistory} from 'history'; import * as moment from 'moment'; import 'moment/locale/zh-cn'; import createStore from '@main/model'; import {render} from './app'; import './index.module.less'; moment.locale('zh_cn'); export const history = createHashHistory(); const {store, persistor} = createStore(history); store.dispatch({type : 'INITIALIZE'}); render(store, history, persistor);