路由可以向應用中快速的添加視圖和數據流,同時保持頁面與URL同步
import {Router ,Route, Link} from 'react-router' class App extends React.Component { render(){ return ( <div> <ul > <li><Link to='/about'>About</Link></li> <li><Link to='/inbox'>Inbox</Link></li> </ul> {this.props.children} </div> )}})
//獲取URL參數
class Message extends React.Component{
componentDidMount(){
const id = this.props.params.id //來自於路徑 ‘/inbox/messages/:id'
fetchMessage(id,function(err,message){
this.setState({message:message})
})
}
render(){
return(
<div>
<h3>message{this.props.params.id}</h3>
</div>
}
React.render(( <Router> <Route path='/' component={App}> <Route path='about' component={About} /> <Route path='inbox' component={Inbox} onEnter={requireAuth}>
<Route path='messages/:id component={Message} />
</Route>
</Route> <Router> ).document.body);
以上獲取URL參數方法:
1 id = this.props.params.id 來自父組件路徑path=‘/index/message.:id'
2 baz= this.props.location.query.bar 訪問路徑為 /foo?bar=baz 時,獲取baz的值
3 newhash = window.location.hash 簡單的用於跳轉,不用存儲location state
添加首頁:IndexRouter 設置默認頁面,當App的render中this.props.children還是undefined時顯示
<IndexRoute component={Dashboard}/>
IndexLink
如果使用<Link to='/'>Home</Link>,他會一直處於激活狀態,因為所有的URL開頭都是‘/'。而我們希望僅僅在home被渲染之后才激活並鏈接它。 可以使用<IndexLink to='/'>home</IndexLink>.
簡潔的URL:使用絕對路徑,無需在URL中添加更多的層級,從而可以使用更簡潔
<Route path='/messages/:id' component={Message}/> 但其可能在動態路由中無法使用
重定向Redirect
<Redirect from='message/:id' to='/message/:id'/>
當有人點擊/inbox/message/5時,會跳到/message/5,以防/inbox/message/5不存在
onEnter和onLeave進入和離開的hook,在頁面跳轉確認時觸發。例如 權限驗證,或數據持久保存
onLeave hook 在所有將離開的路由中觸發,從最下層的字路由開始到最外層的父路由結束。
onEnter hook從最外層的父路由開始到最下層的字路由結束
例如:從/messages/5跳到/about,hook的執行順序:/messages/:id的onLeave->/inbox的onLeave->/about的onEnter
requireAuth(nextState,replace,callback){if(isLogined){replace('./home');}else{replace('./login');}callback();}
路徑語法
:paramName 匹配位於/ ? # 之后的URL;稱作參數
() 在其內部的內容是可選的
* 匹配任意字符
History
一個history知道如何去監聽瀏覽器地址欄的變化,並解析這個url轉化為location對象,然后router使用它匹配到路由,渲染對應的組件。
1 browserHistory
使用瀏覽器中的 historyAPI 用於處理URL,創建 example.com/some/path這樣的真實URL
2 hashHistory
我使用URL中的hash#部分去創建形如 example.com/#/some/path的路由.window.location.hash = newhash
3 creactMemoryHistory
d Memory history 不會在地址欄被操作或讀取,非常適合測試和其他渲染環境。但必須創建
const history = createMemoryHistory(location)
高級進階
動態路由:
根據‘按需加載’的原則,路由非常適合做代碼拆分:他的責任就是配置好每個view。
react router 的 路徑匹配 及 組件加載 都是異步完成的,不僅允許你延遲加載組件,也可以延遲加載 路由的配置。在首次加載包中只需要有一個路徑定義,路由會自動解析剩下的路徑。
route定義的函數 getChildRoutes、getIndexRoute、getComponents 都為異步加載‘逐漸匹配’
const CourseRoute = { path:'cource/:id', getChildRoutes(location,callback){ require.ensure([],function(require){ callback(null,[ require('./routes/Announcements'), require('./routes/Assignments'), require('./routes/Grades'), ])})}, getIndexRoute(location,callback){ require.ensure([],function(require){ callback(null,require('./components/Index')) })}} getComponents(location,callback){ require.ensure([],function(require){ callback(null,require('./components/Course'))})}}
跳轉前確認:React Route 提供一個routerWillLeave的生命周期鈎子,使得react組件可以攔截正在發生的跳轉,或在離開route前提示用戶。
routerWillLeave 返回值有以下兩種:
1 return false 取消此次跳轉
2 return 返回提示信息,在離開route前提示用戶進行確認。
使用:1在route組件中引入 lifecycle mixin來安裝這個鈎子
import { Lifecycle } from 'react-router' const Home = React.creactClass({ //假設Home是一個 route 組件,使用 Lifecycle mixin 去獲取一個 routerWillLeave 方法。 mixins:[Lifecycle], routerWillLeave(nextLocation){ if(!this.state.isSaved) return 'Your work is not saved! Are you sure leave?' }, ..... })
2 如果想在一個深層次嵌套的組件中使用 routerWillLeave 鈎子,只需在route組件中引入RouteContext mixin,這樣就會把route放到context中。
import { Lifecycle, RouteContext } from 'react-router' const Home = React.creactClass ({ //route 會被放在Home 和它子組件及孫子組件的context中,這樣在層級樹中Home及其所有子組件都可以拿到 route。 mixin: [ RouteContext ], render(){ return <NestedForm /> } }) const NestedForm = React.creactClass({ //后代組件使用Lifecycle mixin 獲得 一個routerWillLeave 的方法。 mixins:[Lifecycle], routerWillLeave(nextLocation){ if(!this.state.isSave) return 'your work is not saved !' }, ...... })
服務端渲染:
需求:放生錯誤時返回500, 需要重定向時返回30x,在渲染之前獲得數據router
方法:在<Router>API下一層使用:
1 使用 match 在渲染之前根據 location 匹配route
2 使用 RoutingContext 同步渲染 route 組件
import { renderToString } from 'react-dom/server' import { match,RoutingContext } from 'react-router' import routes from './routes' serve((req,res) => { //這里的req.url應該是從初識請求中獲得的 //完整的URL路徑,包括查詢字符串 match({routes,location:req.url},{error,redirectLocation,renderProps) => { if(error){ res.send(500,error.message) }else if (redirectLocation){ res.redirect(302,redirectLocation.pathname + redirectLocation.search }else if (renderProps){ res.send(200,renderToString(<RoutingContext {...renderProps} //renderProps可以構建任何想要的形勢 }else{ res.send(404,'Not found') }})})
路由組件的生命周期中獲取數據:最簡單的方法是通過router組件的生命周期Hook來實現。
理解當路由改變時組件生命周期的變化,可以在Invoice組件里實現一個簡單的數據獲取功能
let Invoice = React.creactClass({ getInitialState(){ return { invoice:null } }, componentDidMount(){ //初始化數據 this.fetchInvoice() }, componentDidUpdate(prevProps){ //通過參數更新數據 let oldId = prevProps.params.invoiceId let newId = this.props.params.invoiceId if (newId !== oldId) this.fetchInvoice() }, componentWillUnmount(){ //在組件移除前 忽略正在進行中的請求 this.ignoreLastFetch = true }, fetchInvoice(){ let url = '/api/invoices/${this.props.params.invoiceId} this.request = fetch(url,(err,data) => { if (!this.ignoreLastFetch) this.setState({invoice:data.invoice}) }) }, render(){ return <InvoiceView invoice = {this.state.invoice}/> } })
組件外部跳轉:
在組件內部使用this.context.router來實現導航;
在組件外部使用Router組件上被賦予的history可以實現導航。
//main file 里renders a Router import { Router,browserHistory } from 'react-router' import routes from './app/routes' render(<Router history={browserHistory} routers={routes} />.el) // a redux/flux action file: import { browserHistory } from 'react-router' browserHistory.push('/some/path')
瀏覽器的前進后退按鈕發生了什么????
web瀏覽器的歷史記錄相當於一個僅有入棧的棧,當用戶瀏覽器到某一個頁面時將該文檔存入到棧中,點擊「后退」或「前進」按鈕時移動指針到 history 棧中對應的某一個文檔。
- 使用
hashchange
事件來監聽window.location.hash
的變化 - hash 發生變化瀏覽器會更新 URL,並且在 history 棧中產生一條記錄
- 路由系統會將所有的路由信息都保存到
location.hash
中 - 在 react-router 內部注冊了
window.addEventListener('hashchange', listener, false)
事件監聽器 - listener 內部可以通過 hash fragment 獲取到當前 URL 對應的 location 對象
在react中監聽路由hash改變,包括 瀏覽器前進回退按鈕 :componentWillReceiveProps(nextProps){if(nextProps.location.pathname != this.props.location.pathname){window.addEventListener('hashchange',listener,false)};
點擊Link后 路由系統發生????
它的 to、query、hash 屬性會被組合在一起並渲染為 href 屬性。然后調用history.pushState(state,path) ,
然后調用 window.location.hash
或者 window.history.pushState()
修改了應用的 URL