Skip to content

七、React路由

1.路由v5

1.基本步骤

1.安装react-router-dom模块

bash
yarn add react-router-dom@5.3.0

2.导入所需模块

App.js

jsx
import {
  BrowserRouter as Router, // history模式

  HashRouter as Router, 	  // hash模式
  Route,  // 用于定义路由
  Link    // 用于跳转路由(声明式导航)
} from "react-router-dom";

3.定义路由

导航区使用 <Link>,展示区使用 <Route>

App.js

jsx
function Dy() { return <div>电影</div> }
function Yy() { return <div>影院</div> }
function Th() { return <div>特惠</div> }
function My() { return <div>我的</div> }


function App() {
    return (<fieldset>
      <legend>App</legend>
  
      <Router>
          <ul>
              <li><Link to="/dy">电影</Link></li>
              <li><Link to="/yy">影院</Link></li>
              <li><Link to="/th">特惠</Link></li>
              <li><Link to="/my">我的</Link></li>
          </ul>

          <hr />
          <Route path="/" component={Dy} />
          <Route path="/dy" component={Dy} />
          <Route path="/yy" component={Yy} />
          <Route path="/th" component={Th} />
          <Route path="/my"> <My /> </Route>
      </Router>
                
                
    </fieldset>
    );
  }

export default App;

2.路线匹配Switch

作用Switch 可以提高路由匹配效率,如果匹配成功,则不再继续匹配后面的路由,即单一匹配。

步骤

1.导入Switch

js
import { Switch } from "react-router-dom";

2.用<Switch></Switch>包裹所有<Route>

jsx
<Switch>
    <Route path="/" exact component={Dy} />
    <Route path="/dy" component={Dy} />
    <Route path="/yy" component={Yy} />
    <Route path="/th" component={Th} />
    <Route path="/my"> <My /> </Route>
</Switch>

3.精确匹配exact

默认使用模糊匹配(输入的路径必须包含要匹配的路径,且顺序一致)

开启严格匹配:给<Route/>标签添加exact属性

html
<Route exact path="/about" component={About}/>

严格匹配需要再开,开启可能会导致无法继续匹配二级路由

作用:NavLink 可以实现路由链接的高亮,默认添加一个 active 类名。通过 activeClassName 指定样式名,

步骤

1.导入

js
import { NavLink } from "react-router-dom";

2.使用

<Link>换成<NavLink>

jsx
<ul>
    <li><NavLink to="/dy">电影</NavLink></li>
    <li><NavLink to="/yy">影院</NavLink></li>
    <li><NavLink to="/th">特惠</NavLink></li>
    <li><NavLink to="/my">我的</NavLink></li>
</ul>

5.重定向Redirect

作用当所有路由都无法匹配时,跳转到 Redirect 指定的路由,一般写在所有路由注册的最下方

步骤

1.导入

js
import { Redirect } from "react-router-dom";

2.使用

jsx
<Switch>
  <Route path="/about" component="{About}" />
  <Route path="/home" component="{Home}" />
  <Route path="/404" component={NotFound} />
  <Redirect to="/404" />
</Switch>

6.路由传参

参数种类:query、params

传参方式

1.params传参:定义路由时【需要管】刷新【不丢失】

  • 定义路由时在路径后加需要的参数名
jsx
<Route path="/dyDetail/:id" component={Detail} />
  • 跳转和获取参数
js
// 跳转:
this.props.history.push({
    pathname:'/路径/6'
})    	

// 获取:
/this.props.match.params.参数名

2.state传参 :定义路由时【不用管】刷新【BrowserRouter不丢失、HashRouter丢失】

js
// 跳转:
this.props.history.push({
    pathname:'/路径',
    state:{参数名:值,...}
})  
    
// 获取:
this.props.location.state.参数名

2.query传参:定义路由时【不用管】刷新【丢失】

注:此query非比query地址栏是没有? 只是它叫query

js
// 跳转:
this.props.history.push({
    pathname:'/路径', 
    query:{参数名:值,...}
})
    
// 获取:
this.props.location.query.参数名

其他

this.props.history.go(-1/2数字)

7.嵌套路由

  • 注册子路由需写上父路由的 path
  • 路由的匹配是按照注册路由的顺序进行的

父组件

jsx
<MyNavLink to="/about">About</MyNavLink>
<MyNavLink to="/home">Home</MyNavLink>

<Switch>
  <Route path="/about" component="{About}" />
  <Route path="/home" component="{Home}" />
  <Redirect to="/about" />
