【React 純前端實戰】#02 TanStack Router:型別安全的路由系統

測驗:TanStack Router 型別安全的路由系統

共 5 題,點選答案後會立即顯示結果

1. 在 TanStack Router 的 file-based routing 中,檔名 $postId.tsx$ 代表什麼意思?

  • A. 這是一個私有路由,只有登入使用者才能存取
  • B. 這是一個動態參數,會匹配網址中對應位置的任何值
  • C. 這是一個特殊變數,代表金額相關的路由
  • D. 這是一個被忽略的檔案,不會變成路由

2. 在巢狀路由中,<Outlet /> 的作用是什麼?

  • A. 定義路由的出口,讓使用者可以登出應用程式
  • B. 顯示錯誤訊息的預設位置
  • C. 作為插槽,讓子路由的內容渲染在父路由指定的位置
  • D. 連接外部 API 的接口元件

3. 以下是一段路由定義,validateSearch 搭配 Zod schema 的主要好處是什麼?

const searchSchema = z.object({ page: z.number().default(1), sort: z.enum([‘name’, ‘date’]).default(‘date’), }) export const Route = createFileRoute(‘/posts/’)({ validateSearch: searchSchema, component: PostList, })
  • A. 讓搜尋功能的效能更快
  • B. 自動驗證和轉型 search params,提供型別安全和預設值
  • C. 讓 search params 加密,防止使用者竄改網址
  • D. 自動產生搜尋 UI 介面

4. 在 TanStack Router 中,為什麼建議優先使用 <Link> 而不是 useNavigate() 進行頁面導航?

  • A. <Link> 的效能比 useNavigate() 好很多
  • B. useNavigate() 不支援型別安全
  • C. <Link> 可以帶 search params,useNavigate() 不行
  • D. <Link> 會渲染成 <a> 標籤,支援右鍵開新分頁等瀏覽器原生行為

5. 檔案結構中 _auth/ 資料夾用底線 _ 開頭,對路由有什麼影響?

src/routes/ ├── _auth/ │ ├── login.tsx → ? │ └── register.tsx → ? └── _auth.tsx
  • A. _auth 會出現在網址中,所以 login 的路徑是 /_auth/login
  • B. _auth 底下的檔案會被忽略,不會產生任何路由
  • C. _auth 是 pathless layout,不會出現在網址裡,login 的路徑是 /login
  • D. _auth 表示這些路由需要認證才能存取

一句話說明

管理「使用者在哪一頁」和「網址上的參數」,而且打錯字 TypeScript 直接幫你抓出來。

這篇在講什麼

上一篇我們選好了技術棧:React + TanStack Router + TanStack Query。這篇要深入 TanStack Router,讓你看懂 AI 生成的路由程式碼在做什麼。

路由的工作很單純:網址變了,畫面跟著變。TanStack Router 做的就是這件事,但它多了一個殺手級功能——完全的 TypeScript 型別安全。也就是說,你的路由參數、搜尋參數、頁面連結,通通有型別檢查,打錯字在編輯器裡就會看到紅色波浪線。

前置知識

  • 讀完第 1 篇(了解整體架構和為什麼選這套)
  • 基本的 React 和 TypeScript 知識

TanStack Router vs React Router:核心差異

如果你之前用過 React Router(或 AI 幫你用過),先看看 TanStack Router 有什麼不同:

比較項目 React Router TanStack Router
路由定義 JSX 寫在程式碼裡 檔案結構自動產生路由
型別安全 路由參數是 string,要自己轉型 路由參數自動推導型別,打錯字編輯器會報錯
Search Params useSearchParams() 回傳字串,要自己 parse 內建 schema 驗證,自動型別推導
路由樹產生 手動維護 CLI 工具自動掃描檔案、自動產生

白話翻譯:React Router 像是你自己手寫一份「網址對照表」;TanStack Router 像是你把檔案放對位置,它就自動幫你產出對照表,而且還會幫你檢查有沒有打錯字。


