框架
版本

从 React Location 迁移

在开始从 React Location 迁移之前,重要的是您要充分理解 TanStack Router 所使用的路由概念设计决策

React Location 和 TanStack Router 之间的区别

React Location 和 TanStack Router 共享许多相同的设计决策概念,但您应该注意一些关键区别。

  • React Location 使用*泛型*来推断路由类型,而 TanStack Router 使用*模块声明合并*来推断类型。
  • React Location 中的路由配置使用单个路由定义数组完成,而 TanStack Router 中的路由配置使用从根路由开始的路由定义树完成。
  • 基于文件的路由是 TanStack Router 中定义路由的推荐方式,而 React Location 只允许您使用基于代码的方法在单个文件中定义路由。

迁移指南

在本指南中,我们将介绍使用基于文件的路由将 React Location 基本示例迁移到 TanStack Router 的过程,最终目标是实现与原始示例相同的功能(样式和其他非路由相关代码将被省略)。

提示

要使用基于代码的方法定义路由,您可以阅读基于代码的路由指南。

步骤 1:切换到 TanStack Router 的依赖项

首先,我们需要安装 TanStack Router 的依赖项。有关详细安装说明,请参阅我们的如何安装 TanStack Router 指南。

sh
npm install @tanstack/react-router @tanstack/router-devtools
npm install @tanstack/react-router @tanstack/router-devtools

并删除 React Location 依赖项。

sh
npm uninstall @tanstack/react-location @tanstack/react-location-devtools
npm uninstall @tanstack/react-location @tanstack/react-location-devtools

步骤 2:使用基于文件的路由监听器

如果您的项目使用 Vite(或支持的打包器之一),您可以使用 TanStack Router 插件来监听路由文件的更改并自动更新路由配置。

Vite 插件的安装

sh
npm install -D @tanstack/router-plugin
npm install -D @tanstack/router-plugin

并将其添加到您的 vite.config.js

js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { tanstackRouter } from '@tanstack/router-plugin/vite'

export default defineConfig({
  // ...
  plugins: [tanstackRouter(), react()],
})
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { tanstackRouter } from '@tanstack/router-plugin/vite'

export default defineConfig({
  // ...
  plugins: [tanstackRouter(), react()],
})

但是,如果您的应用程序不使用 Vite,您可以使用我们的其他支持的打包器,或者您可以使用 @tanstack/router-cli 包来监听路由文件的更改并自动更新路由配置。

步骤 3:将基于文件的配置文件添加到您的项目

在项目根目录中创建一个 tsr.config.json 文件,内容如下

json
{
  "routesDirectory": "./src/routes",
  "generatedRouteTree": "./src/routeTree.gen.ts"
}
{
  "routesDirectory": "./src/routes",
  "generatedRouteTree": "./src/routeTree.gen.ts"
}

您可以在此处找到 tsr.config.json 文件的完整选项列表。

步骤 4:创建路由目录

在项目的 src 目录中创建一个 routes 目录。

sh
mkdir src/routes
mkdir src/routes

步骤 5:创建根路由文件

tsx
// src/routes/__root.tsx
import { createRootRoute, Outlet, Link } from '@tanstack/react-router'
import { TanStackRouterDevtools } from '@tanstack/router-devtools'

export const Route = createRootRoute({
  component: () => {
    return (
      <>
        <div>
          <Link to="/" activeOptions={{ exact: true }}>
            Home
          </Link>
          <Link to="/posts">Posts</Link>
        </div>
        <hr />
        <Outlet />
        <TanStackRouterDevtools />
      </>
    )
  },
})
// src/routes/__root.tsx
import { createRootRoute, Outlet, Link } from '@tanstack/react-router'
import { TanStackRouterDevtools } from '@tanstack/router-devtools'

export const Route = createRootRoute({
  component: () => {
    return (
      <>
        <div>
          <Link to="/" activeOptions={{ exact: true }}>
            Home
          </Link>
          <Link to="/posts">Posts</Link>
        </div>
        <hr />
        <Outlet />
        <TanStackRouterDevtools />
      </>
    )
  },
})

步骤 6:创建索引路由文件

tsx
// src/routes/index.tsx
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/')({
  component: Index,
})
// src/routes/index.tsx
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/')({
  component: Index,
})

您需要将索引路由所需的任何相关组件和逻辑从 src/index.tsx 文件移动到 src/routes/index.tsx 文件中。

步骤 7:创建帖子路由文件

tsx
// src/routes/posts.tsx
import { createFileRoute, Link, Outlet } from '@tanstack/react-router'

export const Route = createFileRoute('/posts')({
  component: Posts,
  loader: async () => {
    const posts = await fetchPosts()
    return {
      posts,
    }
  },
})

