跳至主要内容

LangGraph 入門教學筆記:打造多步驟 AI 流程的圖形化解決方案 | 學習筆記

· 閱讀時間約 4 分鐘
kdchang

前言

隨著生成式 AI 的應用越來越廣泛,從客服機器人、智慧問答系統到複雜的自動化工作流程,開發者面臨的不再只是單次的文字生成,而是需要處理多步驟的對話邏輯與決策流程

傳統上,這類應用通常透過繁瑣的 if-else 邏輯、狀態機或多層函式巢狀處理,程式碼不易閱讀與維護。為此,LangGraph 應運而生。它是一個開源的 Python 函式庫,讓開發者可以用「流程圖」的方式清晰地定義每一步的處理邏輯,進而打造更穩定且模組化的 AI Workflow。


重點摘要

  • LangGraph 是什麼?

    • 由 LangChain 團隊開發的 AI Workflow 工具,透過流程圖(Graph)定義多步驟的推理過程。
    • 每一個節點(Node)代表一個處理步驟,從 LLM 回覆、條件判斷到工具呼叫等皆可定義成節點。
  • 核心特色

    • 使用有向圖(DAG)表示流程,每個節點都有明確的輸入與輸出狀態。
    • 支援條件分支、迴圈、自訂狀態、記憶上下文。
    • 可與 LangChain、OpenAI、Anthropic 等服務整合。
    • 適合用於構建 Agent、Chatbot、多階段處理流程。
  • 應用情境

    • 客製化對話代理人(如智能客服)
    • 多階段資訊處理(如:檢索、分類、摘要)
    • 工具選擇與執行流程(如:根據輸入選擇工具)
    • 擴展型 LLM 應用(如:RAG、Tool Use)
  • 重要元件說明

    • StateGraph:定義整體流程圖。
    • Node:每個節點代表一個具邏輯意義的步驟。
    • State:儲存目前上下文狀態,可自訂欄位。
    • Conditional Edge:根據邏輯結果決定下一個節點。

實際範例:打造一個 FAQ 對話機器人

本範例將建立一個簡單的對話流程:

  1. 使用者輸入問題。
  2. 呼叫 OpenAI GPT 模型回覆。
  3. 若輸入為 "bye",流程結束;否則持續對話。

1. 安裝套件

pip install langgraph langchain openai

2. 定義狀態與回應節點

from langgraph.graph import StateGraph, END
from langchain.chat_models import ChatOpenAI
from typing import TypedDict

# 自訂狀態格式
class ConversationState(TypedDict):
messages: list[str]
last_user_input: str

# 初始化 LLM
llm = ChatOpenAI(model="gpt-4", temperature=0)

# 處理回應的節點
def generate_response(state: ConversationState):
user_input = state["last_user_input"]
state["messages"].append(f"User: {user_input}")
response = llm.predict(f"請回答以下問題:{user_input}")
state["messages"].append(f"AI: {response}")
return state

3. 定義流程結束條件與流程圖

# 判斷是否要結束對話
def should_continue(state: ConversationState):
if state["last_user_input"].lower().strip() == "bye":
return END
return "generate"

# 建立流程圖
builder = StateGraph(ConversationState)
builder.add_node("generate", generate_response)
builder.set_entry_point("generate")
builder.add_conditional_edges("generate", should_continue)

graph = builder.compile()

4. 執行對話流程

# 初始狀態
state = {
"messages": [],
"last_user_input": "你好,這是什麼系統?"
}

# 執行第一輪
state = graph.invoke(state)

# 模擬第二輪
state["last_user_input"] = "LangGraph 是什麼?"
state = graph.invoke(state)

# 模擬結束對話
state["last_user_input"] = "bye"
state = graph.invoke(state)

# 印出對話記錄
for msg in state["messages"]:
print(msg)

範例輸出結果

User: 你好,這是什麼系統?
AI: 這是一個由 LangGraph 架構的對話系統。
User: LangGraph 是什麼?
AI: LangGraph 是一個讓開發者用流程圖方式設計 AI 應用的工具。
User: bye
AI: 感謝使用,祝您有美好的一天。

總結與延伸

LangGraph 為 LLM 應用程式帶來一個明確的結構化框架,讓我們能夠模組化管理多步驟流程、狀態記憶與條件判斷。相較於傳統方式,它更適合用來構建複雜、可維護的對話式 AI 應用。

延伸應用可以包括:

  • 整合 LangChain 工具(如:向量資料庫、搜尋引擎)
  • 建構具有分支與回饋機制的智能 Agent
  • 開發能根據上下文自我修正的 RAG 系統
  • 將整個 LangGraph 部署為 Web API 或背景工作流程

參考文件

  1. LangGraph: LangChain Agent 的殺手鐧 (入門)

useSWR 入門教學筆記:打造高效、簡潔的資料請求方式 | 學習筆記

· 閱讀時間約 3 分鐘
kdchang

前言

在現代前端開發中,資料的取得與管理是不可或缺的一環。傳統上,我們可能使用 useEffect 搭配 fetchaxios 來處理資料請求,但這樣的方式不僅冗長,還需要手動管理 loading、error 狀態與快取邏輯。為了解決這些問題,Vercel 推出的 SWR(stale-while-revalidate)提供了一種簡潔、聲明式且高效的資料取得方式,特別適合搭配 React 應用開發。

本文將介紹 SWR 的核心觀念、使用方式與基本範例,幫助我們快速上手並應用於實務開發中。


重點摘要

  • SWR 是什麼?

    • SWR 是由 Vercel 開發的 React Hooks 函式庫,提供資料快取與同步機制。
    • 名稱來自 HTTP 快取策略 “stale-while-revalidate”,意指:先顯示舊資料,再重新驗證更新資料
  • 為什麼要使用 SWR?

    • 自動處理資料快取與重新驗證。
    • 簡化資料請求邏輯,減少樣板程式碼。
    • 支援多種進階功能(錯誤重試、revalidate on focus、polling 等)。
  • 基本用法

    • 使用 useSWR(key, fetcher) 進行資料請求。
    • key:唯一識別資料來源的 key,通常為 API 路徑。
    • fetcher:資料請求函式,可使用 fetchaxios 實作。
  • 常見功能

    • isLoadingerror 狀態管理。
    • 自動重試與重新整理資料。
    • 快取與全域共用資料(Shared cache)。
    • 手動重新驗證資料(revalidate)。
    • 支援 SSR、Pagination、Mutation 等進階功能。

