ReactRouter路由学习(一)

182 阅读3分钟

心血来潮,想学学react了,之前学过,但是用的不多,最近感觉又出了很多新的库,闲着没事来学学。具体代码可以看我的仓库

BrowserRouter

浏览器路由,这个貌似当前v6.14.2中最推荐使用的创建路由的方式,采用的是DOM的History Api。

接下来看看是怎么使用的吧。

简易示例

import * as React from "react";
import * as ReactDOM from "react-dom";
import {
  createBrowserRouter,
  RouterProvider,
} from "react-router-dom";

import Root, { rootLoader } from "./routes/root";
import Team, { teamLoader } from "./routes/team";

const router = createBrowserRouter([
  {
    path: "/",
    element: <Root />,
    loader: rootLoader,
    children: [
      {
        path: "team",
        element: <Team />,
        loader: teamLoader,
      },
    ],
  },
]);

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

以上是一个简单的例子,其存在两个路由:/,/team,其中/team/的子路由。这一部分是官方的例子,其中组件也没有具体的展示,因此看看就好了。

嵌套路由

我们创建一个文件router/index.tsx,内容如下:

import * as React from 'react';
import { createBrowserRouter, redirect } from 'react-router-dom';
import Home from '../pages/Home'

const router = createBrowserRouter([
  {
    path: '/',
    element: <App />,
    children: [
      {
        // 表明这个路由是一个索引路由,当路由为/时,会渲染
        // 到父路由的Outlet中,旧相当于默认渲染的路由
        index: true, 
        element: <Home />,
      },
    ],
  },
  {
    path: '*',
    element: <Error404 />,
  },
]);

export default router;

该代码中,<App/>是父路由的组件,<Home/>是子路由的组件,因此,我们想要在父路由的组件中展现子路由的组件,这时该怎么做呢?用<Outlet />:

// App.tsx
import * as React from 'react';
import { Outlet, Link, useNavigation } from 'react-router-dom';

export default function App(props) {
  const navigation = useNavigation();
  return (
    <>
      <div
        style={{
          width: '300px',
          display: 'flex',
          justifyContent: 'space-between',
          marginBottom: '20px',
        }}
      >
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
        <Link to="/post">Post</Link>
        <Link to="/post/20">Post-20</Link>
      </div>
      <Outlet></Outlet>
    </>
  );
}
// Home.tsx
import * as React from 'react';

export default function Home() {
  let data = useLoaderData();
  return (
    <>
         Home
    </>
  );
}

Outlet组件应该用在父路由中,用以展现子路由元素

路由参数

path: '/',增加一个子路由:

{
    path: 'post/:id?',
    element: <Post />,
},

?号表明不是必须的,可以通过/post访问,也可通过/post/10访问

// Post组件
import * as React from 'react';
import { useParams } from 'react-router-dom';
export default function post() {
  // 获取当前路由参数
  const params = useParams();
  return <>Post-{ params.id ?? '' }</>;
} 

404路由

就是给一个兜底匹配,在createBrowserRouter的最后增加:

{
    path: '*',
    element: <Error404 />,
  },

组件内容如下:

// Error404
import * as React from 'react';
export default function Error() {
  return <>Error</>;
}

懒加载

对于新的react router v6,我看网上都是用的react的方式做的懒加载展示,但是作为学习呢,我们用react router v6的方式做懒加载看看。

我们可以先给App添加子路由,主要通过lazy字段,该字段主要返回一个动态加载的组件,如下:

{
  path: 'about',
  async lazy() {
    let { Component } = await import('../pages/About');
    await new Promise((resolve) => {
      setTimeout(() => {
        resolve(1);
      }, 2000);
    });
    return { Component: Component }; 
  },
},

About组件代码如下,为什么这么写呢?因为lazy字段的第一句就说明了,为了保证应用打包后更小和支持代码拆分,每个路由都可以提供一个异步函数,用于解析路由定义中不匹配的部分,包括(loaderactionComponent/elementErrorBoundary/errorElement, etc.)。

当然,用这种方式的话上面的代码就得改了,lazy字段得改为:lazy: () => import('../pages/About'),如此,该段路由会自动应用其中定义的Component,ErrorBoundary等导出的字段。

// About.tsx
import * as React from 'react';
import { isRouteErrorResponse, useRouteError } from 'react-router-dom';

export function Component() {
  return (
    <>
      About
    </>
  );
}

Component.displayName = 'About';

export function ErrorBoundary() {
  let error = useRouteError();
  return isRouteErrorResponse(error) ? (
    <h1>
      {error.status} {error.statusText}
    </h1>
  ) : (
    <h1>{error.message || error}</h1>
  );
}

// If you want to customize the component display name in React dev tools:
ErrorBoundary.displayName = "SampleErrorBoundary";

如此已经算是懒加载的,但当然,我们需要一个Loading组件,没有Loading就没有灵魂了,我们可以在App组件内定义:

// App.tsx
import * as React from 'react';
import { Outlet, Link, useNavigation } from 'react-router-dom';

export default function App(props) {
  const navigation = useNavigation();
  return (
    <>
      <div
        style={{
          width: '300px',
          display: 'flex',
          justifyContent: 'space-between',
          marginBottom: '20px',
        }}
      >
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
        <Link to="/post">Post</Link>
        <Link to="/post/20">Post-20</Link>
        <Link to="/aaa">404</Link>
      </div>
      <div>加载状态:{navigation.state}</div>
      <Outlet></Outlet>
    </>
  );
}

没错,就是通过navigation.state来判断,如果是loading,那么表示组件仍在加载中。

其他

待续...

附件资料