react-router的基本用法

162 阅读5分钟

背景

由于react框架开发的项目是单页面应用,项目中的页面切换一般都是一个个组件之间来回切换,而这种组件之间的来回切换我们需要使用到路由来支持,所以react-router提供了这一解决方案,以下是react-router的安装方法以及使用方式:

安装

npm install react-router-dom -s

使用方式

在react-router-dom中有四种配置路由的方式,分别是BrowseRouter,MemeoryRouter、StaticRouter、HashRouter,一般我们基本使用的都是BrowseRouter,以下是BrowseRouter的基本用法。

路由简单配置

import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter, Routes, Route} from 'react-router-dom'

// A 页面
const PageA = () => {
    return (
        <div>
            <h1>PageA</h1>
        </div>
    )
}

// B 页面
const PageB = () => {
    return (
        <div>
            <h1>PageB</h1>
        </div>
    )
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <BrowserRouter>
    <Routes>
      <Route path="/pageA" element={<PageA />} />
      <Route path="/pageB" element={<PageB />} />
    </Routes>
  </BrowserRouter>
);

基于上面的配置,我们在浏览器中访问http://localhost:3000/pageA 即可切换到A页面,访问http://localhost:3000/pageA 即可切换到B页面。

Outlet实现通用Layout

一般在开发中,一个页面的框架基本都是头部和左侧菜单栏是固定的,右侧是根据路由切换后跟随变动的,基于这种布局样式,我们会创建一个通用的Layout组件来处理这种布局,而Outlet就是用来给这些变换的内容作占位的,下面是基本的实现方式

// Layout.jsx
// 这里只是简单的代码,一般开发中都会用到一些UI库,比如antd
import { Outlet } from 'react-router-dom'
const Layout = () => {
    return (
        <div>
            <div><h1>这里是固定的头部</h1></div>
            <div>
                <div>
                    这里是左侧菜单栏
                </div>
                <Outlet>
            </div>
        </div>
    )
}
// index.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter, Routes, Route} from 'react-router-dom'
import Layout from './Layout';

// A 页面
const PageA = () => {
    return (
        <div>
            <h1>PageA</h1>
        </div>
    )
}

// B 页面
const PageB = () => {
    return (
        <div>
            <h1>PageB</h1>
        </div>
    )
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <BrowserRouter>
    <Routes>
        <Route path="/" element={<Layout />}>
            <Route path="/pageA" element={<PageA />} />
            <Route path="/pageB" element={<PageB />} />
        </Route> 
    </Routes>
  </BrowserRouter>
);

通过浏览器方位根目录,即可访问到通用Layout,访问/pageA,会将/pageA下的内容展示在Layout中的Outlet占位组件中,同理访问/pageB,会将/pageB下的内容展示在Layout中的Outlet占位组件中。

路由切换以及传递数据

切换路由可以使用useNavigate钩子,得到一个navigate方法,通过调用navigate()来实现页面切换(组件切换更准确,因为是单页面应用,只有一个页面,所以路由切换都是组件之间的切换)。通过useLocation钩子获取到location对象,该对象里有个state对象里会接收到navigate方法里传递的参数来实现组件间的数据传递。

// Layout.jsx
// 这里只是简单的代码,一般开发中都会用到一些UI库,比如antd
import { Outlet, useNavigate } from 'react-router-dom'
const Layout = () => {

    const navigate = useNavigate();
    const handleNavigate = (path, params) => {
        navigate(path, {
            state: params
        })
    }

    return (
        <div>
            <div><h1>这里是固定的头部</h1></div>
            <div>
                <div>
                   <button onClick={() => handleNavigate('/pageA',{id: 'a'})}>切换到PageA</button>
                   <button onClick={() => handleNavigate('/pageB',{id: 'b'})}>切换到PageB</button>
                </div>
                <Outlet>
            </div>
        </div>
    )
}
// index.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter, Routes, Route, useLocation} from 'react-router-dom'
import Layout from './Layout';

// A 页面
const PageA = () => {
    // 通过location.state获取传递过来的数据
    const location = useLocation();
    console.log(location.state.id) // a
    return (
        <div>
            <h1>PageA</h1>
        </div>
    )
}