實際範例:取得 GitHub 使用者資料

1. 安裝 SWR

npm install swr
# 或使用 yarn
yarn add swr

2. 撰寫 fetcher 函式

// libs/fetcher.ts
export const fetcher = (url: string) => fetch(url).then((res) => res.json());

3. 在元件中使用 useSWR

// pages/UserProfile.tsx
import useSWR from 'swr';
import { fetcher } from '../libs/fetcher';

const UserProfile = () => {
const { data, error, isLoading } = useSWR('https://api.github.com/users/octocat', fetcher);

if (isLoading) return <div>載入中...</div>;
if (error) return <div>載入失敗:{error.message}</div>;

return (
<div>
<h1>{data.name}</h1>
<p>GitHub:{data.login}</p>
<p>Followers:{data.followers}</p>
<img src={data.avatar_url} width={100} />
</div>
);
};

export default UserProfile;

4. 手動重新驗證資料

const { data, mutate } = useSWR('/api/data', fetcher);

// 手動刷新資料
const handleRefresh = async () => {
await mutate();
};

5. 搭配條件式載入

const shouldFetch = userId !== null;

const { data } = useSWR(shouldFetch ? `/api/users/${userId}` : null, fetcher);

6. 自訂快取與設定

import useSWR from 'swr';

const { data, error } = useSWR('/api/data', fetcher, {
refreshInterval: 10000, // 每 10 秒重新抓資料
revalidateOnFocus: true, // 回到畫面時自動刷新
dedupingInterval: 5000, // 阻止過於頻繁的 API 請求
});

總結

SWR 提供了一種優雅、聲明式的方式來管理 React 應用中的資料請求與快取,不僅能有效簡化程式碼,還能提高使用者體驗與應用效能。其彈性與擴充性也適合應用於中大型專案中。

當我們熟悉了 SWR 的基本用法後,接下來也可以進一步探索以下功能:

  • Mutation API:用於資料寫入後手動更新快取。
  • 依賴 key 的動態載入:搭配 router 參數動態請求資料。
  • 全域快取策略自訂(SWRConfig):統一設定所有請求的行為。

透過 SWR,我們不再需要手動處理快取與副作用邏輯,只需專注於資料的呈現與邏輯本身,是開發現代 React 應用的絕佳利器。

參考文件

  1. React Hooks for Data Fetching

JavaScript 可選鏈接運算符(Optional Chaining)介紹與入門教學 | 學習筆記

· 閱讀時間約 5 分鐘
kdchang

前言

JavaScript 中,處理深層嵌套結構時,我們經常會遇到 nullundefined 的問題。例如,當我們需要訪問一個對象的屬性,而該屬性本身可能不存在時,傳統的做法會導致錯誤,這樣的情況會非常繁瑣。為了解決這個問題,JavaScript 引入了 可選鏈接運算符(Optional Chaining),簡化了屬性訪問過程,並防止了因為屬性為 nullundefined 造成的錯誤。

本文將詳細介紹可選鏈接運算符的概念、用法以及常見的實際應用場景。

1. 可選鏈接運算符的基本語法

