跳至主要内容

Vue i18n 入門教學筆記 | 學習筆記

· 閱讀時間約 3 分鐘
kdchang

1. Vue I18n 簡介

Vue i18n 是 Vue.js 的國際化(Internationalization)解決方案,可用於管理應用程式的多語系內容。當開發需要支援不同語言的應用時,Vue i18n 提供了一個高效的方式來處理翻譯、數字格式、日期格式等。

1.1 為何使用 Vue I18n?

  • 動態切換語言:讓使用者能夠即時更改語言。
  • 簡化多語系管理:集中化翻譯內容,方便維護。
  • 格式化支援:內建日期、時間、數字等格式化功能。

2. 安裝 Vue I18n

Vue I18n 可透過 npm 安裝,適用於 Vue 3 應用。

npm install vue-i18n

3. 設定 Vue I18n

在 Vue 3 中,可以使用 createI18n 來設定 Vue I18n:

import { createApp } from 'vue';
import { createI18n } from 'vue-i18n';
import App from './App.vue';

const messages = {
en: {
welcome: 'Welcome to Vue I18n!'
},
zh: {
welcome: '歡迎使用 Vue I18n!'
}
};

const i18n = createI18n({
locale: 'en', // 預設語言
fallbackLocale: 'zh', // 備用語言
messages
});

const app = createApp(App);
app.use(i18n);
app.mount('#app');

4. 在組件中使用 Vue I18n

4.1 使用 $t 方法

在 Vue 組件中,可以透過 $t 方法來取得翻譯內容。

<template>
<h1>{{ $t('welcome') }}</h1>
</template>

4.2 切換語言

可以透過 this.$i18n.locale 來更改語言:

<template>
<div>
<button @click="changeLanguage('en')">English</button>
<button @click="changeLanguage('zh')">中文</button>
<p>{{ $t('welcome') }}</p>
</div>
</template>

<script>
export default {
methods: {
changeLanguage(lang) {
this.$i18n.locale = lang;
}
}
};
</script>

5. 使用外部 JSON 檔案管理翻譯內容

當專案的翻譯內容較多時,建議將翻譯字串拆分成獨立的 JSON 檔案。

建立語言檔案:

locales/en.json

{
"welcome": "Welcome to Vue I18n!"
}

locales/zh.json

{
"welcome": "歡迎使用 Vue I18n!"
}

修改 i18n 設定:

import { createI18n } from 'vue-i18n';
import en from './locales/en.json';
import zh from './locales/zh.json';

const i18n = createI18n({
locale: 'en',
fallbackLocale: 'zh',
messages: { en, zh }
});

6. 插值與變數

Vue I18n 支援在翻譯字串中插入變數。

const messages = {
en: {
greeting: 'Hello, {name}!'
},
zh: {
greeting: '你好, {name}!'
}
};

在組件中使用:

<p>{{ $t('greeting', { name: 'John' }) }}</p>

7. 訊息格式化與日期、數字處理

Vue I18n 內建 numberFormatsdatetimeFormats,可用於格式化數字與日期。

const i18n = createI18n({
locale: 'en',
fallbackLocale: 'zh',
messages,
numberFormats: {
en: {
currency: {
style: 'currency', currency: 'USD'
}
},
zh: {
currency: {
style: 'currency', currency: 'TWD'
}
}
}
});

在組件中使用:

<p>{{ $n(1000, 'currency') }}</p>

8. 延遲載入翻譯(Lazy Loading)

當應用程式的語言檔案過多時,建議使用動態載入(Lazy Loading)來優化效能。

import { createI18n } from 'vue-i18n';

const i18n = createI18n({
locale: 'en',
fallbackLocale: 'zh',
messages: {}
});

async function loadLocaleMessages(locale) {
const messages = await import(`./locales/${locale}.json`);
i18n.global.setLocaleMessage(locale, messages.default);
i18n.global.locale = locale;
}

9. 結論

Vue I18n 是 Vue.js 中強大的國際化解決方案,透過 $t 方法、插值變數、外部 JSON 檔案管理,以及數字與日期格式化功能,可以讓開發者輕鬆實作多語系應用。本篇筆記介紹了 Vue I18n 的基本使用方式,進一步學習可研究動態載入、多語系 SEO 以及與 Vue Router 的結合。

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

· 閱讀時間約 2 分鐘
kdchang

Cookie 是一種儲存在使用者瀏覽器上的小型文字檔案,用於保存使用者的狀態或資訊。常見用途包含:

紀錄登入狀態:讓使用者在網站上保持登入,不需每次重新輸入帳號。 使用者偏好設定:保存使用者選擇的語言、主題等個人化設定。 追蹤使用者行為:例如分析網站流量、廣告投放追蹤等。

重點摘要

Cookie 依據不同的分類方式,可以分為以下幾種常見種類:

一、依用途分類:

  1. 功能性 Cookie(Functional Cookies)

    • 主要用於提升網站使用體驗,例如記住使用者的登入狀態、語言設定、購物車內容等。
  2. 必要性 Cookie(Strictly Necessary Cookies)

    • 維持網站基本運作所需,例如登入認證、網頁導航等,通常無法被關閉。
  3. 分析型 Cookie(Analytical/Performance Cookies)

    • 用於收集網站流量數據,分析使用者行為,幫助網站優化,例如 Google Analytics。
  4. 廣告追蹤 Cookie(Advertising/Targeting Cookies)

    • 用於追蹤使用者瀏覽行為,以提供個人化廣告或推薦內容。

二、依存放時間分類:

  1. 暫時性 Cookie(Session Cookies)

    • 只在使用者開啟網頁期間有效關閉瀏覽器後即刪除
  2. 永久性 Cookie(Persistent Cookies)

    • 設定到期日期,存放於使用者裝置上,保存時間較長,即使關閉瀏覽器也不會刪除,直到設定的到期日或手動刪除

