React-route详解-11

本文基于Reate-route 6.23.1版本制作

概述

对于多页面应用而言,一个 URL 对应的就是一个 HTML 页面,而对于单页面应用,一个 URL 对应的其实是一个组件的展示,可以通过 URL 来控制 UI 或者 HTML 的展示,这就是Reate-route。React Router 包含了三个库:

  • react-router: 提供最基本的路由功能;
  • react-router-dom: 在浏览器中使用;
  • react-router-native: 在 react-native app开发时中使用,web开发时用不到;

React 路由也遵循:事件 => 状态变化 => 重新渲染这样的事件循环。大概核心内容如下:
在这里插入图片描述

安装

核心架构

React-Route的大体设计如下图所示:
在这里插入图片描述

  • 分为三大部分:管理者(Router)、生产者(Route)、消费者(Link等);
  • Route创建URL和UI之间的mapping关系;
  • Link等为交互动作,可引起应用当前URL的变化;
  • Router负责监听当前应用的URL变化,并根据mapping关系渲染页面;

路由映射配置

路由器-Router

官方一共提供了6个,3个在客户端使用,3个用于服务端渲染。对于web程序建议只使用BrowserRouter,因为它是通过HTML5 提供的 history API(pushState、replaceState 和 popstate 事件)来保持 UI 和 URL 的同步的,兼容性会更好。

在实际应用时,建议使用createBrowserRouter方式来创建,而不要用组件的方式。举例如下:

//index.js
import * as React from "react";
import * as ReactDOM from "react-dom/client";
import { createBrowserRouter, RouterProvider } from "react-router-dom";

import "./index.css";
import App from "./App";

const router = createBrowserRouter([
    {
        path: "/", //主页路径
        element: <App />, //主页
        childld:[],
    },
    //路由二
]);

ReactDOM.createRoot(document.getElementById("root")!).render(
    <React.StrictMode>
        <RouterProvider router={router} />
    </React.StrictMode>
);

鉴于history也可以用这种方式来操作。

<a
  href="/contact"
  onClick={(event) => {
    // stop the browser from changing the URL and requesting the new document
    event.preventDefault();
    // push an entry into the browser history stack and change the URL
    window.history.pushState({}, undefined, "/contact");
    window.location.pathname; // /getting-started/concepts/
	window.location.hash; // #location
	window.location.reload(); // force a refresh w/ the server
  }}
/>

路由组-Routes

就是老版本的switch功能,包裹所有的Route,每当位置发生变化时,都会查看其所有子路由,以找到最佳匹配,并渲染UI的该分支。

//标准定义格式可参考Outlet一节
<Routes>
  <Route path="/" element={<Dashboard />}> {/*这种嵌套,一般会设置父路径为/user/*,这样的格式,注意后面是有个*的*/}
    <Route path="messages" element={<DashboardMessages />}/>
    <Route path="tasks" element={<DashboardTasks />} />
  </Route>
  <Route path="about" element={<AboutPage />} />
</Routes>

此处有时也可以加条件判断,比如:

<Routes>
     <Route exact path='/' component={Home}/>
     { isUserLogin() && <Route exact path='/product' component={Product}/>,}  
     <Route path='/about' component={About}/>
</Routes>      

建议用 ‘useRoutes()’ hook来实现统一管理,见最后,另外有些教程上说的路由嵌套就是在JSX组件中混合了Routes和Route标签。

路由-Route

Route底层实际上就是原有的静态组件最外层包装一个<Route/>组件,由Router负责分析监听 URL 的变化,然后通过相应匹配的Route作出相应的UI改变。

const router = createBrowserRouter(
  createRoutesFromElements(
    <Route
      element={<Team />}
      path="teams/:teamId"
      loader={async ({ params }) => {
        return fetch(
          `/fake/api/teams/${params.teamId}.json`
        );
      }}
      action={async ({ request }) => {
        return updateFakeTeam(await request.formData());
      }}
      errorElement={<ErrorBoundary />}
    />
  )
);

定义如下:

interface RouteObject {
  path?: string; //用于设置匹配到的路径
  index?: boolean;//索引路径,即默认路径
  children?: React.ReactNode;//函数返回要渲染的 React 元素
  caseSensitive?: boolean;//指示路由匹配大小写或不匹配大小写
  id?: string;//不建议使用
  loader?: LoaderFunction;//路由加载器在路由渲染之前被调用,并通过useLoaderData为元素提供数据。
  action?: ActionFunction;//当提交从表单、获取器或提交发送到路由时,会调用路由操作。
  element?: React.ReactNode | null; //当路由与URL匹配时渲染的React元素/组件。
  hydrateFallbackElement?: React.ReactNode | null;//不建议使用
  errorElement?: React.ReactNode | null;//当路由在渲染、loader或action中抛出异常时,此React元素/组件将渲染
  Component?: React.ComponentType | null;//功能同element,不建议使用
  HydrateFallback?: React.ComponentType | null;//服务端渲染使用
  ErrorBoundary?: React.ComponentType | null;//功能同errorElement,不建议使用
  handle?: RouteObject["handle"];//任何特定于应用程序的数据,用于实现复杂功能
  shouldRevalidate?: ShouldRevalidateFunction;//验证功能
  lazy?: LazyRouteFunction<RouteObject>;//异步函数,每个lazy函数通常会返回动态导入的结果
}

路由参数

Route path属性说明

  • 如果路径以/:开头:那么它就变成了“参数”。当路由与URL匹配时,将URL解析后作为params提供给其他路由器API。
<Route
  // - /teams/hotspur
  // - /teams/real
  path="/teams/:teamId"
  loader={({ params }) => { 
    console.log(params.teamId); // "hotspur"
  }}
  action={({ params }) => {}}
  element={<Team />} 
/>;

// and the element through `useParams`
function Team() {
  let params = useParams();
  console.log(params.teamId); // "hotspur"
}
  • 如果路径以?结尾,就变成了可选部分,比如下列匹配"
  // this path will match URLs like
  // - /categories
  // - /en/categories
  // - /fr/categories
  path="/:lang?/categories"
  • 其它例子
// 多个参数的形式
<Route path="/c/:categoryId/p/:productId" />;
//普通定义,一般搜索时按这种方式来写,通过url.searchParams.get("name");方式来获取
<NavLink to='/detail?name=coder&age=18'>详情</NavLink>
//路由定义
import React, { PureComponent } from 'react';
import { Route, Routes, NavLink, Navigate } from 'react-router-dom';

<Route path='/detail/:id' element={<Detail />} />

//点击事件
import React, { PureComponent } from 'react';
import withRouter from '../hoc/withRouter';
 
export class Category extends PureComponent {
  constructor(props) {
    super(props);
    this.state = {
      arrList: [22222, 33333, 44444, 55555]
    };
  }
  itemClick(id) {
    const { router } = this.props;
    // 跳转到详情页
    router.navigate(`/detail/${id}`);
  }
  render() {
    const { arrList } = this.state;
    return (
      <>
        <div>Category</div>
        <ul className='nav'>
          {arrList.map((item, index) => {
            return (
              <li onClick={(e) => this.itemClick(item)} key={index}>
                {item}
              </li>
            );
          })}
        </ul>
      </>
    );
  }
}
 
export default withRouter(Category);
//嵌套路由
<Route path="teams" element={<Teams />}>
  <Route path=":teamId" element={<Team />} />
  <Route path="new" element={<NewTeamForm />} />
  <Route index element={<LeagueStandings />} />
</Route>
//数据访问
let location = useLocation();
let urlParams = useParams();
let [urlSearchParams] = useSearchParams();

Route shouldRevalidate属性说明

数据验证,在使用,<fetcher.Form>,useSubmit,或fetcher.submit场景下生效。

<Route
  path="meals-plans"
  element={<MealPlans />}
  loader={loadMealPlans}
  shouldRevalidate={({ currentUrl }) => {
    // only revalidate if the submission originates from
    // the `/meal-plans/new` route.
    return currentUrl.pathname === "/meal-plans/new";
  }}
>

跳转组件

Link

在不同页面之间进行切换,最终会被翻译成一个<a/>标签。

<div className='header'>
     {/* 设置组件跳转 */}
     <Link to='/home'>首页</Link>
     <Link to='/about'>关于</Link>
</div>

对象定义

interface LinkProps{
  to: To; //路径,指定一个url或组件
  preventScrollReset?: boolean;//防止在单击链接时将滚动位置重置到窗口顶部
  relative?: "route" | "path";
  reloadDocument?: boolean;//跳过客户端路由,直接执行<a>默认行为,即跳转后是否刷新页面
  replace?: boolean;//替换当前history中的堆栈信息
  state?: any;//存储在历史状态内的新位置设置有状态值。随后可以通过useLocation()访问此值
  unstable_viewTransition?: boolean;//为视图过渡应用特定样式
}

NavLink