// B 页面
const PageB = () => {
    // 通过location.state获取传递过来的数据
    const location = useLocation();
    console.log(location.state.id) // b
    return (
        <div>
            <h1>PageB</h1>
        </div>
    )
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <BrowserRouter>
    <Routes>
        <Route path="/" element={<Layout />}>
            <Route path="/pageA" element={<PageA />} />
            <Route path="/pageB" element={<PageB />} />
        </Route> 
    </Routes>
  </BrowserRouter>
);

动态路由

一般情况下,我们会在url中拼接一些动态参数,这时候就需要动态路由来实现了,以下是动态路由的简单实现

// index.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter, Routes, Route, useParams} from 'react-router-dom'

// A 页面
const PageA = () => {
    // 通过useParams获取url上的动态参数
    // 地址栏输入:http://localhost:3000/pageA/a
    const params = useParams();
    console.log(params) // a
    return (
        <div>
            <h1>PageA</h1>
        </div>
    )
}

// B 页面
const PageB = () => {
    // 通过useParams获取url上的动态参数
    // 地址栏输入:http://localhost:3000/pageB/b/zhangsan
    const params = useLocation();
    console.log(params.id) // b
    console.log(params.name) // zhangsan
    return (
        <div>
            <h1>PageB</h1>
        </div>
    )
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <BrowserRouter>
    <Routes>
        <Route path="/pageA/:id" element={<PageA />} />
        <Route path="/pageB/id:/:name" element={<PageB />} />
    </Routes>
  </BrowserRouter>
);

查询参数获取

url上的?name=xxxx这种参数的获取可以通过useSearchParams来获取

// index.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter, Routes, Route, useSearchParams} from 'react-router-dom'

// A 页面
const PageA = () => {
    // 通过useSearchParams获取url上的动态参数
    // 地址栏输入:http://localhost:3000/pageA?name=zhangsan
    const [searchParams, setSearchParams] = useSearchParams();
    console.log(searchParams.get('name')) // zhangsan
    return (
        <div>
            <h1>PageA</h1>
        </div>
    )
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <BrowserRouter>
    <Routes>
        <Route path="/pageA" element={<PageA />} />
    </Routes>
  </BrowserRouter>
);

设置查询参数

可以通过useSearchParams()返回的列表中的第二项来设置url中的?name=xxx的值

// index.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter, Routes, Route, useSearchParams} from 'react-router-dom'

// A 页面
const PageA = () => {
    // 通过useSearchParams获取url上的动态参数
    // 地址栏输入:http://localhost:3000/pageA?name=zhangsan
    const [searchParams, setSearchParams] = useSearchParams();
    console.log(searchParams.get('name')) // zhangsan
    return (
        <div>
            <h1>PageA</h1>
            <button onClick={() => setSearchParamse({name: 'durant', age: 18})}>设置查询参数</button>
        </div>
    )
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <BrowserRouter>
    <Routes>
        <Route path="/pageA" element={<PageA />} />
    </Routes>
  </BrowserRouter>
);

上面的代码中通过点击按钮“设置查询参数”后,url上?后面的内容会变成name=durant&age=18

数据路由

在react-router-dom的6.4版本中推出了数据路由的概念,数据路由主要解决了react中存在的瀑布流问题。首先我们知道,在这之前我们在react项目中切换路由时,我们的项目一般会有以下顺序:

  1. 加载UI组件
  2. 如果存在分包,那么会第二步会异步加载js包
  3. 组件的生命周期执行,异步获取远程数据
  4. 完成页面展示

而且上面的顺序都是按照串行的方式执行的,所以如果期间某一个步骤比较耗时,那后面的就会处于等待的状态,这样有着很明显的用户体验问题以及性能问题,所以为了解决这一问题,react-router-dom在6.4的版本中推出了数据路由,下面我们看看数据路由在切换路由的时候是如何工作的:

  1. 加载UI组件,同时如果存在分包,会异步加载js包,并且会异步获取远程数据。
  2. 完成页面展示

可以看出,数据路由会将传统的路由中的1,2,3步骤并行值性,这样大大提高了用户体验以及性能。这也是react-router-dom在6.4版本中的革命性改动。