跳至主要内容

SASS 語法介紹入門教學筆記 | 學習筆記

· 閱讀時間約 5 分鐘
kdchang

SASS(Syntactically Awesome Style Sheets)是一種 CSS 預處理器,旨在提升 CSS 的功能性,讓開發者能夠以更加靈活和高效的方式編寫樣式表。它提供了許多比 CSS 更強大的功能,例如變數、巢狀規則、混合(mixins)、繼承等。SASS 語法有兩種主要形式:一種是較為常見的 SCSS(Sassy CSS),另一種則是 SASS,本文將主要介紹 SASS 語法。

與 SCSS 不同,SASS 語法採用的是縮排語法,即不使用大括號和分號,而是利用縮排來結構化程式碼。這種語法更簡潔,對於習慣 Python 等語言的開發者來說,學習曲線較為平滑。

安裝與設置

要開始使用 SASS,首先需要安裝 SASS 編譯器。最常見的安裝方式是透過 Node.js 的 npm(Node Package Manager)。請按照以下步驟進行安裝:

  1. 確保已經安裝了 Node.js 和 npm。

  2. 打開終端機,並在專案資料夾中執行以下命令來安裝 SASS:

    npm install -g sass
  3. 安裝完成後,可以通過命令行執行 SASS 編譯命令:

    sass input.sass output.css

這樣,input.sass 就會被編譯為標準的 output.css 檔案。

SASS 語法介紹

1. 變數(Variables)

SASS 的變數功能讓開發者可以將顏色、字體大小、間距等常用值儲存在變數中,這樣能夠提高樣式的可重用性,並讓程式碼更具維護性。在 SASS 中,變數以 $ 符號開頭。

$primary-color: #3498db
$font-size: 16px

body
font-size: $font-size
color: $primary-color

在這段程式碼中,我們定義了 $primary-color$font-size 兩個變數,並將其應用於 body 樣式中。這樣,只需要修改變數的值,即可全局更新相應的樣式。

2. 巢狀規則(Nesting)

SASS 支援巢狀規則,這樣可以讓樣式更加結構化,並且簡化對於子元素樣式的編寫。SASS 使用縮排來表示層級關係,這一點與傳統的 CSS 需要寫出完整選擇器有所不同。

nav
background-color: #333
ul
list-style-type: none
padding: 0
li
display: inline-block
a
color: white
text-decoration: none

這段程式碼會被編譯為:

nav {
background-color: #333;
}
nav ul {
list-style-type: none;
padding: 0;
}
nav li {
display: inline-block;
}
nav a {
color: white;
text-decoration: none;
}

這樣的寫法可以避免寫重複的選擇器,並且清晰地表現出元素之間的層級結構。

3. 混合(Mixins)

混合是 SASS 中一個非常有用的功能,它可以將一段可重用的樣式封裝成一個混合,並且可以接受參數,實現樣式的動態應用。混合類似於函數,能夠在不同地方重複使用。

=mixin border-radius($radius)
-webkit-border-radius: $radius
-moz-border-radius: $radius
border-radius: $radius

.box
@include border-radius(10px)

在這段程式碼中,我們定義了一個名為 border-radius 的混合,接受一個 $radius 參數,用來設置元素的邊框圓角。然後,我們在 .box 中使用 @include 引入這個混合,並傳遞具體的參數。這樣,當需要改變邊框圓角的樣式時,只需要修改混合的參數,而不需要在多個地方重複編寫。

4. 繼承(Inheritance)

SASS 提供了繼承功能,使得一個選擇器可以繼承另一個選擇器的樣式。這有助於減少樣式重複,並保持程式碼的整潔性。SASS 使用 @extend 指令來實現繼承。

%button-base
padding: 10px 15px
background-color: #3498db
color: white
border: none
border-radius: 5px

.button-primary
@extend %button-base
background-color: #1abc9c

.button-secondary
@extend %button-base
background-color: #f39c12

在這段程式碼中,我們定義了一個 %button-base 共享樣式,然後讓 .button-primary.button-secondary 繼承這些樣式,並根據需要進行自定義修改。使用繼承的方式可以減少代碼重複,提高樣式的可維護性。

5. 條件語句(Conditionals)

SASS 支援條件語句,這讓我們可以根據某些條件選擇性地應用不同的樣式。這樣可以使樣式表更具動態性,根據不同情境調整顯示效果。

$theme: light

body
@if $theme == light
background-color: #fff
color: #333
@else
background-color: #333
color: #fff

