跳至主要内容

Git Flow 介紹與 GitHub Flow 介紹入門教學筆記 | 學習筆記

· 閱讀時間約 5 分鐘
kdchang

在軟體開發中,版本控制是協作開發中至關重要的工具。Git 作為當前最受歡迎的版本控制系統,其強大的分支管理和版本控制功能,使得開發團隊能夠高效地進行協作。兩種常見的 Git 工作流是 Git Flow 和 GitHub Flow,這些工作流提供了清晰的分支管理策略,讓團隊可以更加有序地開發和部署代碼。本文將介紹 Git Flow 和 GitHub Flow 的基本概念、使用方法,以及它們之間的區別。

一、Git Flow 介紹

Git Flow 是一個基於 Git 的分支管理模型,主要由 Vincent Driessen 提出,旨在幫助開發團隊實現規範化的分支管理,並對不同開發階段的代碼進行有效的管理。Git Flow 工作流適合需要頻繁發布新版本的項目,並且開發流程中包含多個階段,如開發、測試、發佈等。其工作流程通常包含以下幾個核心分支:

1.1 主要分支

  1. master 分支master 分支是 Git Flow 中的核心分支,所有穩定的代碼都應該存在於此。每當一個新版本的代碼準備好發佈時,都會從 develop 分支合併到 master,並標記相應的版本號。這個分支通常只包含穩定的代碼,並且代表了隨時可以部署的代碼。

  2. develop 分支develop 分支用於開發中的代碼。所有的功能開發都應該從這個分支進行,並且當開發完成後,合併回 develop。這個分支是整個項目的集成區,開發人員應該從 develop 分支開始開發新功能。

1.2 輔助分支

  1. feature 分支feature 分支用於開發新的功能。每個新功能都應該從 develop 分支創建一個新的 feature 分支,並且在功能開發完成後,將該分支合併回 develop 分支。feature 分支的命名通常為 feature/<功能名稱>

  2. release 分支: 當 develop 分支上的代碼準備好進入測試階段時,會創建一個 release 分支。這個分支用來準備最終的發佈版本。在 release 分支上進行的主要工作包括 bug 修復、文檔編寫等,確保最終版本的穩定性。當測試完成並且代碼準備發佈時,release 分支會被合併到 masterdevelop 分支。

  3. hotfix 分支hotfix 分支用於修復已經發佈到 master 分支的緊急錯誤。當發佈的版本發現重大問題時,可以立即從 master 分支創建 hotfix 分支,進行修復後,將其合併回 masterdevelop 分支。

1.3 Git Flow 的流程

Git Flow 的流程大致如下:

  • 開發新功能時,從 develop 分支創建 feature 分支。
  • feature 分支開發完成後,合併回 develop 分支。
  • 當代碼達到穩定版本,創建 release 分支,進行測試和 bug 修復。
  • 測試完成後,將 release 分支合併回 masterdevelop 分支,並標註版本。
  • 若發現緊急錯誤,從 master 分支創建 hotfix 分支,進行修復並合併回 masterdevelop

二、GitHub Flow 介紹

GitHub Flow 是 GitHub 提出的另一種工作流,主要針對快速迭代和持續集成的開發模式。與 Git Flow 複雜的分支管理相比,GitHub Flow 更加簡單和輕量,適合需要快速交付和頻繁部署的團隊。GitHub Flow 主要有以下幾個步驟:

2.1 GitHub Flow 的基本流程

  1. 創建分支: 每當開始一個新功能或修復一個 bug 時,都應該從 main 分支創建一個新的分支。這個分支通常用來開發新的特性或修復某個問題。

  2. 開發與提交: 在新創建的分支上進行開發,並定期將代碼提交到遠程倉庫。這些提交應該是增量的,便於代碼審查和測試。

  3. 發送 Pull Request: 當開發完成後,將自己的分支提交為 Pull Request(PR),並請求團隊成員進行代碼審查。PR 是 GitHub Flow 中的關鍵步驟,能夠確保代碼的質量和協作。

  4. 代碼審查與測試: 團隊成員進行代碼審查,確保代碼的質量和功能正確性。GitHub Flow 推崇頻繁的測試和集成,因此在 Pull Request 發送後,通常會自動觸發 CI(持續集成)測試,檢查代碼是否有錯誤。

  5. 合併分支: 經過代碼審查和測試後,Pull Request 被批准,並將代碼合併到 main 分支。此時,新的代碼版本已經可以進行部署。

  6. 部署與發佈: GitHub Flow 的一大優勢是可以實現持續部署。當代碼合併到 main 分支後,可以立即部署到生產環境,快速交付新版本。

2.2 GitHub Flow 的特點

GitHub Flow 的流程非常簡單,沒有像 Git Flow 那樣的複雜分支結構,適合小型和中型的項目。它的優點在於快速迭代和持續交付,並且便於團隊成員協作。GitHub Flow 更加強調分支管理的簡化,並且強調測試和部署的自動化。

三、Git Flow 與 GitHub Flow 的比較

  • 分支結構: Git Flow 有多個分支(masterdevelopfeaturereleasehotfix),適用於需要穩定版本發佈的團隊。而 GitHub Flow 只有 main 分支和特性分支,適用於快速迭代和持續集成的開發模式。

  • 適用場景: Git Flow 更適合複雜的開發流程,尤其是有多個版本和發佈周期的項目。GitHub Flow 更適合快速迭代的小型或中型項目,尤其是需要頻繁部署和交付的團隊。

  • 流程複雜度: Git Flow 的流程較為繁瑣,對於小型團隊來說可能會顯得過於複雜。GitHub Flow 流程簡單,適合希望保持敏捷和快速交付的團隊。

結論

Git Flow 和 GitHub Flow 各有優缺點,適用的場景也有所不同。選擇哪一種工作流,應根據團隊的規模、項目的複雜度以及發佈頻率來決定。對於需要穩定發佈和多版本管理的項目,Git Flow 是一個更好的選擇。而對於需要快速迭代和頻繁部署的小型項目,GitHub Flow 則是一個更簡單有效的選擇。

