React Router更新到4.0,使用起來發生了很大的變化,這個變化只要是指完全的組件化-----嵌套的JSX路由器!接下來,像剛接觸路由一樣的姿勢,一探究竟(簡書文字太小,建議大家調整一下頁面比例)
使用React腳手架搭建項目結構
create-react-app
內在使用的npm
,所以速度會很慢,你cnpm
的大刀已經饑渴難耐,可以考慮設置npm
為cnpm鏡像源:
npm config set registry http://registry.npm.taobao.org/
//通過get registy檢測是否成功
npm get registry
>http://registry.npm.taobao.org/
生成的項目結構src
目錄如下
因為來回切換頁面可能會破壞讀文的愉悅心情,所以咱們主要在app.js
內部大動手腳
app.js基本內容
import React,{Component} from "react";
//創建App根組件
class App extends Component{
render(){
return(
<h1>hello React!</h1>
)
}
}
export default App;
控制臺輸入
npm start
項目已經啟動,不接受任何形式的報錯
下載react-router
cnpm install react-router-dom --save
在app.js內部引入
import React,{Component} from "react";
//引入路由組件
import { BrowserRouter, Route,Link} from 'react-router-dom';
//創建App根組件
class App extends Component{
render(){
return(
<h1>hello React!</h1>
)
}
}
export default App;
接下來我創建函數式組件作為頁面級組件
//首頁組件
let Index=()=>{
return(
<h2>我是首頁內容</h2>
)
}
//列表組件
let List=()=>{
return(
<div>
<h2>我是列表頁</h2>
<ul>
<li>我是列表元素</li>
<li>我是列表元素</li>
<li>我是列表元素</li>
</ul>
</div>
)
}
在App組件內部配置路由組件
class App extends Component{
render(){
return(
<BrowserRouter>
<div>
<h1>hello React!</h1>
<Link to="/index">首頁</Link>
<Link to="/list">列表頁</Link>
<Route path="/index" component={Index}></Route>
<Route path="/list" component={List}></Route>
</div>
</BrowserRouter>
)
}
}
注意:
- BrowserRouter內部只能放一個子元素
- Route組件類似于判斷,路徑符合的會被顯示
1.如果沒有匹配到路徑怎么辦?
當然是顯示空白,但是使用<Redirect>
組件
路由引入改為
//引入路由組件
import { BrowserRouter, Route,Link,Redirect} from 'react-router-dom';
App寫成
<BrowserRouter>
<div>
<h1>hello React!</h1>
<Link to="/index">首頁</Link>
<Link to="/list">列表頁</Link>
<Route path="/index" component={Index}></Route>
<Route path="/list" component={List}></Route>
<Redirect to="/index" />
</div>
</BrowserRouter>
這樣當匹配不到路徑的時候會被重定向到/index
2.我想把Index
組件path
定義為/
可以嗎?
當然可以,只不過需要注意一些問題
<BrowserRouter>
<div>
<h1>hello React!</h1>
<Link to="/">首頁</Link>
<Link to="/list">列表頁</Link>
<Route path="/" component={Index}></Route>
<Route path="/list" component={List}></Route>
</div>
</BrowserRouter>
瀏覽器訪問,大家會發現,兩個組件同時出現,這是因為路由進行的是非精準匹配
//假設為組件path
let str="/list/hot";
let str2="/list/discount";
//假設為真實訪問路徑
let path="/list";
//假設為Route組件判斷
if(str.indexOf(path+"/")==0){
alert("str")
}
if(str2.indexOf(path+"/")==0){
alert("str2")
}
// 真實路徑 / path:/
// 真實路徑 /list path:/ /list
// 真實路徑 /list/abc path:/ /list /list/abc
毫無懸念,會彈出兩次,相信也都已經理解了,怎么解決這個問題?
1.exact
給Route組件添加exact進行精準匹配,相當于
path==str1
path==str2
你可能覺得非精準匹配的存在是一個煩惱,其實到嵌套路由時它的作用就凸顯出來了
App組件代碼
<BrowserRouter>
<div>
<h1>hello React!</h1>
<Link to="/">首頁</Link>
<Link to="/list">列表頁</Link>
<Route path="/" exact component={Index}></Route>
<Route path="/list" exact component={List}></Route>
</div>
</BrowserRouter>
2.Switch
只顯示一個,匹配成功之后不再往下查找,但是不是精準查找,實際開發過程中需要加上exact
路由引入改為
//引入路由組件
import { BrowserRouter, Route,Link,Redirect,Switch} from 'react-router-dom';
App寫成
<BrowserRouter>
<div>
<h1>hello React!</h1>
<Link to="/">首頁</Link>
<Link to="/list">列表頁</Link>
<Switch>
<Route path="/list" component={List}></Route>
<Route path="/" component={Index}></Route>
<Route path="/list" component={List}></Route>
<Route path="/list" component={List}></Route>
</Switch>
</div>
</BrowserRouter>
說完Route組件再來看一下Link,這個組件最終會被解析成a標簽,在有些場景下使用會很受限,例如程序執行到某個環節需要路由進行跳轉,類似location.href或者說Vue中的router.push。BrowserRouter組件外層包括,將路由信息注入到每個路由組件的props上。接下來將Link換成button
注意:因為App組件不是路由組件,所以該組件內部的props訪問不到路由信息
對組件嵌套結構進行改造:
//創建Content組件
class Content extends Component{
constructor(props){
super(props);
console.log(props);
}
render(){
return(
<div>
<h1>hello React!</h1>
<button onClick={()=>{this.props.history.push("/")}}>Index</button>
<button onClick={()=>{this.props.history.push("/list")}}>List</button>
<Switch>
<Route path="/" exact component={Index}></Route>
<Route path="/list" component={List}></Route>
</Switch>
</div>
)
}
}
//創建App根組件
class App extends Component{
render(){
return(
<BrowserRouter>
<Route path="/" component={Content}></Route>
</BrowserRouter>
)
}
}
withRouter
如上代碼,用Route組件包括Content組件將路由信息注入組件。組件內部需要訪問路由對象時這樣做就太過麻煩了,默認情況下,只有經過路由匹配渲染的組件才能訪問路由對象下的屬性和方法,然而不是所有組件都與路由相連,我們可以使用withRouter將路由對象注入到組件
首先引入withRouter
//引入路由組件
import { BrowserRouter, Route,Link,Redirect,Switch,withRouter} from 'react-router-dom';
上述代碼修改如下
//創建Content組件
class Content extends Component{
constructor(props){
super(props);
console.log(props);
}
render(){
return(
<div>
<h1>hello React!</h1>
<button onClick={()=>{this.props.history.push("/")}}>Index</button>
<button onClick={()=>{this.props.history.push("/list")}}>List</button>
<Switch>
<Route path="/" exact component={Index}></Route>
<Route path="/list" component={List}></Route>
</Switch>
</div>
)
}
}
//將路由對象注入到組件
Content=withRouter(Content)
//創建App根組件
class App extends Component{
render(){
return(
<BrowserRouter>
<Content/>//這里直接使用組件即可
</BrowserRouter>
)
}
}
Link or NavLink
在4.0版本中,這兩個組件都可以解析為a鏈接進行頁面跳轉,工作方式相同,但是NavLink可以根據路由匹配提供一些樣式功能,代碼如下
<BrowserRouter>
<div>
<h1>hello React!</h1>
<NavLink to="/" exact activeClassName="active">Index</NavLink>
<NavLink to="/list" activeClassName="active">List</NavLink>
<Switch>
<Route path="/list" component={List}></Route>
<Route path="/" component={Index}></Route>
<Route path="/list" component={List}></Route>
<Route path="/list" component={List}></Route>
</Switch>
</div>
</BrowserRouter>
當匹配路徑為/
時對應a
鏈接會有active
類名,生效對應樣式
Route
Route組件通過上面的使用,應該不陌生了,匹配路徑顯示對應組件,基礎用法存在一些局限性:
1.當需要往路由組件額外傳一些props
時
這種寫法在List
組件內部是接收不到age
的
<Route path="/list" component={List} age="20"></Route>
需要借助render
屬性
<Route path="/list" render={(props)=>{
return <List age="20"/>
}}></Route>
render
指向一個函數式組件將List組件返回,這樣就可以在List組件上隨意定義props,此種情況下List不再屬于路由組件,所以訪問不到路由相關對象,但是箭頭函數props中包含路由相關信息,所以
<Route path="/list" render={(props)=>{
return <List {...props} age="20"/>
}}></Route>
一切照常進行
2.當在匹配跳轉之前進行一些攔截時(如登錄驗證)
<Route path="/list" render={(props)=>{
if(this.state.login){//根據登錄狀態決定如何響應
return (<List {...props} age="20"></List>)
}else{
return(<Redirect to="/login">)
}
}}></Route>
加油!