這段程式碼中,根據變數 $theme 的值,條件地設定 body 的背景顏色和文字顏色。如果 $themelight,則使用淺色背景和深色文字;否則,使用深色背景和淺色文字。

6. 循環(Loops)

SASS 還支援循環語句,這在需要根據某些條件自動生成多個樣式時非常有用。SASS 的循環可以遍歷一個列表,並生成對應的樣式。

$colors: red, green, blue

@each $color in $colors
.#{$color}-box
background-color: $color

在這段程式碼中,@each 循環將遍歷 $colors 列表,並為每個顏色生成一個相應的類別 .red-box.green-box.blue-box,並將其背景顏色設置為對應的顏色。

總結

SASS 的 SASS 語法是一種簡潔且強大的 CSS 編寫方式,通過簡單的縮排結構,讓開發者能夠更有效率地編寫和維護樣式。SASS 提供的變數、巢狀規則、混合、繼承等功能,能夠使樣式更加模組化、動態化,並大大減少冗餘代碼。學會 SASS 語法後,你可以更靈活地處理大型專案中的 CSS,提升工作效率。如果你還沒有開始使用 SASS,不妨從這些基礎語法開始,體驗它帶來的便利。

軟體工程師和 Web 前後端開發技能樹/學習藍圖(Roadmap)

· 閱讀時間約 1 分鐘
kdchang

Frontend Roadmap / Frontend Beginner Roadmap

  1. HTML
  2. CSS
  3. JavaScript
  4. Version Control Systems/Git/Github
  5. npm
  6. Vue
  7. React
  8. Angular
  9. TailwindCSS
  10. Vitest
  11. Cypress/Jest
  12. TypeScript
  13. PWA
  14. SASS/LESS/PostCSS
  15. NodeJS

後端

Backend Roadmap / Backend Beginner Roadmap

DevOps

DevOps Roadmap / DevOps Beginner Roadmap

全端

Full Stack Roadmap

參考文件

  1. github developer-roadmap
  2. N+1 Queries 效能問題

網站效能診斷入門教學:Lighthouse、Chrome DevTools 與 WebPageTest 實戰指南 | 學習筆記

· 閱讀時間約 4 分鐘
kdchang

前言

現今網站的使用者體驗與載入速度息息相關,無論是 SEO 排名、使用者留存率或轉換率,都受到頁面效能影響。為了協助開發者精準找出網站瓶頸,Google 與其他組織提供了多種強大的診斷工具,其中最常用且具代表性的就是 Lighthouse、Chrome DevTools 與 WebPageTest。

本篇教學筆記將介紹這三種工具的基礎使用方法與診斷技巧,幫助我們從初學者也能迅速上手,實際分析與優化網站效能。


重點摘要

一、Lighthouse:全方位效能檢測工具

  • Google 提供的開源工具,可分析網站效能、可存取性、SEO、最佳實踐等面向
  • 可直接從 Chrome DevTools 或使用 CLI、CI 工具啟用
  • 評分以 0 ~ 100 呈現,並提供具體優化建議

二、Chrome DevTools:開發者內建利器

  • 即時查看資源載入時間、執行效能、DOM 結構變動與網頁回應
  • 適合追蹤 JavaScript 執行瓶頸、CSS 回流與重繪等細節
  • 提供 Coverage 與 Performance 分析工具,可定位未使用程式碼與長任務

三、WebPageTest:真實環境模擬工具

  • 可選擇不同地區、裝置與網路條件進行測試,模擬真實用戶情境
  • 顯示詳細瀏覽器載入瀑布圖與視覺呈現速度
  • 適合做跨國網站或行動裝置載入效能的驗證

實際範例操作

以下以一個虛擬電商網站首頁為例,展示如何使用三種工具進行效能診斷:


一、使用 Lighthouse 快速評估整體效能

  1. 首先,開啟 Chrome,瀏覽欲診斷的頁面
  2. 按下 F12 或右鍵「檢查」開啟開發者工具
  3. 點選上方分頁中的「Lighthouse」
  4. 選擇測試裝置類型(建議選 Mobile)與檢測類型(建議全選)
  5. 點擊「Generate report」開始分析

分析結果:

  • Performance 得分僅有 45,顯示首次繪製(FCP)與最大內容繪製(LCP)時間過長

  • 建議項目包含:

    • 移除未使用 JavaScript(節省 800KB)
    • 延遲載入非關鍵第三方資源
    • 圖片未壓縮且未啟用 lazy loading

