百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

react-router-dom v6实践相关问题

zzlvtu 2024-09-04 23:02 11 浏览

本文记录下在使用 react-router-dom v6时遇到的一些问题。

react-router-dom 的 v6 版本,做了一些修改

  1. 引入了 Hooks API,使得在函数组件中更方便地使用路由功能。比如 useNavigateuseParamsuseSearchParamsuseLocation
  2. 引入了 Routes 组件来替代 v5 中的 Switch 组件,可以更灵活地定义路由匹配规则。
  3. Route 组件 element 属性替代 v5 中的 component 属性,用于指定匹配路由时要渲染的元素。
  4. 引入了 navigate 方法取代了 v5 中的 history,,用于在函数组件中进行编程式导航。
  5. 不再直接支持 query 参数,而是鼓励使用URL搜索参数(search params)来处理查询参数。

常用 Hooks API

useNavigate

做页面跳转等操作用的,替代 v5 的 history。不再支持 query 方式

const navigate = useNavigate();
// 跳转到某个页面
navigate('/Demo');
// 跳转到某个页面并携带参数
navigate('/Demo', {
  state: {
    name: 'wmm66',
    age: 18,
  }
});
// 替换某个页面
navigate('/Demo', {
  replace: true,
  state: {
    name: 'wmm66',
  }
});
// 页面回退
navigate(-1);

useLocation

页面中获取 location 参数用的。

注意:这种方式获取的 location 和 window.location 并不一样

useParams

获取 params 参数用的。

在路由路径中定义动态参数,示例如下

<Route path="/user/:id" element={<User />} />

在 User 组件中使用 useParams 就可以获取到 id 参数的值。

useSearchParams

这是 v6 新加的,获取和设置 searchParams 参数用的。

const [searchParams, setSearchParams] = useSearchParams();
const query = searchParams.get('query');
setSearchParams({ query: 'react' });

实际上是获取的 hash 中 ?后面的参数。比如页面路径是 http://localhost:5173/#/Demo/About?name=wmm66&age=18

我们就可以用 useSearchParams 来获取

const [searchParams] = useSearchParams();
console.log(searchParams.get('name'));

路由自动遍历配置

我们以在 Vite 中为例。Vite 中有 import.meta.glob 方法可以遍历获取某个目录下的文件。我们可以借助此方法实现。

import { lazy, Suspense, Fragment } from 'react';
import { HashRouter, Route, Routes } from 'react-router-dom';
import PageLoading from '@/components/PageLoading';
import PageNotFound from '@/components/PageNotFound';

// 遍历 pages 目录下的所有 tsx 文件
const pages = import.meta.glob('../pages/**/*.ts?(x)');

const routes = Object.keys(pages).reduce((ret: any[], path: string) => {
  // 去掉 尾部的 .tsx 和 前面的 ../pages
  const arr = path.replace(/.tsx?$/, '').split('/').slice(2);
  // 没有,直接return
  if (arr.length === 0) return ret;

  // 过滤目录
  const arrDir = arr.slice(0, - 1);
  const excludeDirs = ['hooks', 'components'];
  for (let i = 0; i < excludeDirs.length; i++) {
    if (arrDir.includes(excludeDirs[i])) return ret;
  }

  // 过滤文件 && hook
  const fileName = arr[arr.length - 1];
  const excludeFiles = ['model'];
  if (excludeFiles.includes(fileName)) return ret;
  if (fileName.slice(0, 3) === 'use') return ret;

  // 对应的组件
  const Component = lazy(pages[path] as any);

  // 组合成 path
  const routePath = arr.reduce((ret, item) => {
    return `${ret}/${item}`;
  }, '');

  // index 做下 兼容
  if (arr[arr.length - 1] === 'index') {
    ret.push({
      path: routePath.slice(0, -6) || '/',
      Component,
    });
  }
  ret.push({
    path: routePath,
    Component,
  });
  return ret;
}, []);