</Switch>

子组件

jsx
<ul className="nav nav-tabs">
  <li>
    <MyNavLink to="/home/news">News</MyNavLink>
  </li>
  <li>
    <MyNavLink to="/home/message">Message</MyNavLink>
  </li>
</ul>

<Switch>
  <Route path="/home/news" component="{News}" />
  <Route path="/home/message" component="{Message}" />
  <Redirect to="/home/news" />
</Switch>

8.withRouter

作用:加工一般组件,让其拥有路由组件的 API ,如 this.props.history.push 等。

语法

1.从react-router-dom导入withRouter

2.最后导出组件时,使用调用withRouter()方法组件为实参

jsx
import React, {Component} from 'react'
import {withRouter} from 'react-router-dom'

class Header extends Component {
  ...
}

export default withRouter(Header)

9.登录鉴权

src/router/private.jsx

jsx
import { Route, Redirect } from "react-router-dom"

function PrivateRoute(props) {
 // 获取用户信息
 let token = localStorage.getItem('token')
 // 判断
 if (token) {
     return <Route {...props} />
 }

 return <Redirect to="/login" />
}

export default PrivateRoute

src/router/index.js

jsx
import {
    BrowserRouter as Router,
    Switch,
    Route,
    Redirect,
} from "react-router-dom";

import PrivateRoute from "./privateRoute";

import Login from "../pages/login";
import Welcome from "../pages/welcome";
import Users from "../pages/users";
import Orders from "../pages/orders";
import Layout from "../layout";
import Err404 from "../components/err/err404";
import Err500 from "../components/err/err500";

function ReactConfig() {
    return (
        <Router>
            <Switch>
                <Route path="/" exact component={Login} />
                <Route path="/login" component={Login} />
                {/* <Route path="/admin" exact component={Layout}/> */}
                <Route path="/admin">
                    <Layout>
                        <Switch>
                            {/* 
                 细节1:path一样 && exact 就好比是别名(注:v6有更好的解决方案)
                 <Route path="/admin/welcome" component={Welcome}/>

                 细节2:父有404  子得单独写404 (注:v6有更好的解决方案)
                 */}
                            <PrivateRoute path="/admin" exact component={Welcome} />
                            <PrivateRoute path="/admin/users" component={Users} />
                            <PrivateRoute path="/admin/orders" component={Orders} />
                            <Redirect to="/404" />
                        </Switch>
                    </Layout>
                </Route>
                <Route path="/404" component={Err404} />
                <Route path="/500" component={Err500} />
                <Redirect to="/404" />
            </Switch>
        </Router>
    );
}

export default ReactConfig;

10.路由组件懒加载

1.从react中导入lazySuspense

js
import { lazy, Suspense } from "react";

2.导入路由组件时用lazy()包裹

jsx
const Login = lazy(() => import("../pages/login"));

3.用<Suspense fallback={<div>Loading...</div>}> </Suspense>包裹定义的路由组件

  • fallbac里的内容是当数据没加载完毕时显示的loading组件
jsx
<Router>
    <Suspense fallback={<div>Loading...</div>}>
        <Switch>
            <Route path="/" exact component={Login} />
            <Redirect to="/404" />
        </Switch>
    </Suspense>
</Router>

2.路由v6

1.升级说明

  • 内置组件的变化:移除 <Switch/>,新增 <Routes/>并且不用再使用exact……
  • 语法变化:component={About} 变成 element={<About/>}……
  • 去掉<Redirect>
  • 新钩子useRoutes代替react-router-config
  • 大小减少:从20kb8kb
  • 新增 hook:useParamsuseNavigateuseMatch……

安装路由

bash
yarn add react-router-dom

2.基本语法

jsx
import {
  BrowserRouter as Router, // history模式

  HashRouter as Router, 	  // hash模式
  
  Routes, // 声明一组路由(注:router@6  将所有`<Switch>`元素升级为`<Routes>`)
  Route,  // 用于定义路由
  Link    // 用于跳转路由(声明式导航)
} from "react-router-dom";


<Router>
    <ul>
        <li><Link to="/dy">电影</Link></li>
        <li><Link to="/yy">影院</Link></li>
        <li><Link to="/th">特惠</Link></li>
        <li><Link to="/my">我的</Link></li>
    </ul>
    <hr />
    
    <Routes>
        <Route path="/" element={<Dy />} />
        <Route path="/dy" element={<Dy />} />
        <Route path="/yy" element={<Yy />} />
        <Route path="/th" element={<Th />} />
        <Route path="/my" element={<My />} />
    </Routes>