二、使用 Chrome DevTools 找出瓶頸細節

使用 Network 分頁觀察資源載入:

  1. 開啟「Network」分頁並刷新頁面

  2. 可看到:

    • 主頁面共載入 150 多個資源
    • 初始 JavaScript bundle 超過 2MB
    • 字體與圖片資源沒有使用快取(Status 200)

使用 Performance 分頁錄製頁面加載過程:

  1. 點選「Performance」分頁,按下「Record」

  2. 刷新頁面並等待數秒,按下「Stop」

  3. 分析結果顯示:

    • 主執行緒中有多個長任務(超過 50ms)
    • 某第三方追蹤腳本佔用主執行緒 600ms
    • 大量 DOM 節點修改導致 Layout Shift(CLS 分數高)

使用 Coverage 分頁掃描未使用程式碼:

  1. 使用 Ctrl+Shift+P 開啟命令列

  2. 輸入「Coverage」並啟用功能

  3. 點選「Reload & Start Capturing Coverage」

  4. 結果顯示:

    • 主要 CSS 檔案使用率僅 18%
    • JavaScript 檔案使用率 35% 左右

三、使用 WebPageTest 模擬不同環境下的體驗

  1. 進入 https://www.webpagetest.org
  2. 輸入目標網站網址,選擇測試地點(如東京)與設備(如 Android + 3G 網路)
  3. 點擊「Start Test」

分析結果:

  • First Byte Time 達 1.2 秒,伺服器回應偏慢
  • Speed Index 高達 6500,顯示可視畫面建立時間過長
  • 瀑布圖顯示首頁載入依賴超過 50 個 JS 檔案,部分為同步載入

總結與行動建議

網站效能優化並非一次性的修補,而是需要透過持續監控與工具診斷的長期工程。Lighthouse 提供了入門評估的方向,Chrome DevTools 協助你深入定位細節,WebPageTest 則讓你檢視在真實用戶情境中的行為。

建議每次上線新功能或導入第三方資源後,皆定期進行這三種工具的檢測,確保網站在不同網路、不同裝置下都有良好的使用者體驗。

資料庫索引(Index)介紹入門教學筆記 | 學習筆記

· 閱讀時間約 4 分鐘
kdchang

前言

在資料庫中,隨著資料筆數的增加,查詢效率成為一個重要的議題。舉例來說,若在百萬筆資料中查找某一筆特定紀錄,如果沒有任何輔助結構,資料庫就必須逐筆掃描(Full Table Scan),效率極差。為了解決這個問題,資料庫系統引入了「索引(Index)」這個概念,來加速查詢與特定操作。索引的設計與使用,是每一位軟體工程師與資料庫設計者都必須掌握的核心技能。


重點摘要

一、索引的主要功能:

  • 加速查詢(SELECT)
  • 優化條件篩選(WHERE)
  • 提升排序效率(ORDER BY)
  • 支援唯一性約束(UNIQUE)
  • 加快資料關聯查詢(JOIN)

二、常見索引類型:

  • B-Tree Index:最常見的預設索引類型,適合範圍查詢。
  • Hash Index:適合等值查詢(=),不支援範圍查詢。
  • Composite Index(複合索引):由多個欄位組成,適用多條件查詢。
  • Full-text Index:針對全文搜尋設計。
  • Spatial Index:用於地理空間查詢。

三、索引的優點:

  • 提升查詢速度
  • 減少 I/O 操作
  • 加快 JOIN 與 GROUP BY 的效能
  • 強化資料的唯一性驗證

四、索引的缺點:

  • 佔用磁碟空間
  • 寫入(INSERT/UPDATE/DELETE)成本增加
  • 過多或不當索引會影響效能
  • 需定期維護(如重建或重組索引)

五、何時應該使用索引:

  • 查詢常出現的欄位(如常出現在 WHERE、JOIN、ORDER BY 中)
  • 欄位選擇性高(值分布離散,代表這個欄位的值分布非常分散,不同值很多、不重複,如身分證字號)
  • 查詢慢、Table Scan 明顯的情況

六、何時不應使用索引:

  • 小資料表(資料筆數很少)
  • 經常被更新的欄位
  • 欄位選擇性低(如性別只有兩種值)

實際範例

1. 基本使用

以 MySQL 為例,假設有一個 users 表格如下:

CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100),
email VARCHAR(100),
age INT,
created_at DATETIME
);

現在若經常根據 email 來查詢使用者:

SELECT * FROM users WHERE email = 'user@example.com';