三、依來源分類:

  1. 第一方 Cookie(First-party Cookies)

    • 使用者瀏覽的網站所設定,通常用於記錄該網站上的互動紀錄
  2. 第三方 Cookie(Third-party Cookies)

    • 由非該網站的第三方(如廣告商)設定,用於跨網站追蹤使用者行為,以推送廣告等。

四、特殊類型:

  1. 安全性 Cookie(Secure Cookies)

    • 只能透過 HTTPS 傳輸,避免被攔截,主要保障敏感資料安全。
  2. HttpOnly Cookie

    • 僅限伺服器端存取,JavaScript 無法讀取,用於防範 XSS 攻擊。
  3. SameSite Cookie

    • 限制跨站請求攜帶 Cookie,減少 CSRF 攻擊風險,值可設為:
    • Strict:禁止跨站請求攜帶 Cookie。
    • Lax:部分允許,如從第三方網站點擊連結進來時可帶 Cookie。
    • None:允許跨站攜帶,但須配合 Secure。

總結

這些分類會依需求搭配使用,例如一個「必要性第一方暫時性 Cookie」可能用於維護登入和操作狀態;一個「第三方廣告追蹤永久性 Cookie」則可能用於跨網站顯示個人化廣告

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

· 閱讀時間約 4 分鐘
kdchang

1. 什麼是 Alpine.js?

Alpine.js 是一個輕量級的 JavaScript 框架,專為增強 HTML 標記而設計。它的語法靈感來自 Vue.js,但更加簡潔,適用於需要簡單互動的網頁。

它的主要特點包括:

  • 使用 HTML 屬性直接定義行為
  • 不需要額外的構建工具
  • 易於學習和使用
  • 與其他框架(如 Vue、React)兼容

2. 安裝與引入

使用 Alpine.js 最簡單的方法是透過 CDN 引入。

<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Alpine.js 入門</title>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
</head>
<body>
<h1>Alpine.js 教學</h1>
</body>
</html>

3. 基本語法與應用

3.1 x-data

x-data 屬性用於定義 Alpine.js 的組件狀態。

<div x-data="{ message: 'Hello, Alpine!' }">
<p x-text="message"></p>
</div>

這段程式碼會顯示 Hello, Alpine!,並且 x-text 會自動更新內容。

3.2 x-bind

x-bind 允許綁定 HTML 屬性。

<div x-data="{ color: 'red' }">
<p x-bind:style="'color: ' + color">這是一段紅色文字</p>
</div>

3.3 x-on

x-on 用於事件監聽,例如點擊事件。

<div x-data="{ count: 0 }">
<button x-on:click="count++">增加</button>
<p>計數:<span x-text="count"></span></p>
</div>

3.4 x-model

x-model 允許雙向綁定表單元素。

<div x-data="{ name: '' }">
<input type="text" x-model="name" placeholder="輸入你的名字">
<p>你好,<span x-text="name"></span></p>
</div>

3.5 x-show

x-show 控制元素顯示或隱藏。

<div x-data="{ isVisible: true }">
<button x-on:click="isVisible = !isVisible">切換顯示</button>
<p x-show="isVisible">這段文字可以顯示或隱藏</p>
</div>

3.6 x-if

x-if 會動態新增或移除元素(比 x-show 更影響 DOM)。

<div x-data="{ isVisible: true }">
<button x-on:click="isVisible = !isVisible">切換</button>
<template x-if="isVisible">
<p>這是一段可動態新增或刪除的文字</p>
</template>
</div>

3.7 x-for

x-for 用於迭代陣列。

<div x-data="{ items: ['蘋果', '香蕉', '橘子'] }">
<ul>
<template x-for="item in items" :key="item">
<li x-text="item"></li>
</template>
</ul>
</div>

3.8 計時器與非同步操作

Alpine.js 支援 setTimeoutfetch 等 JavaScript 方法。

<div x-data="{
message: '載入中...',
async fetchData() {
let response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
let data = await response.json();
this.message = data.title;
}
}" x-init="fetchData">
<p x-text="message"></p>
</div>

4. Alpine.js 與 Tailwind CSS

Alpine.js 常與 Tailwind CSS 搭配使用,打造簡潔的 UI。

<div x-data="{ open: false }" class="p-5">
<button x-on:click="open = !open" class="bg-blue-500 text-white px-4 py-2 rounded">
切換選單
</button>
<ul x-show="open" class="mt-2 border p-2">
<li>選單 1</li>
<li>選單 2</li>
<li>選單 3</li>
</ul>
</div>

5. Alpine.js 進階應用

5.1 Alpine.store

Alpine.store 可用於全域狀態管理。

<script>
document.addEventListener('alpine:init', () => {
Alpine.store('app', { count: 0 });
});
</script>

<div x-data>
<button x-on:click="$store.app.count++">增加</button>
<p>計數:<span x-text="$store.app.count"></span></p>
</div>

5.2 Alpine.plugin

Alpine.js 提供外掛支援,例如 persist(本地儲存)。

<script src="https://cdn.jsdelivr.net/npm/@alpinejs/persist@3.x.x/dist/cdn.min.js"></script>
<script>
document.addEventListener('alpine:init', () => {
Alpine.plugin(Alpine.persist);
});
</script>

<div x-data="{ count: $persist(0) }">
<button x-on:click="count++">增加</button>
<p>計數:<span x-text="count"></span></p>
</div>

6. 總結

Alpine.js 是一個靈活且輕量的框架,適合用於簡單互動需求,如表單驗證、選單切換、即時更新內容等。它不需要複雜的配置,能夠快速增強靜態 HTML 頁面。

如果你的專案需要更強大的功能,可以考慮與 Vue.js 或 React 搭配,或在更大規模的應用中使用其他框架。

