跳至主要內容

8.8 React路由 🎉

刘春龙...大约 12 分钟RRACTWEB前端react

8.8 React路由 🎉

ReactRouter 路由,让用 react.js 构建单页面应用变得轻而易举

单页面应用也称为SPA(Single Page Application),它主要是网页的界面渲染在一个静态的页面上,当用户要从当前界面跳到另一个界面的时候,在这个过程中,不需要重新加载整个页面,所以页面之间的切换十分快速

多页面在进行页面切换时十分缓慢,在路由进行跳转的时候会加载所有的资源,而且页面重复代码多

对比之下,单页面原理是JavaScript动态修改内容而已,资源只需要局部的刷新,因此SPA具有极高的流畅度,有利于提升用户体验

页面与页面之前的切换,是通过不同的地址访问的,地址对应着要显示的组件。而显示这个页面切换显示的功能就是路由的作用

单页面VS多页面

单页面(SPA)多页面(MPA)
组成外壳页面和多个页面片段多个完整页面构成
资源共用共用、只需在外壳部分加载不共用,每个页面都需要加载
刷新方式页面局部刷新或更改整页刷新
URL模式a.com/#/oneopen in new window 或者 a.com/oneopen in new windowa.com/one.htmlopen in new window
用户体验页面切换快,体验好页面切换慢,体验差
数据传递容易(路由或者组件之间传递)依赖URL传参、本地存储传参
SEO不利于SEO(后续有其他解决方案)利于SEO
维护成本相对容易相对复杂

React Router 是一个基于 React之上的强大路由库,它可以让你向应用中快速地添加视图和数据流,同时保持页面与 URL 间的同步

我们的React应用是单页面应用(一般只有一个html文件),路由可以让我们实现页面内容的切换。

  • react-router

    为 React Router 提供核心路由功能,但是你不需要直接安装 react-router

  • react-router-dom

    react-router-dom是基于react-router的,适用于浏览器端应用,react-router-dom包含react-router的所有功能,并且又增加了适合在浏览器运行环境下的一些功能

初步体验 💎

  • 安装react-router-dom
npm install react-router-dom --save

提示

如果你写 React Native 应用,你应该安装 react-router-native而不是 react-router-dom; 当你安装 react-router-dom react-router-native 时,都会将 react-router 作为依赖安装。

先用一个示例来演示一下,下面是我的目录结构

import React from 'react';
import { Routes, Route } from "react-router-dom"
const App = React.lazy(() => import("../App"))
const Home = React.lazy(() => import("../views/home/home"))
const News = React.lazy(() => import("../views/news/news"))
const About = React.lazy(() => import("../views/about/about"))
const About1 = React.lazy(() => import("../views/about/about1/about1"))
const About2 = React.lazy(() => import("../views/about/about2/about2"))
const Error = React.lazy(() => import("../views/error/error"))
const Routers: any = () => {
    return (
        <Routes>
            <Route path='/' element={<App />} >
                <Route index element={<Home />} />
                <Route path='home' index element={<Home />} />
                <Route path='news' element={<News />} />
                <Route path='about' element={<About />} >
                    <Route index element={<About1 />} />
                    <Route path='about1' index element={<About1 />} />
                    <Route path='about2' element={<About2 />} />
                </Route>
            </Route>
            <Route path='*' element={<Error />} />
        </Routes>
    )
}
export default Routers

BrowserRouter和HashRouter 💎

<BrowserRouter>使用HTML5 History API保持页面 和 URL 的同步,使用干净的URL将当前位置存储在浏览器的地址栏中。

<HashRouter>使用 Hash 模式路由保持页面 和 URL 的同步。

BrowserRouter与HashRouter区别

import React from 'react';
import ReactDOM from 'react-dom/client';
//antd国际化处理
import zhCN from 'antd/locale/zh_CN';
import { ConfigProvider } from 'antd'
import 'antd/dist/reset.css';
import "./assets/scss/public.scss"
//路由
import Routers from "./router/router"
import { BrowserRouter } from "react-router-dom"
import { Suspense } from 'react'

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);

