【React 純前端實戰】#01 為什麼選 React + TanStack?架構總覽

測驗:React + TanStack 架構總覽

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

1. 判斷是否需要 SSR 的核心標準是什麼?

  • A. 專案是否有大量的 API 呼叫
  • B. 頁面是否需要被搜尋引擎找到
  • C. 團隊是否熟悉 Node.js 後端開發
  • D. 使用者數量是否超過一定規模

2. 在 React + TanStack 的架構中,TanStack Query 負責的是哪一層的工作?

  • A. UI 層 — 負責畫面的呈現和使用者互動
  • B. 路由層 — 負責網址和頁面的對應關係
  • C. 資料層 — 負責 API 資料的快取和狀態管理
  • D. 後端服務層 — 負責資料的儲存和處理

3. 文章提到 Next.js 的「Vendor Lock-in」問題,這指的是什麼?

  • A. Next.js 只能使用 React,不支援其他前端框架
  • B. Next.js 的程式碼無法遷移到其他專案
  • C. Next.js 必須使用付費的 Vercel 才能部署
  • D. Next.js 與 Vercel 深度綁定,許多優化功能在非 Vercel 環境會受限

4. 看到以下程式碼,queryKey 的作用是什麼?

const { data } = useQuery({ queryKey: [‘user’, userId], queryFn: () => fetch(`/api/users/${userId}`).then(r => r.json()), })
  • A. 定義 API 的 URL 路徑
  • B. 作為快取的識別標籤,相同的 key 共用同一份資料
  • C. 設定資料的 TypeScript 型別
  • D. 指定資料要存到哪個資料庫

5. 你正在開發一個需要登入才能使用的專案管理 SaaS 應用,AI 建議使用 Next.js。根據文章的分析,以下哪個判斷最合理?

  • A. 應該用 Next.js,因為它是最流行的 React 框架
  • B. 應該用 Next.js,因為 SaaS 應用需要伺服器端渲染
  • C. 可以考慮純前端 SPA,因為登入後的應用不需要 SEO,部署也更簡單
  • D. 應該用 Astro,因為 SaaS 以靜態內容為主

一句話說明

用 React + TanStack 建構純前端 SPA,不用全端框架也能搞定路由和資料管理。

這篇在講什麼

如果你用 AI 幫你寫 React 專案,AI 可能會建議你用 Next.js。但很多時候,你的專案根本不需要 SSR(伺服器端渲染)。這篇文章幫你搞懂:什麼時候用純前端就夠了,以及為什麼 React + TanStack 是一套務實的選擇。

前置知識

  • 基本的 React 開發經驗(知道 component、props、hooks)
  • 知道什麼是 SPA(Single Page Application)

SPA vs SSR:什麼時候純前端就夠了?

先搞懂兩種渲染方式

SPA(純前端)                    SSR(伺服器端渲染)
┌─────────────┐                ┌─────────────┐
│  瀏覽器下載 JS  │                │  伺服器先產出 HTML │
│  JS 在瀏覽器跑  │                │  傳給瀏覽器顯示    │
│  畫面都在前端產生│                │  再載入 JS 互動    │
└─────────────┘                └─────────────┘

白話翻譯

  • SPA:瀏覽器下載一包 JavaScript,所有畫面切換都在瀏覽器端完成,不用每次都問伺服器要新的頁面。
  • SSR:每次切換頁面,伺服器先把 HTML 組好再傳過來,搜尋引擎看得到完整內容。

什麼時候 SPA 就夠了?

場景 需要 SSR? 原因
登入後的 Dashboard 不需要 搜尋引擎不會爬登入後的頁面
內部管理工具 不需要 只有內部員工用,不需要 SEO
SaaS 應用的前台 不需要 登入後才能用,不需要被搜尋
部落格 / 行銷官網 需要 SEO 是流量來源
電商產品頁 需要 商品要被 Google 搜到

判斷標準很簡單:**你的頁面需要被搜尋引擎找到嗎?** 不需要的話,SPA 就夠了。