整合 Tailwind CSS 與 daisyUI 的入門教學筆記 | 學習筆記

· 閱讀時間約 4 分鐘
kdchang

一、前言:為什麼選擇 Tailwind CSS 搭配 daisyUI?

在現代前端開發中,Tailwind CSS 提供了高度自訂、原子化的 CSS class,使得開發者能夠以更模組化與語意化的方式撰寫樣式。然而,Tailwind 並不內建 UI 元件,這就使得一些常見元件(如按鈕、表單、卡片)仍需手動組裝樣式。

這時,daisyUI 就成為極佳的搭檔。daisyUI 是一個基於 Tailwind CSS 架構的元件庫,提供豐富的預設元件樣式與主題切換能力,讓你能快速建構出一致、美觀的介面,並維持 Tailwind CSS 的開發哲學。


二、基本安裝流程

1. 初始化專案

首先建立一個新的專案,並安裝 Tailwind CSS。這裡以 Vite 為例:

npm create vite@latest my-app -- --template vanilla
cd my-app
npm install

接著安裝 Tailwind CSS 相關套件:

  1. tailwindcss 核心庫,提供 Tailwind 的 utility class
  2. postcss CSS 處理工具,Tailwind 用它來轉換 CSS
  3. autoprefixer 為 CSS 自動加上瀏覽器前綴,如 -webkit-
# 安裝相關套件
npm install -D tailwindcss postcss autoprefixer
# 初始化設定檔
# 建立 Tailwind CSS 的設定檔 tailwind.config.js
# 加上 -p 參數會同時建立 PostCSS 的設定檔 postcss.config.js
npx tailwindcss init -p

2. 設定 Tailwind

修改 tailwind.config.js

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

建立 ./src/style.css 並加上 Tailwind 的 directives:

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

3. 安裝 daisyUI

npm install daisyui

然後在 tailwind.config.js 中加入插件:

module.exports = {
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {},
},
plugins: [require("daisyui")],
}

可選:設定主題

daisyui: {
themes: ["light", "dark", "cupcake", "synthwave", "dracula"],
},

三、開始使用:元件實戰範例

現在你已成功安裝與整合 Tailwind CSS + daisyUI,接下來讓我們透過實際範例來掌握它們的用法。

範例一:按鈕樣式

daisyUI 提供多種按鈕樣式,只需使用 btn class:

<button class="btn btn-primary">主要按鈕</button>
<button class="btn btn-secondary">次要按鈕</button>
<button class="btn btn-accent">強調按鈕</button>
<button class="btn btn-outline">外框按鈕</button>

也可以搭配大小與形狀:

<button class="btn btn-sm btn-circle btn-error">X</button>
<button class="btn btn-lg btn-square btn-info">i</button>

範例二:表單與輸入框

<div class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">使用者名稱</span>
</label>
<input type="text" placeholder="請輸入名稱" class="input input-bordered w-full" />
</div>

<div class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">選擇國家</span>
</label>
<select class="select select-bordered">
<option disabled selected>選擇一個選項</option>
<option>台灣</option>
<option>日本</option>
<option>美國</option>
</select>
</div>

範例三:卡片元件

<div class="card w-96 bg-base-100 shadow-xl">
<figure><img src="https://placeimg.com/400/225/tech" alt="科技圖" /></figure>
<div class="card-body">
<h2 class="card-title">科技卡片</h2>
<p>這是一個搭配 Tailwind CSS 與 daisyUI 所設計的卡片元件。</p>
<div class="card-actions justify-end">
<button class="btn btn-primary">查看更多</button>
</div>
</div>
</div>

範例四:主題切換

daisyUI 預設支援多主題切換,可透過修改 <html>data-theme 屬性達成:

<html data-theme="dracula">

或者透過 JS 切換:

document.documentElement.setAttribute("data-theme", "light")

四、整合應用:登入頁設計

我們結合 daisyUI 元件製作一個登入表單:

<div class="min-h-screen bg-base-200 flex items-center justify-center">
<div class="card w-full max-w-sm shadow-2xl bg-base-100">
<div class="card-body">
<h2 class="text-2xl font-bold text-center mb-4">登入系統</h2>
<div class="form-control">
<label class="label">
<span class="label-text">電子郵件</span>
</label>
<input type="email" placeholder="you@example.com" class="input input-bordered" />
</div>
<div class="form-control">
<label class="label">
<span class="label-text">密碼</span>
</label>
<input type="password" placeholder="請輸入密碼" class="input input-bordered" />
</div>
<div class="form-control mt-6">
<button class="btn btn-primary">登入</button>
</div>
</div>
</div>
</div>

整合 Tailwind CSS 和 daisyUI 後這個表單完全不需要寫自訂 CSS,就能擁有一致的樣式與響應式設計。


五、總結與延伸學習

整合 Tailwind CSS 和 daisyUI 的好處在於:

  • 可以保有 Tailwind CSS 的彈性與控制權。
  • daisyUI 提供現成且風格統一的元件,開發速度大幅提升。
  • 可輕鬆切換主題,實現暗色模式等功能。
  • 支援任意框架,React、Vue、Svelte、Alpine.js 都適用。

Web 前端效能優化入門教學筆記 | 學習筆記

· 閱讀時間約 4 分鐘
kdchang

在網頁開發中,效能優化是一個不可忽視的重要課題。無論是企業網站、單頁應用(SPA),或是電商平台,效能表現都直接影響使用者體驗與轉換率。

本篇筆記將介紹 Web 前端效能優化的核心概念、常見策略與實務範例,幫助你為專案建立良好的基礎。


為什麼需要前端效能優化?

前端效能不佳會導致:

  • 首次載入時間過長
  • 使用者等待過久,產生跳出行為
  • SEO 表現不佳(Google 會參考 LCP、CLS、FCP 等指標)

透過有效的優化策略,我們能讓網站更快、更穩、更吸引人。


效能優化的三個層面

  1. 載入效能(Loading Performance):提升頁面初始載入速度。
  2. 互動效能(Interaction Performance):優化點擊、滑動等互動的流暢度。
  3. 渲染效能(Rendering Performance):減少重繪、重排與動畫卡頓。

一、減少不必要的資源請求

使用 CDN

透過 CDN(Content Delivery Network)可將靜態資源分發到全球節點,加速載入。

<!-- 使用 Google Fonts CDN -->
<link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" rel="stylesheet" />

合併與壓縮資源(Minify & Bundle)

使用打包工具如 Webpack、Vite,可以:

  • 將 JS / CSS 壓縮(Minify)
  • 移除註解與空白
  • 合併多個檔案減少 HTTP 請求數量

範例(Webpack 設定簡略):

// webpack.config.js
module.exports = {
mode: 'production', // 自動啟用壓縮
entry: './src/index.js',
output: {
filename: 'bundle.js',
},
};

二、圖片與多媒體優化

適當圖片格式

  • 使用 WebPAVIF 取代 JPEG / PNG,可減少檔案體積 25% 以上
  • SVG 適用於圖示與 icon,解析度不會失真

延遲載入圖片(Lazy Loading)

<img src="thumbnail.jpg" loading="lazy" alt="延遲載入圖片" />

或搭配 JavaScript 實現滾動載入。


三、有效使用 Cache(快取)

設定 Cache-Control 標頭

在伺服器上設定靜態資源快取策略:

Cache-Control: max-age=31536000, immutable

適用於版本化的資源檔案(如 main.123abc.js),可快取一年不變。


四、精簡 CSS 與 JavaScript

移除未使用的 CSS(Tree Shaking)

使用工具如 PurgeCSSTailwindCSS JIT Mode 可自動剔除沒用到的樣式。

PurgeCSS 使用方式(簡略):

const purgecss = require('@fullhuman/postcss-purgecss');

module.exports = {
plugins: [
purgecss({
content: ['./**/*.html'],
}),
],
};

延遲載入 JS(Defer / Async)

<script src="main.js" defer></script>
  • defer: 等 DOM 解析完才執行,不阻塞渲染
  • async: 載入完成即執行,適合非必要腳本(如 GA)

五、避免過度重排與重繪

使用 class 切換取代 style 修改

重複直接操作 DOM style 屬性會導致效能下降,改用 CSS class 控制樣式較佳。

// 不推薦
element.style.width = '100px';
element.style.height = '50px';

// 推薦
element.classList.add('resized');
.resized {
width: 100px;
height: 50px;
}

使用 transformopacity 進行動畫

避免透過 topleftwidth 等影響 layout 的屬性來做動畫。

/* 推薦做法:使用 transform */
.card {
transition: transform 0.3s ease;
}
.card:hover {
transform: scale(1.05);
}

六、最佳化 DOM 結構與渲染

  • 減少過深的 DOM 巢狀結構
  • 使用虛擬滾動(Virtual Scroll)載入大量列表
  • 避免頻繁操作 DOM,應該一次性改動(使用 DocumentFragment 或 requestAnimationFrame)

七、使用開發工具檢查效能

Chrome DevTools

  1. Lighthouse:提供整體效能建議
  2. Performance Panel:檢查 JS 執行、動畫、Layout shift 等問題
  3. Network Panel:觀察資源大小、載入順序與快取狀態

總結與建議實作順序

若你剛開始進行專案的效能優化,可以依照以下順序著手:

  1. 壓縮與合併 JS / CSS
  2. 圖片格式轉換與 Lazy Load
  3. CDN 部署靜態資源
  4. 移除未使用樣式與延遲載入腳本
  5. 改善動畫與 DOM 操作
  6. 導入快取策略
  7. 使用 Lighthouse 檢查並優化問題

效能優化並非一次性工作,而是一個持續調整的過程。建議我們可以從專案開始就納入效能考量,將它當成基本開發原則來實踐。

參考文件

  1. Core Web Vitals: An everyday explanation (Taiwanese with English subtitles)

Web 資訊安全入門教學筆記 | 學習筆記

· 閱讀時間約 4 分鐘
kdchang

在現代網頁應用中,資安問題層出不窮,從簡單的跨站攻擊(XSS)到複雜的憑證竊取、身份偽造(CSRF),都可能導致個資外洩、系統遭入侵甚至企業商譽受損。

本篇筆記將從實務角度出發,介紹幾種常見 Web 資安風險及其防範策略,讓你能以最基本的方式保護網站與使用者安全。


一、常見 Web 資安威脅類型

1. XSS(Cross-Site Scripting,跨站腳本攻擊)

原理:攻擊者將惡意腳本注入至網站,當其他使用者瀏覽該頁面時,惡意腳本便會在其瀏覽器上執行,例如竊取 cookie、冒充使用者操作等。

實例:

<!-- 攻擊者輸入的留言內容 -->
<script>alert('你被 XSS 攻擊了');</script>

若網站未正確處理輸入,這段 JavaScript 就會直接被執行。

防範方式:

  • 對所有輸入進行 轉義(Escape)
  • 使用前端框架自動編碼機制(如 React 的 JSX)
  • 避免使用 innerHTML 插入未清洗的資料

範例(JavaScript 轉義文字):

function escapeHtml(str) {
return str
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}

2. CSRF(Cross-Site Request Forgery,跨站請求偽造)

原理:攻擊者引導使用者點擊惡意連結或載入圖片,使其在未察覺情況下對已登入的網站發送請求,進而竄改資料或觸發操作。

實例:

<!-- 攻擊者網站中隱藏圖片,觸發某網站的轉帳請求 -->
<img src="https://bank.com/transfer?amount=1000&to=attacker" />

如果使用者已登入 bank.com 且未防範 CSRF,就可能無意間轉帳。

防範方式:

  • 後端驗證 CSRF Token
  • 使用 SameSite Cookie 屬性
  • 僅允許 POST / PATCH / DELETE 操作變更狀態
  • 驗證 Referer / Origin 標頭(有風險)

範例(Express + CSRF Token):

const csrf = require("csurf");
const cookieParser = require("cookie-parser");

app.use(cookieParser());
app.use(csrf({ cookie: true }));

app.get("/form", (req, res) => {
res.render("form", { csrfToken: req.csrfToken() });
});

3. SQL Injection(SQL 注入)

原理:透過惡意輸入拼接 SQL 查詢語句,導致資料庫查詢異常,甚至刪除資料。

實例:

-- 假設未使用參數化查詢
SELECT * FROM users WHERE username = '' OR 1=1;

攻擊者輸入 ' OR 1=1; -- 可繞過認證邏輯,取得所有帳戶資料。

防範方式:

  • 使用 ORM 或預處理語句(Prepared Statement)
  • 不拼接 SQL 字串,改用參數綁定
  • 限制資料庫帳號權限

範例(Node.js + MySQL):

const username = req.body.username;
const password = req.body.password;

const query = "SELECT * FROM users WHERE username = ? AND password = ?";
connection.query(query, [username, password], function (err, results) {
// 安全地查詢
});

4. 資料傳輸未加密(缺少 HTTPS)

原理:若網站使用 HTTP,使用者資料(如帳號密碼、金流資訊)在傳輸過程中可能被中間人攔截(Man-in-the-Middle attack)。

防範方式:

  • 強制使用 HTTPS(導入憑證)
  • 使用 HSTS(HTTP Strict Transport Security)
  • 移除 HTTP 存取(使用 301 重導)

範例(NGINX 強制 HTTPS):

server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}

5. 檔案上傳風險

原理:攻擊者透過檔案上傳功能傳入惡意腳本(如 .php.jsp),若伺服器未阻擋,可能導致遠端代碼執行。

防範方式:

  • 限制可上傳的檔案類型與副檔名
  • 不讓使用者可直接存取上傳目錄
  • 使用 UUID 改名,避免原檔名執行

附加安全性強化機制

HTTP 安全標頭設定(Security Headers)

使用如 Helmet(Express.js 中間件)快速加入以下標頭:

  • Content-Security-Policy:防止 XSS 和資源注入
  • X-Frame-Options:防止點擊劫持(Clickjacking)
  • X-Content-Type-Options:避免 MIME 類型混淆
  • Strict-Transport-Security:強制 HTTPS

範例(Node.js + Helmet):

const helmet = require("helmet");
app.use(helmet());

總結與建議實作順序

如果剛開始建立 Web 專案,可以按照以下步驟檢查安全性:

  1. 輸入驗證與輸出轉義,防範 XSS
  2. 加入 CSRF Token 機制
  3. 資料庫查詢全面使用預處理語句
  4. 部署 HTTPS 並強制重導
  5. 使用 Helmet 加強安全標頭
  6. 定期檢查依賴套件漏洞(如 npm audit

資訊安全並非一勞永逸,而是一種持續維護的工作。建議我們可以將資安意識融入日常開發流程中,隨時更新資安知識與工具。

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

· 閱讀時間約 4 分鐘
kdchang

一、什麼是 PostCSS?

PostCSS 是一個 CSS 的轉換工具(CSS Transformer),本身不是 CSS 預處理器(如 Sass)或框架(如 Tailwind CSS),但它可以透過「外掛(plugin)」的方式強化你的 CSS 工作流程。

簡單來說,PostCSS 是一個平台,讓你可以用 JavaScript 編寫規則,自動處理 CSS,像是:

  • 自動加上瀏覽器前綴(autoprefixer)
  • 支援未來 CSS 語法(例如 Nesting)
  • 壓縮 CSS、移除重複樣式
  • 搭配 Tailwind CSS 進行原子化設計

二、PostCSS 的運作原理

PostCSS 的流程如下:

  1. 將原始 CSS 解析為 AST(抽象語法樹)
  2. 透過插件對 AST 進行操作
  3. 將修改後的 AST 轉回 CSS 輸出

你可以選擇使用官方插件、社群插件,甚至自己寫 plugin。


三、如何安裝與設定 PostCSS

安裝(使用 npm)

npm install -D postcss postcss-cli autoprefixer

建立 PostCSS 設定檔 postcss.config.js

module.exports = {
plugins: [
require('autoprefixer'),
],
};

這樣 PostCSS 就會讀取這個設定檔,並在處理 CSS 時自動加入瀏覽器前綴。


四、基本使用流程

假設你有一個檔案 src/style.css

/* src/style.css */
:root {
--main-color: #4f46e5;
}

.btn {
display: flex;
background-color: var(--main-color);
user-select: none;
}

執行 PostCSS 處理:

npx postcss src/style.css -o dist/style.css

這會根據你的 postcss.config.js 處理並輸出到 dist/style.css

輸出結果可能為:

:root {
--main-color: #4f46e5;
}

.btn {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
background-color: var(--main-color);
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}

這就是 autoprefixer 自動補完各種瀏覽器相容性的結果。


五、常用插件介紹與範例

1. autoprefixer(最常見)

自動加上 CSS 瀏覽器前綴

npm install -D autoprefixer

可搭配 .browserslistrcpackage.json 設定支援目標:

> 1%
last 2 versions
not dead

2. postcss-preset-env

支援未來 CSS 語法,例如 Nesting、變數、邏輯函數等

npm install -D postcss-preset-env

更新設定檔:

module.exports = {
plugins: [
require('postcss-preset-env')({
stage: 1,
}),
],
};

然後你就可以寫類似這樣的 CSS:

.btn {
color: white;

&:hover {
color: black;
}
}

經過 PostCSS 處理後會被轉成瀏覽器可讀的標準語法。


3. cssnano(壓縮 CSS)

這個插件會讓 CSS 變得更小,適合生產環境使用:

npm install -D cssnano
module.exports = {
plugins: [
require('autoprefixer'),
require('cssnano')({
preset: 'default',
}),
],
};

六、PostCSS 與 Tailwind CSS 的關係

Tailwind CSS 是建立在 PostCSS 基礎上的一個插件。因此在安裝 Tailwind 時你會看到:

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

這個指令會產生 postcss.config.js

module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

Tailwind 本身就是一個 PostCSS 插件,會將你在 HTML 或 JSX 中寫的 class 轉換成實際的 CSS。


七、PostCSS 與其他工具整合

搭配 Vite

Vite 專案中只要有 postcss.config.js 檔案,會自動載入設定,不需額外安裝。

搭配 Webpack

webpack.config.js 加入:

module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader'],
},
],
}

