• 使用 Next.js 和 React Router 构建单页应用程序(SPA)


    创建项目

    先创建个 Next.js 项目

    pnpm create next-app --typescript
    
    • 1

    安装 react-router-dom

    "dependencies": {
      "next": "13.0.0",
      "react": "18.2.0",
      "react-dom": "18.2.0",
      "react-router-dom": "^6.4.2"
    },
    "devDependencies": {
      "@types/node": "18.11.6",
      "@types/react": "18.0.23",
      "@types/react-dom": "18.0.7",
      "eslint": "8.26.0",
      "eslint-config-next": "13.0.0",
      "typescript": "4.8.4"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    创建相关组件

    创建路由组件 components\MyRouter.tsx
    使用 Hash 路由

    import { lazy } from "react";
    import { RouterProvider, createHashRouter as createRouter } from "react-router-dom";
    
    let Login = lazy(() => import("./Login"));
    let Layout = lazy(() => import("./Layout"));
    let Home = lazy(() => import("./Home"));
    let Log = lazy(() => import("./Log"));
    
    // 只是例子,随便写写
    let router = createRouter([
      {
        path: "/login",
        element: <Login />,
        loader: () => {
          return "我是 loder data";
        },
      },
      {
        element: <Layout />,
        children: [
          {
            path: "/",
            element: <Home />,
          },
          {
            path: "/log",
            element: <Log />,
          },
          {
            path: "*",
            element: (
              <>
                <h1>error 404</h1>
              </>
            ),
          },
        ],
      },
    ]);
    export default function MyRouter() {
      return (
        <>
          <RouterProvider router={router}></RouterProvider>
        </>
      );
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    创建组件 components\Layout\index.tsx
    关键代码是 SuspenseOutlet

    路由跳转 JSX 中使用 Link
    Hook 中使用 useNavigate
    react-router 相关请看官网,这里不再赘述.

    import { FC, memo, Suspense } from "react";
    import { Link, Outlet } from "react-router-dom";
    
    interface IProps {}
    let Index: FC<IProps> = function (props) {
      return (
        <>
          <h1>Layout\index.tsx</h1>
          <ul>
            <li>
              <Link to="/login">login</Link>
            </li>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/log">log</Link>
            </li>
          </ul>
          <Suspense fallback="loading">
            <Outlet />
          </Suspense>
        </>
      );
    };
    
    export default memo(Index);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    在 pages/index.tsx 中使用

    pages/index.tsx 中使用 dynamic 导入 MyRouter , 并设置 ssr: false
    使用 Suspense 包裹 MyRouter

    import dynamic from "next/dynamic";
    import { FC, memo, Suspense } from "react";
    const MyRouter = dynamic(() => import("../components/MyRouter"), { ssr: false });
    
    interface IProps {}
    let Index: FC<IProps> = function (props) {
      return (
        <Suspense fallback="loading">
          <MyRouter />
        </Suspense>
      );
    };
    
    export default memo(Index);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    处理 404 页面

    创建页面 pages/404.tsx
    访问除首页(/)外的地址时, Next.js 会进入到这个404页面,
    这里使用 Next.js useRouter 将路由导航到首页 /
    (因为 404 页面是属于 Next.js 的路由页面,
    不在 react-dom 的 RouterProvider 中)

    import { useRouter } from "next/router";
    import { FC, memo, useEffect } from "react";
    interface IProps {}
    let Index: FC<IProps> = function () {
      let router = useRouter();
      useEffect(() => {
        router.push("/");
      }, [router]);
    
      return <></>;
    };
    
    export default memo(Index);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    目录结构

    Next.js 会将 pages 目录自动生成路由,
    所以开发SPA , 自己的代码需要全部写到 components 或其他目录中.

    |next-js-spa-demo
    
      |components
      |  |Home
      |  |  |index.tsx
      |  |Layout
      |  |  |index.tsx
      |  |Log
      |  |  |-index.tsx
      |  |Login
      |  |  |index.tsx
      |  |MyRouter.tsx
      
      |pages
      |  |404.tsx
      |  |index.tsx
      |  |_app.tsx
      
      |pnpm-lock.yaml
      |next.config.js
      |package.json
      |tsconfig.json
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    源码与预览

    https://github.com/xxxxue/next-js-spa-demo

  • 相关阅读:
    dragTabs(vue)
    最新版本 Stable Diffusion 开源 AI 绘画工具之使用篇
    TDDL介绍及原理
    JAVA实现Jfilechooser搜索功能
    【开源】给ChatGLM写个,Java对接的SDK
    Flutter组件渲染集合的几种方式之详解与实战举例(更新)
    Android 11.0 禁止二次展开QuickQSPanel设置下拉QSPanel高度
    嵌入式Qt-做一个秒表
    冥想第六百零三天
    LeetCode75——Day11
  • 原文地址:https://blog.csdn.net/qq_37214567/article/details/127579054