export default function Router() {
  return (
    <HashRouter>
      <Suspense fallback={<PageLoading />}>
        <Routes>
          {routes.map((route) => {
            const { path, Component } = route ?? {};
            return (
              <Fragment key={path}>
                {!!path && !!Component && (
                  <Route
                    key={path}
                    path={path}
                    element={<Component />}
                  />
                )}
              </Fragment>
            );
          })}
          <Route path='*' element={<PageNotFound />} />
        </Routes>
      </Suspense>
    </HashRouter>
  );
}

路由跳转

路由跳转我们一般使用 useNavigate 跳转,方式如下

import { useNavigate } from 'react-router-dom';
// ...
export default function DemoPage() {
  const navigate = useNavigate();
  // ...
  const toAboutPage = () => {
    navigate('/Demo/About', {
      state: {
        name: 'wmm66',
        age: 18,
      },
    });
  }
  // ...
}

在目标页面使用 useLocation 获取传递的参数

import { useLocation } from 'react-router-dom';

const About = () => {
  const location = useLocation();
  console.warn('location: ', location, location.state);
  return <div>demo About Page</div>;
};

export default About;

这种方式有个缺点是:在目标页面的URL中并没有看到传递的参数

比如我们项目的某个页面访问时需要携带参数。就没法使用 url 直接打开。

我们这里采用另外一种方式(searchParams方式)做页面跳转。

import { useNavigate } from 'react-router-dom';
// ...
export default function DemoPage() {
  const navigate = useNavigate();
  // ...
  const toAboutPage = () => {
    navigate('/Demo/About?name=wmm66&age=18');
  }
  // ...
}

我们写一个方法包装一下

// utils/navigate.ts
import { NavigateFunction } from 'react-router-dom';

type CombineUrlQueryParams = Record<string, string | number>;
export function combineUrlQuery(url: string, params?: CombineUrlQueryParams) {
  if (!params) return url;

  const queryStr = Object.keys(params).map((key: string) => {
    return `${key}=${params[key]}`;
  }).join('&');
  if (!queryStr) return url;

  return `${url}?${queryStr}`;
}

// 页面跳转
export function navigateTo(navigate: NavigateFunction) {
  return function(url: string, params?: CombineUrlQueryParams) {
    navigate(combineUrlQuery(url, params));
  }
}

// demo页面中使用
import { useNavigate } from 'react-router-dom';
import { navigateTo } from '@/utils/navigate';
// ...
export default function DemoPage() {
  const navigate = useNavigate();
  // ...
  const toAboutPage = () => {
  //   navigate('/Demo/About?name=wmm66&age=18');
	  navigateTo(navigate)('/Demo/About', {
      name: 'wmm66',
      age: 18,
    });
  }
  // ...
}

在目标页面中,我们使用 useSearchParams 的方式获取,这里也同样包装下。

可以采用上面类似的科里化的方式包装,这里直接写个Hook 吧

// hooks/useSearchQuery.ts
import { useSearchParams } from 'react-router-dom';

export default function useSearchQuery() {
  const [searchParams] = useSearchParams();
  const res: Record<string, any> = {};
  searchParams.forEach((value: string, key: string) => {
    if (typeof res[key] !== 'undefined') {
      res[key] += ',' + value;
    } else {
      res[key] = value;
    }
  });
  return res;
}

// 目标页面中使用
import useSearchQuery from '@/hooks/useSearchQuery';

const About = () => {
  const query = useSearchQuery();
  console.warn('query', query);
  return <div>demo About Page</div>;
};

export default About;

当然我们也可以不用 useSearchParams,自己写一个方法获取。

// utils/index.ts
export const objectUrlQuery = (_str?: string) => {
  const hash = _str || window.location.hash;
  const search = hash.split('?')[1];
  if (!search) return;

  const query: any = {};
  search.replace(/\\b([^&=]*)=([^&]*)/g, (m, key, value): any => {
    if (typeof query[key] !== 'undefined') {
      query[key] += ',' + decodeURIComponent(value);
    } else {
      query[key] = decodeURIComponent(value);
    }
  });
  return query;
};

// 目标页面中使用
import { objectUrlQuery } from '@/utils';