function Posts() {
  const { posts } = Route.useLoaderData()
  return (
    <div>
      <nav>
        {posts.map((post) => (
          <Link
            key={post.id}
            to={`/posts/$postId`}
            params={{ postId: post.id }}
          >
            {post.title}
          </Link>
        ))}
      </nav>
      <Outlet />
    </div>
  )
}
// src/routes/posts.tsx
import { createFileRoute, Link, Outlet } from '@tanstack/react-router'

export const Route = createFileRoute('/posts')({
  component: Posts,
  loader: async () => {
    const posts = await fetchPosts()
    return {
      posts,
    }
  },
})

function Posts() {
  const { posts } = Route.useLoaderData()
  return (
    <div>
      <nav>
        {posts.map((post) => (
          <Link
            key={post.id}
            to={`/posts/$postId`}
            params={{ postId: post.id }}
          >
            {post.title}
          </Link>
        ))}
      </nav>
      <Outlet />
    </div>
  )
}

您需要将帖子路由所需的任何相关组件和逻辑从 src/index.tsx 文件移动到 src/routes/posts.tsx 文件中。

步骤 8:创建帖子索引路由文件

tsx
// src/routes/posts.index.tsx
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/posts/')({
  component: PostsIndex,
})
// src/routes/posts.index.tsx
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/posts/')({
  component: PostsIndex,
})

您需要将帖子索引路由所需的任何相关组件和逻辑从 src/index.tsx 文件移动到 src/routes/posts.index.tsx 文件中。

步骤 9:创建帖子 ID 路由文件

tsx
// src/routes/posts.$postId.tsx
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/posts/$postId')({
  component: PostsId,
  loader: async ({ params: { postId } }) => {
    const post = await fetchPost(postId)
    return {
      post,
    }
  },
})

function PostsId() {
  const { post } = Route.useLoaderData()
  // ...
}
// src/routes/posts.$postId.tsx
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/posts/$postId')({
  component: PostsId,
  loader: async ({ params: { postId } }) => {
    const post = await fetchPost(postId)
    return {
      post,
    }
  },
})

function PostsId() {
  const { post } = Route.useLoaderData()
  // ...
}

您需要将帖子 ID 路由所需的任何相关组件和逻辑从 src/index.tsx 文件移动到 src/routes/posts.$postId.tsx 文件中。

步骤 10:生成路由树

如果您正在使用受支持的打包器之一,当您运行 dev 脚本时,路由树将自动生成。

如果您没有使用受支持的打包器之一,您可以通过运行以下命令来生成路由树

sh
npx tsr generate
npx tsr generate

步骤 11:更新主入口文件以渲染 Router

生成路由树后,您可以更新 src/index.tsx 文件来创建路由器实例并渲染它。

tsx
// src/index.tsx
import React from 'react'
import ReactDOM from 'react-dom'
import { createRouter, RouterProvider } from '@tanstack/react-router'

// Import the generated route tree
import { routeTree } from './routeTree.gen'

// Create a new router instance
const router = createRouter({ routeTree })

// Register the router instance for type safety
declare module '@tanstack/react-router' {
  interface Register {
    router: typeof router
  }
}

const domElementId = 'root' // Assuming you have a root element with the id 'root'

// Render the app
const rootElement = document.getElementById(domElementId)
if (!rootElement) {
  throw new Error(`Element with id ${domElementId} not found`)
}

ReactDOM.createRoot(rootElement).render(
  <React.StrictMode>
    <RouterProvider router={router} />
  </React.StrictMode>,
)
// src/index.tsx
import React from 'react'
import ReactDOM from 'react-dom'
import { createRouter, RouterProvider } from '@tanstack/react-router'

// Import the generated route tree
import { routeTree } from './routeTree.gen'

// Create a new router instance
const router = createRouter({ routeTree })

// Register the router instance for type safety
declare module '@tanstack/react-router' {
  interface Register {
    router: typeof router
  }
}

const domElementId = 'root' // Assuming you have a root element with the id 'root'

// Render the app
const rootElement = document.getElementById(domElementId)
if (!rootElement) {
  throw new Error(`Element with id ${domElementId} not found`)
}

ReactDOM.createRoot(rootElement).render(
  <React.StrictMode>
    <RouterProvider router={router} />
  </React.StrictMode>,
)

完成!

您现在应该已经成功地将您的应用程序从 React Location 迁移到使用基于文件的路由的 TanStack Router。

React Location 还有一些您可能在应用程序中使用的功能。以下是一些帮助您迁移这些功能的指南

TanStack Router 还有一些您可能想要探索的功能

如果您遇到任何问题或有任何疑问,请随时在 TanStack Discord 中寻求帮助。

我们的合作伙伴
Code Rabbit
Netlify
Neon
Clerk
Convex
Sentry
订阅 Bytes

您的每周 JavaScript 资讯。每周一免费发送给超过 10 万开发者。

Bytes

无垃圾邮件。您可以随时取消订阅。

订阅 Bytes

您的每周 JavaScript 资讯。每周一免费发送给超过 10 万开发者。

Bytes

无垃圾邮件。您可以随时取消订阅。