React Router V4 簡介
最近很久沒有React Router扫皱,好像上一次用還在用V3瑰抵,最近被問了個關(guān)于react routerv4的小問題,怎么也想不起來了止吐,決定自己看著文檔蚪腐,再重新demo一遍復(fù)習(xí)一下箭昵。
package
React Router主要包含三個包: react-router
, react-router-dom
,react-router-native
,react-router
包含了主要的route component和功能回季。react-router-dom
和react-router-native
提供了基于不同平臺(web和mobile)的component(類似于react需要把react和react-dom庫分開的原因家制,為了支持不同的平臺)。
Note
但是react-router-dom
和react-router-native
中都包含了react-router
這個庫泡一,因此在項目中使用的時候只需要根據(jù)你的平臺安裝上react-router-dom
或者react-router-native
即可
The Router
本文基于瀏覽器做平臺颤殴。首先你需要確定使用哪一種Router。
React Router中包含兩種router(被分裝成component)鼻忠。
<BrowserRouter>
<HashRouter>
區(qū)別
-
HashRouter:
使用URL中的
hash部分
保持URL和頁面內(nèi)容的同步涵但。- 不支持location.key和location.state
-
BrowserRouter:
URL始終和頁面的UI同步
- 針對于服務(wù)器可以server所有URL,并返回不同的頁面
每一個Router組件只能接受一個child component帖蔓,通常會創(chuàng)建一個App Component作為子組件render app剩下的部分矮瘟。將應(yīng)用從 router 中分離對服務(wù)器端渲染有重要意義,因為我們在服務(wù)器端轉(zhuǎn)換到 <MemoryRouter>
時可以很快復(fù)用 <App>
??????
import { BrowserRouter } from 'react-router-dom'
ReactDOM.render((
<BrowserRouter>
<App />
</BrowserRouter>
), document.getElementById('root'))
History
每一個Router(通常一個SPA會有一個Router)都會創(chuàng)建一個History對象塑娇,可以將這個對象理解成一個Location object數(shù)組澈侠,用來存儲瀏覽器URL的變化軌跡。雖然hostory記錄著歷史埋酬,但是你只能從中g(shù)et到當(dāng)前的Location object哨啃。
每當(dāng)?shù)刂窓诎l(fā)生改變,history對象就會改變(history存儲在react的context)写妥,因此Router component的update就開始了拳球。保證了此時頁面內(nèi)容會隨著每次URL的改變rerender。
因此Router組件必須是最外層組件耳标。
Location Object
Location object反應(yīng)的是你當(dāng)前的地址信息醇坝,包含了很多從地址欄中分出的信息
{
pathname: '/here',
search: '?key=value',
hash: '#extra-information',
state: { modal: true }, //這個是可以attach到這個location上的數(shù)據(jù)邑跪,但是不會出現(xiàn)在URL中
key: 'abc123' //這個location的唯一標(biāo)志次坡,也可以理解成數(shù)據(jù)存放的key
}
Routes
<Route>
組件是 React Router 的主要組成部分,如果你想要在路徑符合的時候在任何地方渲染什么東西画畅,你就應(yīng)該創(chuàng)造一個 <Route>
元素砸琅。
What does the <Route> render?
每一個Route component 都必須有一個屬性,能夠描述當(dāng)這個route被match的時候應(yīng)該render些什么轴踱。以下三個屬性可以用來描述render的內(nèi)容症脂。
component
當(dāng)被match的時候,Route 組件會將會使用React.createElement
創(chuàng)建一個component屬性
指定類型的React component。
render
屬性的類型是一個function诱篷,這個function可以返回一個component(類似于react render props)壶唤。用于當(dāng)你想要傳遞一些屬性給被render的component的時候使用
上面兩個屬性必須使用一個
children
也是一個function return 一個component,對于上兩個屬性棕所,只有route被匹配的時候才會被render闸盔。 這個屬性,只要寫了琳省,不論任何情況這個component都會被render
Path
Route組件需要一個屬性path
迎吵,描述了這個route需要匹配的pathname(僅包含port之后search之前的部分)。比如<Route path='/roster'/> 能夠匹配上的應(yīng)該是pathname以/roster
開頭的所有URL针贬,一旦match上相應(yīng)的component就會被render
<Route path='/roster'/>
// when the pathname is '/', the path does not match
// when the pathname is '/roster' or '/roster/2', the path matches
// If you only want to match '/roster', then you need to use
// the "exact" prop. The following will match '/roster', but not
// '/roster/2'.
<Route exact path='/roster'/>
// You might find yourself adding the exact prop to most routes.
// In the future (i.e. v5), the exact prop will likely be true by
// default. For more information on that, you can check out this
// GitHub issue:
// https://github.com/ReactTraining/react-router/issues/4958
Note:
- react-router只會匹配
pathname
击费,也就意味著對于http://www.example.com/my-projects/one?extra=false
,在react-router中等價于http://www.example.com/my-projects/one
- react-router中route的位置桦他,決定了send的URL匹配的是哪一個route蔫巩。
- 注意選擇是否要加參數(shù)
exact
, 因為不使用exact
的時候快压,只要URL使用path開頭就會被匹配
render component props
對于任何一個被匹配的route批幌,這個route被render的component一定能夠接受三個屬性:
-
location:描述當(dāng)前的所在的URL
hash: "" pathname: "/" search: "" state: undefined
-
history
react router的Router創(chuàng)建的history對象,其中包含history API中包含的function嗓节,還有當(dāng)前URL的location對象
-
match
isExact: true params: {} path: "/" url: "/"
match object
當(dāng)URL和某一個route match上的時候荧缘,會立刻創(chuàng)建一個match object
url | 被match的URL的pathname |
---|---|
params | path params (/a/b/:id中的id就是path params) |
path | route path屬性的值 |
isExact | 是否是exact的完全匹配(path === url) |
Note: match的object存儲在匹配的component的props中
使用browserRouter請求/roaster
總是404
此時我的express是這樣配置的:
const express = require('express');
const path = require('path');
const app = express();
app.use(express.static(path.resolve(__dirname,'../','./dist')));
module.exports = app;
也就意味著,當(dāng)請求/roaster
發(fā)送過來文件/dist/roaster
一定不存在拦宣,所以導(dǎo)致404截粗。
因為單頁應(yīng)用,必須要保證每一個URL發(fā)過來都應(yīng)該返回index.html
然后JS文件回來瀏覽器再處理route問題
const express = require('express');
const path = require('path');
const app = express();
app.use(express.static(path.resolve(__dirname,'../','./dist')));
app.get('*', function (req,res) {
res.sendFile(path.resolve(__dirname,'../','./dist/index.html'))
});
module.exports = app;
使用browserRouter請求/roaster/id
JS file 404
仔細(xì)查看生成的HTML文件鸵隧,發(fā)現(xiàn)<script>
標(biāo)簽包住的JS的打包文件的地址不對類似于roaster/main-12324343543.js
绸罗。
原因是HTML webpack plugin生成HTML文件的時候,JS文件的請求地址是這樣src=main-12312312.js
(相對的)豆瘫,這種情況會導(dǎo)致請求HTML/roaster/id
珊蟀, js的請求文件路徑直接替換最后slack的部分變成roaster/main-12324343543.js
解決只需要簡單的在webpack加上publicPath
output: {
publicPath: '/',
path: __dirname + '/dist',
filename: '[name]-[chunkhash].js'
}
如何創(chuàng)建Nested Routes
由于Routes Component可以用在APP的任意位置,只要作為Router
的child即可外驱。因此你可以使用switch
component將route combine成一個group育灸。
function Main() {
return (
<main>
<Switch>
<Route path='/roster' component={Roster}/>
</Switch>
</main>
);
}
function Roster() {
return (
<div>
<h2>This is a roster page!</h2>
<Switch>
<Route exact path='/roster' component={FullRoster}/>
<Route path='/roster/:number' component={Player}/>
</Switch>
</div>
);
}
- 先創(chuàng)建一個Main component將App的所有Route寫進(jìn)去
- 所有用
roster
開頭的URL都會match到Main的Roster component
- 所有用
- 然后再Roster component中再細(xì)分
/roaster/
之后的path
但是盡管是嵌套路由,在Roaster Component中的Routes仍然需要寫full path(是/roster/:numbe
而不是/:number
)
routes component傳遞data
React Router中使用route傳遞數(shù)據(jù)的方式有三種:
-
path variable:數(shù)據(jù)在URL中:
-
set:
<Route path='/roster/:id' component={Player} /> request: /roster/111
-
get
path variable一般存儲在match object中昵宇,通過
match.params
即可獲取一個object{id:111}
磅崭。match object在任意一個被match的route對應(yīng)的component中都可以通過
this.props.match
獲取
-
-
query parameters:數(shù)據(jù)在query中
-
set:
``` <Route path='/roster' component={Player} /> request: /roster?a=1&b=2&c=3 ```
get
query parameters一般存儲在location object中,通過
location.search
即可獲取一個string"?a=1&b=2&c=3"
注意會帶?
瓦哎。 -
-
location.state: 數(shù)據(jù)在location object中砸喻,只有在react-router中可以使用柔逼。
-
set:由于Link的to屬性以及history.push都能能夠接受兩種類型的值:
string
和 location object「畹海可以通過使用location object作為參數(shù)const location = { pathname: "/roster", search: "?a=1", state: {b:2} } <Link to={location} >roster</Link>
一旦點擊link就會render Roster component愉适,在這個component中,通過
location.state
就可以獲取{b:2}
-
寫在最后
- 你可以創(chuàng)建一個沒有path屬性的Route component癣漆,這個Route會和所有URL match
關(guān)于history object
-
history objects typically have the following properties and methods:
- length - (number) The number of entries in the history stack
- action - (string) The current action (PUSH, REPLACE, or POP)
- location - (object) The current location. May have the following properties:
- pathname - (string) The path of the URL
- search - (string) The URL query string
- hash - (string) The URL hash fragment
- state - (object) location-specific state that was provided to e.g. push(path, state) when this location was pushed onto the stack. Only available in browser and memory history.
- push(path, [state]) - (function) Pushes a new entry onto the history stack
- replace(path, [state]) - (function) Replaces the current entry on the history stack
- go(n) - (function) Moves the pointer in the history stack by n entries
- goBack() - (function) Equivalent to go(-1)
- goForward() - (function) Equivalent to go(1)
- block(prompt) - (function) Prevents navigation (see the history docs)
-
history對象是可變的儡毕,也就是說整個App的history對象只有一個,直接操作它會比較危險扑媚。如果你只是想獲取location的信息腰湾,可以在component中使用
this.props.location
獲取,而不是this.props.history.location
class Comp extends React.Component { componentWillReceiveProps(nextProps) { // location都會創(chuàng)建新的 const locationChanged = nextProps.location !== this.props.location // always true // 而history中的location永遠(yuǎn)只維護(hù)一個疆股,每次都是在這上面修改 const locationChanged = nextProps.history.location !== this.props.history.location } // always false
}
```
**Note: location is also found on history.location but you shouldn’t use that because its mutable.**
https://blog.pshrmn.com/entry/a-little-bit-of-history/#anno-5