跳至主要内容

83 篇文章 含有標籤「前端」

檢視所有標籤

Node.js Express.js 入門教學筆記 | 學習筆記

· 閱讀時間約 3 分鐘
kdchang

1. 簡介

Express.js 是一個基於 Node.js 的 Web 應用框架,提供簡潔且靈活的 API,適用於建立伺服器端應用程式。它可以用來開發 RESTful API、Web 應用或後端服務。

為什麼選擇 Express.js?

  • 輕量且易於學習
  • 擴展性高
  • 內建強大的中介軟體(Middleware)系統
  • 支援各種範本引擎(例如:EJS, Pug)

2. 安裝與專案初始化

安裝 Node.js

在開始使用 Express.js 之前,請先安裝 Node.js

初始化專案

建立一個新的專案資料夾,然後執行以下指令來初始化 Node.js 專案:

mkdir express-app
cd express-app
npm init -y

安裝 Express.js

npm install express

3. 建立第一個 Express 伺服器

建立 server.js 檔案,並加入以下程式碼:

const express = require('express');
const app = express();
const port = 3000;

app.get('/', (req, res) => {
res.send('Hello, Express!');
});

app.listen(port, () => {
console.log(`Server is running at http://localhost:${port}`);
});

啟動伺服器

node server.js

然後在瀏覽器打開 http://localhost:3000/,應該可以看到 Hello, Express!

4. 中介軟體(Middleware)

Express 提供 Middleware,可用來處理請求與回應,例如:解析請求體、驗證請求等。

使用 express.json() 解析 JSON

app.use(express.json());

建立自訂中介軟體

app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
next();
});

5. 路由(Routing)

Express 允許定義不同的 HTTP 方法對應不同的路由。

GET 路由

app.get('/api/users', (req, res) => {
res.json([{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]);
});

POST 路由

app.post('/api/users', (req, res) => {
const newUser = req.body;
res.status(201).json(newUser);
});

參數化路由

app.get('/api/users/:id', (req, res) => {
const userId = req.params.id;
res.json({ id: userId, name: `User ${userId}` });
});

6. 靜態檔案服務

Express 可用來提供靜態檔案,例如 HTML、CSS、JavaScript。

app.use(express.static('public'));

然後在 public/index.html 中放入 HTML,即可直接透過 http://localhost:3000/index.html 存取。

7. 錯誤處理

Express 提供錯誤處理中介軟體,可用來處理應用中的錯誤。

app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ message: 'Something went wrong!' });
});

8. 整合 MongoDB

可以使用 mongoose 來與 MongoDB 互動。

安裝 mongoose

npm install mongoose

連接 MongoDB

const mongoose = require('mongoose');

mongoose.connect('mongodb://localhost:27017/mydatabase', {
useNewUrlParser: true,
useUnifiedTopology: true
}).then(() => {
console.log('Connected to MongoDB');
}).catch(err => {
console.error('MongoDB connection error:', err);
});

9. 部署 Express 應用

可以使用 PM2 來管理 Express 伺服器。

安裝 PM2

npm install -g pm2

啟動應用

pm2 start server.js --name express-app

10. 總結

透過這篇筆記,你已經學會:

  1. 安裝與初始化 Express.js
  2. 建立基本 Web 伺服器
  3. 使用中介軟體與路由
  4. 提供靜態檔案
  5. 錯誤處理
  6. 整合 MongoDB
  7. 部署 Express 應用

這些概念是 Express.js 開發的基礎,熟練後可以進一步學習 JWT 認證、WebSocket、GraphQL 等進階技術!

Pinia Setup Store 與 Option Store 比較介紹入門教學筆記

· 閱讀時間約 4 分鐘
kdchang

前言

Pinia 是 Vue 3 推薦的狀態管理工具,作為 Vuex 的繼任者,Pinia 以簡潔且直觀的 API 設計,為 Vue 生態系帶來更輕量且易於維護的解決方案。Pinia 提供兩種撰寫 store 的方式:Option Store 與 Setup Store。兩者皆支援響應式狀態管理,但在語法結構與彈性上有所不同。理解這兩種寫法的差異,對於新手以及想提升開發效率的工程師來說相當重要。