File-Based Routing:檔案結構就是路由結構

TanStack Router 最大的特色是 file-based routing——你不用手動寫路由設定,只要在 routes/ 資料夾裡建立檔案,路由就會自動產生。

檔案 → 網址 對照表

src/routes/
├── __root.tsx          →  整個應用的最外層(導覽列、Footer 在這裡)
├── index.tsx           →  /
├── about.tsx           →  /about
├── posts/
│   ├── index.tsx       →  /posts
│   └── $postId.tsx     →  /posts/123(動態路由)
└── _auth/
    ├── login.tsx       →  /login(沒有 _auth 在網址裡)
    └── register.tsx    →  /register
Code language: PHP (php)

命名規則速查

檔名 意思 範例
__root.tsx 根路由,整個應用的最外殼 放導覽列、Outlet
index.tsx 該路徑的首頁 routes/index.tsx 對應 /
about.tsx 靜態路徑 對應 /about
$postId.tsx 動態參數,$ 開頭 對應 /posts/123
_auth/ Layout 群組,_ 開頭 不會出現在網址裡
(admin)/ 路由群組,括號包住 只做檔案整理,不影響網址
-helpers.tsx 被忽略的檔案,- 開頭 不會變成路由

記住三個特殊前綴:$ 是動態參數、_ 是隱藏路徑的 Layout、- 是忽略不管。

路由樹自動產生

當你執行 tsr generate 或在開發模式下用 tsr watch,工具會掃描 routes/ 資料夾,自動產生一個 routeTree.gen.ts 檔案。你不需要手動編輯這個檔案——它是自動產生的。

// routeTree.gen.ts(自動產生,不要手動改)
import { Route as rootRoute } from './routes/__root'
import { Route as indexRoute } from './routes/index'
import { Route as aboutRoute } from './routes/about'
import { Route as postsIndexRoute } from './routes/posts/index'
import { Route as postsPostIdRoute } from './routes/posts/$postId'
// ... 自動串接所有路由
Code language: JavaScript (javascript)

白話翻譯:你只管建檔案、寫元件,工具幫你把所有路由串起來。


看懂路由定義檔

每個路由檔案的基本結構都長這樣。我們分兩種來看。

createRootRoute:根路由

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

export const Route = createRootRoute({
  component: RootLayout,
})

function RootLayout() {
  return (
    <div>
      <nav>導覽列</nav>
      <Outlet />          {/* 子路由的內容會渲染在這裡 */}
      <footer>頁尾</footer>
    </div>
  )
}
Code language: JavaScript (javascript)

逐行翻譯

程式碼 翻譯
createRootRoute({...}) 建立應用程式最頂層的路由
component: RootLayout 這個路由要用 RootLayout 元件來顯示
export const Route = ... 匯出路由(檔名和匯出名稱都是固定的)
<Outlet /> 「子頁面的內容插在這裡」——這是巢狀路由的關鍵

createFileRoute:一般路由

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

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

function AboutPage() {
  return <h1>關於我們</h1>
}
Code language: JavaScript (javascript)

逐行翻譯

程式碼 翻譯
createFileRoute('/about') 建立對應 /about 網址的路由
({component: AboutPage}) 這個路由顯示 AboutPage 元件
export const Route = ... 匯出路由定義(每個路由檔案都要這樣寫)

注意 createFileRoute('/about') 裡面的路徑字串是工具自動填入的——你不需要自己寫,tsr watch 會根據檔案位置自動幫你填上正確的路徑。


Path Params:動態路由參數

當網址有動態的部分(像是 /posts/123 裡的 123),就需要用動態路由。

定義動態路由

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

export const Route = createFileRoute('/posts/$postId')({
  component: PostDetail,
})

function PostDetail() {
  const { postId } = Route.useParams()   // 從網址取出 postId
  return <h1>文章 #{postId}</h1>
}
Code language: JavaScript (javascript)

逐行翻譯