若未建立索引,MySQL 將會進行全表掃描。為了加速查詢,我們可以加上索引:

CREATE INDEX idx_email ON users(email);

查詢效率將大幅提升,特別是在資料筆數達到十萬以上的情況。


2. 複合索引

如果我們常查詢條件如下:

SELECT * FROM users WHERE age = 30 AND created_at > '2025-01-01';

可以建立複合索引:

CREATE INDEX idx_age_created ON users(age, created_at);

但注意複合索引有「最左前綴原則」,必須按照索引欄位的排列順序來使用,否則可能無法被使用。

例如:

-- 可使用 idx_age_created
SELECT * FROM users WHERE age = 30;

-- 可使用 idx_age_created
SELECT * FROM users WHERE age = 30 AND created_at > '2025-01-01';

-- 無法使用 idx_age_created
SELECT * FROM users WHERE created_at > '2025-01-01';

3. 檢查查詢是否使用索引

可以利用 EXPLAIN 關鍵字來檢查查詢是否有使用索引:

EXPLAIN SELECT * FROM users WHERE email = 'user@example.com';

key 欄位顯示為 idx_email,表示查詢有使用到索引。


4. 索引對寫入的影響

當對有索引的欄位進行大量更新時,索引也需要同步更新,可能導致效能下降。例如:

UPDATE users SET email = CONCAT(email, '.tw') WHERE age = 30;

此時 email 有索引,會導致該索引也被修改,因此執行效率會受到影響。


5. 刪除不必要的索引

過多的索引會拖慢寫入效能並佔用磁碟空間。可以定期使用以下指令刪除不再使用的索引:

DROP INDEX idx_email ON users;

總結

索引是資料庫查詢效能優化的關鍵利器,但同時也是一把雙面刃。正確使用索引能大幅加速系統效能,不當使用則可能導致資源浪費甚至拖慢效能。實務上應透過實際的查詢分析與監控(如慢查詢日誌、EXPLAIN),謹慎設計與調整索引策略,才能在讀寫效能間取得最佳平衡。

瀏覽器渲染優化入門:掌握重繪、重排與合成的效能關鍵 | 學習筆記

· 閱讀時間約 4 分鐘
kdchang

前言

在現代前端開發中,良好的使用者體驗不僅來自功能與介面設計,更仰賴流暢的渲染效能。網頁在瀏覽器中的呈現涉及一連串複雜的渲染步驟,包含樣式計算、排版、繪製與合成。當我們操作 DOM、修改樣式或觸發動畫時,都可能影響這些渲染流程。如果不了解這些背後的機制,很容易造成畫面卡頓與效能瓶頸。

本篇筆記將介紹瀏覽器渲染的三大核心階段:重排(Reflow)重繪(Repaint)合成(Composite),並透過實際範例說明如何避免過度重排與重繪,提升整體效能。


重點摘要

  • 渲染流程:瀏覽器的渲染過程包括 DOM 構建、樣式計算、布局(Reflow)、繪製(Repaint)與合成(Composite)。

  • Reflow(重排)

    • 代表重新計算元素的尺寸與位置。
    • 會影響該元素及其子元素,成本高昂。
  • Repaint(重繪)

    • 改變元素的外觀但不影響佈局(如背景色、文字顏色)。
    • 相對比重排便宜,但仍需耗費資源。
  • Composite(合成)

    • 將繪製後的圖層合成為畫面。
    • 常與硬體加速 GPU 合作完成,成本最低。
  • 優化原則

    • 減少 DOM 操作與樣式更動次數。
    • 使用 will-change、transform、opacity 等屬性觸發 GPU 合成。
    • 避免觸發同步 reflow(如讀寫 offsetHeight 混用)。
    • 使用 requestAnimationFrame 取代 setInterval 管理動畫。

實際範例

1. 重排(Reflow)觸發範例

<div id="box" style="width: 100px; height: 100px; background-color: blue;"></div>

<script>
const box = document.getElementById('box');
box.style.width = '200px'; // 觸發 reflow:尺寸改變
</script>

這段程式碼會導致重新計算佈局,因為改變了 box 的寬度。若該元素還包含其他內嵌元素,也會一併重新計算,對效能影響較大。

2. 重繪(Repaint)觸發範例

box.style.backgroundColor = 'red'; // 觸發 repaint:外觀改變但位置不變

這只會重新繪製背景色,不涉及位置與尺寸的計算,因此比重排快一些,但仍需資源。

