React 之 react-router-dom

安装

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

使用

全局路由模式

  • 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>
);

常用路由组件和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>

嵌套路由

  • Route 组件中包裹着多个 Route
<Routes>
  <Route path="user" element={<Users />}>
    <Route path=":id" element={<UserDetail />} />
    <Route path="create" element={<NewUser />} />
  </Route>
</Routes>

  • index 指定默认路由(多个子路由情况下)
<Routes>
  <Route path="/" element={<Layout />}>
    <Route index element={<About />} />
    <Route path="user" element={<User />} />
    <Route path="about" element={<About />} />
  </Route>
</Routes>

  • outlet 预留坑位渲染嵌套路由

路由通配符

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

获取参数 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>

集中路由渲染

需知:如要在父路由中显示嵌套路由中的子路由,需要引入 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>
);
  • 新建 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 };
  • 根组件 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;

懒加载(异步加载)

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

使用了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 };

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;

避免闪屏

  • 父路由使用直接加载的方式载入
  • 子路由使用懒加载的方式来载入
/* 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 };
/* /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;

路由监听(守卫)

  • 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;
  • 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 };
/* 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;
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值