可選鏈接運算符(?. 是 JavaScript 中一種新的語法,通過它我們可以安全地訪問對象的屬性,並且在中途如果遇到 nullundefined,就會停止執行並返回 undefined,而不是拋出錯誤。

基本語法結構如下:

object?.property
object?.[key]
object?.method()
  • object?.property:如果 objectnullundefined,則返回 undefined,否則返回對象的 property 屬性。
  • object?.[key]:這是動態屬性名的情況,與 object?.property 類似,當 key 是變數或表達式時,這種語法很有用。
  • object?.method():如果 objectmethodnullundefined,則返回 undefined,不會調用該方法。

2. 為什麼需要可選鏈接運算符?

在傳統 JavaScript 中,當我們處理嵌套對象的屬性時,若某個屬性不存在或是 nullundefined,我們會遇到錯誤。例如:

const user = {
name: 'Alice',
address: {
street: '123 Main St',
},
};

console.log(user.address.street); // "123 Main St"
console.log(user.phone.number); // TypeError: Cannot read property 'number' of undefined

在這個例子中,當我們嘗試訪問 user.phone.number 時,由於 phone 屬性不存在,會拋出錯誤。為了解決這個問題,通常我們需要進行多層檢查:

console.log(user && user.phone && user.phone.number); // undefined

這樣的寫法看起來雜亂,並且很難處理更深層次的嵌套。可選鏈接運算符解決了這個問題,使得代碼更加簡潔和安全。

3. 可選鏈接運算符的應用場景

3.1 訪問對象屬性

當我們需要訪問對象的某一層屬性時,如果中間層級的某個屬性為 nullundefined,那麼使用可選鏈接運算符就能防止錯誤的拋出。

const user = {
name: 'Alice',
address: {
street: '123 Main St',
},
};

console.log(user?.address?.street); // "123 Main St"
console.log(user?.phone?.number); // undefined

在這個例子中,user?.address?.street 會安全地返回 street 屬性,而 user?.phone?.number 會返回 undefined,因為 phone 屬性並不存在。

3.2 訪問數組元素

在操作數組時,如果我們想訪問某個索引的元素,也可以使用可選鏈接運算符來避免錯誤。

const array = [1, 2, 3];

console.log(array?.[1]); // 2
console.log(array?.[10]); // undefined

這裡,array?.[1] 會返回 2,而 array?.[10] 會返回 undefined,即使索引超出了數組的範圍。

3.3 調用對象方法

如果對象的方法不存在,使用可選鏈接運算符可以避免拋出錯誤,並且返回 undefined

const user = {
name: 'Alice',
greet() {
console.log('Hello!');
},
};

user?.greet(); // "Hello!"
user?.sayGoodbye(); // undefined

在這個例子中,user?.greet() 會調用 greet 方法並顯示 "Hello!",而 user?.sayGoodbye() 則返回 undefined,因為 sayGoodbye 方法不存在。

3.4 動態屬性名

可選鏈接運算符也支持用動態屬性名來訪問對象屬性,這在處理具有不確定屬性的對象時非常有用。

const user = {
name: 'Alice',
preferences: {
theme: 'dark',
},
};

const key = 'theme';
console.log(user?.preferences?.[key]); // "dark"

在這個例子中,key 是一個變量,表示要訪問的屬性名,user?.preferences?.[key] 可以安全地獲取 preferences 中的 theme 屬性。

4. 與傳統方法的比較

使用可選鏈接運算符,我們的代碼變得更加簡潔,減少了不必要的檢查。傳統的方式可能需要多次檢查對象的存在,才能安全地訪問某個屬性,而可選鏈接運算符讓這一過程變得直觀且易於維護。

傳統方法:

if (user && user.address && user.address.street) {
console.log(user.address.street);
}

使用可選鏈接運算符:

console.log(user?.address?.street);

5. 可選鏈接運算符與 null 合併運算符(??

可選鏈接運算符經常與 null 合併運算符(??)一起使用。?? 用來返回當前值是否為 nullundefined,如果是則返回其右側的值,否則返回當前值。

const user = null;
const name = user?.name ?? 'Default Name';
console.log(name); // "Default Name"

在這裡,user?.name 會返回 undefined,因為 usernull,而 ?? 會將其替換為 'Default Name'

6. 總結

可選鏈接運算符(?.)是 JavaScript 中非常實用的一個特性,它簡化了嵌套對象屬性訪問的邏輯,避免了 nullundefined 帶來的錯誤,使代碼更加簡潔且容易理解。無論是在處理複雜的 API 返回數據還是操作動態結構的對象時,可選鏈接運算符都能發揮重要作用。在日常開發中,我們可以利用它來編寫更健壯、可讀性更強的代碼。

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

· 閱讀時間約 4 分鐘
kdchang

一、前言

隨著 Web 開發從傳統伺服器渲染演變為前後端分離架構,處理「使用者認證」變得更為複雜。許多開發者不再單純依賴 session + cookie 的方式,而是轉向 token-based 的 JWT 或 OAuth 解決方案。

如果你正在使用 Next.js,那麼 NextAuth.js 是一套高度整合、彈性高且極為方便的認證函式庫。它支援多種認證方式(如 OAuth、Email、Credentials、LDAP、JWT 等),可與 Next.js 無縫整合,適合快速導入登入機制。


二、重點摘要

  • 開源且專為 Next.js 設計的認證函式庫

  • 支援:

    • OAuth 第三方登入(Google、GitHub、Facebook、LINE 等)
    • Email 登入(magic link)
    • 自定義帳密登入(Credentials provider)
    • JWT 無狀態驗證
  • 自動處理:

    • session 建立與維護
    • cookies 管理
    • CSRF 保護
  • 可與資料庫整合(支援 Prisma、TypeORM、MongoDB 等)

  • 可自定義:

    • 登入畫面
    • 回傳的使用者資料
    • 授權邏輯與回傳 token 欄位
  • 適用場景:企業內部登入、SaaS 後台、會員管理系統、整合第三方帳戶等


三、建立 NextAuth 基本專案

1. 建立 Next.js 專案

npx create-next-app@latest nextauth-demo
cd nextauth-demo
npm install next-auth

2. 建立 API Route

pages/api/auth/[...nextauth].js 中新增設定:

// pages/api/auth/[...nextauth].js
import NextAuth from 'next-auth';
import GitHubProvider from 'next-auth/providers/github';

export default NextAuth({
providers: [
GitHubProvider({
clientId: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
}),
],
callbacks: {
async session({ session, token }) {
session.user.id = token.sub;
return session;
},
},
});

3. 設定環境變數

.env.local 中新增:

GITHUB_CLIENT_ID=你的 GitHub OAuth ID
GITHUB_CLIENT_SECRET=你的 GitHub OAuth Secret
NEXTAUTH_SECRET=隨機生成的 secret(可用 openssl rand -base64 32)

若使用 JWT,可加入:

NEXTAUTH_JWT_SECRET=任意密鑰

4. 在前端使用登入與登出功能

// pages/index.js
import { useSession, signIn, signOut } from 'next-auth/react';

export default function Home() {
const { data: session } = useSession();

if (session) {
return (
<>
<p>歡迎,{session.user.name},你已登入</p>
<button onClick={() => signOut()}>登出</button>
</>
);
}

return (
<>
<p>尚未登入</p>
<button onClick={() => signIn('github')}>使用 GitHub 登入</button>
</>
);
}

5. 在 _app.js 包裝 SessionProvider

// pages/_app.js
import { SessionProvider } from 'next-auth/react';

export default function App({ Component, pageProps }) {
return (
<SessionProvider session={pageProps.session}>
<Component {...pageProps} />
</SessionProvider>
);
}

四、自訂登入方式(Credentials)

除了 OAuth,也可以自訂帳號密碼登入:

// [...nextauth].js
import CredentialsProvider from "next-auth/providers/credentials";

providers: [
CredentialsProvider({
name: "Credentials",
credentials: {
username: { label: "帳號", type: "text" },
password: { label: "密碼", type: "password" },
},
async authorize(credentials) {
const user = await verifyUser(credentials.username, credentials.password);
if (user) return user;
return null;
},
}),
],

注意:authorize 函式需自行驗證帳密,並回傳 user 物件,如 { id, name, email }


五、整合 JWT 模式(無狀態認證)

啟用 JWT 模式,只需設定:

session: {
strategy: "jwt",
},
jwt: {
secret: process.env.NEXTAUTH_SECRET,
},

這樣每次登入都會發出加密的 JWT,並由前端自動存於 cookie。你可以透過 getToken() 從 API 端存取 token 中的自訂欄位。


六、取得 Server 端 Session

如果你需要在 API route 或 SSR 頁面取得使用者登入資訊,可使用:

import { getServerSession } from 'next-auth/next';
import { authOptions } from './auth/[...nextauth]';

export async function getServerSideProps(context) {
const session = await getServerSession(context.req, context.res, authOptions);

return {
props: {
session,
},
};
}

七、總結與延伸

功能NextAuth 表現
快速整合第三方登入非常方便,僅需 provider 設定
安全性預設 CSRF 保護、HttpOnly cookie
自訂性高度可調整 callback、UI、資料庫整合
無狀態支援支援 JWT、Access Token
SSR/SPA 支援完整整合 getServerSideProps、Client Hook

延伸功能建議

  • 整合 Prisma 儲存使用者資料與 session(可自動生成 schema)
  • 自訂登入 UI 與跳轉路徑
  • 客製化 JWT payload 內容(如 role, id, user_type)
  • 使用 getToken()useSession() 搭配權限控制
  • 多平台登入支援(Web、Mobile API)

React Context API 入門教學 | 學習筆記

· 閱讀時間約 5 分鐘
kdchang

React Context API 是 React 提供的一種方式,讓我們能夠在組件樹中傳遞資料,而不需要一層層地使用 props。Context API 可以解決多層嵌套組件的傳遞問題,讓我們在深層組件中輕鬆訪問到全局狀態。本文將介紹如何使用 React Context API,並提供一個簡單的範例來展示其實際應用。

什麼是 React Context API

React Context API 是 React 的一個內建功能,它可以讓我們在組件樹中共享資料,避免多層嵌套的 props 傳遞。Context 主要由三個部分組成:

  1. React.createContext():創建一個 Context 物件。
  2. Provider:這是 Context API 中的一個組件,它用來包裹整個應用,並提供一個全局的資料源。
  3. Consumer:這是用來訪問 Context 資料的組件,它能夠獲取 Provider 中傳遞的資料。

使用 Context 的目的,是為了避免將相同的資料層層傳遞到每個組件,這樣可以讓應用的資料流變得更加簡潔。

使用 Context API 的步驟

步驟 1: 創建 Context

首先,我們需要使用 React.createContext() 來創建一個 Context 物件。這個物件會返回一個 ProviderConsumer 組件,讓我們在應用中使用。

import React from 'react';

// 創建 Context
const MyContext = React.createContext();

步驟 2: 使用 Provider 來傳遞資料

Context 的 Provider 是用來包裹應用的,它會接收一個 value 屬性,這個屬性就是要共享給整個組件樹的資料。

const App = () => {
const [user, setUser] = React.useState({ name: 'John', age: 30 });

return (
<MyContext.Provider value={user}>
<UserProfile />
</MyContext.Provider>
);
};

在這個範例中,我們將一個 user 物件傳遞給 MyContext.Providervalue 屬性,這樣整個組件樹中的所有子組件都能夠訪問到這個 user 資料。

步驟 3: 使用 Consumer 來接收資料

在需要使用資料的地方,我們可以使用 MyContext.Consumer 來獲取資料。Consumerchildren 是一個函數,它會接收一個 value 參數,這個參數就是在 Provider 中傳遞的資料。

const UserProfile = () => {
return (
<MyContext.Consumer>
{(user) => (
<div>
<h1>{user.name}</h1>
<p>Age: {user.age}</p>
</div>
)}
</MyContext.Consumer>
);
};

在這個範例中,UserProfile 組件通過 Consumer 來訪問 MyContext 中的 user 資料,並渲染顯示用戶的名字和年齡。

步驟 4: 使用 useContext Hook (React 16.8 及以上)

React 16.8 引入了 useContext Hook,這樣我們可以更方便地在函數組件中使用 Context,而不需要使用 Consumer。這樣的寫法更加簡潔,並且避免了過多的嵌套。

import React, { useContext } from 'react';

const UserProfile = () => {
const user = useContext(MyContext);

return (
<div>
<h1>{user.name}</h1>
<p>Age: {user.age}</p>
</div>
);
};

使用 useContext 可以直接從 Context 中獲取資料,而不需要使用 Consumer。這使得代碼更簡潔,並提高了可讀性。

實際範例

下面是一個完整的範例,展示了如何使用 React Context API 來管理應用中的全局狀態。這個範例將包括一個用戶資料的管理,並能夠在多個組件中共享這些資料。

import React, { useState, useContext } from 'react';

// 創建 Context
const MyContext = React.createContext();

const App = () => {
const [user, setUser] = useState({ name: 'John', age: 30 });

return (
<MyContext.Provider value={user}>
<div>
<UserProfile />
<AgeUpdater />
</div>
</MyContext.Provider>
);
};

const UserProfile = () => {
const user = useContext(MyContext);

return (
<div>
<h1>{user.name}</h1>
<p>Age: {user.age}</p>
</div>
);
};

const AgeUpdater = () => {
const user = useContext(MyContext);
const setUser = useState()[1];

const updateAge = () => {
setUser({ ...user, age: user.age + 1 });
};

return (
<div>
<button onClick={updateAge}>Increase Age</button>
</div>
);
};

export default App;

範例解析

  1. App 組件:在 App 組件中,我們使用 useState 定義了一個 user 資料,並通過 MyContext.Provider 將資料提供給下層組件。
  2. UserProfile 組件UserProfile 使用 useContext 來讀取 MyContext 中的資料,並顯示用戶的名字和年齡。
  3. AgeUpdater 組件:這個組件同樣使用 useContext 來讀取和更新 user 資料。我們在這裡定義了一個按鈕,當按下時,會更新 user 的年齡。

Context API 的優缺點

優點:

  1. 簡化資料傳遞:當我們需要在多層嵌套的組件中共享資料時,使用 Context 可以避免繁瑣的 props 傳遞。
  2. 可擴展性:Context 非常適合用於應用中的全局狀態管理,像是用戶認證、語言設置、主題樣式等。

缺點:

  1. 重新渲染問題:當 Provider 中的資料變更時,所有使用該 Context 的組件都會重新渲染。對於大型應用來說,這可能會影響性能。
  2. 狀態過度共享:Context 主要用於共享全局資料,如果將太多不相關的資料放入同一個 Context,可能會使代碼變得難以維護。

總結

React Context API 是一個強大的工具,可以幫助我們管理應用中的全局狀態。在適當的情況下使用 Context 可以大大簡化代碼,避免深層嵌套的 props 傳遞。但也需要謹慎使用,避免過多不必要的資料共享,從而影響性能和可維護性。在開發中,我們可以根據具體需求來選擇是否使用 Context API,並搭配其他狀態管理工具(如 Redux 或 Zustand)來管理更複雜的應用狀態。

參考文件

  1. 用 React Context API 實作跨組件傳值的功能

Chrome Extension 入門教學筆記 | 學習筆記

· 閱讀時間約 3 分鐘
kdchang

前言

Chrome Extension(Chrome 擴充功能)是針對 Google Chrome 瀏覽器開發的瀏覽器插件,能夠延伸瀏覽器的功能,提供更高效的使用體驗。你可以用它來自動化操作、強化 UI、增加捷徑、記錄內容、攔截請求等等。

本教學將介紹 Chrome Extension 的基本架構、開發流程與一個簡單的實作範例,協助你快速入門。


重點摘要

  1. 基本架構:

    • manifest.json:擴充功能的核心設定檔
    • background.js / service_worker.js:背景邏輯(例如攔截請求、常駐任務)
    • popup.html + popup.js:點擊圖示後的互動 UI
    • content.js:注入頁面的腳本,直接與 DOM 互動
  2. 開發步驟:

    • 建立資料夾結構與設定檔
    • 撰寫功能腳本與 UI
    • 在 Chrome 中載入未封裝的擴充功能
    • 測試與除錯
  3. 核心權限與功能:

    • permissions 欄位需指定所需功能(如 tabs, storage, scripting
    • host_permissions 控制哪些網站允許注入腳本
    • 可與頁面雙向通訊
  4. 常見用途:

    • 提高生產力(截圖、標記、翻譯)
    • 資料擷取與分析(網頁爬蟲輔助)
    • 儲存內容(書籤、待辦清單)
    • 網站 UI 客製化

實際範例:儲存選取文字的小擴充功能

功能簡介

當使用者在網頁上選取一段文字並點擊擴充功能圖示,會將選取的文字儲存到 localStorage,方便後續檢視。


1. 專案結構

my-extension/
├── manifest.json
├── popup.html
├── popup.js
├── content.js

2. manifest.json

{
"manifest_version": 3,
"name": "Save Selected Text",
"version": "1.0",
"description": "儲存網頁中選取的文字",
"permissions": ["scripting", "storage"],
"host_permissions": ["<all_urls>"],
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "icon16.png",
"48": "icon48.png",
"128": "icon128.png"
}
},
"background": {
"service_worker": "background.js"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"]
}
]
}

3. popup.html

<!DOCTYPE html>
<html>
<head>
<title>已儲存文字</title>
</head>
<body>
<h3>你儲存的文字:</h3>
<ul id="text-list"></ul>
<script src="popup.js"></script>
</body>
</html>

4. popup.js

document.addEventListener('DOMContentLoaded', async () => {
chrome.storage.local.get(['savedTexts'], (result) => {
const list = document.getElementById('text-list');
const texts = result.savedTexts || [];
texts.forEach((text) => {
const li = document.createElement('li');
li.textContent = text;
list.appendChild(li);
});
});
});

5. content.js

document.addEventListener('mouseup', () => {
const selectedText = window.getSelection().toString().trim();
if (selectedText) {
chrome.storage.local.get(['savedTexts'], (result) => {
const current = result.savedTexts || [];
current.push(selectedText);
chrome.storage.local.set({ savedTexts: current });
});
}
});

6. 載入擴充功能

  1. 打開 Chrome 瀏覽器
  2. 前往 chrome://extensions/
  3. 開啟右上角「開發人員模式」
  4. 點選「載入未封裝項目」
  5. 選取專案資料夾(my-extension)

7. 測試方式

  1. 任意打開一個網頁
  2. 選取文字後放開滑鼠
  3. 點擊瀏覽器右上角的擴充功能圖示
  4. 在彈出的視窗中查看剛才儲存的文字