滾動視差(Parallax Scrolling)網頁設計入門教學筆記 | 學習筆記

· 閱讀時間約 5 分鐘
kdchang

滾動視差(Parallax Scrolling)是一種前端網頁設計技術,透過不同層的背景與內容以不同的速度滾動,營造出立體感與動態視覺效果。這種技術常見於品牌網站、產品介紹頁面或作品展示頁面,能有效提升使用者的沉浸感與互動體驗。

滾動視差的視覺效果

  1. 背景與內容滾動速率不同:背景滾動速度較慢,前景滾動速度較快,模擬景深效果。
  2. 多層次視覺變化:不同層的元素可以有獨立的滾動行為,增加動態感。
  3. 創造故事性:透過滾動觸發不同場景,讓使用者體驗連貫的視覺敘事。

二、滾動視差的基本實作

1. 使用純 CSS 來製作滾動視差

CSS 提供了 background-attachment: fixed; 屬性,可以讓背景圖像保持固定位置,而前景內容繼續滾動,營造簡單的滾動視差效果。

<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>滾動視差示例</title>
<style>
body, html {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
}

.parallax {
background-image: url('parallax-background.jpg');
height: 100vh;
background-attachment: fixed;
background-position: center;
background-repeat: no-repeat;
background-size: cover;
}

.content {
height: 500px;
background-color: white;
text-align: center;
padding: 50px;
}
</style>
</head>
<body>

<div class="parallax"></div>

<div class="content">
<h2>滾動視差示例</h2>
<p>這是一個簡單的滾動視差網頁,背景固定不動,前景內容滾動。</p>
</div>

<div class="parallax"></div>

</body>
</html>

這種方式雖然簡單,但無法實現更複雜的視差動畫效果。如果想要更進階的效果,通常需要搭配 JavaScript 或 CSS 變形(Transform)。


2. 使用 CSS transform 來實現滾動視差

透過 transform: translateZ() 搭配 perspective(),可以製造出更立體的視差效果。

.parallax-container {
perspective: 1px;
height: 100vh;
overflow-x: hidden;
overflow-y: auto;
}

.parallax-layer {
position: absolute;
width: 100%;
transform: translateZ(-1px) scale(2);
}

這種方式可以讓不同層的元素有不同的滾動速率,達到更細緻的視覺效果。


三、使用 JavaScript 製作更靈活的滾動視差

雖然純 CSS 可以實現基本視差效果,但若要控制不同層級的滾動速度,或者添加額外動畫效果,就需要 JavaScript。

1. 簡單的 JavaScript 滾動視差

使用 window.scrollY 來控制元素的 transform 屬性,使其隨滾動變化。

<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript 滾動視差</title>
<style>
body {
margin: 0;
font-family: Arial, sans-serif;
}
.parallax {
position: relative;
height: 100vh;
overflow: hidden;
background: url('parallax-background.jpg') center/cover no-repeat;
}
.text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 2rem;
color: white;
}
</style>
</head>
<body>

<div class="parallax">
<div class="text">滾動視差效果</div>
</div>

<script>
window.addEventListener('scroll', function() {
let scrollPosition = window.scrollY;
document.querySelector('.parallax').style.backgroundPositionY = scrollPosition * 0.5 + 'px';
});
</script>

</body>
</html>

在這個範例中,我們透過 JavaScript 讓背景圖片根據滾動位置改變 backgroundPositionY,從而達成視差效果。


2. 使用第三方函式庫(如 ScrollMagic)

如果想要更進階的視差效果,可以使用 ScrollMagic 這類 JavaScript 函式庫,來更精確地控制滾動動畫。

首先安裝 ScrollMagic:

npm install scrollmagic

然後在 JavaScript 中設定滾動觸發點:

let controller = new ScrollMagic.Controller();
let scene = new ScrollMagic.Scene({
triggerElement: '.parallax',
duration: '100%',
triggerHook: 0
})
.setTween('.parallax', { y: '50%', ease: Linear.easeNone })
.addTo(controller);

這樣可以讓 .parallax 元素在滾動時逐漸移動,達成視差效果。


四、滾動視差的應用場景

滾動視差可以應用於許多不同類型的網站,例如:

  1. 品牌網站:用來展示產品特性與公司理念,提高視覺吸引力。
  2. 作品集網站:適合攝影師、設計師,讓內容更具層次感。
  3. 故事敘述頁面:用來製作互動式故事,讓用戶在滾動中探索內容。
  4. 促銷與行銷頁面:讓特定區塊在滾動時突出,吸引目光。

五、滾動視差的優勢與缺點

優勢

  • 增強視覺吸引力,使網站更具互動性。
  • 可提升品牌形象,讓內容更具故事性。
  • 創造更流暢的使用者體驗,增加網站的停留時間。

缺點

  • 過多視差效果可能會影響效能,導致頁面滾動不流暢。
  • 可能對行動裝置不友善,需要額外的優化。
  • 過度使用可能會影響可讀性,降低使用者體驗。

六、結論

滾動視差是一種有效提升網站視覺吸引力的技術,適用於品牌展示、作品集與故事敘述類型的網站。透過 CSS 的 background-attachment,JavaScript 的 scrollY,或是使用函式庫如 ScrollMagic,都能實現不同程度的視差效果。

然而,使用時需注意效能與可讀性,確保不會影響使用者體驗。在實作時,建議先從簡單的 CSS 視差效果開始,逐步引入 JavaScript 或函式庫,根據需求選擇最佳實作方式。

TypeScript 入門教學筆記 | 學習筆記

· 閱讀時間約 4 分鐘
kdchang

TypeScript 是由 Microsoft 開發的 JavaScript 超集(Superset),提供靜態型別檢查(Static Type Checking),讓開發者在編譯時發現錯誤,提升程式碼的可靠性與可維護性。TypeScript 會被編譯為標準 JavaScript,並可以運行於任何支援 JavaScript 的環境,例如瀏覽器或 Node.js。

TypeScript 的主要特點

  1. 靜態型別檢查:在開發階段偵測類型錯誤,減少潛在錯誤。
  2. 強大的 IDE 支援:提供自動補全、型別推斷等功能,提高開發效率。
  3. 物件導向特性:支援類別(Class)、介面(Interface)、泛型(Generics)等功能。
  4. 相容 JavaScript:可以與現有的 JavaScript 程式碼共存,逐步導入 TypeScript。
  5. 模組化開發:支援 ES6+ 模組系統,便於管理大型應用程式。

二、安裝與設定 TypeScript

1. 安裝 TypeScript

可以使用 npm 來安裝 TypeScript:

npm install -g typescript

安裝完成後,可以使用以下指令來檢查 TypeScript 版本:

tsc -v

2. 初始化 TypeScript 專案

在專案目錄中執行以下指令,產生 tsconfig.json 設定檔:

tsc --init

這個設定檔可以調整 TypeScript 的編譯選項,例如輸出目錄、是否允許隱式 any 類型等。


三、基本語法

1. 變數與型別

TypeScript 透過 : 來指定變數的型別:

let name: string = 'Alice';
let age: number = 25;
let isStudent: boolean = true;

也可以使用 any 來允許變數接受任何型別:

let value: any = 'Hello';
value = 123; // 不會報錯

2. 陣列與元組

可以使用 type[] 來定義陣列型別:

let numbers: number[] = [1, 2, 3, 4];
let names: string[] = ['Alice', 'Bob'];

元組(Tuple)允許指定固定數量與型別的元素:

let person: [string, number] = ['Alice', 25];

3. 物件與介面

可以使用 interface 來定義物件的結構:

interface Person {
name: string;
age: number;
isStudent?: boolean; // 可選屬性
}

let user: Person = {
name: 'Alice',
age: 25,
};

4. 函式與型別

函式的參數與回傳值可以明確指定型別:

function add(a: number, b: number): number {
return a + b;
}

若函式沒有回傳值,可以使用 void

function logMessage(message: string): void {
console.log(message);
}

也可以使用箭頭函式語法:

const multiply = (a: number, b: number): number => a * b;

5. 型別別名(Type Alias)

type 關鍵字可以為型別取別名:

type ID = string | number;
let userId: ID = 123;

四、進階語法

1. Enum(列舉型別)

enum 允許定義一組具有特定名稱的數值:

enum Direction {
Up = 1,
Down,
Left,
Right,
}

let move: Direction = Direction.Up;

2. 泛型(Generics)

泛型允許函式或類別支援不同的型別,提高可重用性:

function identity<T>(value: T): T {
return value;
}

console.log(identity<string>('Hello'));
console.log(identity<number>(123));

泛型也可用於類別:

class Box<T> {
content: T;
constructor(value: T) {
this.content = value;
}
}

let stringBox = new Box<string>('TypeScript');
let numberBox = new Box<number>(100);

3. 類別與繼承

TypeScript 提供完整的類別支援,與 JavaScript ES6 類似:

class Person {
name: string;
constructor(name: string) {
this.name = name;
}
greet(): void {
console.log(`Hello, my name is ${this.name}`);
}
}

let alice = new Person('Alice');
alice.greet();

繼承類別:

class Student extends Person {
studentId: number;
constructor(name: string, studentId: number) {
super(name);
this.studentId = studentId;
}
}

let bob = new Student('Bob', 123);
bob.greet();

4. 非空斷言(Non-null Assertion)

可以使用 ! 來告訴 TypeScript 變數一定不會是 nullundefined

let value: string | null = 'Hello';
console.log(value!.length);

五、TypeScript 與 JavaScript 的比較

特性JavaScriptTypeScript
型別檢查靜態型別檢查
介面有介面與型別定義
類別與繼承ES6+ 支援支援完整 OOP
泛型支援泛型開發
Enum 列舉有內建 Enum
編譯時錯誤可提前檢查錯誤

六、TypeScript 專案開發

TypeScript 可與現代開發工具整合,如 Webpack、Babel 等。例如,在 React 或 Vue 開發中,可以使用 TypeScript 提供的型別檢查來提升程式碼質量。

在專案中安裝 TypeScript 相依套件:

npm install typescript @types/node --save-dev

若要搭配 React,則需安裝 @types/react@types/react-dom

npm install @types/react @types/react-dom --save-dev

七、總結

TypeScript 提供靜態型別檢查、模組化開發、泛型與完整的物件導向特性,使 JavaScript 程式碼更安全、可維護且容易擴展。透過與現有 JavaScript 相容的特性,可以逐步導入 TypeScript,提升專案的開發體驗與效能。在現代前端與後端開發中,TypeScript 已成為主流選擇之一。

Nuxt.js 入門教學筆記 | 學習筆記

· 閱讀時間約 5 分鐘
kdchang

Nuxt.js 是一個基於 Vue.js 的漸進式框架,專為構建伺服器端渲染(SSR)和靜態站點生成(SSG)應用程式而設計。它提供開發者一個強大的開發體驗,並簡化 Vue.js 應用的架構與設定,適合 SEO 優化、效能最佳化以及提升開發效率。

Nuxt.js 的核心特性包括:

  1. 伺服器端渲染(SSR):增強 SEO 並提升初始載入速度。
  2. 靜態站點生成(SSG):透過預先生成 HTML 提供更快的載入時間。
  3. 自動路由:基於 pages 目錄的檔案自動建立對應的路由,無需額外配置 Vue Router。
  4. 模組系統:支援大量 Nuxt 模組(如 TailwindCSS、PWA、Auth 等)來快速擴展功能。
  5. 組態簡單:預設優化 Vue.js 應用的結構與設定,減少繁瑣的配置工作。
  6. 組件自動載入:Nuxt 可自動載入 components 目錄內的 Vue 組件,減少 import 的需求。

二、安裝與初始化 Nuxt.js