程式碼 翻譯
$postId.tsx 檔名用 $ 開頭,表示這是動態參數
'/posts/$postId' 路徑中 $postId 是佔位符,會匹配任何值
Route.useParams() 從目前網址取出參數
{ postId } 解構出 postId,型別自動推導為 string

型別安全的好處

function PostDetail() {
  const { postId } = Route.useParams()

  // TypeScript 知道 postId 一定存在,型別是 string
  console.log(postId)  // OK

  // 如果你打錯字...
  const { postID } = Route.useParams()
  //      ^^^^^^ TypeScript 報錯:沒有 postID 這個屬性
}
Code language: JavaScript (javascript)

白話翻譯:你不用擔心「這個參數叫什麼名字」,打錯字編輯器馬上告訴你。React Router 的 useParams() 回傳的是 Record<string, string | undefined>,你得自己記住參數名稱——TanStack Router 則是根據路由定義自動推導。


Search Params:型別安全的查詢參數

Search params 就是網址裡 ? 後面的東西,像 ?page=1&sort=name。這是 TanStack Router 比 React Router 強大很多的地方。

為什麼 Search Params 需要型別安全?

傳統做法的問題:

// React Router 的做法(型別不安全)
function PostList() {
  const [searchParams] = useSearchParams()
  const page = searchParams.get('page')     // 型別是 string | null
  const sort = searchParams.get('sort')     // 型別是 string | null

  // 你得自己轉型、自己處理 null、自己驗證格式
  const pageNum = page ? parseInt(page) : 1
  // 如果有人在網址輸入 ?page=abc 呢?parseInt 會回傳 NaN...
}
Code language: JavaScript (javascript)

TanStack Router 的做法:validateSearch

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

// 第一步:定義 search params 的 schema
const postsSearchSchema = z.object({
  page: z.number().default(1),           // 數字,預設值 1
  sort: z.enum(['name', 'date']).default('date'),  // 只能是 'name' 或 'date'
})

// 第二步:在路由定義中使用 validateSearch
export const Route = createFileRoute('/posts/')({
  validateSearch: postsSearchSchema,     // 用 schema 驗證 search params
  component: PostList,
})

// 第三步:在元件中取出(完全型別安全)
function PostList() {
  const { page, sort } = Route.useSearch()
  //      ^^^^ number    ^^^^^ 'name' | 'date'
  //      TypeScript 自動知道型別,不需要自己轉型
  return (
    <div>
      <p>第 {page} 頁,排序方式:{sort}</p>
    </div>
  )
}
Code language: JavaScript (javascript)

逐行翻譯

程式碼 翻譯
z.object({...}) 用 Zod 定義「search params 應該長什麼樣」
z.number().default(1) page 必須是數字,沒給的話預設 1
z.enum(['name','date']) sort 只能是這兩個值之一
validateSearch: postsSearchSchema 告訴路由:「用這個 schema 驗證網址的 search params」
Route.useSearch() 取出驗證過的 search params,型別自動推導

和 React Router 的比較

React Router:
  ?page=abc  → page = "abc"(字串,你得自己處理)
  ?page=     → page = ""(空字串)
  沒有 page   → page = null