</Router>

3.重定向Navigate

v6移除了Redirect

改成如下方式重定向

jsx
<Routes>
    <Route path="/" element={<Dy />} />
    <Route path="/dy" element={<Dy />} />
    <Route path="/yy" element={<Yy />} />
    <Route path="/th" element={<Th />} />
    <Route path="/my" element={<My />} />
    <Route path='*' element={<Navigate to='/' />} />
</Routes>

4.路由传参

params传参:定义路由时【需要管】刷新【不丢失】

跳转:this.props.history.push({pathname:'/路径/6'})    	
获取:this.props.match.params.参数名

state传参 :定义路由时【不用管】刷新【BrowserRouter不丢失、HashRouter丢失】

跳转:this.props.history.push({pathname:'/路径', state:{参数名:值,...}})  
获取:this.props.location.state.参数名

query传参:定义路由时【不用管】刷新【丢失】

跳转:this.props.history.push({pathname:'/路径', query:{参数名:值,...}})
获取:this.props.location.query.参数名

其他

this.props.history.go(-1/2数字)
种类hooks名作用语法
params传参useParams获取params参数const {参数名, ..., } = useParams()
state传参useLocation获取state参数const location = useLocation()
search传参useSearchParams获取带?问号的参数const [search,setSearch] = useSearchParams()
search.get(参数名)
setSearch(参数名=值&..&参数名=值)
声明式导航<Link> 组件<Link to="路径/1"> <Link to="路径?id=1">
编程式导航useNavigateconst navigate = useNavigate()
navigate('路径')
navigate( "路径", { state:{} } )
navigate( "路径?id=6" )
navigate( 1或-1 )

5.嵌套路由

<Route>标签添加index属性,标识默认匹配

jsx
function ReactRouter() {
    return (
        <>
        <Router>
            <Routes>
                <Route path="/" element={<Login />} />
                <Route path="/login" element={<Login />} />
                <Route path="/admin" element={<Layout />}>
                    <Route index element={<Admin />} />
                    <Route path="users" element={<Users />} />
                    <Route path="orders" element={<Orders />} />
                </Route>
                <Route path="/404" element={<Err404 />}></Route>
                <Route path="/500" element={<Err500 />}></Route>
                <Route path="*" element={<Navigate to="/" />} />
            </Routes>
        </Router>
        </>
    );
}

6.useRoutes

集中式路由

src/index.js

jsx
import { createRoot } from "react-dom/client";

import App from "./App";
// 导入BrowserRouter
import { BrowserRouter as Router } from "react-router-dom";
const root = createRoot(document.querySelector("#root") as HTMLElement);


// Router包裹根组件
root.render(
  <Router>
    <App />
  </Router>
);

routes/index.js

和vue路由类似,嵌套用children

jsx
import Dy from '../pages/Dy'
import My from '../pages/My'
import Jb from '../pages/children/Jb'
import Jq from '../pages/children/Jq'

const routes = [
    {
        path: '/dy',
        element: <Dy />,
        children:[
            {
                path:'/dy/jb',
                element:<Jb/>
            },
            {
                path:'/dy/jq',
                element:<Jq/>
            },
        ]
    },
    {
        path: '/My',
        element: <My />,
    },
]

export default routes

App.js

  1. 引入routes
  2. 创建element
  3. 在路由需要显示的地方放{element}
jsx
import { NavLink, useRoutes} from "react-router-dom";
import routes from "./router";

const App = () => {
    const element = useRoutes(routes);
    return (
        <fieldset>
            <legend>App</legend>
            <ul>
                <li>
                    <NavLink to="dy">
                        电影
                        <ul>
                            <li><NavLink to="dy/jb">劲爆电影</NavLink></li>
                            <li><NavLink to="dy/jq">激情电影</NavLink></li>
                        </ul>
                    </NavLink>
                </li>
                <li>
                    <NavLink to="My">我的</NavLink>
                </li>
            </ul>
            {element}
        </fieldset>
    );
};

export default App;

src/pages/Dy.jsx

有嵌套路由时,在需要显示的地方放<Outlet/>

jsx
import { Outlet } from "react-router-dom";
const Dy = () => {
  return (
    <fieldset>
      <legend>电影区</legend>
      <Outlet />
    </fieldset>
  );
};

export default Dy;

MIT Licensed