Skip to content

路由的使用 ​

本节目标:理解“路由的使用”的核心思路,并能把它用于实际开发或面试表达。

说明:原 OneNote 中有图片引用,但图片未包含在导出目录中;本页保留了可用的文字与代码内容。 ==现在使用的路由是====React-Router-4====版本====,====包====名====叫====react-router-dom== 安装 ==yarn add react-router-dom (====不需要====--save)== ==np====m I== ==--save react-router-dom== ==4.0====版本已不需要路由配置,一切皆组件==

==它====内部的路由容器组件====,====主要包含==
==BrowserRouter====,====HashRouter====,====MemoryRouter====,====Route====,====Link====,====NavLink====,====Switch====。==
==其中====Route====还包括属性====path,exact,component,render==
==NavLink,Link==
==Switch==
==Redirect==
**HashRouter****和****BrowserRouter**
**4.0****中增加了****Render()****方法**
**to可以是字符串,也可以是对象**
**Switch****匹配成功后不会再往下匹配**
**Redirect**
**容器组件的区别**
==BrowserRouter:== ==浏览器自带的====H5 API,restful====风格====,====需要配合后台;==
==HashRouter:== ==使用====hash====方式进行路由====,====路径后均有====#====;==
==MemoryRouter:== ==在内存中管理====history====,地址栏不会变化。在====reactNative====中使用。==
==在开发时我们一般使用====HashRouter====,====上线后我们改用====BrowserRouter==
==首先建立====index.js====文件==
==并在首部引入我们要用到的页面组件==
//components
import App from './containers/App';
import Home from './containers/Home';
import Profile from './containers/Profile';
import User from './containers/User';
//一般情况下,我们在index.js中配置路由即可
//1 首先在头部引入要用到的组件{HashRouter Route Switch Redirect}
//2 Router是路由的包裹容器
//3 Route 是单个路由
//4 switch的作用让Route匹配后就不再匹配下面的内容
//exact确切的表示只有路径完全相等时才匹配
//Redirect表示重定向
//如果想要路径错误后但不重定向,再配置一个无路径的路由,表示所有路径都匹配
//<Route component={Home} />
//https://reacttraining.cn/教程地址
//另外,Link必须被包含在Router内,所以最终App组件是在Router组件里的
import { HashRouter as Router, Route, Switch, Redirect } from 'reat-router-dom';
ReactDOM.render(
<Router>
<App>
<Switch>
<Route path='/' exact={true} component={Home} />
<Route path='/profile' component={Profile} />
<Route path='/user' component={User} />
<Redirect to='/' />
</Switch>
</App>
</Router>,
document.getElementById('root')
)
==我样通过不同的链接来管理不同的地址,而不同的地址则指向不同的组件==
==建立====App.js====,作为其它页面的容器,同时也是导航条的容器,不管路由到哪个组件,导航条始终可见。==
import React from 'react';
//import Nav component
import Nav from '../components/Nav';
export default class App extends React.Component {
render() {
return (
<div className="flex-wrapper col-direction">
<Nav />
{this.props.children}
</div>
)
}
}
==这样,我们新建一个====Nav====组件====Nav.js====,然后在其中定义链接====,用====Link====指向不同的地址,然后再不同的地址指向不同的组件==
//使用路由组件的功能
import { Link } from 'react-router-dom';
return (
<div>
<ul>
<li><Link to='/' exact={true}>首页</Link></li>
<li><Link to='/uses'>用户列表</Link></li>
<li><Link to='/profile'>个人中心</Link></li>
</ul>
</div>
)

==我们在Profile组件中,要定义一个二级路由,用来管理用户====。====思路一样的,在profile组件中,使用====Link====,用pathname属性指向不同的地址,然后不同的地址指向我们想要路由的组件====,我们新增两个组件:==

==Add:用来新增用户==
==List:显示用户清单==

==其中Add组件是包含一个表单输入框;== ==List====组件只是简单地显示出组件。==