TanStack Router + validateSearch:
  ?page=abc  → page = 1(不合格式,用預設值)
  ?page=     → page = 1(不合格式,用預設值)
  沒有 page   → page = 1(沒給,用預設值)
  ?page=3    → page = 3(數字 3,不是字串 "3"Code language: JavaScript (javascript)

Search params 被 TanStack Router 當作「一等公民」處理——有型別、有驗證、有預設值。你不用再寫一堆防禦性程式碼。


巢狀路由和 Layout:用 Outlet 組合頁面

巢狀路由讓你把多個頁面共用的結構(像側邊欄、頂部標籤)抽出來,避免重複。

基本概念

瀏覽器畫面:
┌──────────────────────────┐
│  導覽列(__root.tsx)        │
├──────────────────────────┤
│  ┌────────────────────┐  │
│  │ 側邊欄(posts 的 layout)│  │
│  │  ┌──────────────┐  │  │
│  │  │ 文章內容       │  │  │
│  │  │($postId.tsx) │  │  │
│  │  └──────────────┘  │  │
│  └────────────────────┘  │
└──────────────────────────┘

對應的路由結構:
__root.tsx        → <nav> + <Outlet />
  └── posts/
      ├── route.tsx   → 側邊欄 + <Outlet />   (posts 的 layout)
      └── $postId.tsx → 文章內容
Code language: HTML, XML (xml)

實際程式碼

根路由放導覽列:

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

export const Route = createRootRoute({
  component: () => (
    <div>
      <nav>全站導覽列</nav>
      <Outlet />       {/* 子路由渲染在這裡 */}
    </div>
  ),
})
Code language: JavaScript (javascript)

Posts 區域有自己的 layout:

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

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

function PostsLayout() {
  return (
    <div style={{ display: 'flex' }}>
      <aside>文章側邊欄</aside>
      <main>
        <Outlet />     {/* posts 底下的子頁面渲染在這裡 */}
      </main>
    </div>
  )
}
Code language: JavaScript (javascript)

文章列表和文章詳情:

// src/routes/posts/index.tsx → /posts
export const Route = createFileRoute('/posts/')({
  component: () => <div>文章列表</div>,
})

// src/routes/posts/$postId.tsx → /posts/123
export const Route = createFileRoute('/posts/$postId')({
  component: () => {
    const { postId } = Route.useParams()
    return <div>文章 #{postId}</div>
  },
})
Code language: JavaScript (javascript)

Outlet 的角色<Outlet /> 就像一個「插槽」。父路由放了 <Outlet />,子路由的內容就會渲染在那個位置。一層一層嵌套下去,就形成了完整的頁面結構。

Pathless Layout:用 _ 前綴

有時候你想讓幾個頁面共用一個 layout,但 layout 本身不佔網址路徑:

src/routes/
├── _auth/
│   ├── login.tsx      →  /login(不是 /_auth/login)
│   └── register.tsx   →  /register
└── _auth.tsx          →  _auth 的 layout 元件
// src/routes/_auth.tsx
import { createFileRoute, Outlet } from '@tanstack/react-router'

export const Route = createFileRoute('/_auth')({
  component: () => (
    <div className="auth-layout">
      <div className="auth-card">
        <Outlet />     {/* login 或 register 的內容 */}
      </div>
    </div>
  ),
})
Code language: JavaScript (javascript)

白話翻譯_auth_ 表示「我只是個 layout,不要出現在網址裡」。所以 /login 頁面會套上 _auth 的外殼,但網址不會變成 /_auth/login


導航方式:Link 和 useNavigate

Link 元件:宣告式導航

import { Link } from '@tanstack/react-router'

function Navigation() {
  return (
    <nav>
      {/* 基本連結 */}
      <Link to="/about">關於我們</Link>

      {/* 帶 path params */}
      <Link to="/posts/$postId" params={{ postId: '42' }}>
        查看文章 #42
      </Link>

      {/* 帶 search params(型別安全!) */}
      <Link to="/posts" search={{ page: 2, sort: 'name' }}>
        文章列表第 2 頁
      </Link>

      {/* 目前頁面會自動加上 active class */}
      <Link to="/posts" activeProps={{ className: 'active' }}>
        文章
      </Link>
    </nav>
  )
}
Code language: JavaScript (javascript)

型別安全的威力

// 打錯路徑 → TypeScript 報錯
<Link to="/abuot">關於</Link>
//        ^^^^^^ 錯誤:'/abuot' 不是有效的路由路徑

// 漏了必要參數 → TypeScript 報錯
<Link to="/posts/$postId">查看文章</Link>
// 錯誤:缺少 params.postId

// search params 型別錯誤 → TypeScript 報錯
<Link to="/posts" search={{ page: "abc" }}>列表</Link>
//                                ^^^^^ 錯誤:page 應該是 number
Code language: HTML, XML (xml)

useNavigate:命令式導航

有時候你不是點連結,而是在某個操作完成後跳轉頁面(例如表單送出後),這時用 useNavigate

import { useNavigate } from '@tanstack/react-router'

function CreatePostForm() {
  const navigate = useNavigate()

  async function handleSubmit(data: PostData) {
    const newPost = await createPost(data)

    // 建立成功後,跳轉到新文章的頁面
    navigate({
      to: '/posts/$postId',
      params: { postId: newPost.id },
    })
  }

  return <form onSubmit={handleSubmit}>...</form>
}
Code language: JavaScript (javascript)

Link vs useNavigate 怎麼選?

場景 用什麼
導覽列、選單、文字連結 <Link>
表單送出後跳轉 useNavigate()
按鈕點擊後做完某些事再跳 useNavigate()
href、能右鍵新分頁開啟 <Link>(它會渲染成 <a>

經驗法則:**能用 <Link> 就用 <Link>**,因為它會渲染成真正的 <a> 標籤,支援右鍵開新分頁、瀏覽器的上一頁/下一頁等原生行為。


Route Guards:用 beforeLoad 做路由守衛

有些頁面只有登入的使用者才能看(例如 Dashboard)。TanStack Router 用 beforeLoad 來處理這個需求。

基本認證檢查

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

export const Route = createFileRoute('/dashboard')({
  beforeLoad: ({ context }) => {
    // 進入這個頁面之前,先檢查有沒有登入
    if (!context.auth.isAuthenticated) {
      // 沒登入?丟到登入頁面
      throw redirect({
        to: '/login',
        search: { redirect: '/dashboard' },  // 登入後跳回來
      })
    }
  },
  component: DashboardPage,
})
Code language: JavaScript (javascript)

逐行翻譯

程式碼 翻譯
beforeLoad 進入頁面「之前」執行的函式
context.auth.isAuthenticated 從路由的 context 取出認證狀態
throw redirect({...}) 沒登入就強制跳轉到登入頁
search: { redirect: '/dashboard' } 在網址帶上原本要去的頁面,方便登入後跳回

保護多個頁面:用 Layout Route

如果有很多頁面都需要認證,不用每個都寫 beforeLoad。用 layout route 一次搞定:

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

export const Route = createFileRoute('/_authenticated')({
  beforeLoad: ({ context, location }) => {
    if (!context.auth.isAuthenticated) {
      throw redirect({
        to: '/login',
        search: { redirect: location.href },
      })
    }
  },
  component: () => <Outlet />,  // 通過認證就正常顯示子頁面
})
Code language: JavaScript (javascript)
src/routes/
├── _authenticated/
│   ├── dashboard.tsx    →  /dashboard(需要登入)
│   ├── settings.tsx     →  /settings(需要登入)
│   └── profile.tsx      →  /profile(需要登入)
├── _authenticated.tsx   →  認證守衛(beforeLoad 在這裡)
├── login.tsx            →  /login(不需要登入)
└── __root.tsx

白話翻譯_authenticated 是一個隱形的保護層。底下的所有頁面都會在載入前先跑認證檢查,通過了才能看到內容。這比在每個頁面都寫一次認證邏輯乾淨很多。


核心概念翻譯

你會看到 意思
createRootRoute 建立最頂層的根路由(放導覽列、全站 layout)
createFileRoute('/path') 建立檔案路由,路徑字串由工具自動填入
export const Route = ... 每個路由檔案都要匯出 Route
<Outlet /> 子路由的內容插在這裡
Route.useParams() 取得網址中的動態參數(/posts/$postIdpostId
Route.useSearch() 取得驗證過的 search params(?page=1page
validateSearch 用 schema 定義和驗證 search params
beforeLoad 進入頁面前執行的中介函式(常用於認證)
throw redirect({...}) 在 beforeLoad 中強制跳轉
<Link to="/path"> 型別安全的連結元件
useNavigate() 命令式導航(操作完成後跳轉用)
routeTree.gen.ts 自動產生的路由樹檔案,不要手動修改

AI 最常這樣用

用法 1:基本的 CRUD 路由結構

src/routes/
├── __root.tsx
├── index.tsx              →  首頁
├── posts/
│   ├── index.tsx          →  /posts(列表頁)
│   ├── $postId.tsx        →  /posts/123(詳情頁)
│   └── new.tsx            →  /posts/new(新增頁)
└── _authenticated/
    ├── dashboard.tsx      →  /dashboard
    └── settings.tsx       →  /settings
Code language: PHP (php)

用法 2:帶分頁和篩選的列表頁

const searchSchema = z.object({
  page: z.number().default(1),
  q: z.string().optional(),
  status: z.enum(['all', 'active', 'archived']).default('all'),
})

export const Route = createFileRoute('/posts/')({
  validateSearch: searchSchema,
  component: PostList,
})

function PostList() {
  const { page, q, status } = Route.useSearch()
  // 根據 search params 抓資料、顯示列表
}
Code language: PHP (php)

用法 3:帶 context 的路由(傳遞全域狀態)

// src/routes/__root.tsx
import { createRootRouteWithContext } from '@tanstack/react-router'

interface RouterContext {
  auth: { isAuthenticated: boolean; user: User | null }
}

export const Route = createRootRouteWithContext<RouterContext>()({
  component: RootLayout,
})
Code language: JavaScript (javascript)

翻譯createRootRouteWithContext 讓你在根路由定義一個 context(例如認證狀態),所有子路由的 beforeLoad 都能透過 context 存取到。


Vibe Coder 檢查點

看到 AI 幫你寫了 TanStack Router 的程式碼時,確認這些事:

  • [ ] 路由檔案結構對嗎? routes/ 資料夾的檔案命名要遵守規則($ 動態參數、_ pathless layout、__root.tsx 根路由)
  • [ ] 每個路由檔案都有 export const Route 這是必須的匯出,少了路由不會生效
  • [ ] 有用到 search params 的頁面有 validateSearch 嗎? 如果網址有 ?page=1 之類的參數,路由定義應該要有 schema 驗證
  • [ ] 巢狀路由的 parent 有放 <Outlet /> 嗎? 沒放 Outlet,子路由的內容不會顯示
  • [ ] 需要認證的頁面有 beforeLoad 嗎? 敏感頁面應該在載入前檢查登入狀態
  • [ ] 連結用的是 <Link> 而不是 <a> 用原生 <a> 會造成整頁重新載入,失去 SPA 的優勢
  • [ ] routeTree.gen.ts 有被加進 .gitignore 嗎? 這個檔案是自動產生的,不應該手動編輯

必看懂 vs 知道就好

必看懂(本系列會一直出現)

概念 為什麼重要
createFileRoute / createRootRoute 每個路由檔案的基本結構
Route.useParams() 取得動態路由參數,每個詳情頁都會用
Route.useSearch() + validateSearch Search params 的型別安全存取
<Outlet /> 巢狀路由的組合方式
<Link> 頁面間的導航
beforeLoad 路由守衛,認證必備

知道就好(遇到再查)

  • Route Masking:讓顯示的網址和實際路由不同(例如 modal 路由)
  • Lazy Loading:用 .lazy.tsx 拆分路由的程式碼,減少初始載入量
  • Pending Component:路由載入中時顯示的過渡畫面
  • Error Component:路由載入失敗時顯示的錯誤畫面
  • Route Context:在路由之間傳遞共享資料

小結

這篇幫你看懂了 TanStack Router 的核心概念:

  1. File-based routing — 檔案結構就是路由結構,工具自動幫你產生路由樹
  2. 路由定義createRootRoutecreateFileRoute 是兩個基本 API,每個路由檔案都要 export const Route
  3. Path Params — 用 $ 開頭的檔名定義動態參數,用 Route.useParams() 取得
  4. Search Params — 用 validateSearch 搭配 Zod schema 做型別安全的查詢參數
  5. 巢狀路由 — 用 <Outlet /> 組合父子路由的畫面結構
  6. 導航<Link> 做宣告式導航、useNavigate() 做命令式導航
  7. Route Guards — 用 beforeLoad + throw redirect() 做認證檢查

下一篇,我們會進入 TanStack Query,看懂 useQueryuseMutation 怎麼管理 API 資料和快取。

進階測驗:TanStack Router 型別安全的路由系統

測驗目標:驗證你是否能在實際情境中應用所學。
共 5 題,包含情境題與錯誤診斷題。

1. 你正在開發一個部落格應用,需要一個文章列表頁面,支援分頁(page)和排序(sort)功能,而且希望使用者可以透過網址分享特定的篩選結果(例如 /posts?page=3&sort=name)。你應該怎麼做? 情境題

  • A. 用 useState 管理 page 和 sort 狀態,在元件內自己處理
  • B. 在 beforeLoad 中手動解析 window.location.search
  • C. 在路由定義中用 validateSearch 搭配 Zod schema 定義 search params
  • D. 在 createFileRoutecomponent 裡用 useSearchParams() 取得

2. 你的應用有 Dashboard、Settings、Profile 三個頁面都需要登入才能存取,另外還有 Login 和 Register 頁面不需要登入。你想用最乾淨的方式實作認證保護,應該怎麼組織路由結構? 情境題

  • A. 在每個需要保護的路由檔案(dashboard.tsx、settings.tsx、profile.tsx)各自加上 beforeLoad 認證檢查
  • B. 建立 _authenticated.tsx layout route,在其中統一寫 beforeLoad,把三個需要保護的頁面放在 _authenticated/ 資料夾下
  • C. 在 __root.tsxbeforeLoad 中對所有頁面做認證檢查,再排除 login 和 register
  • D. 在每個元件的 useEffect 中檢查登入狀態,未登入就跳轉到 login 頁面

3. 你有一個表單頁面,使用者送出表單後(建立新文章),需要跳轉到該文章的詳情頁 /posts/$postId。你應該用什麼方式做這個跳轉? 情境題

  • A. 用 window.location.href = '/posts/' + newPost.id
  • B. 用 <Link to="/posts/$postId" params={{ postId: newPost.id }}>
  • C. 用 history.pushState() 手動修改瀏覽器網址
  • D. 用 useNavigate() 取得 navigate 函式,在送出成功的回呼中呼叫 navigate({ to: '/posts/$postId', params: { postId: newPost.id } })

4. 同事寫了以下巢狀路由結構,但子路由的內容始終不會顯示在畫面上。最可能的問題是什麼? 錯誤診斷

// src/routes/posts/route.tsx import { createFileRoute } from ‘@tanstack/react-router’ export const Route = createFileRoute(‘/posts’)({ component: PostsLayout, }) function PostsLayout() { return ( <div> <h1>文章專區</h1> <aside>側邊欄</aside> <main> {/* 子路由的內容應該在這裡… */} </main> </div> ) }
  • A. 缺少 export default,應該用 default export 而不是 named export
  • B. 缺少 <Outlet />,沒有放置子路由渲染的插槽
  • C. createFileRoute 的路徑應該寫成 /posts/ 而不是 /posts
  • D. 需要在 PostsLayout 中手動 import 並渲染子路由元件

5. AI 幫你生成了以下 <Link> 元件,但 TypeScript 顯示紅色波浪線錯誤。根據文章內容,最可能的原因是什麼? 錯誤診斷

<Link to=”/posts/$postId”> 查看文章 </Link>
  • A. to 屬性不支援動態路徑,應該寫成 to={`/posts/${postId}`}
  • B. 忘記從 @tanstack/react-router import Link 元件
  • C. 路由路徑包含動態參數 $postId,但缺少必要的 params 屬性(例如 params={{ postId: '42' }}
  • D. <Link> 元件不能包含子元素文字,應該用 label 屬性

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *