路由基础概念

简单来说 : 浏览器地址栏 输入URL   --> 请求 --> 后端服务器

典型流程: : 前端路由拦截不同的URL -->不同的组件渲染 -->组件中可能向后端请求API接口数据-->后端路由接收不同的 URL -->不同的代码处理 ---> 响应不同的数据 (多数情况就是指API接口) -->前端渲染

1. 基本使用

第一步肯定还是用脚手架起项目

npm create vite@latest my-router
# 进入项目
cd my-router
# 安装依赖
npm install
npm install react-router-dom
# 启动开发服务器
npm run dev

1. 创建路由对象

这里我就是写一个标签试一下,element部分应该填写相关组件

const router = createBrowserRouter([
  {
    path : '/',
    element :<h1>hello</h1>
  }
])

2. 配置使用路由对象(入口文件处)

createRoot(document.getElementById('root')).render(
  <StrictMode>
    <RouterProvider router={router}/>
  </StrictMode>,
)

2. 失败处理

1. 自定义一个错误页面 error-page.jsx

下面是官方文档写法

import { useRouteError } from "react-router-dom";

export default function ErrorPage() {
//捕获错误信息
  const error = useRouteError();
  console.error(error);

  return (
    <div id="error-page">
      <h1>Oops!</h1>
      <p>Sorry, an unexpected error has occurred.</p>
      <p>
        <i>{error.statusText || error.message}</i>
      </p>
    </div>
  );
}

2. 应用

const router = createBrowserRouter([
  {
    path : '/',
    element :<Root></Root>,
    errorElement : <ErrorPage/>
  }
])

3. 嵌套路由

首先创建一个子组件文件 src/routes/contact.jsx

接着我们进行嵌套处理,分成两步

1.  路由上嵌套

添加childern属性,原路径后拼接 contacts/随便 都可以访问到 <Contact/>