3. 合成層(Composite Layer)優化範例

#box {
will-change: transform;
}
box.style.transform = 'translateX(100px)';

使用 transform 搭配 will-change 可以避免 reflow 與 repaint,直接由 GPU 處理合成層的位移,對效能非常有幫助,適合用於動畫或互動操作。

4. 錯誤的讀寫順序導致多次重排

const box = document.getElementById('box');

// 壞寫法:不當的交錯讀寫
box.style.width = '300px';
const height = box.offsetHeight; // 強制同步計算 reflow
box.style.height = height + 10 + 'px';

這段程式碼會導致瀏覽器為了提供最新的 offsetHeight,先強制執行一次 reflow,然後再處理 style.height 的設定,又引發一次 reflow。解法如下:

// 好寫法:集中寫入,延後讀取
box.style.width = '300px';
requestAnimationFrame(() => {
const height = box.offsetHeight;
box.style.height = height + 10 + 'px';
});

這樣可以將樣式修改與讀取解耦,避免過度同步重排。


常見優化建議

  1. 避免頻繁操作 DOM

    • 將多次操作集中處理,例如使用 documentFragment 或暫時隱藏元素再更新。
  2. 善用 CSS 動畫與硬體加速

    • 使用 transformopacity 搭配 will-change 避免重排。
  3. 使用 requestAnimationFrame 控制動畫節奏

    • 與瀏覽器畫面更新同步,避免畫面撕裂與卡頓。
  4. 減少強制同步 reflow 操作

    • 避免混用讀寫,例如同時使用 offsetTopstyle.left
  5. 審慎使用大型影響範圍的樣式變更

    • 例如更動 <body> padding 可能導致整頁重排。

總結

掌握瀏覽器的渲染流程,是優化前端效能的基礎功。過度重排與重繪是畫面卡頓與耗電的主因,而合成則是成本最低的操作。了解並合理運用這三者,才能打造真正流暢、回應迅速的使用體驗。

作為開發者,我們應從 DOM 操作、CSS 寫法到動畫設計,全面考量渲染機制對效能的影響。透過實踐優化策略,讓產品在各種裝置上都能展現卓越表現。

若我們需要進一步探索,可研究 Chrome DevTools 的「Performance」面板,實際觀察每次重排與重繪的開銷,進一步精細調整效能瓶頸。

Vue v-cloak 入門教學筆記 | 學習筆記

· 閱讀時間約 2 分鐘
kdchang

前言

v-cloak 在 Vue 3 依然存在,作用與 Vue 2 相同:在 Vue 實例或組件掛載完成之前,避免模板中的插值語法(如 {{ message }})閃爍顯示給使用者。搭配 CSS 可在 Vue 掛載完成前隱藏元素,增進首屏體驗。


重點摘要

  • v-cloak 為特殊屬性,Vue 3 掛載完成會自動移除。
  • 必須搭配 CSS [v-cloak] { display: none; } 讓元素在掛載前隱藏。
  • 在 Vue 3,掛載方式改用 createApp
  • 可用於根組件或子組件的根元素。
  • 不會與 v-ifv-show 衝突。

Vue 3 實際範例

<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8" />
<title>Vue 3 v-cloak 範例</title>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app" v-cloak>{{ message }}</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;

createApp({
data() {
return {
message: 'Vue 3 掛載完成!',
};
},
}).mount('#app');
</script>
</body>
</html>

Vue 3 單文件組件 (SFC) 範例

<template>
<div v-cloak>
<p>{{ message }}</p>
</div>
</template>

<script>
export default {
data() {
return {
message: '這是 Vue 3 SFC 範例',
};
},
};
</script>

<style>
[v-cloak] {
display: none;
}
</style>

總結

  • v-cloak 在 Vue 3 的核心概念不變,仍用於避免模板閃爍。
  • 需配合 CSS [v-cloak] 隱藏元素,等 Vue 掛載後自動移除。
  • 掛載方式使用 createApp,搭配 .mount()
  • 可應用於純 HTML 或組件模板中。

TanStack Query 入門教學筆記 | 學習筆記

· 閱讀時間約 4 分鐘
kdchang

前言

在現代前端開發中,資料取得(Data Fetching)與狀態管理是非常重要的課題。傳統上,我們可能會使用 useEffect 搭配 fetchaxios 來手動管理資料請求,並維護載入狀態、錯誤狀態與資料快取等,但這樣往往容易導致重複程式碼、管理複雜以及效能不佳。