小技巧與補充

  • 使用 TypeScript:可結合 Vitewebpack 實現模組化開發。
  • Hot Reload:透過專案如 crxjs 可達成自動刷新套件。
  • 權限最小化原則:僅使用必要權限以通過審核。
  • Storage API:可選擇使用 localStoragechrome.storage.localsync 等不同儲存方式。
  • message passing:背景與 content script 可用 chrome.runtime.sendMessage 溝通。

總結

Chrome Extension 是一個強大的平台,讓開發者能以 HTML、CSS、JavaScript 等前端技術打造自己的工具。無論是為了解決個人需求、改善使用體驗,還是作為產品 MVP 的雛型開發平台,Chrome Extension 都是值得投資時間學習的技術。

從簡單的文字儲存開始,你可以慢慢拓展功能,加入右鍵選單、快捷鍵、網頁改寫、API 串接等高階應用,打造出屬於你自己的瀏覽器擴充小工具。

chrome.storage.local vs. localStorage:資料儲存方式差異入門教學筆記 | 學習筆記

· 閱讀時間約 4 分鐘
kdchang

前言

在前端與 Chrome 擴充功能(Chrome Extension)開發中,「資料儲存」是一項常見需求。開發者常見的兩種方式為:

  • localStorage:瀏覽器原生提供的本地儲存 API
  • chrome.storage.local:Chrome Extension 提供的本地儲存 API

兩者名稱相似,功能也都能儲存 key-value 結構資料,但其用途、行為、效能與限制卻有顯著差異。本篇筆記將帶你掌握這兩者的差別,並透過實作範例協助你在開發時做出正確選擇。


重點摘要

1. 定義與使用場景

  • localStorage 是 Web API,適用於一般網頁與 Content Script,操作簡便但同步。
  • chrome.storage.local 是專為 Chrome 擴充功能設計的非同步儲存 API,適用於 Background、Popup、Options、Content Script 等擴充功能組件。

2. 主要差異比較

項目chrome.storage.locallocalStorage
API 類型非同步同步
儲存容量約 5MB 以上,可依平台調整約 5MB(視瀏覽器而定)
使用範圍限於 Chrome Extension網頁與 Content Script
安全性與隔離性高(與其他網站與擴充隔離)中(每個 domain 隔離)
可與 background/popup 共用
是否支援跨裝置同步使用 chrome.storage.sync 可支援