**Link****组件**
==import { Link } from 'react-router-dom';==
==<li>==
==<Link to={'/home'}>====首页====</Link>==
==</li>==
==<li>==
==<Link to={'/user'}>====用户管理====</Link>==
==</li>==
==<li>==
==<Link to={'/profile'}>====个人中心====</Link>==
==</li>==
==我们可以直接在====App====中通过====children====的方式引入==
**增加导航**
==import React from 'react';========import {Link} from 'react-router-dom';========export default class App extends React.Component{======    ==render(){======        ==return (======            ==<div>======                ==<div className="navbar-inverse navbar">======                    ==<div className="container-fluid">======                        ==<div className="navbar-header">======                            ==<div className="navbar-brand">======                                ==用户管理系统======                            ==</div>======                        ==</div>======                        ==<ul className="navbar-nav nav">======                            ==<li>======                                ==<Link to={'/home'}>====首页====</Link>======                            ==</li>======                            ==<li>======                                ==<Link to={'/user'}>====用户管理====</Link>======                            ==</li>======                            ==<li>======                                ==<Link to={'/profile'}>====个人中心====</Link>======                            ==</li>======                        ==</ul>======                    ==</div>======                ==</div>======                ==<div className="container">======                    ==<div className="row">======                        ==<div className="col-md-12">======                            =={this.props.children}======                        ==</div>======                    ==</div>======                ==</div>======            ==</div>======        ==)======    ==}========}======
==这里使用====Link====组件它可以替代我们自己写的====a====标签====,====因为后面我们可能会用到====browserHistory====,跳转可能需要用到====H5====的====api====进行跳转====,Link====组件是====react====路由中提供的声明式组件====,====可以帮我们区分路由的模式来实现路由的跳转。==
**页面级组件**
==这里我们先将对应的组件代码代码贴出来====,====后面我们来完善对用的逻辑==
==// Home.js========import React from 'react';========export default class Home extends React.Component{======    ==render(){======        ==return (======            ==<div>Home</div>======        ==)======    ==}========}========// Profile.js========import React from 'react';========export default class Profile extends React.Component{======    ==render(){======        ==return (======            ==<div>Profile</div>======        ==)======    ==}========}========// User.js========import React from 'react';========export default class User extends React.Component{======    ==render(){======        ==return (======            ==<div>User</div>======        ==)======    ==}========}======
**二级路由**
==刚才我们实现了一级导航====,====但是一般情况下管理系统都会拥有二级导航====,====比如说我们希望用户管理中包含用户列表和添加用户====,====这两个菜单应该属于用户列表下的子导航====,====我们先来看下效果==
**实现二级路由**
==二级导航就是在某个一级路由中继续嵌套路由==
==import React from 'react';========import {Route,Link} from 'react-router-dom'========import UserList from './UserList'========import UserAdd from './UserAdd'========export default class User extends React.Component{======    ==render(){======        ==return (======            ==<div className="row">======                ==<div className="col-md-2">======                    ==<div className="nav nav-stacked">======                        ==<li><Link to='/user/list'>====用户列表====</Link></li>======                        ==<li><Link to='/user/add'>====增加用户====</Link></li>======                    ==</div>======                ==</div>======                ==<div className="col-md-10">======                    ==<Route path="/user/list" component={UserList}/>======                    ==<Route path="/user/add" component={UserAdd}/>======                ==</div>======            ==</div>======        ==)======    ==}========}======
==这里并没有什么需要注意的新用法====,====只是路径要特殊处理一下因为是二级路由====,====要保证一级路由也同时显示所以开头要和一级路由路径相同。如果多次点击相同路由时会触发====Hash history cannot PUSH the same path;====这样的一个警告====,====这个是无法去除的但是改成====BrowserHistory====就不会触发此警告了。所有不用担心====.==
**UserList****和****Add****组件**
==// UserList.js========import React from 'react';========export default class UserList extends React.Component{======    ==render(){======        ==return (======            ==<div>UserList</div>======        ==)======    ==}========}========// UserAdd.js========import React from 'react';========export default class UserAdd extends React.Component{======    ==render(){======        ==return (======            ==<div>UserAdd</div>======        ==)======    ==}========}======
==这里我们先不写任何逻辑,先将具体的功能实现出来。这样我们就实现了二级路。==
**路由跳转和路径参数**
==本节我们完善一下内部的逻辑====,====进入到添加列表页可以实现用户的添加并且可以跳转到列表页面渲染出添加的用户列表====,====页面间的通信我们采用====localStorage====。并且点击某个用户可以进入到用户详情页。==
**实现添加用户**
==//====所有通过====route====渲染的组件,====porps====都会多出三个属性====match, history, location==
==import React from 'react';========export default class UserAdd extends React.Component{======    ==handleSubmit = (e) =>{======        ==e.preventDefault();======        ==let localStr = localStorage.getItem('lists');======        ==let list = JSON.parse(localStr)|| [];======        ==list.push({id:Math.random(),name:this.name.value});======        ==localStorage.setItem('lists',JSON.stringify(list));======        ==this.props.history.push('/user/list')======    ==};======    ==render(){======        ==return (======            ==<div>======                ==<form onSubmit={this.handleSubmit}>======                    ==<div className="form-group">======                        ==<label htmlFor="name" className="control-label">======                            ==用户名====:======                        ==</label>======                        ==<input type="text" className="form-control" ref={x=>this.name = x}/>======                    ==</div>======                    ==<div className="form-group">======                        ==<button className="btn btn-primary">====添加====</button>======                    ==</div>======                ==</form>======            ==</div>======        ==)======    ==}========}======
==这里我们在====props====中使用了====history====的====API,====所有通过路由渲染的组件都拥有一些路由的属性后面我们会一一介绍到。这里我们采用编程式的方式跳转了路径。==
**列表页展示**
==import React from 'react';========import {Link} from 'react-router-dom'========export default class UserList extends React.Component{======    ==constructor(){======        ==super();======        ==this.state = {}======    ==}======    ==componentWillMount(){======        ==let userList = JSON.parse(localStorage.getItem('lists'));======        ==this.setState({======            ==userList======        ==})======    ==}======    ==render(){======        ==return (======            ==<div>======                ==<ul className="list-group">======                    =={this.state.userList.map((user,index)=>(======                        ==<li className="list-group-item" key={index}>======                            ==<Link to={"/user/detail/"+user.id}>{user.name}</Link>======                        ==</li>======                    ==))}======                ==</ul>======            ==</div>======        ==)======    ==}========}======
==这里的逻辑比较简单====,====我们又添加了一个====detail====路径同样也是一个二级路由,点击用户名可以显示具体的用户====id====和用户名。==
**引入****UserDetail****组件**
==+ import UserDetail from './UserDetail';======  ==<Route path="/user/list" component={UserList}/>======  ==<Route path="/user/add" component={UserAdd}/>========+ <Route path="/user/detail/:id" component={UserDetail}/>======
==这里我们采用了模糊匹配的方式====,====这就是我们常说的路径参数。也就是说====id====可以代表任何值,我们可以在详情页中通过====this.props.match.params.id====获取到真实传入的====id==
**UserDetail****详情页**
==import React from 'react';========export default class UserDetail extends React.Component{======    ==render(){======        ==let {id } = this.props.match.params;======        ==let {name} = JSON.parse(localStorage.getItem('lists')).find(item=>item.id == id);======        ==return (======            ==<table className="table table-bordered">======                ==<thead>======                    ==<tr>======                        ==<th>id====号====</th>======                        ==<th>====标题====</th>======                    ==</tr>======                ==</thead>======                ==<tbody>======                    ==<tr>======                        ==<td>{id}</td>======                        ==<td>{name}</td>======                    ==</tr>======                ==</tbody>======            ==</table>======        ==)======    ==}========}======
==这里要注意通过====params====取出的结果都是字符串类型====,====所有匹配到的参数都会放在====match.params====的属性上。==
**路由匹配**
==有的时候我们希望对路由匹配有些限制====,====比如说严格对某个路径进行匹配====,====或者匹配到某个路径时就不在匹配==
**新增路由匹配**
==<App>========+       <Route path="/" render={()=><h1>====首页====</h1>}/>========+       <Route path="/:name" render={()=><h1>zfpx</h1>}/>======        ==<Route path="/home" component={Home}/>======        ==<Route path="/profile" component={Profile}/>======        ==<Route path="/user" component={User}/>========</App>======
==在此我们会发现当访问====/home====时以上====首页====和====zfpx====和====home====组件====都会访问到====,====而我们只希望在访问====/====时才会显示首页====,====我们可以在某个====Route====上增加====exact====属性==
**exact**
==<Route path="/" exact render={()=><h1>====首页====</h1>}/>======
==你会发现这样就实现啦====~,====只会当访问====/====时才可以匹配到。==
**Switch**
==但是现在访问====/home====时我们依然有两个组件会被匹配到,我们希望匹配到一个后就停止匹配,不在继续匹配下一个路由,我们可以使用====Switch====组件==
==import {HashRouter,Route,Switch} from 'react-router-dom';========<Switch>======    ==<Route path="/" exact render={()=><h1>====首页====</h1>}/>======    ==<Route path="/:name" render={()=><h1>====首页====</h1>}/>======    ==<Route path="/home" component={Home}/>======    ==<Route path="/profile" component={Profile}/>======    ==<Route path="/user" component={User}/>========</Switch>======
==现在访问====/home====你会发现====Home====组件永远都不会显示出来啦!==
**受保护的路由**
==我们想对一些路由进行屏蔽====,====例如登录后才能访问====,====这里我们在本地存一个变量来表示是否登录====,====增加一个登录路由,点击登录按钮将本地变量改为登录成功状态====,====即可以访问用户列表页面==
**受保护路由**
==我们匹配到====/user====路由时要根据状态判断是否有权限,如果没权限需要跳转到登录页面,主要靠的是高阶组件的思想来实现====:==
**Protected****组件**
==import React from 'react';========import {Route,Redirect} from 'react-router-dom'========export default ({component:Component,...others})=>{======   ==return <Route {...others} render={(props)=>{======       ==return localStorage.getItem('loginSystem')?<Component {...props}/>:<Redirect to={{======           ==pathname:'/login',======           ==from:props.match.url======       ==}}/>======   ==}}/>========}======
==Redirect====组件是用来重定向的,我们新增====from====属性来记录当前匹配的====url,====为了保证登录后可以在跳回到当前匹配的路径==
**新增****Login****组件**
==<div className="container">======    ==<Route path="/home" component={Home}/>========-   <Route path="/profile" component={Profile}/>========+   <PrivateRoute path="/profile" component={Profile}/>======    ==<Route path="/user" component={User}/>========+   <Route path="/login" component={Login}/>========</div>======
==Login.js========import React from 'react';========export default class Login extends React.Component{======    ==render(){======        ==return (======            ==<div>======                ==<button className="btn btn-primary" onClick={()=>{======                    ==window.localStorage.setItem('loginSystem',true);======                   ==this.props.history.push(this.props.location.from)======                ==}}>====登录====</button>======            ==</div>======        ==)======    ==}========}======
==我们发现默认点击====profile====会默认跳转到====login====组件中,点击登录可以再次跳回====profile====。这样我们就实现了受保护的路由。==
**自定义菜单**
==我们想给点击后的菜单增加激活样式,同样依然采用高阶组件的方式进行包装==
**实现****MenuLink****组件**
==此组件是用来替换掉原有的====Link====组件,并且在内部进行判断是否增加激活状态==
==import React from 'react';========import {Route,Link} from 'react-router-dom'========export default ({to,label})=>{======    ==return <Route children={(props)=>{======        ==return <li className={props.match?'active':''}><Link to={to}>{label}</Link></li>======    ==}}/>========}======
==这里有个====children====属性和以前====render====不同,====children====无论是否路由匹配到都会执行此函数。而====render====只要在匹配到后才会执行==
**Prompt**
==我们希望在添加页的输入框中输入内容后点击其他路由时先询问一下是否需要跳转。==
==constructor(){======    ==super();======    ==this.state = {show:false}========}========<div>======    ==<Prompt when={this.state.show} message={location => (======        ==`Are you sure you want to go to ${location.pathname}?`======    ==)}/>======    ==<form onSubmit={this.handleSubmit}>======        ==<div className="form-group">======            ==<label htmlFor="username">====用户名====</label>======            ==<input type="text" className="form-control" ref={(x)=>this.x=x}======               ==onChange={(e)=>{======                   ==if(e.target.value.length>0){======                       ==this.setState({show:true})======                   ==}======               ==}======            ==/>======        ==</div>======        ==<div className="form-group">======            ==<button className="btn btn-primary" >====添加====</button>======        ==</div>======    ==</form>========</div>======
==监听输入框中的内容====,====当有内容时将状态====show====变为====true,====当需要跳转路由时====Prompt====的组件====when====属性为====true====就会提示对应的====message,====当然我们点击添加时不需要弹出====,====所以先将状态改为====false====在进行跳转即可。==
==handleSubmit=(e)=>{======    ==e.preventDefault();======    ==let list = JSON.parse(localStorage.getItem('lists'))||[];======    ==list.push({id:Math.random(),name:this.x.value});======    ==localStorage.setItem('lists',JSON.stringify(list));========+   this.setState({show:false},()=>{======        ==this.props.history.push('/profile/list')========+   })========};======
==要等待状态改变后在执行跳转,因为====setState====是异步的所以要将跳转逻辑放的回调函数中。==
**NotFound****页面**
==我们需要当路由都匹配不到时显示一个====404====页面====,====增加一个====404====组件==
==import React from 'react';========export default class NotFound extends React.Component{======    ==render(){======        ==return (======            ==<div>NotFound</div>======        ==)======    ==}========}======
**新增****404****路由**
==<div className="container">======    ==<Switch>======        ==<Route path="/home" component={Home}/>======        ==<PrivateRoute path="/profile" component={Profile}/>======        ==<Route path="/user" component={User}/>======        ==<Route path="/login" component={Login}/>========+       <Route component={NotFound}/>======    ==</Switch>========</div>======
==这里我们使用====switch====组件当全部匹配不到时会默认渲染====404====路由====,====这样我们就实现了====404====页面==

本站总访问 — 次