root.render(
  <React.StrictMode>
    <ConfigProvider locale={zhCN} theme={{ token: { colorPrimary: "#00b96b" } }}>
      <Suspense fallback={<div>页面跳转中...</div>}>
        <BrowserRouter>
          <Routers />
        </BrowserRouter>
      </Suspense>
    </ConfigProvider>
  </React.StrictMode >
);








 
 
 








 
 
 
 
 



Route和Routes 💎

  • <Route>用来配置URL与UI,如果其路径与当前地址栏中URL匹配,则会呈现其元素。

属性

  • path:设置访问路径
  • element:设置对应URL要渲染的组件
  • caseSensitive:设置路径是否区分大小写,默认为false
  • index:确定路由是否为索引路由
  • <Routes>包裹一组<Route>,每当地址发生变化时,<Routes>都会查看其所有子<Route>元素,以找到最佳路径匹配并呈现对应的UI

属性

  • path:设置访问路径
  • element:设置对应URL要渲染的组件
import React from 'react';
import { Routes, Route } from "react-router-dom"
import App from "../App"
import Home from "../views/home/home"
import News from "../views/news/news"
import About from "../views/about/about"
import About1 from "../views/about/about1/about1"
import About2 from "../views/about/about2/about2"
import Error from "../views/error/error"
const Routers = () => {
    return (
        <Routes>
            <Route path='/' element={<App />} >
                <Route index element={<Home />} />
                <Route path='home' index element={<Home />} />
                <Route path='news' element={<News />} />
                <Route path='about' element={<About />} >
                    <Route index element={<About1 />} />
                    <Route path='about1' index element={<About1 />} />
                    <Route path='about2' element={<About2 />} />
                </Route>
            </Route>
            <Route path='*' element={<Error />} />
        </Routes>
    )
}
export default Routers











 




 
 
 
 
 


 



Outlet 💎

父路由元素中应使用<Outlet>来渲染其子路由元素。这允许在呈现子路由时显示嵌套UI。

如果URL跟父路由的路径完全匹配,则会默认渲染设置了index属性的子路由,如果子路由中没有设置index的,则不渲染任何子路由。

import React from 'react';
import { Routes, Route } from "react-router-dom"
import App from "../App"
import Home from "../views/home/home"
import News from "../views/news/news"
import About from "../views/about/about"
import About1 from "../views/about/about1/about1"
import About2 from "../views/about/about2/about2"
import Error from "../views/error/error"
const Routers = () => {
    return (
        <Routes>
            <Route path='/' element={<App />} >
                <Route index element={<Home />} />
                <Route path='home' index element={<Home />} />
                <Route path='news' element={<News />} />
                <Route path='about' element={<About />} >
                    <Route index element={<About1 />} />
                    <Route path='about1' index element={<About1 />} />
                    <Route path='about2' element={<About2 />} />
                </Route>
            </Route>
            <Route path='*' element={<Error />} />
        </Routes>
    )
}
export default Routers
















 
 
 
 
 






<Link>用户可以通过点击它来导航跳转到另一个页面。

属性

  • to :设置点击后要跳转的页面地址
    • string:to的值为字符串,代表要跳转的路径
    • object: to的值为对象,该对象可以包含下列属性:
      • pathname: <string> 表示要链接到的路径
      • search: <string> 查询参数
      • hash: 一个放在URL中的Hash,例如 #a-hash.
  • reloadDocument:跳过路由,则直接实现<a href/>的功能(刷新浏览器)
  • state:属性可用于为存储在 history state 中的新位置设置有状态值。随后可以通过 useLocation() 访问该值

<NavLink>是一个特殊的<Link>,它会知道当前被选中的项是哪个。被选中选会添加.active class。

