前端開發指南
概述
本指南涵蓋使用 React、Vue、Angular 和 Svelte 的現代前端開發,包括元件架構、效能最佳化、無障礙性和測試。在建構網頁應用程式、元件庫或最佳化前端效能時使用。
框架與佈局決策規則
- 選擇框架時,應根據團隊專業知識和專案限制來搭配;若需要廣泛生態系統,預設使用 React;若要對現有頁面進行漸進增強,使用 Vue;若應用程式對套裝大小極為敏感,使用 Svelte;若專案需要具備內建依賴注入和路由的意見式完整框架,使用 Angular。
- 實作設計時,對二維頁面佈局使用 CSS Grid,對一維元件對齊使用 Flexbox;避免使用絕對定位進行佈局,因為它會破壞響應式重排。
- 建構元件庫時,每個元件應以具 TypeScript props 介面的命名匯出、一個 Storybook 故事和一個單元測試的形式公開——缺少三者中任何一項的元件不會被合併。
- 與後端 API 整合時,將 fetch 邏輯集中到一個型別化的 API 客戶端層(例如單一
api.ts模組,使用fetch或axios搭配攔截器),以便在一個地方處理驗證標頭、錯誤轉換和重試。
效能決策規則
- 當頁面的 Largest Contentful Paint 在 Lighthouse CI 中超過 2.5 秒時,將其視為阻塞性錯誤——使用 Chrome DevTools Performance 標籤進行分析,並在合併前修復最大的瓶頸。
- 加入動畫時,使用 CSS
transform和opacity屬性(僅合成器),而不是width、height或top/left,以避免觸發導致卡頓的佈局重新計算。 - 當應用程式需要離線支援時,註冊一個 Service Worker,對靜態資源使用快取優先策略,對 API 請求使用網路優先策略,並在離線時回退到快取的回應。
- 當初始 JS 套裝超過預算(例如 gzipped 後 200 KB)時,使用
React.lazy()或動態import()新增基於路由的程式碼分割,並將非關鍵腳本延遲到摺疊之後。 - 支援舊版瀏覽器時,定義一個 browserslist 設定,讓建置工具(Vite、Webpack)自動填充 polyfill;除非 browserslist 覆蓋範圍不足,否則不要手動添加 polyfill 或功能檢查。
- 加入圖片時,使用帶有 WebP/AVIF 來源和明確
width/height屬性的<picture>,以防止佈局偏移;對於摺疊下方的圖片,添加loading="lazy"。 - 當某個路由在初始頁面載入時不需要,將其包裹在
React.lazy()(或框架等效項)中,並添加<Suspense>回退,以便主套裝排除該路由的程式碼。 - 提供靜態資源時,設定 CDN 或伺服器在內容雜湊檔案名稱上設置
Cache-Control: public, max-age=31536000, immutable,並在index.html上設置no-cache。
無障礙性決策規則
- 建構任何互動元件時,添加 ARIA 屬性、鍵盤處理器(適當時使用
Enter、Space、Escape),並在標記任務完成前使用 axe-core 進行測試。 - 建構表單時,透過
htmlFor/id將每個<input>與<label>關聯,提供透過aria-describedby連結的可見錯誤訊息,並確保表單完全可透過僅鍵盤導航操作。 - 使用顏色傳達含義時(例如錯誤狀態、狀態徽章),始終包含次要指示符號(圖示、文字、圖案),以便色盲使用者能區分狀態。
- 添加模態框或下拉選單時,在其開啟時將焦點限制在該元素內,並在關閉時將焦點返回觸發元素;透過不使用滑鼠的 tab 鍵操作測試整個流程。
- 當 Pull Request 新增一個新的互動元件時,PR 必須包含一個 axe-core 整合測試,該測試斷言零個 WCAG 2.1 AA 違規,才能被合併。
CSS 除錯決策規則
當佈局出現問題時,根據症狀進行診斷:
- Flex 子項溢出其容器:為 Flex 子項添加
min-width: 0(行)或min-height: 0(列)。Flex 項目的min-width預設為auto,這會防止它們縮小到內容大小以下。 - Grid 項目忽略欄寬:使用
minmax(0, 1fr)而不是1fr。單純的1fr表示minmax(auto, 1fr),這會讓內容將欄位推得比預期更寬。 position: sticky沒有固定:檢查每個祖先元素是否有overflow: hidden、overflow: auto或overflow: scroll。任何這些屬性都會建立一個新的滾動上下文,包含該 sticky 元素。同時確認該元素已設定top/bottom值。- 使用
margin: auto置中的元素未置中:確認該元素有明確的width(區塊元素)或父元素有display: flex(Flex 子項)。margin: auto在滿寬的區塊元素上無效。 z-index不起作用:該元素需要position: relative/absolute/fixed/sticky來建立堆疊上下文。同時檢查是否有祖先元素建立了堆疊上下文(透過transform、opacity < 1、filter或will-change),這會限制z-index的作用範圍。- 行內區塊/行內元素之間的間隙:HTML 中的空白會產生間隙。修復方法:在父元素上使用
display: flex(首選),或在父元素上設定font-size: 0並在子元素上重設。 - 相鄰元素之間的 1px 間隙:子畫素渲染問題。修復方法:除錯時使用
outline而非border,或在父元素上設定background以遮蓋間隙。 - 文字截斷省略符號無效:需要同時滿足三個條件:
overflow: hidden、white-space: nowrap、text-overflow: ellipsis。對於多行截斷,請使用-webkit-line-clamp搭配display: -webkit-box和-webkit-box-orient: vertical。 - 容器查詢未生效:確認祖先元素有
container-type: inline-size(或size)。元素本身不能是自己的容器——查詢必須引用一個父元素。
建置工具決策規則
- Vite(預設):用於所有新專案。透過原生 ESM 實現即時開發伺服器、快速 HMR、基於 Rollup 的生產建置。如果專案使用在 Vite 下會失敗的 CommonJS-only 依賴項,請為那些套件添加
optimizeDeps.include——不要切換到 Webpack。 - Webpack:僅用於已在使用它的現有專案,或當特定的 Webpack-only 外掛沒有 Vite 等效項時(很少見)。有機會時遷移到 Vite(參閱遷移決策規則)。
- Turbopack:僅在 Next.js 14+ 內部使用(
next dev --turbo)。目前無法獨立使用。為 Next.js 專案提供比 Webpack 更快的開發建置。如果建置速度已經很快(<5 秒),則不需要切換。 - esbuild:用於函式庫打包、腳本或 CLI 工具——不適用於完整應用程式。沒有 HTML 入口點處理,沒有按路由的程式碼分割。使用
tsup(esbuild 包裝器)來發布 npm 套件。 - SWC vs Babel:使用 SWC(透過 Vite 外掛或 Next.js 預設)。SWC 的轉換速度快 20-70 倍。僅當專案依賴沒有 SWC 等效項的 Babel-only 外掛時才使用 Babel(這種情況越來越少)。
- Monorepo 工具:<5 個套件 = 使用 npm/pnpm workspaces,無需額外工具。5-20 個套件 = 使用 Turborepo 進行任務快取。>20 個套件或多語言專案 = 使用 Nx。切勿對單一套件專案添加 monorepo 工具。
SSR 與伺服器元件決策規則
何時使用 SSR vs CSR vs SSG
- SSG(靜態網站生成):用於每小時變化少於一次的內容(行銷頁面、文件、部落格文章)。在部署時建置。提供最快的 TTFB。
- SSR(伺服器端渲染):用於個人化內容(儀表板、使用者個人資料)、具有動態資料的 SEO 關鍵頁面,或無法接受過時資料的頁面。每個請求都會增加伺服器延遲——使用
Cache-Control或 CDN 積極快取。 - CSR(客戶端渲染):用於僅限已驗證使用者的頁面(管理面板、內部工具),這類頁面 SEO 不重要且互動性是首要考慮。最簡單的建置和部署方式(靜態託管)。
- ISR(增量靜態生成):用於大部分靜態但需要定期更新的頁面(產品列表、定價頁面)。根據資料可以過時的程度設定
revalidate間隔:價格用 60 秒,部落格文章用 3600 秒。
React Server Components (RSC) 決策規則
- 預設使用 Server Components:每個元件預設為 Server Component,除非它需要互動性。如果一個元件有
onClick、useState、useEffect或瀏覽器 API——它必須是一個 Client Component('use client')。 - 資料取得:在 Server Component 中直接用
async/await取得資料——不需要useEffect、不需要 TanStack Query、不需要載入狀態。資料在 HTML 發送前就已解析完成。 - Server Component 中的授權:在 Server Component 中讀取 session/cookie,並將使用者物件作為 prop 傳遞給 Client Component。永遠不要在 Client Component 中讀取 cookie 或 session——它們在客戶端執行。
- 何時使用 Server Actions vs API routes:對於從 UI 呼叫的表單提交和變更操作,使用 Server Actions(
'use server')。對於 Webhook、第三方整合或外部服務呼叫的端點,使用 API routes(/api/*)。 - 在 Server 和 Client Component 之間共享狀態:將可序列化的資料作為 props 從 Server 傳遞到 Client。如果需要從伺服器資料衍生的客戶端狀態,用伺服器 prop 初始化
useState,並在客戶端管理更新。 - 大型資料集:使用
<Suspense>邊界進行串流。將慢速的 Server Component 包裹在<Suspense fallback={<Skeleton />}>中。外殼會立即渲染;慢速元件在準備好時串流進來。將 Suspense 邊界放在有意義的 UI 區塊(側邊欄、主要內容、評論)——而不是每個元件周圍。
Hydration 錯誤決策樹
- 錯誤:「Text content does not match」:伺服器和客戶端渲染之間的值不同。常見原因:
- 在渲染中使用
Date.now()、Math.random()或new Date().toLocaleString()→ 移至useEffect+useState。 typeof window !== 'undefined'條件渲染 → 使用useEffect設定一個hasMounted標記。- 瀏覽器擴充功能注入 DOM 節點 → 不是你的錯誤,但在無痕模式中測試以確認。
- 在渲染中使用
- 錯誤:「Hydration failed because the server rendered HTML didn't match」:結構性差異(不同的元素)。常見原因:
<p>巢狀在<p>內,或<div>巢狀在<p>內 → 修正巢狀,HTML 規範不允許。- 第三方元件在伺服器和客戶端上渲染不同 → 包裹在
dynamic(() => import(...), { ssr: false })中。 - HTML 需要明確關閉標籤時缺少閉合標籤或自閉合標籤。
效能除錯工作流程
當頁面速度慢時,請依序執行以下步驟——不要跳過任何步驟:
步驟 1:識別瓶頸類型
- 執行 Lighthouse。檢查哪個指標失敗:
- LCP >2.5 秒:最大的可見元素載入太慢。前往步驟 2。
- FID/INP >200 毫秒:使用者互動被 JavaScript 阻塞。前往步驟 3。
- CLS >0.1:初始繪製後發生佈局偏移。前往步驟 4。
步驟 2:修復 LCP
- 檢查 LCP 元素是什麼(Lighthouse 會顯示)。通常是:英雄圖片、標題文字或影片。
- 如果是圖片:添加
priority(Next.js)或fetchpriority="high"。添加明確的width/height。以 WebP/AVIF 格式提供。如果透過 CDN 提供,驗證快取標頭。 - 如果是文字:檢查是否有阻塞渲染的字型。使用
font-display: swap或font-display: optional。使用<link rel="preload" as="font" crossorigin>預載關鍵字型檔案。 - 如果被 JS 阻塞:套裝太大。檢查步驟 3。LCP 元素必須等到建立它的 JS 被載入並執行後才能渲染。
- 檢查伺服器回應時間(TTFB)。如果 TTFB >600 毫秒,瓶頸在後端或 CDN——而不是前端。
步驟 3:修復 INP(Interaction to Next Paint)
- 打開 Chrome DevTools → Performance → 錄製一次點擊/輸入互動。
- 找到長任務(>50 毫秒的黃色條)。點擊它查看呼叫堆疊。
- 如果長任務是你的程式碼:使用
requestIdleCallback、scheduler.yield()(Chrome 115+)或setTimeout(fn, 0)將工作分解成較小的區塊,以便在畫面之間讓步給瀏覽器。 - 如果長任務是React 重新渲染:打開 React DevTools Profiler。找到重新渲染的元件。常見修復方法:
React.memo(昂貴的子項)、useMemo(衍生的值),或將狀態移到更接近使用它的地方。如果單一狀態更改導致超過 500 個元件重新渲染,則狀態在樹中的位置過高。 - 如果長任務是第三方腳本(分析工具、廣告):使用
async或defer屬性延遲載入,或在requestIdleCallback之後載入。
步驟 4:修復 CLS
- 檢查哪個元素偏移(Lighthouse 會顯示它,或使用 DevTools →「Layout Shift Regions」)。
- 沒有尺寸的圖片/影片:添加
width和height屬性(或aspect-ratioCSS)。瀏覽器會在資源載入前保留空間。 - 字型替換:後備字型與網路字型有不同的度量。使用
@font-face中的size-adjust來匹配度量,或使用font-display: optional完全消除替換。 - 動態內容注入到摺疊上方:廣告、橫幅、Cookie 通知。使用容器的
min-height保留空間。如果內容高度變化,使用contain: layout防止偏移傳播。 - 延遲載入的 CSS:如果樣式表在初次繪製後載入並改變了可見佈局,請內聯關鍵 CSS 或預載樣式表。
程式碼品質決策規則
- 撰寫測試時,要求每個元件至少有一個涵蓋其主要渲染路徑的單元測試和一個互動測試(點擊、鍵盤)——透過 CI 覆蓋率閾值 80% 的程式碼行覆蓋率來強制執行。
- 啟動專案時,從第一天起在
tsconfig.json中啟用strict: true;隨著程式碼庫增長,事後強制啟用嚴格模式會變得呈指數級困難。 - 當 API 呼叫或非同步操作失敗時,顯示一個使用者可見的錯誤訊息,並附帶重試操作——永遠不要默默吞嚥錯誤或顯示原始的例外文字。