• React 之 react-router-dom


    安装

    # 从 5 开始就放弃了原有的 react-router 库,统一命名为 react-router-dom
    npm install react-router-dom
    
    • 1
    • 2

    使用

    全局路由模式

    • HashRouter URL 使用hash(#) 来创建路由
      • www.example.com/#/
    • BrowserRouter 采用真实的 URL 资源

    入口文件 index.js 中引入路由

    import React from 'react';
    import ReactDOM from 'react-dom/client';
    import './index.css';
    import App from './App';
    +import { BrowserRouter } from 'react-router-dom';
    
    
    const root = ReactDOM.createRoot(document.getElementById('root'));
    root.render(
      <React.StrictMode>
    +    <BrowserRouter>
          <App />
    +    </BrowserRouter>
      </React.StrictMode>
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    常用路由组件和hooks

    组件名作用说明
    一组路由代替原来 Switch,所有子路由都用基础的 Router children 来表示
    基础路由Router 可嵌套,解决了原有 v5 中严格模式
    导航组件在实际页面中跳转使用
    自适应渲染组件根据实际路由 url 去自动选择组件
    hooks名作用说明
    useParams返回当前参数根据路径读取参数
    useNavigate返回当前路由代替原有 v5 中的 useHistory
    useOutlet返回根据路由生成的 element
    useLocation返回当前 location 对象
    useRoutes同 Routers 组件一样,只不过是在 js 中使用
    useSearchParams用来匹配 URL 中 后面的搜索参数

    组件直接使用

    • Route 必须包裹在 Routes 里面使用(配置路由)
    • Link 相当于 a 标签,跳转对应页面
    <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
    </Routes>
    
    
    <Link to="/">home</Link>
    <Link to="/about">about</Link>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    嵌套路由

    • Route 组件中包裹着多个 Route
    <Routes>
      <Route path="user" element={<Users />}>
        <Route path=":id" element={<UserDetail />} />
        <Route path="create" element={<NewUser />} />
      </Route>
    </Routes>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • index 指定默认路由(多个子路由情况下)
    <Routes>
      <Route path="/" element={<Layout />}>
        <Route index element={<About />} />
        <Route path="user" element={<User />} />
        <Route path="about" element={<About />} />
      </Route>
    </Routes>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • outlet 预留坑位渲染嵌套路由

    路由通配符

    • * 只能在 / 后面使用,不可在实际路径中间
    /home
    /home/admin
    /users/:id
    /users/:id/messages
    /files/*
    /files/:id/*
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • NotFound 类路由,可以用 * 替代
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="dashboard" element={<Dashboard />} />
      <Route path="*" element={<NotFound />} />
    </Routes>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    获取参数 useParamsuseSearchParams

    • useParams() 直接返回 param 对象
    • useSearchParams() 返回一个 包含 searchParams 对象、setSearchParams 方法 的对象
      • 可使用 searchParams.get(key) 获得参数
      • setSearchParams({key: value}) 来改变路由

    useNavigate

    //js写法
    let navigate = useNavigate();
    function handleClick() {
        navigate("/home");
    }
    //组件写法
    function App() {
        return <Navigate to="/home" replace state={state} />;
    }
    //替代原有的go goBack和goForward
    <button onClick={() => navigate(-2)}>
        Go 2 pages back
    </button>
    <button onClick={() => navigate(-1)}>
        Go back
    </button>
    <button onClick={() => navigate(1)}>
    	Go forward
    </button>
    <button onClick={() => navigate(2)}>
    	Go 2 pages forward
    </button>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    集中路由渲染

    需知:如要在父路由中显示嵌套路由中的子路由,需要引入 react-router-dom 库中的 Outlet 组件

    • 入口文件 index.js 中引入 BrowserRouter ,启用全局路由
    // index.js
    import React from 'react';
    import ReactDOM from 'react-dom/client';
    import './index.css';
    import App from './App';
    import { BrowserRouter } from 'react-router-dom';
    
    
    const root = ReactDOM.createRoot(document.getElementById('root'));
    root.render(
      <React.StrictMode>
        <BrowserRouter>
          <App />
        </BrowserRouter>
      </React.StrictMode>
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 新建 src/router/index.js,配置项目路由
    • 引入 react 中的 lazy 方法,实现嵌套路由中的子组件的懒加载,减少首屏需要加载的数据量,避免首屏加载缓慢
    • 配合使用 Suspense 组件包裹在 lazy 方法返回值的 node,使用 fallback 属性,实现懒加载完毕后的消息提醒
    // index.js
    import { lazy, Suspense } from 'react';
    const CityList = lazy(() => import("../pages/CityList"));
    const AppLayout = lazy(() => import('../pages/AppLayout'));
    const Index = lazy(() => import("../pages/Index"));
    const News = lazy(() => import('../pages/News'));
    const lazyLoad = (children) => {
      return <Suspense fallback={<>Loading</>}>
        {children}
      </Suspense>;
    
    };
    const routes = [
      {
        path: '/',
        element: <AppLayout />,
        children: [
          {
            path: '/index',
            element: lazyLoad(<Index />)
          },
          {
            path: '/news',
            element: lazyLoad(<News />)
          }
        ]
      },
      {
        path: '/cityList',
        element: <CityList />
      },
    ];
    
    
    export { routes };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 根组件 App.js 中引入 编写好的 routes
    // App.js
    import './App.css';
    import { useRoutes } from 'react-router-dom';
    import { routes } from './router/index';
    function App() {
    
      return useRoutes(routes);
    }
    
    export default App;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    懒加载(异步加载)

    • 优化应用的启动速度,首屏加载的速度有明显提升(首屏不需要的组件,直到使用的那一刻才会加载)
    • 导致了闪屏(加载对应页面需要等待一段时间,网络质量越差,该现象越明显)

    使用了react库中的 lazy 配合 Suspense(本质是 promise)

    根组件 App.js 中使用 Suspense 组件包裹

    import { lazy, Suspense } from 'react';
    
    const AppLayout = lazy(() => import('../pages/AppLayout'));
    const Home = lazy(() => import('../pages/Home'));
    const CityList = lazy(() => import('../pages/CityList'));
    const News = lazy(() => import('../pages/News'));
    
    
    const routes = [
      {
        path: '/',
        element: <AppLayout />,
        children: [
          {
            index: true,
            element: <Home />
          },
          {
            path: 'news',
            element: <News />
          }
        ]
      },
      {
        path: '/citylist',
        element: <CityList />
      }
    ];
    
    
    export { routes };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    
    import './App.css';
    
    // UI组件库: antd-mobile
    import { Button } from 'antd-mobile';
    
    import { routes } from './router/index';
    import { useRoutes } from 'react-router-dom';
    import { Suspense } from 'react';
    
    
    function App() {
      let element = useRoutes(routes);
      return (
        <Suspense fallback={<>loading</>}>
          {element}
        </Suspense>
      );
    }
    
    export default App;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    避免闪屏

    • 父路由使用直接加载的方式载入
    • 子路由使用懒加载的方式来载入
    /* src/router/index.js */
    
    import { lazy, Suspense } from 'react';
    
    import AppLayout from '../pages/AppLayout';
    const Home = lazy(() => import('../pages/Home'));
    const CityList = lazy(() => import('../pages/CityList'));
    const News = lazy(() => import('../pages/News'));
    
    
    const lazyLoad = (children) => {
      return <Suspense fallback={<>Loading...</>}>
        {children}
      </Suspense>;
    };
    
    const routes = [
      {
        path: '/',
        element: <AppLayout />,
        children: [
          {
            index: true,
            element: lazyLoad(<Home />)
          },
          {
            path: 'news',
            element: lazyLoad(<News />)
          },
          {
            path: 'citylist',
            element: lazyLoad(<CityList />)
          },
        ]
      },
    ];
    
    
    export { routes };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    /* /src/App.js */
    
    import './App.css';
    
    // UI组件库: antd-mobile
    import { Button } from 'antd-mobile';
    
    import { routes } from './router/index';
    import { useRoutes } from 'react-router-dom';
    
    function App() {
      let element = useRoutes(routes);
      return element;
    }
    
    export default App;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    路由监听(守卫)

    • useEffect 该 Hook 类似生命周期函数 componentWillUnmountcomponentDidMountcomponentDidUpdate(可看做是三者的结合)
      • useEffect 支持你在函数组件(该概念区别于类组件)中执行副作用操作
        • 数据获取
        • 设置订阅
        • 手动更改 React 组件中的 DOM
      • 参数一:处理逻辑
      • 参数二:依赖的数据源['data']
      • 该 Hook 解决了 类组件中经常将不相关的逻辑放在同一个生命周期函数中,将相关的逻辑分离在多个不同的生命周期函数中 的这个问题
      • 通过使用多个 useEffect 来分离这些需要在各个生命周期函数中处理的逻辑
    
    import './App.css';
    
    // UI组件库: antd-mobile
    import { Button } from 'antd-mobile';
    
    import { routes } from './router/index';
    import { useRoutes, useLocation } from 'react-router-dom';
    import { Suspense, useEffect } from 'react';
    
    
    function App() {
      const location = useLocation();
      useEffect(() => {
    
        console.log(location.pathname, 'enter: 路由前置守卫');
    
        return () => {
          console.log(location.pathname, 'leave: 路由后置守卫');
        };
      }, [location.pathname]);
      let element = useRoutes(routes);
      return element;
    }
    
    export default App;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • useState 该 Hook ,通过数组解构可在 函数组件 中实现类似 setState() 的效果
      • const [count, setCount] = useState('banana')
      • 通过 setCount(new value) 来实现状态更新
    • 路由监听操作 封装成一个函数传递给 App.js 根组件
    /* /src/router/index.js  */
    
    import { lazy, Suspense } from 'react';
    import { matchRoutes } from 'react-router-dom';
    
    import AppLayout from '../pages/AppLayout';
    import NotFound from '../pages/NotFound';
    const Home = lazy(() => import('../pages/Home'));
    const CityList = lazy(() => import('../pages/CityList'));
    const News = lazy(() => import('../pages/News'));
    
    
    
    
    const lazyLoad = (children) => {
      return <Suspense fallback={<>Loading...</>}>
        {children}
      </Suspense>;
    };
    
    
    
    const routes = [
      {
        path: '/',
        element: <AppLayout />,
        children: [
          {
            index: true,
            element: lazyLoad(<Home />)
          },
          {
            path: 'news',
            element: lazyLoad(<News />)
          },
          {
            path: 'citylist',
            element: lazyLoad(<CityList />)
          },
        ]
      },
      {
        path: '/404',
        element: <NotFound />
      }
    ];
    
    const routerListener = (path, navigator) => {
      return () => {
    
        const isMatch = matchRoutes(routes, path);
        if (!isMatch) {
          navigator('/404');
        }
    
        console.log(path, 'enter: routerEnterEvent');
    
        return () => {
          console.log(path, 'leave: routerLeaveEvent');
        };
    
      };
    };
    
    
    export { routes, routerListener };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    /* App.js */
    
    
    import './App.css';
    
    // UI组件库: antd-mobile
    import { Button } from 'antd-mobile';
    
    import { routes, routerListener } from './router/index';
    import { useRoutes, useLocation, useNavigate } from 'react-router-dom';
    import { useEffect, useState } from 'react';
    
    
    function App() {
      const location = useLocation();
      const navigator = useNavigate();
      let [pathname, setPathname] = useState(location.pathname);
    
      useEffect(
        routerListener(pathname, navigator),
        [pathname]
      );
    
      let element = useRoutes(routes);
      return element;
    }
    
    export default App;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
  • 相关阅读:
    SSM学习42:SpringMVC入门案例(重点)
    python学习 数据分析模块pandas
    重磅:百度李彦宏、中科院曾毅入选,《时代周刊》AI最有影响力100人!
    基于CNN-LSTM的时序预测MATLAB实战
    第四代智能井盖传感器:智能井盖监测传感器怎么监测井盖位移
    【开源库推荐】go-linq 强大的语言集成查询库如,ORM一般丝滑处理内存数据
    域名生命周期是多久,有几个阶段?
    如何冻结模型,避免 model.train() 改变模型部分模块
    springboot-基础-eclipse配置+helloword示例
    Servlet的生命周期
  • 原文地址:https://blog.csdn.net/VOID_Pointer_G/article/details/126397372