TanStack Query(舊名 React Query)是一個專門用來管理伺服器狀態(server state)的函式庫,極大地簡化了資料抓取、快取、同步、重新整理與錯誤處理的流程。它不只支援 React,也能搭配 Vue、Svelte 等其他框架。

這篇筆記將介紹 TanStack Query 的核心概念與使用方式,並提供一個簡單範例,幫助你快速上手。


重點摘要

  • TanStack Query 是什麼? 一個用於管理非同步伺服器資料的 React Hooks 函式庫,能自動處理快取、背景重新整理、錯誤重試等。

  • 主要功能

    • 自動快取資料,減少重複請求。
    • 自動背景刷新(Background Refetching)保持資料最新。
    • 支援資料同步與離線快取。
    • 強大的錯誤重試與錯誤處理機制。
    • 方便整合 Pagination、Infinite Query。
    • 非常輕量且易於擴充。
  • 核心概念

    • Query:對伺服器資料的請求。
    • Query Key:唯一標識一個 Query 的鍵,決定快取與重新抓取。
    • useQuery Hook:用於發起資料請求並取得資料狀態。
    • Query Client:全域管理 Query 狀態與快取。
  • 安裝方式

    npm install @tanstack/react-query
    # 或者
    yarn add @tanstack/react-query
  • 基本使用步驟

    1. 在 React 根組件包裹 <QueryClientProvider>
    2. 使用 useQuery Hook 進行資料請求。
    3. 使用 dataerrorisLoading 等狀態呈現畫面。
  • 常用參數

    • queryKey:快取與識別用的陣列或字串。
    • queryFn:負責非同步取得資料的函式。
    • enabled:是否啟用該 query。
    • staleTime:資料被視為新鮮的時間(避免頻繁重新抓取)。
    • cacheTime:資料快取存在時間。
    • retry:失敗時重試次數。

實際範例

以下以 React 搭配 TanStack Query 實現一個簡單的使用者列表資料抓取為例:

1. 設定 Query Client

在應用程式最外層(通常是 App.jsindex.js)包裹 QueryClientProvider

import React from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import UserList from './UserList';

const queryClient = new QueryClient();

function App() {
return (
<QueryClientProvider client={queryClient}>
<UserList />
</QueryClientProvider>
);
}

export default App;

2. 建立 UserList 元件,使用 useQuery 抓取資料

import React from 'react';
import { useQuery } from '@tanstack/react-query';

async function fetchUsers() {
const res = await fetch('https://jsonplaceholder.typicode.com/users');
if (!res.ok) {
throw new Error('網路錯誤');
}
return res.json();
}

function UserList() {
const { data, error, isLoading, isError } = useQuery(['users'], fetchUsers, {
staleTime: 1000 * 60 * 5, // 5分鐘內資料視為新鮮
retry: 2, // 失敗重試2次
});

if (isLoading) return <div>資料載入中...</div>;

if (isError) return <div>錯誤發生:{error.message}</div>;

return (
<ul>
{data.map((user) => (
<li key={user.id}>
{user.name}{user.email}
</li>
))}
</ul>
);
}

export default UserList;

3. 說明

  • useQuery 第一個參數為 query key,這裡用 ['users'] 表示抓取使用者列表。

  • 第二個參數是非同步函式 fetchUsers,負責呼叫 API 並回傳資料。

  • 返回的物件中包含多種狀態:

    • isLoading:資料還在請求中。
    • isErrorerror:請求失敗時的錯誤狀態與訊息。
    • data:請求成功後的資料。
  • staleTime 設定資料多久內視為新鮮,避免頻繁重新抓取。

  • retry 設定失敗時自動重試的次數。


進階應用

  • 背景重新抓取 使用者回到頁面或重新聚焦視窗時,TanStack Query 預設會重新抓取最新資料,保持資料同步。 可透過 refetchOnWindowFocus 控制是否啟用。

  • 資料快取與共享 不同組件如果使用相同 queryKey,會共用快取資料,避免重複請求。

  • Mutation 除了讀取資料,TanStack Query 也提供 useMutation 用於資料修改(新增、更新、刪除)並自動管理快取更新。

  • 分頁與無限滾動 支援分頁資料抓取的 useInfiniteQuery,適合實作滾動載入。


總結

TanStack Query 是一個強大且方便的資料狀態管理工具,解決了傳統手動管理非同步請求的種種痛點。它自動快取、背景重新整理、錯誤重試等機制,大幅提升開發效率與使用者體驗。