3. 實作差異

  • localStorage 使用方式簡單、同步,可立即取得值。
  • chrome.storage.local 是非同步設計,需透過 callback 或 Promise 取得值。

實際範例比較

以下為兩者的典型儲存與讀取操作實作方式。


一、使用 localStorage(同步)

儲存資料

localStorage.setItem('username', 'kdchang');

讀取資料

const name = localStorage.getItem('username');
console.log(name); // 輸出:kdchang

刪除資料

localStorage.removeItem('username');

優點與限制

  • 優點:簡單、直覺、無需 callback
  • 限制:無法在 background script 中使用、同步操作可能阻塞 UI、無跨 component 溝通機制

二、使用 chrome.storage.local(非同步)

儲存資料

chrome.storage.local.set({ username: 'kdchang' }, () => {
console.log('儲存成功');
});

讀取資料

chrome.storage.local.get(['username'], (result) => {
console.log('讀到的值:', result.username);
});

刪除資料

chrome.storage.local.remove('username', () => {
console.log('已刪除 username');
});

優點與限制

  • 優點:資料與擴充功能隔離、安全性高、能跨組件共享
  • 限制:需處理非同步流程(可用 async/await 解決)

實戰應用:擴充功能記錄使用者偏好設定

專案背景

你開發了一個可自訂主題配色的擴充功能,使用者可以切換「深色」或「淺色」模式,並希望記錄下來。


localStorage 實作方式(限 Content Script)

// 儲存使用者偏好
localStorage.setItem('theme', 'dark');

// 頁面載入時讀取
const theme = localStorage.getItem('theme');
document.body.setAttribute('data-theme', theme);

此方法雖然簡便,但無法在 background script、popup 等元件中共用。


chrome.storage.local 實作方式(推薦)

// 儲存
chrome.storage.local.set({ theme: 'dark' }, () => {
console.log('儲存主題成功');
});

// 讀取
chrome.storage.local.get(['theme'], (result) => {
document.body.setAttribute('data-theme', result.theme || 'light');
});

這種方式可讓 background.js、popup.html、options.html 都能取得相同資料,並透過 message passing 進一步溝通。


使用建議與最佳實踐

選擇依據:

  • Chrome Extension 專案開發時:建議一律使用 chrome.storage.local,搭配 async/await 管理非同步流程。
  • Content Script 或網頁前端小工具:若不考慮擴充功能架構,可使用 localStorage 快速開發。

注意事項:

  • chrome.storage.local 每次寫入都是非同步,避免過度頻繁更新(例如輸入框每秒觸發)
  • localStorage 資料若寫入太大或格式不當,可能造成同步錯誤或被清除

總結

問題建議做法
在 popup、background、content script 共享設定資料使用 chrome.storage.local
快速暫存使用者動作、不需跨頁可用 localStorage
需考慮非同步、可擴充、跨頁共享與安全性優先使用 chrome.storage.local

chrome.storage.locallocalStorage 各有適用場景與特點,選擇時需考量使用環境、效能、安全性與 API 特性。透過本文你應該能更清楚何時該用哪一種方式,並應用在擴充功能與網頁開發中,打造穩定且高效的儲存邏輯。

Claude Code 入門教學筆記 | 學習筆記

· 閱讀時間約 4 分鐘
kdchang

前言

隨著 AI 編程助手的發展,越來越多開發者開始使用 AI 工具來加速開發流程。由 Anthropic 推出的 Claude Code,是一個專為程式設計任務設計的生成式 AI 模型。它結合 Claude 的強大語言理解能力,專注於代碼撰寫、重構、除錯與文件生成,並支援多種程式語言。

與 GitHub Copilot、ChatGPT 等工具類似,Claude Code 目標是協助開發者更有效率地完成日常開發任務。不過,它也有獨特的優勢:例如更嚴謹的安全性考量、對上下文理解的廣度,以及與 Anthropic 所提倡的 Constitutional AI 架構相結合的「安全設計」。

本篇筆記將說明 Claude Code 的特色與基礎使用方式,協助你快速上手。


重點摘要

  • Claude Code 是什麼?

    • 由 Anthropic 推出的 AI 編程輔助模型,基於 Claude 模型微調。
    • 專注於程式碼相關任務,如生成、除錯、補完與重構。
  • 支援的平台與模式

    • 可透過 Claude 官網、Slack 整合、API 介面使用。
    • Claude 3 模型家族中的 Sonnet 與 Opus 皆支援 Code 能力。
  • 支援語言與任務

    • 語言:Python、JavaScript、TypeScript、Java、C/C++、HTML/CSS、SQL 等。
    • 任務類型:代碼生成、文件產生、除錯、測試碼產出、重構與最佳化。
  • 與其他工具的差異

    • 支援更長的上下文(可達 200k tokens)
    • 回應更具可讀性與解釋性
    • Anthropic 特別強調「可控性」與「安全性」
  • 使用方式

    • Claude 聊天介面中貼上程式碼片段與指令
    • 使用 prompt 提示具體任務,例如 “請優化這段程式碼” 或 “請解釋下列 Python 函數的功能”

實際範例教學

以下範例將以 Claude Code 處理 Python 程式碼為例,展示常見任務的應用。

1. 程式碼生成

Prompt:

請用 Python 寫一個函式,接收一個整數列表,回傳最大值與最小值。

Claude Code 回應:

def find_min_max(numbers):
if not numbers:
return None, None
return min(numbers), max(numbers)

# 使用範例
nums = [3, 8, 1, 4, 9]
min_val, max_val = find_min_max(nums)
print(f"最小值: {min_val}, 最大值: {max_val}")

2. 程式碼除錯

輸入錯誤程式碼:

def divide(a, b):
return a / b

print(divide(10, 0))

Prompt:

這段程式碼會發生什麼錯誤?要怎麼修改?

Claude Code 回應:

這段程式碼會在除以 0 時產生 ZeroDivisionError 錯誤。你可以加入 try-except 來處理例外:

建議修改版本:

def divide(a, b):
try:
return a / b
except ZeroDivisionError:
return "除數不能為零"

print(divide(10, 0))

3. 重構程式碼

Prompt:

請幫我重構以下 Python 程式,使其更簡潔易讀

原始程式碼:

def process(data):
result = []
for item in data:
if item % 2 == 0:
result.append(item * item)
return result

Claude Code 回應:

def process(data):
return [item ** 2 for item in data if item % 2 == 0]

實用建議與提示

  • 具體的指令更有效:直接說明你要做什麼,例如「請將下列 TypeScript 重構為具名函式」,比「幫我修改程式碼」效果更好。
  • 使用上下文編輯技巧:可貼上整個 class 或 module,再指明哪個函式需要優化。
  • 加入期望輸出範例:讓 Claude Code 理解你的預期輸入與輸出格式。
  • 搭配 API 使用:進階用戶可透過 Anthropic API 將 Claude Code 整合進開發工作流程或 IDE 插件。

總結

Claude Code 是新一代 AI 編程輔助工具的代表之一,其簡潔清晰的語言理解能力與較高的上下文記憶範圍,使其特別適合處理複雜的程式重構與跨檔案邏輯分析。無論你是初學者還是資深開發者,只要善用 prompt 的語言描述能力與 Claude Code 的生成特性,就能提升開發效率與程式品質。

若你正尋求 Copilot 或 ChatGPT 的替代方案,Claude Code 值得一試。


若你有特定開發環境(如 VSCode、CLI、Slack)或語言需求,我可以進一步為你量身規劃 Claude Code 的使用方式與整合建議。需要的話也可以補充教學範例。

參考文件

  1. 最佳 GitHub Copilot 設定
  2. 從「寫程式」到「與 AI 共舞」── 我在公司推動 Vibe Coding 的經驗分享
  3. 2025 年最強推薦 Vibe Coding 工具一次看懂

GitHub Copilot 與 Copilot Agent 入門教學筆記 | 學習筆記

· 閱讀時間約 5 分鐘
kdchang

前言

自 2021 年推出以來,GitHub Copilot 已成為全球數百萬開發者的編程助手,能根據上下文即時補全程式碼。2024 年,GitHub 推出進階功能 Copilot WorkspaceCopilot Agents,進一步從單一代碼補全進化為具備「理解、操作與協作」能力的智慧 AI 開發助理。

這些新功能不僅能幫助你完成程式碼撰寫,還能協助理解需求、分析問題、規劃專案、重構模組,甚至提出 pull request 變更。本文將帶你全面認識 GitHub Copilot 及其 Agent 功能,從基本操作到進階應用,協助你提升開發效率與品質。


重點摘要

1. GitHub Copilot 基本功能

  • AI 程式碼補全:根據上下文推薦下一行或整段程式碼。
  • 註解驅動撰寫:使用自然語言描述意圖,自動轉換為程式碼。
  • 測試與樣板產生:自動產出單元測試與樣板邏輯。
  • 支援 VS Code、JetBrains IDE、Neovim 等。

2. Copilot Agents 新功能

  • 工作區理解(Workspace Contextualization):理解整個專案結構,非僅單一檔案。
  • 任務導向操作(Task-oriented Autonomy):透過自然語言指令,Agent 可主動找出需要修改的檔案與程式邏輯。
  • 互動式任務視窗(Copilot Workspace):能與你共同規劃、分解、執行修改。
  • 自動產生 Pull Request:Agent 能根據需求描述,自動建立符合需求的 PR 變更建議。
  • 支援自然語言任務指令:如 “Add pagination to the user list page” 或 “Refactor login logic to support OAuth”。

3. 適用場景

  • 新功能導入(快速建構雛型)
  • 現有程式碼重構與除錯
  • 快速理解大型專案結構
  • 撰寫測試與文件
  • 協作開發與程式碼審查輔助

使用條件與環境設置

  1. 基本需求

    • GitHub 帳號
    • VS Code 或 GitHub 官方網站(Copilot Workspace)
    • GitHub Copilot 訂閱(個人 / 企業方案)
  2. 啟用方式

    • 在 VS Code Marketplace 安裝「GitHub Copilot」擴充功能
    • 若已啟用 Agent 功能(目前逐步開放),將會在 Copilot 介面看到 Workspace 或 Chat 模式
  3. 使用 Copilot Workspace(預設僅企業帳戶開放測試)

    • 登入 GitHub,進入某個 repo
    • 點擊 Copilot 按鈕開啟 Workspace
    • 輸入任務描述,例如:Convert all date logic to use UTC
    • Copilot 會分析整個程式碼庫,自動生成變更建議與 Pull Request

實際範例

範例一:註解驅動補全

輸入:

// 寫一個可以將字串反轉的函式

Copilot 自動補全:

function reverseString(str) {
return str.split('').reverse().join('');
}

範例二:Copilot Chat 對話式問答

使用快捷鍵 Cmd+I 或從 VS Code Copilot Chat 面板提問:

請幫我解釋這段程式碼的作用:parseDate(input, 'YYYY-MM-DD')

Copilot 回應:

這段程式碼會將字串 input 解析為特定格式的日期物件,格式為 'YYYY-MM-DD',可能使用的是 dayjs 或 moment 函式庫。

範例三:Copilot Agent 自動任務執行(需 Workspace 模式)

任務描述:

Refactor login controller to support Google OAuth

流程:

  1. Copilot 會分析 controllers/login.js 或相似檔案
  2. 自動識別現有的登入邏輯
  3. 插入 Google OAuth 處理邏輯(可能使用 passport.js 或其他第三方庫)
  4. 產生變更摘要與建議 Pull Request
  5. 開發者審閱後即可 merge

範例四:整合單元測試建議

輸入:

def is_palindrome(s):
return s == s[::-1]

在測試檔案中輸入:

def test_is_palindrome():

Copilot 自動補全:

    assert is_palindrome("racecar") == True
assert is_palindrome("hello") == False
assert is_palindrome("") == True

建議與最佳實踐

  1. 善用自然語言描述 Copilot Agent 具有語意理解能力,越具體的任務描述,越能產出正確結果。

  2. 審核與測試不可省略 AI 輔助雖強,但仍可能產生誤邏輯或不符合風格的程式碼,請務必加入測試與審查。

  3. 適合用於重複與樣板任務 例如 CRUD、測試、自動 refactor、邏輯轉換等。

  4. 搭配 GitHub Actions 使用更強大 可結合 CI/CD 流程,自動執行 Copilot Agent 產生的 PR。