為什麼不直接用 Next.js?

AI 很喜歡推薦 Next.js,但它不一定適合你的專案。來看三個常見的「過度工程化」陷阱:

陷阱 1:部署複雜度

Next.js 部署                     純前端 SPA 部署
┌──────────────┐               ┌──────────────┐
│ 需要 Node.js 伺服器 │               │ 丟上任何靜態檔案伺服器│
│ 或用 Vercel 託管   │               │ Netlify / S3 / 任何CDN│
│ 有 serverless 冷啟動│               │ 零伺服器成本        │
└──────────────┘               └──────────────┘

SPA 只是一堆靜態檔案(HTML + JS + CSS),丟到任何地方都能跑。Next.js 則需要一個 Node.js 伺服器處理 SSR。

陷阱 2:你不需要的功能在增加複雜度

// Next.js 的 Server Component —— 如果你不需要 SSR,這些都是多餘的
// app/dashboard/page.tsx
export default async function DashboardPage() {
  const data = await fetch('https://api.example.com/stats')  // 伺服器端抓資料
  return <Dashboard data={data} />
}
// ↑ 看起來簡單,但背後牽涉到:伺服器環境、快取策略、revalidation...
Code language: JavaScript (javascript)
// 純前端做法 —— 直覺、單純
function DashboardPage() {
  const { data } = useQuery({ queryKey: ['stats'], queryFn: fetchStats })
  return <Dashboard data={data} />
}
// ↑ 所有邏輯都在瀏覽器端,你看得到、除得了錯
Code language: JavaScript (javascript)

陷阱 3:Vendor Lock-in

Next.js 和 Vercel 深度綁定。雖然可以自己部署,但很多優化功能(Image Optimization、ISR 等)在非 Vercel 環境會受限。純前端 SPA 則沒有這個問題——你可以部署到任何地方。

什麼時候該用 Next.js?

別誤會,Next.js 是很好的框架。如果你的專案符合以下條件,它仍然是好選擇:

  • 需要 SEO(部落格、電商、行銷頁面)
  • 需要在伺服器端處理敏感邏輯(API key 不能暴露在前端)
  • 團隊已經熟悉 Next.js 生態系

但如果你做的是 Dashboard、內部工具、或登入後才能用的 SaaS,純前端通常是更簡單的選擇。


TanStack 生態系:各自解決什麼問題?

選擇純前端之後,你需要解決兩個核心問題:頁面怎麼切換資料怎麼管理。這正是 TanStack Router 和 TanStack Query 的工作。

TanStack Router:型別安全的路由

一句話:管理「使用者在哪一頁」以及「網址列上的資訊」。

// 定義一個路由
const dashboardRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/dashboard',
  component: DashboardPage,
})
// ↑ 當網址是 /dashboard 時,顯示 DashboardPage
Code language: JavaScript (javascript)

它和傳統 React Router 最大的差別是 完全的 TypeScript 型別安全——路由參數、搜尋參數都有型別檢查,打錯字在編輯器裡就會被抓到。

TanStack Query:資料抓取與快取

一句話:管理「從 API 拿回來的資料」,包含快取、重新抓取、載入狀態。

// 抓取使用者資料
const { data, isLoading, error } = useQuery({
  queryKey: ['user', userId],
  queryFn: () => fetch(`/api/users/${userId}`).then(r => r.json()),
})
// ↑ queryKey 是快取的鑰匙,queryFn 是實際抓資料的函式
Code language: JavaScript (javascript)

它幫你處理了這些你不想自己寫的東西:

  • 載入中、成功、失敗三種狀態
  • 自動快取(同樣的資料不重複抓)
  • 背景重新抓取(使用者切回來時自動更新)
  • 重試機制(網路斷了會自動重試)

核心概念翻譯