const About = () => {
  const query = objectUrlQuery();
  console.warn('query', query);
  return <div>demo About Page</div>;
};

export default About;

相关推荐

什么是DPDK?DPDK的原理及学习学习路线总结

一、什么是DPDK  对于用户来说,它可能是一个性能出色的包数据处理加速软件库;对于开发者来说,它可能是一个实践包处理新想法的创新工场;对于性能调优者来说,它可能又是一个绝佳的成果分享平台。 ...

每天进步一点:两分钟解决kvm下windows虚拟机鼠标不跟随

跟随昨天文章做测试的朋友们应该和我一样遇到了vnc连接windows鼠标不跟随的问题,经过一番查找有两种解决办法:1.编辑配置文件命令virshedittest或者直接vi/etc/libvir...

PC虚拟化主流:KVM、XEN、OpenVZ详解

目前,PC的虚拟化逐渐成为互联网发展的大趋势,我们知道,KVM、XEN、OpenVZ是虚拟化的三种方式,今天我们就来探讨这三种虚拟化的优势和劣势。1、pc虚拟化——KVMKVM是完整的硬件虚拟化,可在...

Windows上使用QEMU创建aarch64(ARM64)虚拟机

前言随着国产化的推进,现在采用ARM、MIPS的机器越来越多,作为开发、运维人员要调测软件总不能每种架构的机器都去买一台吧?主要像博主这样的穷B,实在也是承受不起。。需要的工具...

高度致敬Windows!开源优麒麟20.04 LTS发布:支持5年

优麒麟团队宣布,优麒麟(UbuntuKylin)开源操作系统20.04LTS正式版已经发布,代号FocalFossa,全球同步发布的还有Ubuntu20.04、Lubuntu20.04、Xub...

极空间虚拟机上线了,一学就会!小白保姆级使用教程

友情提示本文涉及内容较多,篇幅在4500字左右,为了对小白用户更加友好,图片示例多达60张。整个文章部分为三个阶段,准备-初探-实战。其中实战部分包含Windows系统,ikuai软路由系统,iSto...

Windows Subsystem for Linux现以应用形式上架Microsoft Store

微软今天宣布WindowsSubsystemforLinux(WSL)作为一款应用上架Windows11端的MicrosoftStore。也就是说,现在WSL以应用的方式通过...

Windows Server 2019 Core 虚拟机系统镜像制作

WindowsServer2019Core简介WindowsServer2019是微软于2018年11月13日发布的新一代WindowsServer服务器操作系统,基于Win10180...

微软商店中的WSL预览版现已可用!Windows 11用户狂喜

...

在NAS上安装Win10,24小时待命的云电脑达成√

#头条创作挑战赛#引子...

免费开源虚拟机VirtualBox 7.0.12发布:修复TPM和黑屏问题

IT之家10月18日消息,甲骨文近日发布了VirtualBox7.0.12维护版本更新,重点修复此前版本中用户反馈和官方发现的BUG,改善了对LinuxKernel6.4/6.5...

KVM Cloud 虚拟机管理系统安装部署

KVMCloud介绍KVMCloud是一款基于KVM实现的适用于小微企业的虚拟机管理系统,支持如下功能:基于KVM的VM基础功能(创建、启动、停止、重装、webVNC等功能)使用NFS作为磁盘...

个人KVM 虚拟化学习笔记(kvm虚拟化管理平台)

一、KVM原理二、KVM基础功能2.1CPU2.2内存2.3存储2.4网络三、KVM高级功能...

kvm虚拟化之ESXi到KVM之v2v迁移(esxi虚拟机迁移到另一个esxi)

1.ESXi到KVM之v2v情况说明(1).配置任务列表:1)VMwareESXi虚拟平台下linux系统迁移到KVM虚拟平台。2)VMwareESXi虚拟平台下windows系统迁移到KVM虚拟平台...

unraid下虚拟机安装Windows(vmware安装unraid)

unraid下虚拟机安装Windows使用unraid也有一段时间了,主要是做数据备份,以及docker容器的安装测试,今天有空测试一下VMS虚拟机的使用,用在unraid上安装windows7操作系...