八、自訂 PostCSS 插件(進階)

你也可以自己撰寫插件來操作 CSS AST:

module.exports = () => {
return {
postcssPlugin: 'my-plugin',
Declaration(decl) {
if (decl.prop === 'color' && decl.value === 'red') {
decl.value = 'blue';
}
},
};
};
module.exports.postcss = true;

這個簡單的插件會把 color: red 自動換成 color: blue


九、總結

PostCSS 雖然不像 Sass 有自己的語法,也不像 Tailwind 有明確的設計架構,但它是現代前端 CSS 處理流程中不可或缺的工具,擁有高度彈性與強大生態系。

我們可以根據需求只加幾個插件,也可以像 Tailwind 那樣完全建構在它上面。尤其當你的專案需要支援多瀏覽器、使用最新 CSS 語法、進行建置壓縮時,PostCSS 是最佳解法之一。

如果你正在使用 Vite、Next.js、或是 Tailwind CSS,幾乎都已內建 PostCSS 支援。了解它的運作方式能幫你更精準地控制前端樣式處理流程。

AOS(Animate On Scroll Library)入門教學筆記 | 學習筆記

· 閱讀時間約 5 分鐘
kdchang

在現代網頁設計中,視覺效果和互動性已經成為提升使用者體驗的重要元素。當使用者向下滾動頁面時,如果某些區塊能夠平滑地出現、滑入、淡入或放大,能大大提升網站的專業感和吸引力。這類效果通常被稱為「滾動動畫」。

AOS (Animate On Scroll) 是一個輕量級的 JavaScript 函式庫,讓你可以輕鬆地為 HTML 元素加上動畫效果,並且在元素進入畫面時自動播放動畫。它不需要你手動寫 JavaScript 控制事件,也不依賴大型框架,學習曲線平緩,非常適合想快速加上動畫效果的開發者。


一、AOS 的特點

  • 輕量:只有數十 KB,對效能影響小。
  • 易於使用:只需要加幾個 data-aos 屬性在 HTML 元素上即可。
  • 可自訂動畫:支援動畫類型、延遲時間、持續時間、執行次數等調整。
  • 無框架依賴:可搭配純 HTML/CSS/JS 使用,也支援 React、Vue 等框架。
  • 良好的瀏覽器支援:支援所有現代瀏覽器。

二、如何安裝 AOS

方法一:使用 CDN(最簡單方式)

在你的 HTML 檔案 <head> 加入 CSS:

<link href="https://unpkg.com/aos@2.3.4/dist/aos.css" rel="stylesheet">

<body> 結尾加入 JS:

<script src="https://unpkg.com/aos@2.3.4/dist/aos.js"></script>
<script>
AOS.init();
</script>

方法二:透過 npm 安裝(建議在模組化專案中使用)

npm install aos --save

在你的 JavaScript 中引入與初始化:

import AOS from 'aos';
import 'aos/dist/aos.css';

AOS.init();

三、基本用法

只要在 HTML 元素上加入 data-aos 屬性即可。例如:

<div data-aos="fade-up">
這段文字將會在滾動時從下方淡入出現
</div>

常見的動畫效果:

效果名稱說明
fade淡入
fade-up向上淡入
fade-down向下淡入
fade-left向左淡入
fade-right向右淡入
zoom-in放大淡入
zoom-out縮小淡入
flip-left向左翻轉進場
slide-up向上滑入
slide-down向下滑入

四、進階設定

AOS 提供許多自訂參數,以下為常用屬性說明:

1. data-aos-duration

動畫持續時間(毫秒),預設為 400ms:

<div data-aos="fade-up" data-aos-duration="1000">
動畫持續 1 秒
</div>

2. data-aos-delay

動畫延遲時間(毫秒),可用來讓多個元素依序出現:

<div data-aos="fade-up" data-aos-delay="0">第一個</div>
<div data-aos="fade-up" data-aos-delay="200">第二個</div>
<div data-aos="fade-up" data-aos-delay="400">第三個</div>

3. data-aos-once

設定是否只執行一次動畫(預設為 false,表示每次滾動進入都會重新動畫):

<div data-aos="fade-up" data-aos-once="true">
只播放一次動畫
</div>

4. data-aos-offset

控制動畫啟動前的滾動距離(單位:像素):

<div data-aos="fade-up" data-aos-offset="300">
滾動到距離螢幕頂端 300px 時才開始動畫
</div>

五、實際範例

以下是一個完整的簡單 HTML 範例:

<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8">
<title>AOS 教學範例</title>
<link href="https://unpkg.com/aos@2.3.4/dist/aos.css" rel="stylesheet">
<style>
body {
font-family: sans-serif;
padding: 0;
margin: 0;
}
section {
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
border-bottom: 1px solid #ccc;
}
</style>
</head>
<body>

