ReactRouter基本使用 | 青训营笔记

133 阅读6分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 8 天

路由是前端框架必备的知识点,了解路由能帮我们更加轻松地管理代码,提高项目的可维护性。如今 ReactRouter 版本默认为6,这个笔记也讲解了5的使用,可以帮我们管理一些老项目。

React Router

1. V5

react router 可以将 url 地址和组件进行映射。当用户访问某个地址时,与其对应的组件会自动的挂载

安装: npm i react-router-dom@5 -S

1.1 HashRouter 和 BrowserRouter

HashRouter 会通过url地址中的hash值来对地址进行匹配

例如: http://127.0.0.1/#/about/me

HashRouter 对于 seo 不是很友好

BrowserRouter 直接通过url地址进行组件的跳转

使用过程中和普通的url地址没有区别

BrowserRouter 问题在于当我们通过 标签跳转时,刷新页面会重新向服务器请求数据,这时候页面会返回404,因为这次请求没有经过 react router。

解决方法:

  1. 使用 HashRouter,服务器不会判断 hash 值
  2. 修改服务器的配置(nginx),将所有请求都转发到 index.html

1.2 具体使用

main.jsx 中使用

  1. 引入 react-router-dom 包

    // 这里重命名是为了以后更换路由模式更加方便
    import { BrowerRouter as Router } from 'react-router-dom'
    
  2. 将 组件包裹

    <Router>
        <App />
    </Router>
    
  3. 最终代码

    // main.jsx
    import React from 'react'
    import ReactDOM from 'react-dom/client'
    import App from './App'
    import { BrowserRouter as Router } from 'react-router-dom'ReactDOM.createRoot(document.getElementById('root')).render(
      <React.StrictMode>
        <Router>
          <App />
        </Router>
      </React.StrictMode>,
    )
    

App.jsx其他组件 中使用

  1. 引入 Route 标签和组件

    import { Route } from 'react-router-dom'
    import About from './components/About'
    import Home from './components/home'
    import Menu from './components/Menu'
    import Student from './components/Student'
    
  2. 配置路由

    配置路由有很多方式,依次介绍

    2.1 component 方式:

    <Route exact path="/" component={Home} />
    <Route path="/about" component={About} />
    <Route path="/student/:id" component={Student} />
    
    属性作用
    path路由映射的 url 地址,其中上述例子中的 :id 为 uri 参数
    component路由要挂载的组件
    exact路径是否完全匹配,React Router5 默认不是完全匹配,例如指定 path=“/about”,访问 .../about/hello 也可以看到被挂载的组件

    这时在 Student组件 中就可以访问到路由参数了

    import React from 'react'
    const STU_DATA = [
      { id: 1, name: 'xj' },
      // ...
    ]
    ​
    const Student = (props) => {
      console.log(props) // 路由参数
      const stu = STU_DATA.find(item => item.id === +props.match.params.id)
      return (
        <div>
          {stu.id} --- {stu.name}
        </div>
      )
    }
    ​
    export default Student
    

    2.2 render 方式

    render 也可以用来指定要挂载的组件,不过需要一个回调函数作为参数,回调函数的返回值最终会被挂载

    注意: 如果希望被挂载的组件访问到路由参数,一定要指定组件的路由属性

    <Route path="/student/:id" render={(routeProps) => {
            console.log(routeProps)
            return <Student {...routeProps} /> {/* 一定要这样传递参数,不然 Student 组件无法访问路由参数 */}
          }}
    />
    

    2.3 children 方式

    children 也可以用来指定被挂载的组件,用法有两种:

    1. 和 render 类似,传递回调函数,注意:当children设置一个回调函数时组件无论路径是否匹配都会挂载,所以不建议
    2. 可以传递组件
    <Route path="/student/:id" children={(routeProps) => <Student {...routeProps} />} />
    <Route path="/student/:id" children={<Student />} />
    

    2.4 直接写组件

    <Route path="student/:id">
        <Student />
    </Route>
    

    这种方式更加清晰,但需要注意的是,Student组件是无法直接打印 props 来获取路由参数的,但是我们可以通过钩子函数,这也是更加常用的情况

    import React from 'react'
    import { useHistory, useLocation, useParams, useRouteMatch } from 'react-router-dom'
    const STU_DATA = [
      { id: 1, name: 'xj' },
      // ...
    ]
    const Student = (props) => {
      // 通过钩子函数获取路由参数
      const match = useRouteMatch()
      const location = useLocation()
      const history = useHistory()
      const params = useParams()
      const stu = STU_DATA.find(item => item.id === +params.id)
    ​
      return (
        <div>
          {stu.id} --- {stu.name}
        </div>
      )
    }
    ​
    export default Student
    
  3. 路由参数

    参数名作用
    match匹配的信息
    location路由地址信息
    history控制页面的跳转

    3.1 match 参数

    参数名类型含义
    isExactBoolean是否与指定路径完全匹配
    params对象路由传递的参数,例如 student/:id 中的 id 便是 params 的一个属性
    pathString指定路由的地址,例如 <Route path=“/student/:id” />,那么 path 就是 “/student/:id”
    urlString浏览器实际访问的路由地址,例如访问 http://127.0.0.1/student/3,那么 url 就是 “/student/3”

    3.2 location 参数

    参数名类型含义
    hash字符串hash地址
    pathname字符串与 match 的 path 参数一致
    search字符串查询字符串,即?之后的参数
    state用户指定类型使用 history.push({path: ‘/student/2’, state: {msg: ‘沈昕姐姐,嘿嘿’}}),手动跳转地址时,state会显示

    3.3 history 参数

    参数名类型作用
    pushFunction跳转页面,带后退前进功能
    replaceFunction也是跳转页面,不过没有后退前进功能