const router = createBrowserRouter([
  {
    path : '/',
    element :<Root></Root>,
    errorElement : <ErrorPage/>,

    children : [{
      path: "contacts/:contactId",
      element : <Contact/>
    }]
  }
])

    2. 组件上嵌套

    抽象一点说就是在父组件里面挖个洞,加上<Outlet />,意思就是子组件以后放在这个位置

    import { Outlet } from "react-router-dom";
    
    export default function Root() {
      return (
        <>
          {/* all the other elements */}
          <div id="detail">
            <Outlet />
          </div>
        </>
      );
    }
    

    4. Link组件  vs a标签

    <a>标签(原生 HTML)点击后不会被前端路由拦截,浏览器会直接向服务器发送 HTTP 请求,页面会 完全刷新,加载新的 HTML 页面

    Link 组件(React Router 提供)点击后React Router 会拦截点击事件,更新浏览器地址栏,对应的 路由组件渲染(父路由 + 子路由)而不会刷新整个页面

    5. 理解loader

    先看这样一个流程

    首次组件渲染(显示初始 / loading 状态)
            ↓
    异步请求发送
            ↓
    请求完成 → 更新 state
            ↓
    组件重新渲染 → 显示最终数据

    这里我们渲染了两次,其实如果有多个请求就会渲染多次,那么如果在路由匹配成功后组件渲染前我们就发送这个请求,会不会渲染的更快一些呢

    其实是可以的

    1.先将异步函数封装成loader

    import { Outlet, Link } from "react-router-dom";
    import { getContacts } from "../contacts";
    
    export async function loader() {
      const contacts = await getContacts();
      return { contacts };
    }
    

    2.在路由中配置

    这样在路由匹配成功后就会立即调用loader函数

    /* other imports */
    import Root, { loader as rootLoader } from "./routes/root";
    //用as给来自根组件的loader取别名,因为后续可能还有其他组件的loader函数
    const router = createBrowserRouter([
      {
        path: "/",
        element: <Root />,
        errorElement: <ErrorPage />,
        loader: rootLoader,
        children: [
          {
            path: "contacts/:contactId",
            element: <Contact />,
          },
        ],
      },
    ]);
    

    3.接收数据渲染组件

    import {
      Outlet,
      Link,
      useLoaderData,
    } from "react-router-dom";
    import { getContacts } from "../contacts";
    
    /* other code */
    
    export default function Root() {
       //这里调用useLoaderData()方法获取请求得来的数据,{}作用是解构
      const { contacts } = useLoaderData();
      return (
        <>
          <div id="sidebar">
            <h1>React Router Contacts</h1>
            {/* other code  根据需求渲染 */}
            
        </>
    

    作用:获取数据 → 作为组件的初始渲染数据。

    6. 理解action

    在传统网页中,HTML 表单<form>是改变数据的主要方式,本质上,表单提交就像点击链接,浏览器会直接向服务器发送 HTTP 请求,页面会 完全刷新,加载新的 HTML 页面,只不过可以带数据和选择请求方式(GET / POST)

    而React Router 里的<Form>组件模拟了这种 表单导航行为,但不会真的发送 HTTP 请求到服务器,它会把表单提交的数据 拦截到客户端,然后发送到 路由对应的 action(客户端逻辑处理),让页面局部更新

    来看一下如何使用action

    1. 将原来的<form>用<Form>替代

              <Form method="post">
                <button type="submit">New</button>
              </Form>
               //这里没有显式写action,数据和请求方法(POST)交给 当前路由的 action
    
              /* <Form method="post" action="/contacts/new">
                <button type="submit">New</button>
              </Form> */
               //如果写了,那么这次提交就会被交给 /contacts/new 路由的 action。

    2. 将表单提交时要用到的异步函数封装成action

    export async function action() {
      const contact = await createContact();
      return { contact };
    }

    3. 在路由中配置

    /* other imports */
    
    import Root, {
      loader as rootLoader,
      action as rootAction,
    } from "./routes/root";
    
    const router = createBrowserRouter([
      {
        path: "/",
        element: <Root />,
        errorElement: <ErrorPage />,
        loader: rootLoader,
        action: rootAction,
        children: [
          {
            path: "contacts/:contactId",
            element: <Contact />,
          },
        ],
      },
    ]);
    

    调用时间 : 当用户 提交 <Form> 或调用 fetcher.submit,并且表单 method 不是 GET(通常是 POST / PUT / PATCH / DELETE)

    作用 :处理数据变更(Create / Update / Delete)。

    这里提一下action和loader的区别

    • loader

      • 在页面渲染前获取数据 → useLoaderData()
      • 比如编辑页面需要先拿到原始联系人信息显示在表单里。
    • action:专门处理“提交行为”(表单提交 / useSubmit / fetcher 提交)

      • 不会在进入页面时自动跑

      • 只有用户触发表单提交的时候才会执行。

    所以进入新页面,loader会自动执行,获取数据渲染表单,而对数据进行更新操作,才会触发action,更新数据可能跳转

    7. 使用URL Params 获取具体信息

    其实还是通过loader

    1. loader 的参数由 React Router 提供

    React Router 在路由匹配时,会自动给 loader 函数传一个对象,通常包括:

    { params, // URL 参数,如 /contacts/:contactId 中的 contactId 
      request, // Request 对象,包含 method、formData、URL 等 
      context, // 可选,路由上下文 }

    2. params 的来源

    • 如果 URL 是 /contacts/123,路由里写了 :contactId

    • React Router 会把 URL 解析成:

      params = { contactId: "123" };
    • loader 自动接收这个 params,就可以在函数里使用了。

    3. 在loader函数里解构调用即可

    export async function loader({ params }) {
      const id = params.contactId;
      const contact = await getContact(id);
      return {contact};  
    }

    Logo

    Agent 垂直技术社区,欢迎活跃、内容共建。

    更多推荐