属性

  • end:精准匹配,使用<NavLink>进行跳转时,默认会给选中的元素添加active的类属性,但是,当点击/about时,首页也会高亮,因为首页的路由地址为/,而/about字符中开头字符也是/,这就造成了模糊匹配,影响精确度。解决办法:开启精准匹配 end
    <NavLink to="/" end>home</NavLink>
    <NavLink to="/about" >about1</NavLink>
    <NavLink to="/login">login</NavLink>
    
  • className:修改默认的class名字.active

增加公共样式

.active{
    color:red;
}

点击跳转,查看Link和NavLink的区别

import React from 'react'
import { Outlet } from 'react-router-dom'
import { Link, NavLink } from 'react-router-dom'
const App = () => {
    return (
        <>
            <h1>Header</h1>
            <div>
                <Link to="/about/about1">about1页</Link>
                <Link to={{ pathname: '/about/about2' }} >about2页</Link>
                <br />
                <NavLink to="/about/about1">about1页</NavLink>
                <NavLink to={{ pathname: '/about/about2' }}>about2页</NavLink>
                <NavLink to={{ pathname: '/home' }}>home页</NavLink>
                <NavLink to={{ pathname: '/news' }}>news页</NavLink>
            </div>
            <Outlet></Outlet>
            <h1>Footer</h1>
        </>
    )
}
export default App

当渲染一个 <Navigate> 元素的时候,就会执行它指向的路由跳转(重定向)

一般逻辑发生变化后,想要跳转到其他页面(非手动点击方式)使用。

import { Navigate } from 'react-router-dom'

const [isLogin, setIsLogin] = useState(false)
// 如果输入了用户名跟密码。则登录成功,跳转到首页
if (nameRef.current.value && pwdRef.current.value) {
    setIsLogin(true)
} 
return (
    <div>
      <h2>登录</h2>
      <form onSubmit={onLogin}>
         	用户名:<input ref={nameRef} required/>
         	密码:<input type='password' ref={pwdRef} required/>
        	<button type='submit'>登录</button>
      </form>
       {/* isLogin为ture,则跳转到首页 */}
      {isLogin && <Navigate to='/' />} 
    </div>
)
import { Navigate } from 'react-router-dom'

<Router>
    <Routes>
        <Route path='/login' element={<Login />}></Route>
        <Route path='/' element={<Home />}></Route>
        <Route path='/home' element={<Navigate to="/" />}></Route>
     </Routes>
</Router>

路由传参 💎

params传参 👻

在要跳转的路由路径中使用:params这种语法传递参数,然后在跳转后的页面中使用useParams读取传递过来的参数

路由中定义参数名称

const Routers = () => {
    return (
        <Router>
            <Routes>
                <Route path='/' element={<Home />} />
                <Route path='/news/:city' element={<News />} />
                {/* 设置参数的key值,这里意味着我就可以传递一个名字为city的参数了 */}
                <Route path='/about' element={<About />} >
                    <Route path='about1' element={<About1 />} />
                    <Route path='about2' element={<About2 />} />
                </Route>
            </Routes>
        </Router>
    )
}





 









页面跳转时传递参数值

import React from 'react'
import { NavLink } from 'react-router-dom'
export default function Home() {
    return (
        <div>
            <NavLink to="/news/beijing">用户页面</NavLink>
        </div>
    )
}





 



跳转到的页面接收参数值

import React from 'react'
import { useParams } from 'react-router-dom'
export default function News() {
    const params = useParams()
    console.log(params)
    return (
        <div>News</div>
    )
}

 

 
 




search传参 👻

如果to接收一个对象,则可以通过对象的属性search传递参数。

search只支持传递字符串

页面跳转时传递参数值