1. 使用 Nuxt CLI 安裝(推薦方式)

Nuxt 提供官方 CLI 工具 nuxi 來建立新專案。

npx nuxi init my-nuxt-app
cd my-nuxt-app
npm install

上述指令會自動建立一個 my-nuxt-app 專案,並下載 Nuxt 相關相依套件。

2. 使用 create-nuxt-app 安裝(舊版方式)

如果要使用較舊的安裝方式,也可以透過 create-nuxt-app 指令來建立專案:

npx create-nuxt-app my-nuxt-app

此方法會提供互動式選單,讓開發者選擇 UI 框架(TailwindCSS、Bootstrap)、插件(Axios、PWA)以及 Nuxt 模式(SSR 或 SSG)。

3. 啟動開發伺服器

安裝完成後,可以執行以下指令來啟動開發環境:

npm run dev

預設會啟動本機伺服器 http://localhost:3000,可在瀏覽器中打開檢視。


三、專案結構

Nuxt.js 採用約定式(Convention over Configuration)架構,專案目錄結構如下:

my-nuxt-app/
│── assets/ # 未編譯的靜態資源,如 CSS、圖片
│── components/ # Vue 組件(自動載入)
│── layouts/ # 頁面佈局
│── pages/ # 自動建立的路由頁面
│── plugins/ # Nuxt 插件,如 Vue 插件或第三方庫
│── public/ # 靜態資源,可直接透過 URL 存取
│── server/ # 伺服器端 API(Nuxt 3)
│── store/ # Vuex 狀態管理(Nuxt 2,Nuxt 3 改用 `pinia`)
│── nuxt.config.ts # Nuxt 設定檔
│── package.json # npm 套件設定

四、路由與頁面

1. 自動建立路由

Nuxt.js 會根據 pages/ 目錄內的 Vue 檔案自動產生對應的路由。例如,在 pages/index.vue 建立首頁:

<template>
<div>
<h1>歡迎來到 Nuxt.js</h1>
</div>
</template>

若在 pages/about.vue 建立新的 Vue 檔案,則 http://localhost:3000/about 會自動對應到該頁面。

2. 動態路由

可以使用 _ 命名的方式建立動態路由。例如,在 pages/blog/_id.vue

<template>
<div>
<h1>文章 ID: {{ route.params.id }}</h1>
</div>
</template>

<script setup>
import { useRoute } from 'vue-router';
const route = useRoute();
</script>

訪問 http://localhost:3000/blog/123,頁面將顯示 文章 ID: 123


五、Nuxt 組件與佈局

1. 自動載入組件

components/ 內的 Vue 檔案會自動載入,例如建立 components/Navbar.vue

<template>
<nav class="bg-blue-500 p-4 text-white">
<h1>網站導覽列</h1>
</nav>
</template>

然後在 pages/index.vue 內直接使用 <Navbar />,無需 import

<template>
<div>
<Navbar />
<h1>首頁內容</h1>
</div>
</template>

2. 佈局(Layouts)

佈局是共享的頁面結構,可在 layouts/default.vue 內定義:

<template>
<div>
<Navbar />
<slot />
</div>
</template>

所有 pages/ 內的頁面會自動套用 default.vue 佈局。


六、Nuxt 伺服器端 API(Nuxt 3)

Nuxt 3 內建簡單的 API 伺服器,可在 server/api/hello.ts 新增 API:

export default defineEventHandler((event) => {
return { message: "Hello from Nuxt API" };
});

這樣就可以透過 http://localhost:3000/api/hello 訪問該 API。


七、Nuxt 資料獲取

1. useFetch() 獲取 API 資料

Nuxt 3 提供 useFetch 來處理 API 讀取,例如:

<template>
<div>
<h1>{{ data.message }}</h1>
</div>
</template>

<script setup>
const { data } = useFetch('/api/hello');
</script>

這會自動調用 server/api/hello.ts 並顯示回應內容。


八、部署 Nuxt 應用

1. 生成靜態站點

若要將 Nuxt 部署為靜態網站,可執行:

npm run build
npm run generate

這會在 dist/ 目錄內產生靜態 HTML 檔案,可直接部署到 Netlify 或 Vercel。

2. 部署至 Vercel

使用 Vercel CLI 部署:

npm install -g vercel
vercel

即可快速部署 Nuxt 應用。


九、結語

Nuxt.js 提供強大的功能來簡化 Vue.js 開發,透過自動路由、組件自動載入、伺服器 API 以及資料獲取等功能,大幅提升開發效率。對於需要 SEO 優化或靜態站點的專案而言,Nuxt 是一個非常適合的選擇。

Django「一對一」、「一對多」、「多對多」關聯入門教學筆記 | 學習筆記

· 閱讀時間約 2 分鐘
kdchang

當你在學 Django 或資料庫設計時,常會遇到「一對一」、「一對多」、「多對多」這三種關聯(Relationship)。這些是資料庫中表與表之間的關係,下面用簡單的例子幫你搞懂它們的差異:


一對一(One-to-One)

概念:

一個資料只對應另一個資料,彼此之間是唯一配對的關係。

例子:

每個人都有一張身份證 → 一個人對應一張身份證,一張身份證只對應一個人。

Django 實作:

class Person(models.Model):
name = models.CharField(max_length=100)

class IDCard(models.Model):
number = models.CharField(max_length=20)
owner = models.OneToOneField(Person, on_delete=models.CASCADE)

一對多(One-to-Many)

概念:

一筆資料可以對應到多筆資料,但每一筆資料只能對應回唯一一筆上層資料。

例子:

一個作者可以寫很多本書 → 一個作者對應多本書,但一本書只會有一個作者。

Django 實作:

class Author(models.Model):
name = models.CharField(max_length=100)

class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(Author, on_delete=models.CASCADE)

多對多(Many-to-Many)

概念:

一筆資料可以對應到多筆資料,而對方也可以對應回來多筆資料。

例子:

學生選修多門課程,一門課也有很多學生 → 學生對多門課,課程對多位學生。

Django 實作:

class Student(models.Model):
name = models.CharField(max_length=100)
courses = models.ManyToManyField('Course')

class Course(models.Model):
title = models.CharField(max_length=100)

(也可以在 Course 裡加 students = models.ManyToManyField(Student),結果會一樣)


小整理比較表:

類型關係形式範例Django 欄位
一對一A ➝ B 且 B ➝ A人 → 身份證OneToOneField
一對多A ➝ 多個 B作者 → 書ForeignKey
多對多A ⇄ 多個 B,互相多對多學生 ⇄ 課程ManyToManyField

Emotion CSS 入門教學筆記 | 學習筆記

· 閱讀時間約 3 分鐘
kdchang

1. Emotion CSS 簡介

Emotion 是一款強大的 CSS-in-JS 解決方案,提供高效能且靈活的樣式管理方式,適用於 React 應用。Emotion 支援兩種主要的使用方式:

  1. CSS Prop(使用 JSX 內嵌樣式)
  2. styled API(使用 styled 函式創建元件)

2. 安裝 Emotion

使用 npm 或 yarn 安裝 Emotion 核心套件:

npm install @emotion/react @emotion/styled

如果專案使用 Babel,建議安裝 Emotion 的 Babel 插件來提高效能:

npm install --save-dev @emotion/babel-plugin

並在 .babelrc 中添加:

{
"plugins": ["@emotion"]
}

3. CSS Prop 用法

CSS Prop 允許你直接在 JSX 中定義 CSS 樣式。

基本範例

/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';

const style = css`
background-color: lightblue;
padding: 20px;
border-radius: 8px;
`;

function App() {
return <div css={style}>Hello, Emotion!</div>;
}

export default App;

傳遞 props 變更樣式

const dynamicStyle = (color) => css`
background-color: ${color};
padding: 20px;
border-radius: 8px;
`;

function App({ color = 'lightblue' }) {
return <div css={dynamicStyle(color)}>Dynamic Emotion</div>;
}

4. styled API 用法

styled API 讓我們能夠創建具備 CSS 樣式的 React 元件。

基本範例

import styled from '@emotion/styled';

const Button = styled.button`
background-color: #3498db;
color: white;
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
&:hover {
background-color: #2980b9;
}
`;

function App() {
return <Button>Click Me</Button>;
}

export default App;

傳遞 props 變更樣式

const Button = styled.button`
background-color: ${(props) => props.bg || '#3498db'};
color: ${(props) => props.color || 'white'};
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
&:hover {
background-color: ${(props) => (props.bg ? darken(0.1, props.bg) : '#2980b9')};
}
`;

function App() {
return <Button bg="tomato">Custom Button</Button>;
}

5. 全域樣式與 Theme

使用 Global 樣式

import { Global, css } from '@emotion/react';

const globalStyles = css`
body {
margin: 0;
font-family: Arial, sans-serif;
}
`;

function App() {
return (
<>
<Global styles={globalStyles} />
<div>Hello, Global Emotion!</div>
</>
);
}

使用 Theme

import { ThemeProvider } from '@emotion/react';
import styled from '@emotion/styled';

const theme = {
colors: {
primary: '#3498db',
secondary: '#2ecc71'
}
};

const ThemedButton = styled.button`
background-color: ${(props) => props.theme.colors.primary};
color: white;
padding: 10px 20px;
border-radius: 5px;
`;

function App() {
return (
<ThemeProvider theme={theme}>
<ThemedButton>Themed Button</ThemedButton>
</ThemeProvider>
);
}

6. 總結

Emotion 是一款靈活且高效的 CSS-in-JS 解決方案,適用於 React 應用。它提供 css prop 以及 styled API 來幫助開發者管理樣式,並支援全域樣式與主題設定。這篇教學筆記涵蓋了基礎概念與常見用法,希望對你的開發過程有所幫助。

Tailwind CSS 入門教學筆記 | 學習筆記

· 閱讀時間約 5 分鐘
kdchang

Tailwind CSS 是一款以「工具類別優先」(Utility-First)為核心設計理念的 CSS 框架,與傳統的 CSS 框架(如 Bootstrap、Bulma)不同,它不提供預設的 UI 元件,而是提供大量的樣式工具類別,讓開發者可以快速組合來建構 UI,而不需要額外撰寫自訂 CSS。

相較於傳統 CSS 框架,Tailwind CSS 具有以下幾個主要優勢:

  1. 開發效率高:只需使用類別組合即可完成設計,無需撰寫額外 CSS。
  2. 高度可客製化:可以透過設定檔調整顏色、字型、間距等設計。
  3. 內建響應式支援:透過 sm: md: lg: xl: 等前綴輕鬆定義不同裝置的樣式。
  4. 一致性強:開發團隊可以統一使用 Tailwind CSS 定義的設計樣式,減少 UI 風格不一致的問題。
  5. 效能優化:可透過 PurgeCSS 移除未使用的樣式,確保最小化 CSS 檔案大小,提高網站載入速度。

二、安裝 Tailwind CSS

Tailwind CSS 可以透過多種方式安裝,以下介紹幾種常見的方法:

1. 使用 CDN(適合快速測試)

如果只是想快速體驗 Tailwind CSS,可以直接在 HTML 檔案中加入以下 <link>

<link href="https://cdn.jsdelivr.net/npm/tailwindcss@3.0.0/dist/tailwind.min.css" rel="stylesheet">

這種方式適合小型專案或測試 Tailwind CSS 的功能,但不適合正式開發,因為無法使用客製化設定。

2. 透過 npm 安裝(推薦方式)

在正式開發環境中,建議使用 npm 或 yarn 來安裝 Tailwind CSS,以便進行客製化設定。

安裝 Tailwind CSS

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

執行上述指令後,會在專案根目錄中產生 tailwind.config.js 設定檔,可用來調整 Tailwind 的預設樣式。

設定 Tailwind CSS