<section>
<div data-aos="fade-up">第一個區塊</div>
</section>

<section>
<div data-aos="zoom-in" data-aos-delay="200">第二個區塊</div>
</section>

<section>
<div data-aos="slide-left" data-aos-duration="800">第三個區塊</div>
</section>

<script src="https://unpkg.com/aos@2.3.4/dist/aos.js"></script>
<script>
AOS.init();
</script>

</body>
</html>

你可以將這段程式碼儲存為 HTML 檔案後,直接打開瀏覽器觀看滾動動畫效果。


六、常見問題與注意事項

  1. AOS 沒有效果?
    確認你有正確載入 aos.css 和執行 AOS.init()

  2. 元素進入畫面但沒有動畫?
    嘗試調整 data-aos-offset 或確認元素是否真的進入視窗中。

  3. 動畫無法重複?
    預設會重複,除非你加上了 data-aos-once="true"

  4. 與 SPA 框架(如 React、Vue)整合時動畫失效?
    動態渲染的頁面需在 DOM 更新完後重新執行 AOS.refresh()


總結

AOS 是一個非常實用又容易上手的前端動畫工具。你不需要寫複雜的 JavaScript,只要加幾行 data-aos 屬性就能為你的網站加入吸引人的動態效果。無論是製作個人作品集、商業網站或展示頁面,AOS 都是快速提升視覺層次感的絕佳利器。

當你需要更細緻的動畫控制或複雜的交互邏輯,也可以進一步探索 GSAPScrollMagic 等更強大的動畫工具。但若只是想快速達到流暢的滾動動畫,AOS 絕對是一個值得推薦的選擇。

參考文件

  1. gsap
  2. scrollmagic

Django CRUD(不使用 ModelForm)入門教學筆記 | 學習筆記

· 閱讀時間約 4 分鐘
kdchang

在 Django 中,ModelForm 提供了一個快速建立表單與驗證的工具,但在某些情境下,我們可能希望自己掌控表單結構與驗證流程。這篇筆記將示範如何不依賴 ModelForm,手動實作一套 CRUD 系統,幫助你更深入理解 Django 表單處理的基本原理。

我們將製作一個簡單的「書籍管理系統」,支援新增(Create)、讀取(Read)、更新(Update)與刪除(Delete)書籍資訊。

1. 建立 Django 專案與應用

首先,安裝 Django 並建立新的專案與應用:

pip install django
django-admin startproject myproject
cd myproject
python manage.py startapp books

註冊 books 應用於 myproject/settings.pyINSTALLED_APPS

INSTALLED_APPS = [
...
'books',
]

2. 定義模型(Model)

books/models.py 中定義一個簡單的書籍模型:

from django.db import models

class Book(models.Model):
title = models.CharField(max_length=200)
author = models.CharField(max_length=100)
published_date = models.DateField()

def __str__(self):
return self.title

遷移資料庫:

python manage.py makemigrations
python manage.py migrate

3. 撰寫 Views(不使用 ModelForm)

books/views.py 中撰寫手動處理的 CRUD 功能。

新增書籍(Create)

from django.shortcuts import render, redirect
from .models import Book
from django.utils.dateparse import parse_date

def create_book(request):
if request.method == 'POST':
title = request.POST.get('title')
author = request.POST.get('author')
published_date_str = request.POST.get('published_date')
published_date = parse_date(published_date_str)

if title and author and published_date:
Book.objects.create(title=title, author=author, published_date=published_date)
return redirect('book_list')
else:
error = "所有欄位皆為必填"
return render(request, 'books/book_form.html', {'error': error})
return render(request, 'books/book_form.html')

讀取書籍(Read)

def book_list(request):
books = Book.objects.all()
return render(request, 'books/book_list.html', {'books': books})

更新書籍(Update)

from django.shortcuts import get_object_or_404

def update_book(request, pk):
book = get_object_or_404(Book, pk=pk)
if request.method == 'POST':
title = request.POST.get('title')
author = request.POST.get('author')
published_date_str = request.POST.get('published_date')
published_date = parse_date(published_date_str)

if title and author and published_date:
book.title = title
book.author = author
book.published_date = published_date
book.save()
return redirect('book_list')
else:
error = "所有欄位皆為必填"
return render(request, 'books/book_form.html', {'book': book, 'error': error})
return render(request, 'books/book_form.html', {'book': book})

刪除書籍(Delete)

def delete_book(request, pk):
book = get_object_or_404(Book, pk=pk)
if request.method == 'POST':
book.delete()
return redirect('book_list')
return render(request, 'books/book_confirm_delete.html', {'book': book})

4. 設定 URL 路由

books/urls.py 中設定對應的路由:

from django.urls import path
from . import views

urlpatterns = [
path('', views.book_list, name='book_list'),
path('create/', views.create_book, name='create_book'),
path('update/<int:pk>/', views.update_book, name='update_book'),
path('delete/<int:pk>/', views.delete_book, name='delete_book'),
]

並在 myproject/urls.py 引入 books 路由:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
path('admin/', admin.site.urls),
path('books/', include('books.urls')),
]

5. 建立模板(Templates)

手動撰寫簡單的 HTML 表單與顯示畫面。

book_list.html

<h1>書籍列表</h1>
<a href="{% url 'create_book' %}">新增書籍</a>
<ul>
{% for book in books %}
<li>
{{ book.title }} - {{ book.author }} - {{ book.published_date }}
<a href="{% url 'update_book' book.id %}">編輯</a>
<a href="{% url 'delete_book' book.id %}">刪除</a>
</li>
{% endfor %}
</ul>

book_form.html

<h1>{% if book %}編輯書籍{% else %}新增書籍{% endif %}</h1>

{% if error %}
<p style="color:red;">{{ error }}</p>
{% endif %}