import React from 'react'
import { NavLink } from 'react-router-dom'
export default function Home() {
    return (
        <div>
            <NavLink to={{ pathname: '/news', search: `city=beijing` }}  >用户页面</NavLink>
            {/* 或者 */}
            <NavLink to={{ pathname: '/news?name=beijing' }}  >用户页面</NavLink>
            {/* 或者 */}
            <NavLink to= '/news?name=beijing'>用户页面</NavLink>
        </div>
    )
}

跳转到的页面接收参数值

import React from 'react'
import { useSearchParams } from 'react-router-dom'
export default function News() {
    const [searchParams, setSearchParams] = useSearchParams()
    console.log(searchParams.get("city"))
    return (
        <div>News</div>
    )
}

reateSearchParams使用

params传递参数传递参数需要在路由中提前定义字段,search传递参数只能传递字符串,那么我想传递复杂数据,就可以使用createSearchParams传递参数

使用createSearchParams支持传递对象或者数组

import React from 'react'
import { NavLink, createSearchParams } from 'react-router-dom'
export default function Home() {
    const arr: any = [1, 2, 3, "lcl"]
    const obj: any = {
        name: "lcl",
        age: 30
    }
    return (
        <div>
            <NavLink to={{ pathname: '/news', search: `${createSearchParams({ value1: JSON.stringify(arr), value2: JSON.stringify(obj) })}` }}  >用户页面</NavLink>
        </div>
    )
}

跳转到的页面接收参数值

import React from 'react'
import { useSearchParams } from 'react-router-dom'
export default function News() {
    const [searchParams, setSearchParams] = useSearchParams()
    const arr = JSON.parse(searchParams.get("value1") as any)
    const obj = JSON.parse(searchParams.get("value2") as any)
    console.log(arr)
    console.log(obj)
    return (
        <div>News</div>
    )
}

state传参 👻

使用上述三种方式传递的参数都会显示在地址栏,那么如何不让他显示进行参数传递呢?

通过state属性传递参数

使用useLocation来接收state传递的参数,useLocation会返回一个location对象,只要浏览器地址发生改变,则这个location就会随着更新。

通过state属性传递参数

import React from 'react'
import { NavLink } from 'react-router-dom'
export default function Home() {
    return (
        <div>
            <NavLink to='/news' state={{ city: 'beijing', arr: [1, 2, 3] }}>用户页面</NavLink>
        </div>
    )
}

跳转到的页面接收参数值

import React from 'react'
import { useLocation } from 'react-router-dom'
export default function News() {
    const location = useLocation()
    console.log(location.state)//访问location的state属性即可
    return (
        <div>News</div>
    )
}

编程式导航及传参 💎

这个hook,这个函数可以让我们进行编程式路由跳转

import React from 'react'
import { useNavigate } from 'react-router-dom'
export default function Home() {
    const navigate = useNavigate()
    const jump = () => {
        navigate("/news")   //不传递参数
        //navigate("/news/beijing")   //params传参(需要提前在路由中定义)
        //navigate({ pathname: '/news', search: `city=beijing` })  //search传参
        //navigate('/news', { state: { city: 'beijing' } })  //state传参
    }
    return (
        <div>
            <button onClick={jump}>跳转到News</button>
        </div>
    )
}

至于这三种方式传参的接收方式,上个章节已经提到,这里不再做过多说明

路由懒加载 💎

路由懒加载是非常有必要的,当项目过于庞大,一次性加载太多页面容易造成卡顿,此时懒加载页面就可以改变这一问题!

import React from 'react';
import { Routes, Route } from "react-router-dom"
const App = React.lazy(() => import("../App"))
const Home = React.lazy(() => import("../views/home/home"))
const News = React.lazy(() => import("../views/news/news"))
const About = React.lazy(() => import("../views/about/about"))
const About1 = React.lazy(() => import("../views/about/about1/about1"))
const About2 = React.lazy(() => import("../views/about/about2/about2"))
const Error = React.lazy(() => import("../views/error/error"))
const Routers: any = () => {
    return (
        <Routes>
            <Route path='/' element={<App />} >
                <Route index element={<Home />} />
                <Route path='home' index element={<Home />} />
                <Route path='news' element={<News />} />
                <Route path='about' element={<About />} >
                    <Route index element={<About1 />} />
                    <Route path='about1' index element={<About1 />} />
                    <Route path='about2' element={<About2 />} />
                </Route>
            </Route>
            <Route path='*' element={<Error />} />
        </Routes>
    )
}
export default Routers


 
 
 
 
 
 
 


















