在開始從 React Location 遷移之前,請確保您已充分理解 TanStack Router 所使用的路由概念與設計決策。
React Location 和 TanStack Router 共享許多設計決策概念,但仍有一些關鍵差異需要注意:
本指南將逐步說明如何將 React Location Basic 範例 遷移至 TanStack Router,並使用檔案式路由。最終目標是實現與原始範例相同的功能(樣式和其他與路由無關的代碼將省略)。
Tip
若需使用程式碼式路由定義方式,請參閱程式碼式路由指南。
首先,安裝 TanStack Router 的依賴套件:
npm install @tanstack/react-router @tanstack/router-devtools
npm install @tanstack/react-router @tanstack/router-devtools
並移除 React Location 的依賴套件:
npm uninstall @tanstack/react-location @tanstack/react-location-devtools
npm uninstall @tanstack/react-location @tanstack/react-location-devtools
若您的專案使用 Vite(或其他支援的打包工具),可以使用 TanStack Router 插件來監聽路由檔案的變更,並自動更新路由配置。
安裝 Vite 插件:
npm install -D @tanstack/router-plugin
npm install -D @tanstack/router-plugin
並將其加入 vite.config.js:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { TanStackRouterVite } from '@tanstack/router-plugin/vite'
export default defineConfig({
// ...
plugins: [TanStackRouterVite(), react()],
})
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { TanStackRouterVite } from '@tanstack/router-plugin/vite'
export default defineConfig({
// ...
plugins: [TanStackRouterVite(), react()],
})
若您的專案未使用 Vite,可選擇其他支援的打包工具,或使用 @tanstack/router-cli 套件來監聽路由檔案變更並自動更新配置。
在專案根目錄建立 tsr.config.json 檔案,內容如下:
{
"routesDirectory": "./src/routes",
"generatedRouteTree": "./src/routeTree.gen.ts"
}
{
"routesDirectory": "./src/routes",
"generatedRouteTree": "./src/routeTree.gen.ts"
}
完整的 tsr.config.json 選項清單請參考此處。
在專案的 src 目錄下建立 routes 子目錄:
mkdir src/routes
mkdir src/routes
// 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 />
</>
)
},
})
// 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。
// 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。
// 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。
// 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()
// ...
}
需將 src/index.tsx 中與文章詳情路由相關的元件和邏輯移至 src/routes/posts.$postId.tsx。
若使用支援的打包工具,執行開發腳本時會自動生成路由樹。
若未使用支援的打包工具,可執行以下指令生成路由樹:
npx tsr generate
npx tsr generate
生成路由樹後,更新 src/index.tsx 以建立路由實例並渲染:
// src/index.tsx
import React from 'react'
import ReactDOM from 'react-dom'
import { createRouter, RouterProvider } from '@tanstack/react-router'
// 匯入生成的路由樹
import { routeTree } from './routeTree.gen'
// 建立新的路由實例
const router = createRouter({ routeTree })
// 註冊路由實例以確保類型安全
declare module '@tanstack/react-router' {
interface Register {
router: typeof router
}
}
const domElementId = 'root' // 假設有一個 id 為 'root' 的根元素
// 渲染應用程式
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 { routeTree } from './routeTree.gen'
// 建立新的路由實例
const router = createRouter({ routeTree })
// 註冊路由實例以確保類型安全
declare module '@tanstack/react-router' {
interface Register {
router: typeof router
}
}
const domElementId = 'root' // 假設有一個 id 為 'root' 的根元素
// 渲染應用程式
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 中尋求協助。
Your weekly dose of JavaScript news. Delivered every Monday to over 100,000 devs, for free.