<form method="post">
{% csrf_token %}
<p>
標題:<input type="text" name="title" value="{{ book.title|default_if_none:'' }}">
</p>
<p>
作者:<input type="text" name="author" value="{{ book.author|default_if_none:'' }}">
</p>
<p>
出版日期(格式 yyyy-mm-dd):<input type="text" name="published_date" value="{{ book.published_date|default_if_none:'' }}">
</p>
<button type="submit">儲存</button>
</form>

<a href="{% url 'book_list' %}">返回列表</a>

book_confirm_delete.html

<h1>刪除書籍</h1>
<p>確定要刪除 "{{ book.title }}" 嗎?</p>
<form method="post">
{% csrf_token %}
<button type="submit">確定刪除</button>
</form>
<a href="{% url 'book_list' %}">取消</a>

6. 啟動伺服器測試

啟動 Django 開發伺服器:

python manage.py runserver

在瀏覽器開啟 http://127.0.0.1:8000/books/,你將可以新增、查詢、編輯和刪除書籍。


總結

這篇教學示範了在 不使用 ModelForm 的情況下,手動撰寫表單處理與資料驗證,完整實作了 Django 的 CRUD 功能。

這種方式的優點在於靈活度高,你可以完全控制表單的結構、驗證邏輯與錯誤處理,非常適合需要客製化表單行為或前後端分離的專案。不過,相較於使用 ModelForm,開發成本略高,也容易產生重複程式碼,因此適時選擇工具是重要的工程判斷。

進一步的優化方向包括:

  • 加入更完整的資料驗證
  • 增加欄位格式錯誤提示
  • 使用 JavaScript 增強表單互動
  • 將表單資料與邏輯封裝成 Class-Based Views(CBV)

透過本篇範例,希望你對 Django 低階處理表單與 CRUD 流程有更深入的理解。

FastAPI 入門教學筆記:打造現代 Python Web API 的入門教學筆記 | 學習筆記

· 閱讀時間約 3 分鐘
kdchang

什麼是 FastAPI?

FastAPI 是一個現代、快速(高效能)、基於 Python 3.7+ 類型提示的 Web 框架,用於建構 API。其核心優勢包含:

  • 自動生成文件:內建 OpenAPI 與 Swagger UI 支援
  • 高效能:基於 Starlette 和 Pydantic,效能可媲美 Node.js 與 Go
  • 開發快速:強大的 IDE 支援與自動補全功能
  • 自動驗證與序列化:透過 Pydantic 型別自動完成

FastAPI 適合快速構建 RESTful API,尤其在開發微服務、機器學習模型部署、或任何 API 後端都非常合適。


快速開始:環境準備與安裝

建議使用虛擬環境管理專案依賴。

python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install fastapi uvicorn
  • fastapi:核心框架
  • uvicorn:ASGI Server,用來啟動應用程式

第一個 FastAPI 程式:Hello API

# main.py

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
return {"message": "Hello, FastAPI!"}

啟動應用程式:

uvicorn main:app --reload
  • --reload:啟用熱重載,開發時會自動重新載入程式

訪問 http://127.0.0.1:8000/,你會看到:

{
"message": "Hello, FastAPI!"
}

自動生成的互動式 API 文件

FastAPI 自動提供兩個 API 文件頁面:

  • Swagger UI:http://127.0.0.1:8000/docs
  • Redoc:http://127.0.0.1:8000/redoc

這些文件會根據程式中的路由與型別提示自動生成,方便前後端協作與測試。


使用路由參數與查詢參數

@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
return {"item_id": item_id, "q": q}

範例請求:

GET /items/42?q=fastapi

回應結果:

{
"item_id": 42,
"q": "fastapi"
}
  • item_id 是路由參數,會自動轉換為 int
  • q 是查詢參數,預設為 None

接收與驗證請求資料:使用 Pydantic 模型

from pydantic import BaseModel

class Item(BaseModel):
name: str
description: str = None
price: float
in_stock: bool = True

@app.post("/items/")
def create_item(item: Item):
return {"message": "Item created", "item": item}

發送 POST 請求:

POST /items/
{
"name": "Laptop",
"description": "A powerful device",
"price": 1299.99,
"in_stock": true
}

回應:

{
"message": "Item created",
"item": {
"name": "Laptop",
"description": "A powerful device",
"price": 1299.99,
"in_stock": true
}
}
  • Pydantic 會自動進行資料驗證與轉換
  • FastAPI 可根據模型自動生成 API 文件

表單與檔案上傳支援

from fastapi import Form, UploadFile, File

@app.post("/submit/")
def submit_form(username: str = Form(...), file: UploadFile = File(...)):
return {
"username": username,
"filename": file.filename,
"content_type": file.content_type
}

這對於處理使用者上傳檔案與表單資料非常方便。


回傳自定義 HTTP 狀態碼與錯誤處理

from fastapi import HTTPException

@app.get("/users/{user_id}")
def read_user(user_id: int):
if user_id != 1:
raise HTTPException(status_code=404, detail="User not found")
return {"user_id": user_id, "name": "Alice"}

這會回傳:

{
"detail": "User not found"
}

並帶有 HTTP 404 錯誤。


小結與下一步學習方向

FastAPI 提供了一種現代化且優雅的方式來構建 API:

  • 強大的型別檢查與 IDE 支援
  • 直覺的程式結構與文件生成
  • 高效能適合用於生產環境

建議學習方向:

  1. 路由分割與模組化管理
  2. 使用依賴注入(Depends)
  3. 整合資料庫(如 SQLAlchemy)
  4. JWT 身份驗證與 OAuth2
  5. 測試與部署(例如 Docker、Gunicorn)

如果你是從 Flask 或 Django REST Framework 轉過來,會發現 FastAPI 提供了相當先進的開發體驗與高效能,是非常值得學習與投入的框架。

參考文件

  1. fastapi API