NavLink 是特殊的 Link,可以为与当前 URL 匹配的 Link 元素增加样式属性。在匹配后NavLink就会添加上一个名字为active 的class。

<NavLink
  to="/messages"
  className={({ isActive, isPending }) =>
    isPending ? "pending" : isActive ? "active" : ""
  }
>
  Messages
</NavLink>;

Navigate

Navigate用于重定向,当这个组件出现时,就会执行跳转到对应的to路径中,即原来的Redirect。这个组件一般用于在代码中编程使用

//用于显示默认显示的页面
<Route path='/' element={<Navigate to='/home' />} />

//条件判断
{!isLogin ? <button onClick={(e) => this.login()}>登录</button> : <Navigate to='/home' />}

也可以编码实现:redirect

import { redirect } from "react-router-dom";
const loader = async () => {
  const user = await getUser();
  if (!user) {
    return redirect("/login");
  }
  return null;
};

Outlet

用于在父路由元素中作为子路由的占位元素。
在这里插入图片描述

//定义Outlet
function Dashboard() { //做为父路由htmh的element属性值
  return (
    <div>
      <h1>Dashboard</h1>
      {/* 当URL=/messages时,则显示<DashboardMessages>;
          当URL=/tasks时,则显示<DashboardTasks>;
          当URL=/时,则显示Dashboard
      */}
      <h2>
      	<Link to="/">Home</Link>
      	<Link to="/messages">Message</Link>
      	<Link to="/tasks">Tasks</Link>
      </h2>
      <Outlet /> {/*占位符*/}
    </div>
  );
}

//定义路由
function App() {
  return (
    <Routes>
      <Route path="/" element={<Dashboard />}> {/*父路由*/}
        <Route index element={<Home />} />  {/*默认路径,也可用Navigate来替换*/}
        <Route path="messages" element={<DashboardMessages />}/>
        <Route path="tasks" element={<DashboardTasks />} />
        <Route path="*" element={<NoMatch />} /> {/*配置错误页面*/}
      </Route>
    </Routes>
  );
}

//定义具体样式组件
function Home(){};
function DashboardMessages() {
    return (
        <div>
            <h2>Dashboard</h2>
        </div>
    );
}
function DashboardTasks(){};
function NoMatch(){};

Form

注意这个Form第一个字母是大写的,这个在写代码时很容易弄混,其定义如下:

function Form(props: FormProps): React.ReactElement;

interface FormProps extends React.FormHTMLAttributes<HTMLFormElement> {
  method?: "get" | "post" | "put" | "patch" | "delete";
  encType?:
    | "application/x-www-form-urlencoded"
    | "multipart/form-data"
    | "text/plain";
  action?: string;
  onSubmit?: React.FormEventHandler<HTMLFormElement>;
  fetcherKey?: string;
  navigate?: boolean;
  preventScrollReset?: boolean;
  relative?: "route" | "path";
  reloadDocument?: boolean;
  replace?: boolean;
  state?: any; //state={{ some: "value" }
  unstable_viewTransition?: boolean;
}

例子如下:

  • 定义表单
import { Form } from "react-router-dom";

function FilterForm() {
  return (
    <Form method="get" action="/slc/hotels">
      <select name="sort">
        <option value="price">Price</option>
        <option value="stars">Stars</option>
        <option value="distance">Distance</option>
      </select>

      <fieldset>
        <legend>Star Rating</legend>
        <label>
          <input type="radio" name="stars" value="5" />{" "}
          ★★★★★
        </label>
        <label>
          <input type="radio" name="stars" value="4" /> ★★★★
        </label>
        <label>
          <input type="radio" name="stars" value="3" /> ★★★
        </label>
        <label>
          <input type="radio" name="stars" value="2" /> ★★
        </label>
        <label>
          <input type="radio" name="stars" value="1" /></label>
      </fieldset>

      <fieldset>
        <legend>Amenities</legend>
        <label>
          <input
            type="checkbox"
            name="amenities"
            value="pool"
          />{" "}
          Pool
        </label>
        <label>
          <input
            type="checkbox"
            name="amenities"
            value="exercise"
          />{" "}
          Exercise Room
        </label>
      </fieldset>
      <button type="submit">Search</button>
    </Form>
  );
}
  • 处理表单 :/slc/hotels?sort=price&stars=4&amenities=pool&amenities=exercise
<Route
  path="/:city/hotels"
  loader={async ({ request }) => {
    let url = new URL(request.url);
    let sort = url.searchParams.get("sort");
    let stars = url.searchParams.get("stars");
    let amenities = url.searchParams.getAll("amenities");
    return fakeGetHotels({ sort, stars, amenities });
  }}