# Switch 标签

可以将 Route 统一放到 Switch 中,一个 Switch 中只会有一个路由显示

注意:根标签要加上 exact,不然只显示 Home 组件了

<Switch>
      <Route exact path="/" component={Home} />
      <Route path="/about">
        <About />
      </Route>
      <Route path="/login">
        <Login />
      </Route>
      
      <Route path="/student/:id">
        <Student />
      </Route>
</Switch>

# Redirect

Redirect 用于重定向,例如未成功登录强制跳转到登录页面,登陆成功跳转到内容页面

<Route path="/form">
      { isLogin ? <MyForm /> : <Redirect to={"/login"} /> }
</Route>

当然也可以指定 哪个路由跳转到哪个路由,例如下面指的是当访问路由地址为 /abc 时跳转到 /login,访问其他路由地址就不会跳转

<Redirect from={"/abc"} to={"/login"} />

# 路由嵌套

在 App.jsx 中使用

// App.jsx
import React from 'react'
import { Route } from 'react-router-dom'
import About from './components/About'
import Hello from './components/Hello'const App = () => {
  return (
    <div>
        <Route path="/about">
          <About />
          <Route path="/about/hello">
            <Hello />  
          </Route>
        </Route>
    </div>
  )
}
​
export default App

当然也可以直接在 About 组件中嵌套

import React from 'react'
import { Redirect, Route, useRouteMatch } from 'react-router-dom'
import Hello from './Hello'const About = (props) => {
  // 获取父路径
  const { path } = useRouteMatch()
  return (
    <div>
      <h2>关于我们</h2>
      <ul>
        <li>xj</li> <li>sx</li> <li>yq</li> <li>xm</li>
      </ul>
      {/* 子路由嵌套 */}
      <Route path={`${path}/hello`}>
        <Hello />
      </Route>
    </div>
  )
}
​
export default About

# 路径不匹配

如果用户随便输入了路径地址,此时要做一定的处理

<Route path="*">
    路径匹配错误
</Route>

4.1.3 总结

一般使用组件嵌套 + 钩子函数组合使用

// main.jsx
import React from 'react'
import { Route } from 'react-router-dom'
import About from './components/About'
// ...const App = () => {
    return (
        <div className="APP">
            <Menu />
            <Route path="/">
               <Home />
            </Route>
            <Route path="/about">
                <About />
            </Route>
            <Route path="/student/:id">
                <Student />
            </Route>
        </div>
    )
}
​
export default App
// student.jsx
import React from 'react'
import { useHistory, useLocation, useParams, useRouteMatch } from 'react-router-dom'
const STU_DATA = [
  { id: 1, name: 'xj' },
  // ...
]
const Student = () => {
  const match = useRouteMatch()
  const location = useLocation()
  const history = useHistory()
  const params = useParams()
  const stu = STU_DATA.find(item => item.id === +params.id)
  return (
    <div>
      {stu.id} --- {stu.name}
    </div>
  )
}
​
export default Student

2. V6

2.1 具体使用

React Router 6 更改的特性有很多,例如:

  1. <Route /> 组件必须由 <Routes /> 组件包裹
  2. 指定被挂载的组件需要使用 JSX 形式,如 element={<Xxx />}
  3. path 指定路径可以不用加 /
  4. 子路由也不用加上父路由根路径
// main.jsx
<Routes>
    <Route path="student/:id" element={<Student />}></Route>
    <Route path="about" element={<About />}>
        {/* Hello组件路由地址为: /about/hello */}
        <Route path="hello" element={<Hello />}></Route>
        {/* Abc组件路由地址为: /about/abc */}
        <Route path="abc" element={<Abc />}></Route>
    </Route>
</Routes>

组件中的使用,以 About 组件为例,只需要加入 <Outlet /> 组件,即可在此区域显示子路由内容

Outlet 用来标识路由中的组件:

  • 当嵌套路由中的路径匹配成功了,Outlet则表示嵌套路由中的组件
  • 当嵌套路由中的路径没有匹配成功,Outlet就什么都不会显示
import React from 'react'
import { Routes, Route, Outlet } from 'react-router-dom'
const About = () => {
  return (
    <div>
      <h2>这是About组件</h2>
      <Outlet />
    </div>
  )
}
​
export default About

当然也可以使用另一种方法

// main.jsx
<Routes>
    <Route path="about" element={<About />}></Route>
</Routes>

About 组件:

// component/About.jsx
import React from 'react'
import Hello from './Hello'
import { Routes, Route } from 'react-router-dom'
const About = () => {
  return (
    <div>
      {/* 通过子路由来对 Hello 进行映射, /about/hello  */}
      <Routes>
        <Route path="hello" element={<Hello />} />
      </Routes>
    </div>
  )
}
​
export default About

# Navigate 组件

Navigate 组件与 V5 版本的 Redirect 组件类似,用来指定重定向

Navigate 默认使用 push 跳转,但是可指定 replace 属性更换为 replace 跳转

<Navigate to="student/1" replace />

# Link 与 NavLink 组件

Link 组件与 V5 版本一致,NavLink 在指定链接样式的时候,需要一个回调函数

回调函数中有一个 isActive 布尔值形参,代表链接是否激活

<NavLink
    style={({ isActive }) => {
        return isActive ? { backgroundColor: 'red' } : null
    }}
    to="/home"
>主页</NavLink>