要注意引入方式的变化

import Header from './components/header';

更改为

const About = React.lazy(() => import("./components/header"))

除了引入方式更改外,还需使用Suspense进行包裹

import React from 'react';
import ReactDOM from 'react-dom/client';
//antd国际化处理
import zhCN from 'antd/locale/zh_CN';
import { ConfigProvider } from 'antd'
import 'antd/dist/reset.css';
import "./assets/scss/public.scss"
//路由
import Routers from "./router/router"
import { BrowserRouter } from "react-router-dom"
import { Suspense } from 'react'

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);

root.render(
  <React.StrictMode>
    <ConfigProvider locale={zhCN} theme={{ token: { colorPrimary: "#00b96b" } }}>
      <Suspense fallback={<div>页面跳转中...</div>}>
        <BrowserRouter>
          <Routers />
        </BrowserRouter>
      </Suspense>
    </ConfigProvider>
  </React.StrictMode >
);










 








 



 



错误匹配 💎

import React from 'react';
import { Routes, Route } from "react-router-dom"
const App = React.lazy(() => import("../App"))
const Home = React.lazy(() => import("../views/home/home"))
const News = React.lazy(() => import("../views/news/news"))
const About = React.lazy(() => import("../views/about/about"))
const About1 = React.lazy(() => import("../views/about/about1/about1"))
const About2 = React.lazy(() => import("../views/about/about2/about2"))
const Error = React.lazy(() => import("../views/error/error"))
const Routers: any = () => {
    return (
        <Routes>
            <Route path='/' element={<App />} >
                <Route index element={<Home />} />
                <Route path='home' index element={<Home />} />
                <Route path='news' element={<News />} />
                <Route path='about' element={<About />} >
                    <Route index element={<About1 />} />
                    <Route path='about1' index element={<About1 />} />
                    <Route path='about2' element={<About2 />} />
                </Route>
            </Route>
            <Route path='*' element={<Error />} />
        </Routes>
    )
}
export default Routers






















 




注意

定义错误配置的路由必须放到最后面

使用useRoutes 💎

useRoutes<Routes>有同等的功能,只不过它使用对象来替代<Route>

下面我用HooKs来改造路由(目录结构仍旧一致)

import React from 'react';
import { useRoutes } from "react-router-dom"
const App = React.lazy(() => import("../App"))
const Home = React.lazy(() => import("../views/home/home"))
const News = React.lazy(() => import("../views/news/news"))
const About = React.lazy(() => import("../views/about/about"))
const About1 = React.lazy(() => import("../views/about/about1/about1"))
const About2 = React.lazy(() => import("../views/about/about2/about2"))
const Error = React.lazy(() => import("../views/error/error"))
const Routers = () => {
    return (useRoutes([
        {
            path: '/',
            element: <App />,
            children: [
                {
                    index: true,
                    element: <Home />
                },
                {
                    path: 'home',
                    element: <Home />,
                },
                {
                    path: 'news',
                    element: <News />
                },
                {
                    path: 'about',
                    element: <About />,
                    children: [
                        {
                            index: true,
                            element: <About1 />
                        },
                        {
                            path: 'about1',
                            element: <About1 />
                        },
                        {
                            path: 'about2',
                            element: <About2 />
                        },
                    ]
                },
            ]
        },
        {
            path: '*',
            element: <Error />
        },
    ]))
}
export default Routers
上次编辑于:
贡献者: 刘春龙
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.7