React Custom Hooks 入門教學 | w3schools 學習筆記

· 閱讀時間約 4 分鐘
kdchang

前言

在 React 應用程式中,隨著功能的增加與維護需求的提升,我們常常會遇到多個元件需要共享相同邏輯的情況。為了避免重複撰寫相似程式碼、增加維護成本,React 提供了一個優雅的解法:自訂 Hook(Custom Hook)

自 React 16.8 版本開始,我們能使用 Hook 來管理狀態與副作用,而自訂 Hook 則是讓我們可以把邏輯封裝起來,像一般函式一樣在多個元件中重複使用。這篇文章將介紹如何從實際範例中將撰寫資料抓取的邏輯封裝成一個 Custom Hook,並展示其應用方式。


重點摘要

  • React Hook 是一種能在函式元件中使用的功能,讓你能使用狀態(state)與其他 React 功能。
  • Custom Hook 是自訂的 Hook 函式,其命名必須以 use 開頭(例如:useFetch)。
  • Custom Hook 讓我們可以封裝與重複使用邏輯,而不重複撰寫相同的程式碼。
  • 可透過傳入參數來讓 Hook 更具彈性與重用性。
  • 可將如資料抓取(fetching)、表單邏輯、事件監聽等邏輯封裝成 Custom Hook。

實作範例

初始範例:在元件中撰寫抓取邏輯

以下是一個簡單的 React 元件 Home,它在載入時從 JSONPlaceholder 抓取待辦事項(todo)資料,並顯示在頁面上:

// index.js
import { useState, useEffect } from 'react';
import ReactDOM from 'react-dom/client';

const Home = () => {
const [data, setData] = useState(null);

useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/todos')
.then((res) => res.json())
.then((data) => setData(data));
}, []);

return (
<>
{data &&
data.map((item) => {
return <p key={item.id}>{item.title}</p>;
})}
</>
);
};

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Home />);

這段程式碼可以正常運作,但若我們之後在其他元件中也需要類似的資料抓取功能,就會發現邏輯無法重用,這時就是 Custom Hook 派上用場的時候。


抽出邏輯:撰寫 Custom Hook

我們可以將 fetch 的邏輯抽出成一個名為 useFetch 的自訂 Hook,放在 useFetch.js 檔案中:

// useFetch.js
import { useState, useEffect } from 'react';

const useFetch = (url) => {
const [data, setData] = useState(null);

useEffect(() => {
fetch(url)
.then((res) => res.json())
.then((data) => setData(data));
}, [url]);

return [data];
};

export default useFetch;

這個 Hook 會接收一個 URL 作為參數,並返回從該 URL 抓取到的資料。


使用 Custom Hook 的元件

有了 useFetch 之後,我們就可以在 Home 元件中直接使用這個 Hook,如下所示:

// index.js
import ReactDOM from 'react-dom/client';
import useFetch from './useFetch';

const Home = () => {
const [data] = useFetch('https://jsonplaceholder.typicode.com/todos');

return (
<>
{data &&
data.map((item) => {
return <p key={item.id}>{item.title}</p>;
})}
</>
);
};

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Home />);

說明與分析

這個自訂 Hook 的設計有以下幾個特點:

  1. 命名規則:遵守 React 規範,Hook 函式以 use 開頭(此例為 useFetch),才能被 React 正確識別為 Hook。
  2. 封裝邏輯:將 useStateuseEffect 的邏輯封裝在 useFetch 中,不再重複撰寫於元件內。
  3. 提高重用性:只要傳入不同的 URL,就能從其他 API 抓取資料,使用方式彈性且簡潔。
  4. 維護簡便:若日後需要修改資料抓取的邏輯,只需在 useFetch.js 中調整一次,即可影響所有使用該 Hook 的元件。

總結

自訂 Hook 是 React 開發中非常實用的工具,讓我們能夠撰寫模組化、可重複使用且容易維護的邏輯。當你在多個元件中發現重複的邏輯(如資料抓取、訂閱事件、表單處理等),就可以考慮將其封裝成一個 Custom Hook,提升程式碼品質與開發效率。

透過本文介紹的 useFetch 範例,你應該能夠掌握如何將常見的副作用邏輯抽離為一個可重用的 Hook,也歡迎在實務中嘗試建立更多適合自己應用場景的自訂 Hook。


如果你想了解更多關於資料抓取的技術細節,可以進一步學習 JavaScript Fetch API 的用法,並搭配錯誤處理(如 .catch())與 loading 狀態等進行擴充。這將使你的 Custom Hook 更加完善。