你會看到 意思
useQuery 「從 API 讀取資料」的 hook
useMutation 「對 API 寫入/修改資料」的 hook
queryKey 快取的識別標籤,同樣的 key 共用同一份資料
queryFn 實際去抓資料的函式
createRoute 定義一個路由(網址對應哪個畫面)
loader 進入頁面前先抓資料的機制

技術棧架構圖:每一層負責什麼

這是你整個專案的分工圖:

┌─────────────────────────────────────────────┐
│                 使用者的瀏覽器                   │
├─────────────────────────────────────────────┤
│                                             │
│   React(UI 層)                              │
│   「畫面長什麼樣」                               │
│   - Component 組合                           │
│   - 事件處理(onClick 等)                      │
│   - 本地狀態(useState)                       │
│                                             │
├─────────────────────────────────────────────┤
│                                             │
│   TanStack Router(路由層)                    │
│   「使用者在哪一頁」                             │
│   - 網址 ↔ 畫面 的對應                         │
│   - 路由參數(/users/123 的 123)              │
│   - 搜尋參數(?page=2&sort=name)             │
│   - 頁面切換的 loader(預先抓資料)              │
│                                             │
├─────────────────────────────────────────────┤
│                                             │
│   TanStack Query(資料層)                     │
│   「Server State 管理」                       │
│   - API 資料的快取                             │
│   - 載入 / 成功 / 失敗狀態                      │
│   - 自動重新抓取與背景更新                       │
│   - 樂觀更新(Optimistic Update)              │
│                                             │
├─────────────────────────────────────────────┤
│                                             │
│   API / BaaS(後端服務)                       │
│   「資料存在哪裡」                               │
│   - Supabase / Firebase / 自建 API           │
│   - REST 或 GraphQL                          │
│                                             │
└─────────────────────────────────────────────┘

每一層的職責翻譯

使用者點擊「訂單列表」
    │
    ▼
TanStack Router:「網址要換成 /orders」
    │
    ▼
TanStack Router 的 loader:「進入頁面前先抓訂單資料」
    │
    ▼
TanStack Query:「快取裡有嗎?沒有就去 API 抓」
    │
    ▼
API / Supabase:「這是訂單資料」
    │
    ▼
TanStack Query:「存到快取,標記為成功」
    │
    ▼
React:「資料來了,畫出訂單列表」

與其他方案的快速比較

React Router vs TanStack Router

比較項目 React Router TanStack Router
TypeScript 支援 有,但型別較鬆散 完全型別安全,路由參數自動推導
搜尋參數管理 手動解析 useSearchParams 內建 JSON 搜尋參數、schema 驗證
資料預載 v6.4+ 支援 loader 原生 loader + 與 TanStack Query 深度整合
學習曲線 較低,文件多 較高,但型別提示能幫你少犯錯
適合場景 小型專案、快速原型 中大型 SPA、需要型別安全的團隊

SWR vs TanStack Query

比較項目 SWR TanStack Query
核心理念 輕量、簡單 功能完整、企業級
DevTools 無官方工具 有官方 DevTools,可視化快取狀態
Mutation 支援 基本 強大(樂觀更新、rollback)
分頁/無限捲動 要自己實作 內建 useInfiniteQuery
Bundle 大小 較小 (~4KB) 較大 (~13KB)
適合場景 小型專案、簡單讀取 複雜的 CRUD 應用

如果你的專案有大量的 CRUD 操作(建立、讀取、更新、刪除),TanStack Query 的 mutation 和 DevTools 會幫你省很多時間。


最小範例:整體長什麼樣

讓我們看一個最精簡的範例,感受一下整個技術棧怎麼組在一起:

// main.tsx —— 應用程式入口
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { RouterProvider, createRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'  // 自動產生的路由樹

const queryClient = new QueryClient()     // 建立 Query 的快取管理器
const router = createRouter({ routeTree }) // 建立路由器

function App() {
  return (
    <QueryClientProvider client={queryClient}>  {/* 提供快取管理器給整個應用 */}
      <RouterProvider router={router} />          {/* 提供路由器給整個應用 */}
    </QueryClientProvider>
  )
}
Code language: JavaScript (javascript)

逐行翻譯

  1. QueryClient — 建立一個「快取管理器」,所有 API 資料都存在這裡
  2. createRouter — 根據路由定義建立路由器
  3. QueryClientProvider — 讓所有 component 都能存取快取
  4. RouterProvider — 讓所有 component 都能做頁面切換

看到 AI 生成類似的代碼時,你只要知道:這是在設定應用程式的兩大基礎設施(路由 + 資料層)


這套技術棧適合什麼專案?

非常適合

  • Dashboard / 管理後台:登入後使用,不需要 SEO,大量的資料讀取與操作
  • 內部工具:員工使用的管理系統、CRM、庫存管理
  • SaaS 前台:登入後的應用介面,例如專案管理工具、分析平台
  • 表單密集的應用:搭配 TanStack Form 可以處理複雜表單

不太適合

  • 需要 SEO 的公開頁面:部落格、電商、行銷官網 — 考慮 Next.js 或 Astro
  • 靜態內容為主:文件站、個人網站 — 考慮 Astro 或 VitePress
  • 需要即時通訊:聊天室、協作工具 — 需要額外的 WebSocket 方案

本系列學習路線圖

你在這裡
  ↓
#01 架構總覽(本篇)
  │  瞭解「為什麼選這套」和「整體架構」
  ↓
#02 TanStack Router
  │  學會讀路由定義、參數、巢狀路由
  ↓
#03 TanStack Query
  │  學會讀 useQuery / useMutation、快取機制
  ↓
#04 Router + Query 整合
  │  學會讀 loader 模式、預取策略
  ↓
#05 實戰:Supabase CRUD
     把所有東西串起來,做一個完整的應用
Code language: PHP (php)

每一篇都會延續 Vibe Coder 的學習方式:重點不是教你從頭寫,而是讓你看懂 AI 生成的代碼在做什麼


Vibe Coder 檢查點

看到 AI 幫你建了一個 React + TanStack 專案時,確認這些事:

  • [ ] 有沒有搞清楚需不需要 SSR? 如果你的應用不需要 SEO,純前端 SPA 就夠了
  • [ ] 架構分層對嗎? React 管 UI、Router 管頁面切換、Query 管 API 資料
  • [ ] 有沒有不必要的框架? 如果 AI 建議 Next.js,但你做的是登入後的 Dashboard,考慮用純前端
  • [ ] QueryClient 和 Router 有正確設定嗎? 入口檔案應該有 QueryClientProviderRouterProvider
  • [ ] Query 的 key 有意義嗎? queryKey 應該能描述這筆資料是什麼(例如 ['users', userId]

必看懂 vs 知道就好

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

概念 為什麼重要
useQuery / useMutation 每個頁面都會用到的資料操作
queryKey / queryFn 控制快取和資料來源的核心
createRoute / routeTree 定義你的應用有哪些頁面
RouterProvider / QueryClientProvider 應用程式入口的基本設定
loader 進入頁面前預先載入資料

知道就好(遇到再查)

  • Devtools:TanStack Query 和 Router 都有除錯工具,可以看快取狀態和路由狀態
  • Optimistic Update(樂觀更新):先假設操作成功更新 UI,失敗再 rollback
  • Infinite Query(無限捲動):處理「載入更多」的分頁模式
  • Route Masking(路由遮罩):顯示的網址和實際路由不同

小結

這篇幫你建立了整體概念:

  1. 不是每個專案都需要 Next.js — Dashboard、內部工具、SaaS 前台用純前端 SPA 更簡單
  2. TanStack Router 管路由 — 型別安全的頁面切換和參數管理
  3. TanStack Query 管資料 — API 資料的快取、狀態管理、自動更新
  4. 四層架構:React(UI)→ Router(導航)→ Query(資料)→ API/BaaS(後端)

下一篇,我們會深入 TanStack Router,看懂路由定義、參數傳遞、和巢狀路由。

進階測驗:React + TanStack 架構總覽

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

1. 你的公司要開發一套內部庫存管理系統,只有倉儲人員登入後才能使用。PM 問你技術選型,AI 建議用 Next.js + Vercel 部署。你會怎麼回應? 情境題

  • A. 同意 AI 的建議,Next.js 是目前最主流的 React 框架,功能最完整
  • B. 改用 Astro,因為它比 Next.js 更輕量
  • C. 建議用純前端 SPA(React + TanStack),因為內部工具不需要 SEO,部署也更簡單
  • D. 用 Next.js 但自己架 Node.js 伺服器,避免 Vercel 的 vendor lock-in

2. 你正在用 AI 開發一個 Dashboard 應用,需要從 API 抓取統計資料並顯示在頁面上。AI 生成了以下程式碼,你想確認資料流是否正確。請問從使用者打開頁面到看到資料,正確的流程順序是? 情境題

// DashboardPage.tsx function DashboardPage() { const { data, isLoading } = useQuery({ queryKey: [‘dashboard-stats’], queryFn: fetchDashboardStats, }) if (isLoading) return <Loading /> return <StatsDisplay data={data} /> }
  • A. React 畫出頁面 -> API 回傳資料 -> TanStack Query 快取 -> 顯示資料
  • B. Router 匹配路由 -> TanStack Query 檢查快取並發送 API 請求 -> 資料存入快取 -> React 畫出頁面
  • C. TanStack Query 預先抓取所有資料 -> Router 決定顯示哪個頁面 -> React 畫出頁面
  • D. API 主動推送資料 -> TanStack Query 接收 -> Router 切換頁面 -> React 畫出頁面

3. 你的團隊同時開發兩個專案:一個是公司的行銷官網(需要 SEO),另一個是客戶的 CRM 系統(登入後使用)。關於技術選型,以下哪個組合最合理? 情境題

  • A. 兩個都用 Next.js,統一技術棧方便維護
  • B. 兩個都用純前端 SPA,減少學習成本
  • C. 行銷官網用純前端 SPA,CRM 用 Next.js
  • D. 行銷官網用 Next.js(需要 SEO),CRM 用純前端 SPA(不需要 SEO,部署更簡單)

4. 同事用 AI 生成了以下入口檔案,但應用啟動後路由切換正常,API 資料卻完全沒有快取效果——每次切換頁面回來都會重新發送 API 請求。最可能的原因是什麼? 錯誤診斷

// main.tsx import { RouterProvider, createRouter } from ‘@tanstack/react-router’ import { routeTree } from ‘./routeTree.gen’ const router = createRouter({ routeTree }) function App() { return <RouterProvider router={router} /> }
  • A. createRouter 少了 queryClient 參數
  • B. 缺少 QueryClientQueryClientProvider,TanStack Query 的快取管理器沒有被設定
  • C. routeTree 沒有正確匯入,導致路由無法載入資料
  • D. 少了 StrictMode 包裹,React 沒有啟用快取功能

5. AI 幫你生成了兩個不同頁面的資料抓取程式碼,但你發現「使用者詳情頁」每次進入都會重新發送 API 請求,即使剛從「使用者列表頁」點進來也一樣。請問最可能的原因是什麼? 錯誤診斷

// 使用者列表頁 const { data: users } = useQuery({ queryKey: [‘users’], queryFn: fetchAllUsers, }) // 使用者詳情頁(點擊列表中的某個使用者後進入) const { data: user } = useQuery({ queryKey: [‘user-detail’, userId], queryFn: () => fetchUser(userId), })
  • A. fetchUser 函式有問題,沒有正確回傳資料
  • B. 兩個頁面使用了不同的 QueryClient 實例
  • C. 兩個頁面的 queryKey 不同(['users'] vs ['user-detail', userId]),TanStack Query 視為不同的快取資料
  • D. TanStack Query 不支援跨頁面的快取共享

發佈留言

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