開啟 tailwind.config.js,找到 content 設定,確保 Tailwind 只處理專案內的相關檔案,例如:

module.exports = {
content: ["./src/**/*.{html,js,ts,jsx,tsx}"],
theme: {
extend: {},
},
plugins: [],
};

這樣 Tailwind CSS 就只會掃描 src 資料夾內的 HTML、JavaScript 和 TypeScript 檔案,避免產生不必要的 CSS。

引入 Tailwind 樣式

src/index.cssstyles.css 檔案中加入以下內容,讓 Tailwind CSS 套用基礎樣式:

@tailwind base;
@tailwind components;
@tailwind utilities;

最後,在開發環境中執行 Tailwind 編譯指令:

npx tailwindcss -i ./src/index.css -o ./dist/output.css --watch

這會自動監聽檔案變化,並輸出 Tailwind CSS 的樣式到 output.css


三、Tailwind CSS 常用類別

Tailwind CSS 提供大量的工具類別,以下介紹幾個常用的類別和應用範例。

1. 文字樣式

Tailwind 允許透過類別來快速調整字體大小、顏色、行距等樣式,例如:

<p class="text-lg font-bold text-gray-700 leading-relaxed">
這是一段示範文字
</p>

其中:

  • text-lg:字體大小
  • font-bold:加粗
  • text-gray-700:深灰色文字
  • leading-relaxed:行距較寬

2. 背景與邊框

可以使用背景色與邊框樣式來設計 UI,示例如下:

<div class="bg-blue-500 text-white p-4 rounded-lg shadow-lg">
Tailwind CSS 範例
</div>
  • bg-blue-500:藍色背景
  • text-white:白色文字
  • p-4:內距 16px
  • rounded-lg:圓角
  • shadow-lg:大型陰影

3. Flexbox 佈局

Tailwind CSS 提供完整的 Flexbox 工具類別,讓開發者能夠快速進行佈局:

<div class="flex items-center justify-between p-4 bg-gray-200">
<div>左側內容</div>
<div>右側內容</div>
</div>
  • flex:啟用 Flexbox
  • items-center:垂直置中
  • justify-between:左右對齊

4. Grid 佈局

使用 Grid 來建立多欄式佈局:

<div class="grid grid-cols-3 gap-4">
<div class="bg-red-200 p-4">1</div>
<div class="bg-green-200 p-4">2</div>
<div class="bg-blue-200 p-4">3</div>
</div>
  • grid:啟用 Grid
  • grid-cols-3:建立三欄
  • gap-4:欄間距

5. 響應式設計

Tailwind 內建響應式前綴,適用於不同裝置:

<div class="text-base md:text-lg lg:text-xl xl:text-2xl">
響應式字體大小
</div>
  • text-base:手機(預設)
  • md:text-lg:平板(min-width: 768px
  • lg:text-xl:筆電(min-width: 1024px
  • xl:text-2xl:桌機(min-width: 1280px

四、Tailwind CSS 進階功能

1. 自訂樣式

可以透過 tailwind.config.js 來擴充自訂樣式,例如新增顏色:

module.exports = {
theme: {
extend: {
colors: {
primary: "#1E40AF",
secondary: "#9333EA",
},
},
},
};

這樣就可以在 HTML 中使用 text-primarybg-secondary 來套用顏色。

2. 使用 @apply 簡化樣式

在 CSS 檔案中使用 @apply 來重複使用 Tailwind 類別:

.btn {
@apply bg-blue-500 text-white px-4 py-2 rounded;
}

然後在 HTML 中只需寫:

<button class="btn">按鈕</button>

五、總結

Tailwind CSS 透過工具類別的方式大幅提升開發效率,並提供響應式設計、靈活佈局與高度客製化的功能。對於前端開發者而言,熟悉 Tailwind CSS 可以讓 UI 設計更加直觀、高效,並減少對自訂 CSS 的依賴,進而提升開發維護性。

CSS Animation 入門教學筆記 | 學習筆記

· 閱讀時間約 3 分鐘
kdchang

在現代網頁開發中,動畫是一種強而有力的表現手法,能提升使用者體驗,使介面更加生動、有趣。相比 JavaScript,CSS Animation 提供一種更簡潔、性能更佳的動畫解法,尤其適合製作 UI 中的小型動畫效果。

本篇教學筆記將介紹 CSS Animation 的基本語法、常見屬性與一個實際範例,幫助你快速上手。


為什麼使用 CSS Animation?

CSS Animation 的優點:

  1. 簡單易用:使用純 CSS 撰寫動畫,不需額外 JavaScript。
  2. 效能佳:瀏覽器針對 CSS Animation 做過優化,效能較好。
  3. 語意清楚:樣式與行為分離,程式碼結構清晰。

基本語法

CSS 動畫的基本概念分為兩大部分:

  1. @keyframes:定義動畫的每個階段(狀態)
  2. 動畫屬性(如 animation-nameanimation-duration 等):設定動畫的名稱、時間、重複次數等

1. 定義動畫步驟:@keyframes

@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}

也可以使用百分比表示中間過程:

@keyframes moveBox {
0% {
transform: translateX(0);
}
50% {
transform: translateX(100px);
}
100% {
transform: translateX(0);
}
}

2. 套用動畫屬性

.box {
animation-name: fadeIn;
animation-duration: 1s;
animation-timing-function: ease-in-out;
animation-delay: 0s;
animation-iteration-count: 1;
animation-fill-mode: forwards;
}

也可以使用簡寫語法:

animation: fadeIn 1s ease-in-out 0s 1 forwards;

常見屬性說明

屬性說明
animation-name對應的 @keyframes 名稱
animation-duration動畫持續時間(如 2s500ms
animation-delay動畫延遲時間
animation-iteration-count播放次數(數字或 infinite
animation-direction播放方向(如 normalreversealternate
animation-timing-function動畫速度曲線(如 lineareaseease-in-outcubic-bezier
animation-fill-mode動畫結束後元素狀態(如 noneforwardsbackwardsboth

實際範例:淡入移動效果

HTML

<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS Animation 入門範例</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="animated-box">Hello CSS Animation</div>
</body>
</html>

CSS(style.css)

body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f0f0f0;
}

.animated-box {
padding: 20px 40px;
background-color: #4a90e2;
color: white;
font-size: 24px;
border-radius: 8px;
opacity: 0;
transform: translateY(30px);
animation: fadeSlideIn 1s ease-out forwards;
}

@keyframes fadeSlideIn {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}

效果說明

當頁面載入時,.animated-box 元素會從下方淡入移動到原位。這種效果常用於元件出現時的動畫,例如:卡片載入、提示訊息出現等。


進階補充

多個動畫階段

可以使用百分比定義多個狀態:

@keyframes bounce {
0% { transform: translateY(0); }
30% { transform: translateY(-50px); }
50% { transform: translateY(0); }
70% { transform: translateY(-25px); }
100% { transform: translateY(0); }
}

搭配 hover 做互動動畫

.button {
transition: transform 0.3s ease;
}

.button:hover {
transform: scale(1.1);
}

雖然這是使用 transition,但若需要更多控制,就可以改用 animation


總結

CSS Animation 提供一套簡潔的方式,能有效為網頁增添動態效果。適合使用在進場動畫、提示動畫、狀態變化等場合。若需求單純、動畫步驟不複雜,建議優先使用 CSS Animation 來取代 JavaScript,提升效能與維護性。

如果你剛開始學前端,建議從觀察現有網站的動畫效果出發,試著模仿並自己實作,會對 CSS Animation 有更深的理解。下一步你可以學習 transitiontransformcubic-bezier,讓動畫更自然、細緻。

Django Middleware 入門教學筆記

· 閱讀時間約 4 分鐘
kdchang

前言

在 Web 應用程式中,每一次的 HTTP 請求與回應都會經過一連串的處理流程。在 Django 框架中,這些處理流程的攔截點與操作邏輯就是透過「Middleware(中介軟體)」來實現。

Middleware 是 Django 請求與回應處理流程中一個極為關鍵的機制,允許開發者在請求進入 View 前或回應送出前進行特定操作,例如:身份驗證、日誌紀錄、權限控制、跨域處理、錯誤攔截、壓縮回應等。

掌握 Middleware 的基本原理與撰寫方式,將有助於你更靈活地控制整個應用程式的行為與安全性。


重點摘要

  • Middleware 是介於「請求 → View → 回應」流程中,可插入自訂邏輯的中介處理元件。

  • 每個 Middleware 類別皆需實作特定的方法,如 __call__()process_request()process_response()

  • Middleware 可用於:日誌、認證、CORS、壓縮、IP 限制、防止 CSRF 等。

  • 所有 Middleware 的執行順序依 settings.pyMIDDLEWARE 設定順序為準。

  • Middleware 執行順序為:

    • 請求階段(request):從上往下
    • 回應階段(response):從下往上

Middleware 執行流程簡述

以下是 Django 處理請求的標準流程:

Request

Middleware(request)→ View 處理 → Middleware(response)

Response

舉例來說,假設你有三個 Middleware:

MIDDLEWARE = [
'myproject.middleware.FirstMiddleware',
'myproject.middleware.SecondMiddleware',
'myproject.middleware.ThirdMiddleware',
]

執行順序會是:

  • 請求階段:First → Second → Third → View
  • 回應階段:Third → Second → First → Client

Middleware 的基本結構與方法

從 Django 1.10 起,Middleware 使用「新式 Middleware」架構,基於 __call__() 方法設計。基本範例如下:

# middleware.py
class SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# 這裡是初始化,只在伺服器啟動時執行一次

def __call__(self, request):
# 處理請求邏輯(View 前)
print("Before view")

response = self.get_response(request)

# 處理回應邏輯(View 後)
print("After view")

return response

實際範例一:請求時間計算 Middleware

此範例會計算 View 執行所花費的時間,並加入到 HTTP 回應標頭中。

# middleware.py
import time

class TimerMiddleware:
def __init__(self, get_response):
self.get_response = get_response

def __call__(self, request):
start_time = time.time()
response = self.get_response(request)
duration = time.time() - start_time
response["X-Process-Time"] = f"{duration:.4f}s"
return response

settings.py 中啟用:

MIDDLEWARE = [
# 其他內建 middleware...
'myapp.middleware.TimerMiddleware',
]

當你訪問任意頁面時,HTTP 回應標頭中會出現類似:

X-Process-Time: 0.0321s

實際範例二:只允許特定 IP 的 Middleware

你可以建立一個 Middleware,用來限制只有某些 IP 位址可以訪問網站。

# middleware.py
from django.http import HttpResponseForbidden

class IPWhitelistMiddleware:
ALLOWED_IPS = ["127.0.0.1", "192.168.1.100"]

def __init__(self, get_response):
self.get_response = get_response

def __call__(self, request):
ip = request.META.get("REMOTE_ADDR")
if ip not in self.ALLOWED_IPS:
return HttpResponseForbidden("Access denied.")
return self.get_response(request)

這可以用來防止內部 API 被外部訪問。


常見用途與建議實作

用途建議工具或方式
請求/回應紀錄實作 logging Middleware
使用者權限控管自訂 Middleware 或使用 Django Authentication
防止過多請求與 Redis 結合實作 Rate Limiting Middleware
跨域(CORS)處理使用 django-cors-headers 套件
錯誤攔截與通知捕捉例外並結合外部通知工具(如 Sentry)

注意事項與開發建議

  1. Middleware 應保持單一職責:一個 Middleware 做一件事,方便測試與維護。
  2. 避免過度耦合 View 或 Model:Middleware 應專注於請求與回應處理,不應過度介入業務邏輯。
  3. 注意 Middleware 順序:Django 會依照 MIDDLEWARE 陣列順序執行,不當排序可能導致無效或錯誤。
  4. 善用內建 Middleware:如 SecurityMiddleware, CommonMiddleware, AuthenticationMiddleware,無須重造輪子。
  5. 除錯技巧:可在 Middleware 中使用 print()logging 或加入 HTTP 標頭協助觀察執行情況。

總結

Middleware 是 Django 框架中攔截與處理請求/回應流程的核心機制之一。學會撰寫與使用 Middleware,能讓你更有效地實現各種橫向功能(如安全、效能、紀錄等),並保持應用架構的乾淨與可維護性。

透過本教學筆記,你應該已理解:

  • Middleware 的基本概念與執行流程
  • 如何撰寫一個簡單的 Middleware 類別
  • 幾種常見的 Middleware 實用場景與範例

進一步建議可以閱讀 Django 官方文件中的 Middleware 章節,並觀察第三方套件的 Middleware 是如何實作的,將更有助於理解其威力與應用彈性。

Django 中的 n+1 問題入門教學筆記

· 閱讀時間約 4 分鐘
kdchang

前言

Django 作為一個功能完整的 Python Web 框架,其 ORM(Object-Relational Mapping)能讓開發者以物件導向方式操作資料庫。然而,這樣的便利也容易隱藏一些效能陷阱,其中最常見也最容易忽略的就是 n+1 查詢問題(n+1 query problem)

n+1 問題會導致程式在執行查詢時產生大量多餘的 SQL 語句,影響效能並拖慢頁面載入速度,特別是在處理關聯資料時(如 ForeignKey 或 ManyToManyField)。本篇筆記將帶你認識 n+1 問題在 Django 中的成因、辨識方法與解法。


重點摘要

  • n+1 問題定義:查詢一個主物件(n 筆),卻對每筆物件再執行一次額外查詢,總共造成 n+1 次查詢。

  • 常見發生情境:在模板或程式中存取 ForeignKey 或 ManyToManyField 時,未預先載入(eager loading)相關資料。

  • 效能影響:每個物件觸發一次額外 SQL,當資料量增加時,查詢數可能達到數百次以上。

  • 解法

    • 使用 select_related() 預先載入「多對一」與「一對一」的關聯。
    • 使用 prefetch_related() 預先載入「一對多」與「多對多」的關聯。
  • 如何偵測 n+1 問題

    • 開啟 django.db.backends 日誌觀察查詢數量與內容。
    • 使用 Django Debug Toolbar 查看 SQL 查詢次數與細節。

實際範例:部落格文章與作者

假設有以下兩個模型:

# models.py
class Author(models.Model):
name = models.CharField(max_length=100)

class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey(Author, on_delete=models.CASCADE)

n+1 問題範例

# views.py
def post_list(request):
posts = Post.objects.all()
return render(request, "blog/post_list.html", {"posts": posts})
{# post_list.html #}
<ul>
{% for post in posts %}
<li>{{ post.title }} - {{ post.author.name }}</li>
{% endfor %}
</ul>

這段程式會發生 n+1 問題:

  • 第一次查詢取得所有文章 SELECT * FROM post
  • 每篇文章查詢一次作者 SELECT * FROM author WHERE id = ?

如果有 100 篇文章,總共會執行 101 次查詢。


# views.py(改進)
def post_list(request):
posts = Post.objects.select_related("author").all()
return render(request, "blog/post_list.html", {"posts": posts})

select_related 會使用 SQL JOIN 一次把作者資料一起載入。查詢數減少為 1 次:

SELECT post.*, author.*
FROM post
JOIN author ON post.author_id = author.id;

這種方式適合用於 ForeignKey(多對一)與 OneToOneField 關聯。


若改為一對多或多對多關係,例如:

class Tag(models.Model):
name = models.CharField(max_length=30)

class Post(models.Model):
title = models.CharField(max_length=200)
tags = models.ManyToManyField(Tag)

如果在模板中使用:

{% for post in posts %}
{{ post.title }}:
{% for tag in post.tags.all %}
{{ tag.name }}
{% endfor %}
{% endfor %}

這樣會產生 n+1 查詢問題(每個 post 查一次 tag)。解法:

posts = Post.objects.prefetch_related("tags").all()

prefetch_related() 會先查出所有關聯,再用 Python 記憶體關聯對應資料,不用 JOIN。

SELECT * FROM post;
SELECT * FROM post_tags WHERE post_id IN (...);
SELECT * FROM tag WHERE id IN (...);

特性select_relatedprefetch_related
關係類型一對一、外鍵(ForeignKey)一對多、多對多
查詢方式使用 JOIN 一次查出分別查詢後用 Python 關聯
查詢數量一次查詢即可完成最少兩次查詢
效能適用情境關聯資料不多且關係單純關聯資料多或複雜嵌套

如何偵測與除錯 n+1 問題

  1. 開啟 SQL 日誌: 在 settings.py 中設定:

    LOGGING = {
    "version": 1,
    "handlers": {
    "console": {
    "class": "logging.StreamHandler",
    },
    },
    "loggers": {
    "django.db.backends": {
    "handlers": ["console"],
    "level": "DEBUG",
    },
    },
    }
  2. 使用 Django Debug Toolbar: 安裝與設定後可視覺化查詢次數與內容。

  3. 查看 QuerySet 查詢次數: 使用 len(connection.queries) 或中間件分析每個 request 的查詢數。


總結

n+1 問題是 Django ORM 中最常見的效能陷阱之一,但只要了解其原理與解法,透過 select_related()prefetch_related() 搭配得當,幾乎可以完全避免這個問題。

掌握以下原則即可:

  • 遇到 ForeignKey 或 OneToOne 時用 select_related()
  • 遇到 ManyToMany 或反向 ForeignKey 時用 prefetch_related()
  • 避免在模板中直接使用 .related_set.all() 未預先載入資料
  • 對列表頁或頻繁查詢頁進行效能測試與 SQL 分析

良好的 ORM 使用習慣能大幅提升系統穩定性與使用者體驗,是每位 Django 開發者必備的基礎功。