框架
版本

从 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 Basic 示例迁移到 TanStack Router 的过程,最终目标是具有与原始示例相同的功能(将省略样式和其他非路由相关代码)。

Tip

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

步骤 1:切换到 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:创建 posts 路由文件

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>
  )
}

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

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

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,
})

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

步骤 9:创建 posts 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()
  // ...
}

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

步骤 10:生成路由树

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

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

sh
npx tsr generate
npx tsr generate

步骤 11:更新主入口文件以渲染路由器

生成路由树后,您可以更新 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 中寻求帮助。