React Router
1.什么是React Router?
React Router 是一個(gè)基于 React 之上的強(qiáng)大路由庫(kù),它可以讓你向應(yīng)用中快速地添加視圖和數(shù)據(jù)流,同時(shí)保持頁(yè)面與 URL 間的同步。
2.為什么要用React Router?
- React Router 知道如何為我們搭建嵌套的 UI,因此我們不用手動(dòng)找出需要渲染哪些
<Child>
組件。 - 獲取URL參數(shù)。當(dāng)渲染組件時(shí),React Router 會(huì)自動(dòng)向 Route 組件中注入一些有用的信息,尤其是路徑中動(dòng)態(tài)部分的參數(shù)。
3.基礎(chǔ)
3.1 路由配置
路由配置是一組指令,用來告訴 router 如何匹配 URL以及匹配后如何執(zhí)行代碼。
示例:
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>
<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} />
</Route>
</Route>
</Router>
), document.body)
可以使用IndexRoute
給路徑/
添加默認(rèn)首頁(yè)
import { IndexRoute } from 'react-router'
const Dashboard = React.createClass({
render() {
return <div>Welcome to the app!</div>
}
})
React.render((
<Router>
<Route path="/" component={App}>
{/* 當(dāng) url 為/時(shí)渲染 Dashboard */}
<IndexRoute component={Dashboard} />
<Route path="about" component={About} />
<Route path="inbox" component={Inbox}>
<Route path="messages/:id" component={Message} />
</Route>
</Route>
</Router>
), document.body)
現(xiàn)在,App
的 render
中的 this.props.children
將會(huì)是 <Dashboard>
這個(gè)元素。
現(xiàn)在的sitemap
如下所示:
URL | 組件 |
---|---|
/ | App -> Dashboard |
/about | App -> About |
/inbox | App -> Inbox |
/inbox/messages/:id | App -> Inbox -> Message |
絕對(duì)路徑可以將 /inbox
從 /inbox/messages/:id
中去除,并且還能夠讓 Message
嵌套在 App -> Inbox
中渲染。
React.render((
<Router>
<Route path="/" component={App}>
<IndexRoute component={Dashboard} />
<Route path="about" component={About} />
<Route path="inbox" component={Inbox}>
{/* 使用 /messages/:id 替換 messages/:id */}
<Route path="/messages/:id" component={Message} />
</Route>
</Route>
</Router>
), document.body)
在多層嵌套路由中使用絕對(duì)路徑使我們無需在 URL 中添加更多的層級(jí),從而可以使用更簡(jiǎn)潔的 URL。
絕對(duì)路徑可能在動(dòng)態(tài)路由中無法使用。
上面的修改使得URL發(fā)生了改變,我們可以使用Redirect
來兼容舊的URL。
import { Redirect } from 'react-router'
React.render((
<Router>
<Route path="/" component={App}>
<IndexRoute component={Dashboard} />
<Route path="about" component={About} />
<Route path="inbox" component={Inbox}>
<Route path="/messages/:id" component={Message} />
{/* 跳轉(zhuǎn) /inbox/messages/:id 到 /messages/:id */}
<Redirect from="messages/:id" to="/messages/:id" />
</Route>
</Route>
</Router>
), document.body)
3.2 路由匹配原理
路由擁有三個(gè)屬性來決定是否“匹配“一個(gè) URL:
-
嵌套關(guān)系
嵌套路由被描述成一種樹形結(jié)構(gòu)。React Router 會(huì)深度優(yōu)先遍歷整個(gè)路由配置來尋找一個(gè)與給定的 URL 相匹配的路由。
-
路徑語(yǔ)法
:paramName
– 匹配一段位于/
、?
或#
之后的 URL。 命中的部分將被作為一個(gè)參數(shù)()
– 在它內(nèi)部的內(nèi)容被認(rèn)為是可選的*
– 匹配任意字符(非貪婪的)直到命中下一個(gè)字符或者整個(gè) URL 的末尾,并創(chuàng)建一個(gè) splat 參數(shù)<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
如果一個(gè)路由使用了相對(duì)路徑,那么完整的路徑將由它的所有祖先節(jié)點(diǎn)的路徑和自身指定的相對(duì)路徑拼接而成。使用絕對(duì)路徑可以使路由匹配行為忽略嵌套關(guān)系。
-
優(yōu)先級(jí)
路由算法會(huì)根據(jù)定義的順序自頂向下匹配路由。因此,當(dāng)擁有兩個(gè)兄弟路由節(jié)點(diǎn)配置時(shí),必須確認(rèn)前一個(gè)路由不會(huì)匹配后一個(gè)路由中的路徑。例如:
<Route path="/comments" ... /> <Redirect from="/comments" ... />
3.3 History
一個(gè) history
知道如何去監(jiān)聽瀏覽器地址欄的變化, 并解析這個(gè) URL 轉(zhuǎn)化為 location
對(duì)象, 然后 router 使用它匹配到路由,最后正確地渲染對(duì)應(yīng)的組件。
常用的 history 有三種形式, 但也可以使用 React Router 實(shí)現(xiàn)自定義的 history
browserHistory
hashHistory
createMemoryHistory
3.3.1 browerHistory
Browser history 是使用 React Router 的應(yīng)用推薦的 history。它使用瀏覽器中的 History API 用于處理 URL,創(chuàng)建一個(gè)像example.com/some/path
這樣真實(shí)的 URL 。
3.3.2 hashHistory
Hash history 使用 URL 中的 hash(#)
部分去創(chuàng)建形如 example.com/#/some/path
的路由。
3.3.3 createMemoryHistory
Memory history 不會(huì)在地址欄被操作或讀取。同時(shí)它也非常適合測(cè)試和其他的渲染環(huán)境(像 React Native )。這種history需要?jiǎng)?chuàng)建。
const history = createMemoryHistory(location)
3.3.4 實(shí)現(xiàn)示例
import React from 'react'
import { render } from 'react-dom'
import { browserHistory, Router, Route, IndexRoute } from 'react-router'
import App from '../components/App'
import Home from '../components/Home'
import About from '../components/About'
import Features from '../components/Features'
render(
<Router history={browserHistory}>
<Route path='/' component={App}>
<IndexRoute component={Home} />
<Route path='about' component={About} />
<Route path='features' component={Features} />
</Route>
</Router>,
document.getElementById('app')
)
3.4 默認(rèn)路由(IndexRoute)與IndexLink
3.4.1 IndexRoute
當(dāng)用戶訪問 /
時(shí), App 組件被渲染,但組件內(nèi)的子元素卻沒有, App
內(nèi)部的 this.props.children
為 undefined 。Home
無法參與到比如 onEnter
hook 這些路由機(jī)制中來。 在 Home
的位置,渲染的是 Accounts
和 Statements
。 router 允許使用 IndexRoute
,以使 Home
作為最高層級(jí)的路由出現(xiàn)。
<Router>
<Route path="/" component={App}>
<IndexRoute component={Home}/>
<Route path="accounts" component={Accounts}/>
<Route path="statements" component={Statements}/>
</Route>
</Router>
現(xiàn)在 App
能夠渲染 {this.props.children}
了, 也有了一個(gè)最高層級(jí)的路由,使 Home
可以參與進(jìn)來。
3.4.2 IndexLink
如果需要在 Home
路由被渲染后才激活的指向 /
的鏈接,請(qǐng)使用 <IndexLink to="/">Home</IndexLink>
4.高級(jí)用法
4.1 動(dòng)態(tài)路由
React Router 里的路徑匹配以及組件加載都是異步完成的,不僅允許延遲加載組件,并且可以延遲加載路由配置。
React Router
會(huì)逐漸的匹配 URL 并只加載該 URL 對(duì)應(yīng)頁(yè)面所需的路徑配置和組件。
結(jié)合Webpack
可以在路由發(fā)生改變時(shí),資源按需加載。
const CourseRoute = {
path: 'course/:courseId',
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'))
})
}
}
4.2 跳轉(zhuǎn)前確認(rèn)
React Router
提供一個(gè) routerWillLeave
生命周期鉤子,這使得 React 組件可以攔截正在發(fā)生的跳轉(zhuǎn),或在離開 route 前提示用戶。routerWillLeave
返回值有以下兩種:
-
return false
取消此次跳轉(zhuǎn) -
return
返回提示信息,在離開 route 前提示用戶進(jìn)行確認(rèn)。
可以在 route 組件 中引入 Lifecycle
mixin 來安裝這個(gè)鉤子。
import { Lifecycle } from 'react-router'
const Home = React.createClass({
// 假設(shè) Home 是一個(gè) route 組件,它可能會(huì)使用
// Lifecycle mixin 去獲得一個(gè) routerWillLeave 方法。
mixins: [ Lifecycle ],
routerWillLeave(nextLocation) {
if (!this.state.isSaved)
return 'Your work is not saved! Are you sure you want to leave?'
},
// ...
})
推薦使用 React.createClass
來創(chuàng)建組件,初始化路由的生命周期鉤子函數(shù)。
如果想在一個(gè)深層嵌套的組件中使用 routerWillLeave
鉤子,只需在 route 組件 中引入 RouteContext
mixin,這樣就會(huì)把 route 放到 context 中。
import { Lifecycle, RouteContext } from 'react-router'
const Home = React.createClass({
// route 會(huì)被放到 Home 和它子組件及孫子組件的 context 中,
// 這樣在層級(jí)樹中 Home 及其所有子組件都可以拿到 route。
mixins: [ RouteContext ],
render() {
return <NestedForm />
}
})
const NestedForm = React.createClass({
// 后代組件使用 Lifecycle mixin 獲得
// 一個(gè) routerWillLeave 的方法。
mixins: [ Lifecycle ],
routerWillLeave(nextLocation) {
if (!this.state.isSaved)
return 'Your work is not saved! Are you sure you want to leave?'
},
// ...
})
4.3 服務(wù)端渲染
服務(wù)端渲染與客戶端渲染有些許不同,因?yàn)槟阈枰?/p>
-
發(fā)生錯(cuò)誤時(shí)發(fā)送一個(gè)
500
的響應(yīng) -
需要重定向時(shí)發(fā)送一個(gè)
30x
的響應(yīng) -
在渲染之前獲得數(shù)據(jù) (用 router 完成這點(diǎn))
為了迎合這一需求,要在 API 下一層使用:
- 使用
match
在渲染之前根據(jù)location
匹配route
- 使用
RoutingContext
同步渲染 route 組件
import { renderToString } from 'react-dom/server'
import { match, RoutingContext } from 'react-router'
import routes from './routes'
serve((req, res) => {
// 注意!這里的 req.url 應(yīng)該是從初始請(qǐng)求中獲得的
// 完整的 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} />))
} else {
res.send(404, 'Not found')
}
})
})
4.4 組件生命周期
假如路由配置如下:
<Route path="/" component={App}>
<IndexRoute component={Home}/>
<Route path="invoices/:invoiceId" component={Invoice}/>
<Route path="accounts/:accountId" component={Account}/>
</Route>
路由切換時(shí),組件生命周期的變化情況如下:
-
當(dāng)用戶打開應(yīng)用的
/
頁(yè)面組件 生命周期 App componentDidMount
Home componentDidMount
Invoice N/A Account N/A -
當(dāng)用戶從
/
跳轉(zhuǎn)到/invoice/123
組件 生命周期 App componentWillReceiveProps
,componentDidUpdate
Home componentWillUnmount
Invoice componentDidMount
Account N/A -
App
從 router 中接收到新的 props(例如children
、params
、location
等數(shù)據(jù)), 所以App
觸發(fā)了componentWillReceiveProps
和componentDidUpdate
兩個(gè)生命周期方法 -
Home
不再被渲染,所以它將被移除 -
Invoice
首次被掛載
-
-
當(dāng)用戶從
/invoice/123
跳轉(zhuǎn)到/invoice/789
組件 生命周期 App componentWillReceiveProps
,componentDidUpdate
Home N/A Invoice componentWillReceiveProps
,componentDidUpdate
Account N/A 所有的組件之前都已經(jīng)被掛載, 所以只是從 router 更新了 props.
-
當(dāng)從
/invoice/789
跳轉(zhuǎn)到/accounts/123
組件 生命周期 App componentWillReceiveProps
,componentDidUpdate
Home N/A Invoice componentWillUnmount
Account componentDidMount
雖然還有其他通過 router 獲取數(shù)據(jù)的方法, 但是最簡(jiǎn)單的方法是通過組件生命周期 Hook 來實(shí)現(xiàn)。示例如下,在 Invoice
組件里實(shí)現(xiàn)一個(gè)簡(jiǎn)單的數(shù)據(jù)獲取功能。文章來源:http://www.zghlxwxcb.cn/news/detail-720857.html
let Invoice = React.createClass({
getInitialState () {
return {
invoice: null
}
},
componentDidMount () {
// 上面的步驟2,在此初始化數(shù)據(jù)
this.fetchInvoice()
},
componentDidUpdate (prevProps) {
// 上面步驟3,通過參數(shù)更新數(shù)據(jù)
let oldId = prevProps.params.invoiceId
let newId = this.props.params.invoiceId
if (newId !== oldId)
this.fetchInvoice()
},
componentWillUnmount () {
// 上面步驟四,在組件移除前忽略正在進(jìn)行中的請(qǐng)求
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} />
}
})
4.5 組件外部跳轉(zhuǎn)
雖然在組件內(nèi)部可以使用 this.context.router
來實(shí)現(xiàn)導(dǎo)航,但許多應(yīng)用想要在組件外部使用導(dǎo)航。使用Router組件上被賦予的history可以在組件外部實(shí)現(xiàn)導(dǎo)航。文章來源地址http://www.zghlxwxcb.cn/news/detail-720857.html
// your main file that renders a Router
import { Router, browserHistory } from 'react-router'
import routes from './app/routes'
render(<Router history={browserHistory} routes={routes}/>, el)
// somewhere like a redux/flux action file:
import { browserHistory } from 'react-router'
browserHistory.push('/some/path')
到了這里,關(guān)于【React Router】React Router學(xué)習(xí)筆記的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!