總結

GitHub Copilot 已從單純的程式補全工具,蛻變為具備理解上下文、可互動操作的智慧開發代理人。透過 Copilot Agent,你不僅能提升撰寫速度,還能更高層次地規劃、修改與維護專案。

對個人開發者而言,Copilot 是每日開發的好助手;對團隊而言,Copilot Agent 更像是一位全天候不下班的開發實習生,協助你管理技術債、維護程式庫、強化開發工作流。

參考文件

  1. 最佳 GitHub Copilot 設定
  2. 從「寫程式」到「與 AI 共舞」── 我在公司推動 Vibe Coding 的經驗分享
  3. 2025 年最強推薦 Vibe Coding 工具一次看懂

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

· 閱讀時間約 5 分鐘
kdchang

前言

在現代軟體開發與數位工作流程中,「自動化」已成為提升效率的關鍵。無論是處理 API 整合、資料同步、通知發送,或是各種無需人工介入的重複性工作,選擇一套靈活的工作流程自動化工具是必要的。

n8n(pronounced "n-eight-n") 是一個強大的開源自動化工具,具備類似 Zapier 的拖拉式流程設計介面,但更強調可自建、開源、自主掌控資料與程式彈性。它支援超過 400 個內建整合(如 Slack、Google Sheets、Notion、MySQL、HTTP API 等),同時允許用戶透過 JavaScript 編寫邏輯節點,打造彈性極高的自動化流程。

相較於其他自動化工具,n8n 的最大優勢在於:「你可以在本機或伺服器上自行部署,資料完全由你掌控。」這點對於重視隱私或希望建立企業內部自動化平台的團隊尤其重要。


重點摘要

  • n8n 是什麼?

    • 一個開源、自託管、可視化的工作流程自動化工具。
    • 名稱源自 “Node + Node = Workflow”,以節點(Node)為單位建構流程。
  • 主要特色

    • 開源、自託管、資料不外流。
    • 拖放式流程設計器,門檻低但彈性高。
    • 可撰寫 JavaScript 處理邏輯。
    • 支援 webhook、排程、event-based 工作流程。
    • 擁有超過 400 個現成整合節點(Google、Slack、GitHub、HTTP Request 等)。
  • 使用場景

    • 第三方 API 整合與自動同步。
    • 資料轉換與清洗(ETL)。
    • 表單提交後自動寄信/填寫 Google Sheets。
    • 發布內容到社群平台。
    • 團隊協作通知(如 Jira、Slack 整合)。
    • 自動監控 RSS、新留言、社群評論等。
  • 部署方式

    • 支援 Docker、本地安裝、雲端託管(n8n.cloud)、Render、Railway 等平台。
    • 可選擇免費版本或企業進階授權。

安裝與啟用方式

1. 使用 Docker 部署(推薦)

docker run -it --rm \
-p 5678:5678 \
-v ~/.n8n:/home/node/.n8n \
n8nio/n8n

開啟瀏覽器進入 http://localhost:5678 即可開始使用。

2. 使用 npm 安裝

npm install n8n -g
n8n

同樣可透過 http://localhost:5678 開啟界面。


使用介面簡介

n8n 的主界面由以下幾個部分組成:

  • Canvas(畫布):可拖拉節點設計流程。
  • 左側工具列:內建各種整合節點分類(Webhook、HTTP Request、Email 等)。
  • 節點屬性面板:選取節點後可編輯其參數設定。
  • 執行與測試工具:可以逐步執行流程、查看輸入與輸出資料。

實際應用範例

範例一:接收 Webhook 並寄送 Slack 通知

目標流程:當收到 Webhook(如表單送出),自動發送一則 Slack 通知。

  1. 新增節點 Webhook

    • 設定 HTTP 方法為 POST
    • 指定路徑為 /contact
  2. 新增節點 Slack

    • 動作選擇 Send Message
    • 填入 Slack OAuth 認證與頻道資訊
    • 訊息可使用 {{$json["name"]}} 這類變數取得 webhook 傳入資料
  3. WebhookSlack 連接起來。

  4. 啟用工作流程,對 /webhook/contact 發送 POST 請求,即會收到通知。


範例二:每天早上 9 點將 MySQL 資料匯出至 Google Sheets

  1. 使用 Cron 節點設定時間為每天早上 9 點。

  2. 使用 MySQL 節點撈出指定資料表的內容。

  3. 使用 Google Sheets 節點將資料新增至指定試算表。

  4. 可加上一個 Function 節點清洗或轉換資料格式。

這樣可輕鬆建立每日自動報表流程,無需撰寫一行 shell script 或排程任務。


範例三:API 整合流程(串接 ChatGPT 回覆留言)

  1. 使用 Webhook 節點接收前端留言內容。

  2. 使用 OpenAI 節點(或 HTTP Request + 自行送出 API 請求)傳送留言給 ChatGPT 並取得回覆。

  3. 使用 Send EmailTelegram 節點自動回覆用戶。

n8n 支援 JSON 處理與條件邏輯節點,讓你可根據留言內容分類處理或轉送不同部門。


實用建議

  • 建議搭配 版本控制(匯出 workflow JSON),便於多人協作與備份。
  • 若要部署至生產環境,建議設定密碼驗證與 HTTPS 保護。
  • 可以將變數或 API key 設定為「環境變數」集中管理,提升安全性與可維護性。
  • 利用 FunctionSet 節點進行複雜邏輯處理與欄位映射。
  • 若有即時性需求,可使用 webhook + queue-based 設計方式,避免封鎖主流程。

總結

n8n 是一款功能強大且靈活的開源工作流程自動化工具,不僅適合開發者,也適合營運、行銷、客服等跨部門自動化需求。透過拖拉節點的方式,任何人都可以建立自動化流程,取代繁瑣重複的手動操作。

與 Zapier、Make 等 SaaS 工具不同,n8n 提供了極高的可控性與可擴充性。無論是 API 整合、資料清洗、事件觸發,還是每日任務排程,n8n 都能成為你構建智慧工作流程的得力助手。

參考文件

  1. n8n 新手中文教學:6 步安裝、7 步驟部署第一支工作流!費用?