重點摘要

  • Option Store 是物件式語法,結構清晰,適合剛接觸 Pinia 或 Vuex 過渡的開發者。
  • Setup Store 採用 Composition API 語法,彈性更大,易於搭配 Vue 3 的其它 API 使用。
  • Option Store 通常使用 stategettersactions 三大屬性分隔管理狀態、計算屬性與方法。
  • Setup Store 透過 refreactive 直接定義響應式狀態,並以函式回傳公開 API,結構更自由。
  • Setup Store 更方便與 computedwatchasync/await 等 Composition API 功能整合。
  • Option Store 語法更接近 Vuex,學習門檻較低,但在複雜邏輯時不易拆分。
  • Setup Store 支援多 store 之間更靈活的組合與重用。

1. Option Store 範例

// stores/counterOption.js
import { defineStore } from 'pinia';

export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
}),
getters: {
doubleCount(state) {
return state.count * 2;
},
},
actions: {
increment() {
this.count++;
},
incrementBy(amount) {
this.count += amount;
},
},
});

特點說明

  • state 是一個函式,回傳物件,定義響應式資料。
  • getters 為基於 state 的計算屬性,類似 Vue 的 computed。
  • actions 用於定義操作狀態的方法,且可包含異步邏輯。
  • 語法簡單,概念清晰,適合快速上手。

2. Setup Store 範例

// stores/counterSetup.js
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';

export const useCounterStore = defineStore('counter', () => {
const count = ref(0);

const doubleCount = computed(() => count.value * 2);

function increment() {
count.value++;
}

function incrementBy(amount) {
count.value += amount;
}

return {
count,
doubleCount,
increment,
incrementBy,
};
});

特點說明

  • 直接使用 Vue Composition API 的 refcomputed
  • 可自由使用所有 Vue Composition API 來封裝邏輯。
  • 方法與狀態同時定義在函式作用域內,結構更模組化。
  • 支援更複雜狀態邏輯與副作用控制。

3. 比較分析

項目Option StoreSetup Store
語法風格物件式,類似 Vuex函式式,基於 Composition API
學習曲線容易入門,結構清楚需要熟悉 Vue 3 Composition API
狀態管理透過 state 函式返回物件使用 refreactive 定義狀態
計算屬性透過 getters 定義使用 computed 定義
方法定義放在 actions直接函式內定義,結構更自由
邏輯拆分較不靈活,所有邏輯集中可拆分成多個函式模組,彈性更高
異步處理支援,寫法較直觀支援,且更方便搭配 async/await
重用性較弱易於組合與重用,搭配 Composition API 更佳
持久化插件支持兩者皆支持兩者皆支持
與 Vue 3 整合一般深度整合,充分發揮 Vue 3 特性

4. 什麼時候用哪種?

  • **初學者或 Vuex 使用者:**建議先使用 Option Store,語法熟悉、過渡平滑。
  • **專案中需大量組合邏輯、重用邏輯時:**Setup Store 更適合,也更符合 Vue 3 生態。
  • **大型專案或團隊合作:**Setup Store 可拆分細節更清楚,便於維護與測試。
  • **希望在 Store 中使用複雜副作用(例如 watch、watchEffect):**必須使用 Setup Store。

5. 總結與建議

Pinia 的 Option Store 與 Setup Store 各有優劣,根據團隊熟悉度、專案規模與需求選擇合適的寫法。掌握兩者能幫助你靈活應對不同開發場景。


補充:如何在專案中使用

  1. 安裝 Pinia:
npm install pinia
  1. main.js 初始化:
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';

const app = createApp(App);
const pinia = createPinia();

app.use(pinia);
app.mount('#app');
  1. 直接在組件中使用 store:
<script setup>
import { useCounterStore } from '@/stores/counterSetup';

const counter = useCounterStore();

function increase() {
counter.increment();
}
</script>

<template>
<div>
<p>Count: {{ counter.count }}</p>
<p>Double: {{ counter.doubleCount }}</p>
<button @click="increase">增加</button>
</div>
</template>

滾動視差(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 是一個非常適合的選擇。

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 開發者必備的基礎功。