React之路由
路由安装先用create-react-app生成一个react项目进入该项目安装包 react-router-dom分为2种模式:hash(哈希),browser(浏览器的那种,H5提供的API)认识路由路由模块,需要一个路由容器,标识着使用了那种模式的路由import {容器} from "react-router-dom";// 容器中存放的就是路由:Has
路由
安装
- 先用create-react-app生成一个react项目
- 进入该项目
- 安装包 react-router-dom
- 分为2种模式:hash(哈希),browser(浏览器的那种,H5提供的API)
认识路由
路由模块,需要一个路由容器,标识着使用了那种模式的路由
import {容器} from "react-router-dom";
// 容器中存放的就是路由:HashRouter 或者 BrowserRouter
// 这2种都可以,并且都是组件,为了我们方便来回使用方便一般都会给他们起个别名
//⚠️ 起完别名之后,则BrowserRouter这个名字就作废了。
import {BrowserRouter as Router} from "react-router-dom";
在以下案例中均已,Router 为BrowserRouter。
Route
- 路由一条一条的路由,也是个组件
import {BrowserRouter as Router,Route} from "react-router-dom";
渲染路由
⚠️ Router 路由盒子,只能有一个根节点
- 先渲染Router
- 在渲染Route
修改前
import React,{Component} from "react";
import ReactDOM from "react-dom";
import {BrowserRouter as Router,Route} from "react-router-dom";
render(<Router>
<Route></Route>//直接Router 放在第一个根节点会报错,Error: Router只能有一个child
<Route></Route> //所以要加一个 <></>或者 div 包起来
</Router>,wondow.root)
修改后
import React,{Component} from "react";
import ReactDOM from "react-dom";
import {BrowserRouter as Router,Route} from "react-router-dom";
render(<Router>
<>
<Route></Route>
<Route></Route>
</>
</Router>,wondow.root)
每条路由都和对应的组件创建链接
默认路由都是从上到下匹配,如果匹配成功就会渲染对应的组件
- path 路由地址
- component 对应的组件
- exact 是否严格匹配,默认为不严格匹配(false)。
import React,{Component} from "react";
import ReactDOM from "react-dom";
import {BrowserRouter as Router,Route} from "react-router-dom";
import Home from "./pages/Home";
import Proile from "./pages/Proile";
render(<Router>
<>
<Route path="/home" component={Home}></Route>
<Route path="/prfile" component={User}></Route>
</>
</Router>,wondow.root)
路由匹配的问题
- 路由只匹配开头
- path="/home" 和 path="/home/user" 都有/home 会渲染2个组件
- home 一般用/路径表示,在浏览器中输入/会显示home,输入/user或者其他的/… 也会显示home页面
此时,只需要加入是否严格匹配 exact
render(<Router>
<>
<Route path="/home" exact={true} component={Home}></Route>
<Route path="/prfile" component={User}></Route>
</>
</Router>,wondow.root)
解决404问题,在后端处理,找不到的话就返回首页,首页会根据当前的路径再次渲染路由。
- Vue 中有专门说根据那种方式来处理的
https://router.vuejs.org/guide/essentials/history-mode.html#example-server-configurations - react-router-dom的解决方案
- 添加一个Redirect 组件用来解决找不到对应的页面的时候
- to 找不到页面是定位到哪里页面的地址
- 存在的问题当我们给home的地址设置为 / 时,这是Redirect的地址也是**/** ,页面会报错:Error 你尝试去定位一个相同的路由
import {BrowserRouter as Router,Route,Redirect} from "react-router-dom"; render(<Router> <> <Route path="/home" exact={true} component={Home}></Route> <Route path="/prfile" component={User}></Route> <Route to="/" /> </> </Router>,wondow.root)
- 解决方案
希望路由匹配到一个只会,就不要向下匹配了。 - 添加一个Switch 组件
- Switch 里面只能包路由Route
- 把包裹Route的元素换成Switch就好
import {BrowserRouter as Router,Route,Redirect} from "react-router-dom"; render(<Router> <Switch> <Route path="/home" exact={true} component={Home}></Route> <Route path="/prfile" component={User}></Route> <Route to="/" /> </Switch> </Router>,wondow.root)
- 添加一个Redirect 组件用来解决找不到对应的页面的时候
路由导航
- Link
- NavLink
- 这2个都是组件,用法也都一样,唯一的就是:NavLink 可以给选中的按钮配置高亮样式
- 默认都是a标签
- 不支持指定标签名
- to 路径
- 使用NavLink 要注意当碰到首页是一个**/的时候加exact属性严格匹配,不然会出现多个元素都有高亮**类名
import {BrowserRouter as Router,Route,Redirect,NavLink} from "react-router-dom";
render(<Router>
<>
<div>
// <NavLink to="/" exact={true}></NavLink>
<NavLink to="/home"></NavLink>
<NavLink to="/user"></NavLink>
</div>
<Switch>
<Route path="/home" exact={true} component={Home}></Route>
<Route path="/user" component={User}></Route>
<Route to="/" />
</Switch>
</>
</Router>,wondow.root)
优化
每个页面都各自处理自己的内容
布局:nav.js 存放导航,index.js 存放路由并渲染到页面,home.js /user.js 是导航对应的内容
需求:每个页面都各自存放自己的东西,采用另一个app.js 页面把路由和导航组合到一起,这样就不需要把导航文件引入到index.js路由页面了
- 把导航nav.js 页面在app.js 页面引入
- 把app.js 页面在 index.js 路由页面引入
- 用引入的App组件包裹 ‘Switch’ 组件
- 在app.js 里面使用 {this.props.children} 把路由在这里展示
index.js
import App from "./app";
render(<Router>
<App>
<Switch>
<Route path="/home" exact={true} component={Home}></Route>
<Route path="/user" component={User}></Route>
<Route to="/" />
</Switch>
</App>
</Router>,wondow.root)
App.js
import React,{Component} from "react";
import ReactDOM from "react-dom";
import Nav from "./nav";
export default class App extends Component{
constructor(){
super();
}
render(
return(
<div>
<Nav></Nav>
{this.props.children}
</div>
)
)
}
添加二级导航
- 在user.js为例
改写user.js
import React, { Component } from 'react';
import SliderBar from '../components/SliderBar';
import {Route,Switch} from 'react-router-dom';
import Add from './Add'
import UserDetail from './UserDetail'
import List from './List'
export default class User extends Component {
state = {
// 二级路由地址和标题
sliderBarData: [
{ path: '/user/add', content: '用户添加' },
{ path: '/user/list', content: '用户list' },
]
}
render() {
return (<div>
<div className="col-md-3">
<SliderBar sliderBarData={this.state.sliderBarData}></SliderBar>
</div>
<div className="col-md-9">
<Switch>
{/* 二级菜单 默认展示添加路由 */}
<Route path='/user' exact={true} component={Add} /> //当点击user路径的时候默认展示Add组件的内容。这样不会当我们点击user路径的时候页面空着
<Route path='/user/add' component={Add}/>
<Route path='/user/list' component={List}/>
<Route path='/user/detail/:uid' component={UserDetail}/>
///user/detail/:uid 路径参数
</Switch>
</div>
</div>)
}
}
SliderBar.js 二级导航
import React, { Component } from 'react';
import { Link} from 'react-router-dom'
export default class SliderBar extends Component {
constructor() {
super();
}
render() {
return (<nav className="nav nav-stacked" >
{this.props.sliderBarData.map((slide,key)=>(
<li key={key}><Link to={slide.path}>{slide.content}</Link></li>
))}
</nav>)
}
}
Add.js
- 使用了 Router 容器,路由容器上会挂在着一些属性,Provider(供应商的意思,在最外层组件提供一些属性,所有子孙都可以应用),histoty…
- Route 组件中 可以获取到 父级提供的属性,Route来消费,并把这些属性传递给了渲染组件_
- 在Add.js 中console.log(this.props);可以看到有3个属性分别是: history,location,match
加粗为常用方法- history 类似浏览器的history,可以理解为主要是实现跳转
history下的方法- go 去哪
- goBack 后退
- goForward 往前走
- push 跳转页面
…
- location 当前关于路径的一些信息。
- hash
- key
- _pathname _
- state 跳转页面时带的数据放在这里
…
- match 匹配,当前路径是否和我的匹配
- path
- url
- params:{} 当前路径的参数
- isExact:false/true 是否严格匹配
- history 类似浏览器的history,可以理解为主要是实现跳转
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
export default class Add extends Component {
input = React.createRef();
constructor() {
super();
}
handleSubmit = (e) => {
e.preventDefault(); // 阻止默认行文
let username = this.input.current.value;
let lists = JSON.parse(localStorage.getItem('lists'))|| [];
lists.push({username,id: Math.random()});
localStorage.setItem('lists', JSON.stringify(lists));
// 使用路由容器后 路由容器上挂载着一些属性 Provider history..
// Route组件中可以获取到 父级提供的属性,Route来消费 并且把这些属性传递给了渲染的组件
this.props.history.push('/user/list'); //跳转到 list页面
}
render() {
return (<div>
<form onSubmit={this.handleSubmit}>
<input type="text" className="form-control" required ref={this.input}/>
<button className="btn btn-primary">添加</button>
</form>
</div>)
}
}
List.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import {Link} from "react-router-dom";
export default class List extends Component {
state = {
users: JSON.parse(localStorage.getItem('lists')) || [] //取app.js 添加的数据
}
render() {
return (<table className="table table-borderd">
<thead>
<tr>
<th>id</th>
<th>name</th>
</tr>
</thead>
<tbody>
{this.state.users.map((user,index)=>{
return <tr key={index}>
<td><Link to={{
pathname: `/user/detail/${user.id}`,//点击的时候把列表的id作为参数带入
state: user.username // 这个状态只有点击的时候才有
}}>{user.id}</Link></td>
//如果不传state数据可以直接这样:
// <td><Link to={`/user/detail/${user.id}`}>{user.id}</Link></td>
<td>{user.username}</td>
</tr>
})}
</tbody>
</table>)
}
}
UserDetail.js 二级内容 当点击list的时候进入,展示id和内容
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
export default class UserDetail extends Component{
constructor(){
super();
}
render(){
return (<div>
UserDetail
//浏览器的地址 真实的 去到id
{/* /user/detail/1 /user/detail/:id => id:1 */}
//拿到当前路径的参数
{this.props.match.params.uid}
{/* 如果没拿到状态 就在获取一遍 通过id*/}
{this.props.location.state} //拿到传入的数据
</div>)
}
}
Route 嵌套组件
过程
- 最开始渲染Route组件
- Route 又帮我们渲染 传入的组件
方法1
修改App.js
import React,{Component} from "react";
import ReactDOM from "react-dom";
import {Route} from "react-router-dom";
import Nav from "./nav";
export default class App extends Component{
constructor(){
super();
}
render(
return(
<div>
// <Nav></Nav>改为
//这样可以使 Nav下面的所有导航都可以有history
//所有用Route 渲染的组件 都有history
<Route path="/" component={Nav}>
{this.props.children}
</div>
)
)
}
方法2
把App.js 复原
import React,{Component} from "react";
import ReactDOM from "react-dom";
import Nav from "./nav";
export default class App extends Component{
constructor(){
super();
}
render(
return(
<div>
<Nav></Nav>
{this.props.children}
</div>
)
)
}
Nav.js修改
import React, { Component } from 'react';
import { NavLink, Route, withRouter } from 'react-router-dom';
import MenuLink from './MenuLink';
//希望给当前组件带上一个Route,模拟的
// let withRouter = (Component) => {
// return ()=>{
// return <Route component={Component}/>
// }
// }
// 高阶组件 就是组件返回组件 ,可以把公共的功能放到父亲来做
// 封装公共方法的组件
class Nav extends Component {
constructor() {
super();
}
handleClick = () => {
this.props.history.push('/')
}
render() {
return (
<nav className="navbar navbar-inverse">
<div className="container-fluid">
<div className="navbar-header">
<a className="navbar-brand" onClick={this.handleClick}>
路由系统
</a>
</div>
<div className="navbar-nav nav">
<MenuLink to="/" exact={true}>首页</MenuLink>
<MenuLink to="/user">用户</MenuLink>
<MenuLink to="/profile">个人中心</MenuLink>
<MenuLink to="/login">登录</MenuLink>
</div >
</div >
</nav >
)
}
}
// 如果某个组件 不是通过route来渲染的还想用里面的props 可以使用withRouter
export default withRouter(Nav); // 可以改写成 @withRouter的形式
权限校验
需求:判断用户是否登入,没有登入则跳转到登入页面,
login.js
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
export default class Login extends Component{
constructor(){
super();
}
render(){
return (<div>
<button onClick={()=>{
localStorage.setItem('login',true) //点击登入设置login,login用来判断用户是否登入
}}>登录</button>
<button onClick={()=>{
localStorage.clear();//点击清除所有的值
}}>取消登录</button>
</div>)
}
}
给index.js 添加login
import Login from './Login';
<Route path="/login" component={Login}></Route>
给nav.js 添加login导航
<NavLink to="/login">登录</NavLink>
- 这里拿user来做案例
- 在进入user之前,在外面在包一层做校验
- 叫做:高阶组件中判断用户是否校验,如果没做校验就跳转
添加一个protected.js
import React,{Component} from 'react';
import { Route,Redirect} from 'react-router-dom';
// 函数组件 参数是属性
// 把component 拿出来 重新命名Component 组件名必须大写
// props就是其他属性
// route中可以放置 component
// render render可以放一个函数 他会渲染这个函数的返回值
let Protected = ({component:Component,...props})=>{
return <Route {...props} render={(props)=>{ // p=> history,match,lication
return localStorage.getItem('login') ? <Component {...props}/> : <Redirect to="/login"/>
}}/>
}
export default Protected
修改index.js
import Protected from './Protected'
render(<Router>
<App>
{/* switch 会判断path */}
<Switch>
<Protected path="/" exact={true} component={Home}></Protected>
<Route path="/user" component={User}></Route>
{/* 高阶组件中判断用户是否登录过,如果没登陆跳转 Protected */}
<Protected path="/profile" component={Profile}></Protected>
<Route path="/login" component={Login}></Route>
<Redirect to="/"/>
</Switch>
</App>
</Router>, window.root)
更多推荐
所有评论(0)