/>

Hooks

更多Hooks可参考https://reactrouter.com/en/main/hooks/use-action-data

useNavigate() 手动路由跳转


import { Route, Routes,useNavigate } from 'react-router-dom';

export function App() {
    // 1.定义Hook
    const navigate = useNavigate();

    return (
        <div className=' app'>
            <div className='header'>
                {/* 2. 使用,点击跳转,注意我的这个链接没有对应的路由,此时一般会显示一个空白页面 */}
                <button onClick={(e) => navigate('/category')}>分类</button>
                <button onClick={(e) => navigate('/profily')}>我的</button>
            </div>
            <hr />
            <div className='content'>
                {/* 3. 定义路由 */}
                <Routes>
                    <Route path='/category' element={<Category />} />
                </Routes>
            </div>
            <hr />
            <div className='footer'>footer</div>
        </div>
    );
}

function Category() {
    return(
        <div>Category</div>
    )
}

export default App;

useRoutes() 路由配置集中管理

代替<Routes>组件,实现路由配置集中管理。

import * as React from "react";
import { useRoutes } from "react-router-dom";

function App() {
  let element = useRoutes([
    {
      path: "/",
      element: <Dashboard />,
      children: [
        {
          path: "messages",
          element: <DashboardMessages />,
        },
        { path: "tasks", element: <DashboardTasks /> },
      ],
    },
    { path: "team", element: <AboutPage /> },
  ]);

  return element;
}

Ajax访问

除了使用mwpf的fetch函数,也可以通过 cnpm i axios -D 组件来进行Ajax访问。

import axios from 'axios'

// get
axios.get('/user?ID=12345')
  .then(function (response) {
    console.log(response.data);
  })
  .catch(function (error) {
    console.log(error);
  });

// get带参数
axios.get('/user', {
    params: {
      ID: 12345
    }
  })
  .then(function (response) {
    console.log(response.data);
  })
  .catch(function (error) {
    console.log(error);
  });

// post请求
axios.post('/user', {
  firstName: 'Fred',
  lastName: 'Flintstone'
})
.then(function (response) {
console.log(response.data);
})
.catch(function (error) {
console.log(error);
});

JSON处理

new Response(JSON.stringify(someValue), {
  headers: {
    "Content-Type": "application/json; utf-8",
  },
});

路径生成

generatePath("/users/:id", { id: "42" }); // "/users/42"
generatePath("/files/:type/*", {
  type: "img",
  "*": "cat.jpg",
}); // "/files/img/cat.jpg"

相关推荐

  1. 13 React useEffect 详解

    2024-06-07 00:34:02       35 阅读
  2. # 14 React 自定义Hook详解

    2024-06-07 00:34:02       41 阅读
  3. React详解

    2024-06-07 00:34:02       41 阅读
  4. react详解

    2024-06-07 00:34:02       32 阅读
  5. React 18中hook函数详解之useState和useEffect

    2024-06-07 00:34:02       42 阅读
  6. React 18中hook函数详解之useRef

    2024-06-07 00:34:02       50 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-06-07 00:34:02       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-06-07 00:34:02       106 阅读
  3. 在Django里面运行非项目文件

    2024-06-07 00:34:02       87 阅读
  4. Python语言-面向对象

    2024-06-07 00:34:02       96 阅读

热门阅读

  1. 递推7-2 sdut-C语言实验-养兔子分数

    2024-06-07 00:34:02       24 阅读
  2. MacBook M系列芯片安装php8.2

    2024-06-07 00:34:02       31 阅读
  3. 【html知识】html中常用的表单元素+css格式美化

    2024-06-07 00:34:02       32 阅读
  4. Facebook海外企业广告账户是什么?有什么优势?

    2024-06-07 00:34:02       31 阅读
  5. Emacs Verilog Mode 简单使用指南

    2024-06-07 00:34:02       35 阅读
  6. 【Qt快速入门(一)】- Qt简介

    2024-06-07 00:34:02       27 阅读
  7. js平滑滚动元素使其可见

    2024-06-07 00:34:02       33 阅读
  8. C++在构造函数中使用new

    2024-06-07 00:34:02       31 阅读
  9. zs6d配置-2

    2024-06-07 00:34:02       27 阅读
  10. 7-14 字节序(Endianness)---PTA实验C++

    2024-06-07 00:34:02       30 阅读