參考文件

  1. React Custom Hooks

React Hooks useMemo 入門教學 | w3schools 學習筆記

· 閱讀時間約 3 分鐘
kdchang

前言

在 React 的開發中,我們常會遇到一些運算成本高昂(expensive)的函式,例如複雜的計算或操作大量資料。如果這些運算每次重新渲染都會重新執行,可能會導致效能低落,甚至造成使用者界面的卡頓或延遲。此時,我們可以使用 React 的 useMemo Hook,透過「記憶化」(memoization)技術,避免不必要的重新計算,從而提升效能。

本文將說明 useMemo 的用途、使用時機,並透過實際範例展示其效能優化的實際效果。


重點摘要

  • useMemo 是 React 提供的 Hook,用來「記憶化」一個計算結果,僅在依賴(dependencies)變動時才重新計算。

  • 它的語法為:useMemo(() => { return value }, [dependencies])

  • useMemo 適合用在:

    • 資源密集的計算
    • 必須避免重複執行的運算
  • useMemouseCallback 類似,但:

    • useMemo 回傳的是 記憶化的值
    • useCallback 回傳的是 記憶化的函式
  • 搭配依賴陣列使用,確保只在必要時重新計算


範例一:未使用 useMemo,效能不佳

以下是一個基本範例,展示若每次重新渲染都執行一次昂貴的計算函式 expensiveCalculation,會造成延遲的情形:

import { useState } from 'react';
import ReactDOM from 'react-dom/client';

const App = () => {
const [count, setCount] = useState(0);
const [todos, setTodos] = useState([]);
const calculation = expensiveCalculation(count); // 每次 render 都執行

const increment = () => {
setCount((c) => c + 1);
};
const addTodo = () => {
setTodos((t) => [...t, 'New Todo']);
};

return (
<div>
<div>
<h2>My Todos</h2>
{todos.map((todo, index) => (
<p key={index}>{todo}</p>
))}
<button onClick={addTodo}>Add Todo</button>
</div>
<hr />
<div>
Count: {count}
<button onClick={increment}>+</button>
<h2>Expensive Calculation</h2>
{calculation}
</div>
</div>
);
};

const expensiveCalculation = (num) => {
console.log('Calculating...');
for (let i = 0; i < 1000000000; i++) {
num += 1;
}
return num;
};

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

問題說明:

當我們只是點擊「Add Todo」按鈕,實際上並未更動 count,但昂貴的 expensiveCalculation(count) 卻仍然被重新執行,造成整體效能低落。


範例二:使用 useMemo 優化效能

接下來我們使用 useMemo 來包裝昂貴的計算函式,使其僅在 count 變動時才重新執行:

import { useState, useMemo } from 'react';
import ReactDOM from 'react-dom/client';

const App = () => {
const [count, setCount] = useState(0);
const [todos, setTodos] = useState([]);
const calculation = useMemo(() => expensiveCalculation(count), [count]);

const increment = () => {
setCount((c) => c + 1);
};
const addTodo = () => {
setTodos((t) => [...t, 'New Todo']);
};

return (
<div>
<div>
<h2>My Todos</h2>
{todos.map((todo, index) => (
<p key={index}>{todo}</p>
))}
<button onClick={addTodo}>Add Todo</button>
</div>
<hr />
<div>
Count: {count}
<button onClick={increment}>+</button>
<h2>Expensive Calculation</h2>
{calculation}
</div>
</div>
);
};

const expensiveCalculation = (num) => {
console.log('Calculating...');
for (let i = 0; i < 1000000000; i++) {
num += 1;
}
return num;
};

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

優化結果:

  • 當你按下「Add Todo」時,不再觸發 expensiveCalculation
  • 僅當 count 變動時,才會重新執行昂貴的計算
  • 大幅減少不必要的運算與效能浪費

總結

useMemo 是 React 提供用來優化效能的重要工具,尤其在面對昂貴計算或複雜資料處理時,能有效避免不必要的運算。雖然 useMemo 不該過度使用於簡單運算,但在大型應用中,對於效能的提升非常有幫助。

使用時機建議:

  • 當某個函式或邏輯的執行成本高昂
  • 該邏輯只需在某些依賴值變化時執行一次
  • 搭配 useMemo 可減少重複運算、提升 UI 響應速度

記得:不要為了使用而使用,只有在效能瓶頸或實際需求下,useMemo 才能發揮其最大效益。

參考文件

  1. React Custom Hooks