跳至主要内容

React Render HTML 入門教學 | w3schools 學習筆記

· 閱讀時間約 2 分鐘
KD Chang
Builder, Curator & Lifelong Learner

前言​

React 是一個專門用於構建使用者介面(UI)的 JavaScript 函式庫,其主要目標就是將 HTML 呈現在網頁中。透過 React 的核心函式 createRoot() 和方法 render(),開發者可以將 UI 元件渲染到指定的 HTML 元素中。本文將介紹 React 如何在網頁上渲染 HTML,包括核心函式與方法的用途、應用位置與語法示範,並介紹與 JSX 語法的結合使用方式。

重點摘要​

  • React 透過 createRoot() 函式與 render() 方法將 HTML 渲染到網頁上。
  • createRoot() 用於指定要渲染的目標 HTML 元素。
  • render() 用於將 React 元件實際渲染到指定元素中。
  • 標準的 React 專案會在 public/index.html 中提供 <div id="root"> 作為渲染容器。
  • JSX 語法允許開發者在 JavaScript 中撰寫類似 HTML 的程式碼。
  • 根節點不必一定是 <div>,也不一定要命名為 root。

實際範例​

  1. 渲染一段簡單的段落:
const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);
root.render(<p>Hello</p>);

上述程式碼會將 "Hello" 文字渲染到 HTML 文件中的:

<body>
<div id="root"></div>
</body>
  1. 使用 JSX 撰寫 HTML 表格並渲染:
const myelement = (
<table>
<tr>
<th>Name</th>
</tr>
<tr>
<td>John</td>
</tr>
<tr>
<td>Elsa</td>
</tr>
</table>
);

const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);
root.render(myelement);

此段程式會顯示一個包含名稱的簡單表格,渲染至 root 節點中。

  1. 更換根節點的標籤與 id: 不一定要使用 <div id="root"> 作為渲染節點,可以自行命名:
<body>
<header id="sandy"></header>
</body>

對應的 React 程式碼如下:

const container = document.getElementById('sandy');
const root = ReactDOM.createRoot(container);
root.render(<p>Hallo</p>);

此範例將段落渲染至 header 元素。

總結​

React 的渲染邏輯核心在於兩個部分:定義渲染目標與執行渲染動作。透過 createRoot() 指定 HTML 元素、render() 注入 React 元件,開發者可以將 UI 動態顯示在任何節點上。搭配 JSX 語法,不僅可以撰寫更具可讀性的 UI 結構,也讓程式碼維護更為便利。

參考文件​

  1. React Render HTML

React 與 ES6 語法教學入門 | w3schools 學習筆記

· 閱讀時間約 3 分鐘
KD Chang
Builder, Curator & Lifelong Learner

前言​

ES6(ECMAScript 2015)是 JavaScript 的第六版,於 2015 年發布,為 JavaScript 帶來了重大語法革新。React 作為現今最受歡迎的前端框架和函式庫之一,其核心設計與語法極度依賴 ES6 的各項功能。因此,學習 React 前,理解 ES6 的語法特性將大幅提升開發效率與理解深度。

本文將說明 React 常用的 ES6 特性,並透過簡明範例幫助你掌握其實作方式。

重點摘要​

  • ES6 是 ECMAScript 第六版,又稱 ECMAScript 2015。

  • React 使用大量 ES6 語法,包括:

    • Class 類別
    • 箭頭函式
    • let、const 變數宣告
    • 陣列方法(如 .map)
    • 解構(Destructuring)
    • 模組系統(import/export)
    • 三元運算子(Ternary Operator)
    • 展開運算子(Spread Operator)

1. 類別與繼承(Classes & Inheritance)​

ES6 引入了 class 關鍵字來定義類別:

class Car {
constructor(name) {
this.brand = name;
}
present() {
return 'I have a ' + this.brand;
}
}

const mycar = new Car('Ford');
console.log(mycar.present());

繼承使用 extends 關鍵字:

class Model extends Car {
constructor(name, model) {
super(name);
this.model = model;
}
show() {
return this.present() + ', it is a ' + this.model;
}
}

const mycar = new Model('Ford', 'Mustang');
console.log(mycar.show());

2. 箭頭函式(Arrow Functions)​

簡化函式的寫法:

const hello = () => 'Hello World!';
const greet = (name) => `Hello ${name}`;

3. 變數宣告(let、const、var)​

var x = 5; // 函式作用域
let y = 10; // 區塊作用域
const z = 15; // 常數,不可重新指派

const 宣告的是參考不可變:

const arr = [1, 2];
arr.push(3); // 可以修改內容

4. 陣列方法(Array.map)​

在 React 中常用於渲染列表:

const fruits = ['apple', 'banana', 'orange'];
const listItems = fruits.map((fruit) => <p>{fruit}</p>);

5. 解構賦值(Destructuring)​

解構陣列:

const vehicles = ['mustang', 'f-150', 'expedition'];
const [car, , suv] = vehicles;

解構函式回傳值:

function calc(a, b) {
return [a + b, a - b];
}

const [sum, diff] = calc(5, 3);

6. 展開運算子(Spread Operator)​

複製或合併陣列與物件:

const numbers1 = [1, 2];
const numbers2 = [3, 4];
const all = [...numbers1, ...numbers2];

const car = { brand: 'Ford', color: 'red' };
const update = { color: 'blue' };
const updatedCar = { ...car, ...update };

7. 模組系統(Modules)​

export 和 import 用於模組化程式碼:

命名匯出:​

// person.js
export const name = 'Jesse';
export const age = 40;
// 使用
import { name, age } from './person.js';

預設匯出:​

// message.js
const message = () => 'Hello';
export default message;
import message from './message.js';

8. 三元運算子(Ternary Operator)​

條件判斷簡化語法:

const isAuth = true;
isAuth ? renderApp() : renderLogin();

結語​

ES6 為 JavaScript 帶來嶄新的語法與思維方式,也為 React 帶來強大的表達力與模組化能力。熟練掌握這些語法,將能讓你在開發 React 專案時更加順手、高效並撰寫出更具可維護性的程式碼。若你尚未熟悉這些語法,建議你從簡單的練習開始,搭配 React 實際開發經驗進行吸收與內化。

參考文件​

  1. React ES6

React 入門教學 | w3schools 學習筆記

· 閱讀時間約 3 分鐘
KD Chang
Builder, Curator & Lifelong Learner

前言​

在當代的前端開發中,React 是最具代表性的 JavaScript 函式庫之一。由 Facebook 軟體工程師 Jordan Walke 所開發,React 被廣泛應用於建構動態使用者介面,尤其適用於大型單頁應用(SPA, Single Page Application)。本篇文章將帶你認識 React 的核心概念、其背後的工作原理、使用前的基礎知識與發展歷史,並透過實例進行初步實作。


重點摘要​

  1. 什麼是 React?

    • React 是一個前端 JavaScript 函式庫,用來建構使用者介面(UI)。
    • React 的別稱包括 React.js 與 ReactJS。
    • 它專注於「元件化思維」,每個 UI 元素都被視為一個可重複使用的元件(Component)。
  2. React 如何運作?

    • React 在記憶體中建立一個「虛擬 DOM」(Virtual DOM)。
    • 所有 DOM 操作會先發生在虛擬 DOM 中,再批次更新實體 DOM。
    • 這樣的差異化更新策略可提升效能,只變更必要的部分。
  3. React 的優勢

    • 高效能:避免不必要的 DOM 操作。
    • 組件化:提升程式碼的重用性與可維護性。
    • 單向資料流:資料流動清晰易懂。
    • 強大社群與生態圈:大量開源資源與工具支援。
  4. 使用 React 前的必要基礎

    • 熟悉 HTML 結構與語意標記。
    • 理解 CSS 排版與樣式應用。
    • 掌握 JavaScript 基本語法與函數觀念(如 ES6 語法、變數宣告、陣列方法等)。
  5. React 的發展歷程

    • 2011 年:React 首次應用於 Facebook 的新聞動態功能(Newsfeed)。
    • 2013 年 7 月:React 對外發布首個公開版本 0.3.0。
    • 2024 年 12 月:React 最新穩定版本為 19.0.0。

實際範例​

以下是一個簡單的 React 程式碼範例,展示如何建立與渲染一個元件。

// 引入 React 與 ReactDOM 函式庫
import React from 'react';
import ReactDOM from 'react-dom/client';

// 建立一個簡單的元件
function Welcome() {
return <h1>你好,React 世界!</h1>;
}

// 在指定的 DOM 節點中渲染該元件
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Welcome />);

解說:​

  1. import React from 'react'; 是使用 React 所必須的匯入語句。
  2. Welcome 是一個函數式元件(Functional Component),回傳的是 JSX 語法。
  3. ReactDOM.createRoot 創建一個 root 對象,負責將元件掛載至網頁的 #root DOM 節點。
  4. root.render(<Welcome />) 是將我們的元件渲染到網頁中。

總結​

React 是一個現代網頁開發的重要工具,特別適合構建具互動性與模組化結構的網頁。透過虛擬 DOM 技術,React 讓介面更新更有效率,並提供清晰的元件架構設計。對於有基本前端知識的開發者而言,React 是進階的最佳入門選擇之一。建議你在學習 React 前,先打好 HTML、CSS 與 JavaScript 的基礎,將能更順利掌握 React 的概念與應用。

接著我們將學習 React 的 JSX 語法、狀態管理(useState)、事件處理、生命週期(useEffect)等核心概念,逐步建立自己的 React 專案。

參考文件​

  1. React Custom Hooks

事件物件屬性 e.target 與 e.currentTarget 的差異與應用 | 學習筆記

· 閱讀時間約 4 分鐘
KD Chang
Builder, Curator & Lifelong Learner

前言​

在網頁開發中,事件處理是前端工程師必須熟悉的基礎知識之一。當我們使用 JavaScript 或框架(如 Vue、React)監聽 DOM 事件時,瀏覽器會提供一個事件物件(event object)給監聽函式,透過它我們能取得觸發事件的相關資訊。而在事件物件中,最常被初學者混淆的兩個屬性就是 e.target 與 e.currentTarget。

這兩者的差別看似微小,但實際上與事件冒泡機制息息相關。如果理解不清,會導致點擊判斷錯誤、事件綁定邏輯混亂,甚至出現無法預期的行為。本文將從概念、重點摘要,再到程式碼範例,逐步說明 e.target 與 e.currentTarget 的異同,並介紹實際開發中如何正確使用。


重點摘要​

  1. 事件冒泡(Event Bubbling)

    • 當某個元素觸發事件時,事件會從最深層的子元素向外層父元素依序傳遞。
    • 這意味著外層元素的事件監聽器也可能被觸發。
  2. e.target

    • 代表「事件最初發生的元素」。
    • 使用者實際點擊或操作的 DOM 元素,不論事件是否冒泡到其他父層。
  3. e.currentTarget

    • 代表「當前正在處理事件的元素」。
    • 永遠指向事件監聽器綁定的元素。
  4. 差異核心

    • e.target = 使用者操作的來源元素。
    • e.currentTarget = 當前呼叫事件處理函式的元素。
  5. 常見用途

    • e.target:用於判斷使用者點擊了哪個子元素,常用於事件代理(event delegation)。
    • e.currentTarget:用於保證取得事件綁定的元素本身,避免因冒泡導致判斷錯誤。

範例一:基本點擊事件​

以下是一個簡單的 HTML 結構:

<div id="outer" style="padding:20px; background:lightgray;">
Outer Div
<button id="inner">Click Me</button>
</div>

<script>
const outer = document.getElementById('outer');

outer.addEventListener('click', (e) => {
console.log('target:', e.target.id);
console.log('currentTarget:', e.currentTarget.id);
});
</script>

執行結果​

  1. 點擊按鈕(id="inner"):

    • e.target.id → inner
    • e.currentTarget.id → outer
  2. 點擊外層灰色區塊(id="outer"):

    • e.target.id → outer
    • e.currentTarget.id → outer

說明​

  • 使用者實際點擊按鈕,所以 e.target 是 inner。
  • 但因為事件冒泡,outer 的監聽器被觸發,因此 e.currentTarget 永遠是 outer。
  • 這正好展示了兩者的不同:一個描述事件來源,一個描述處理者。

範例二:事件代理(Event Delegation)​

事件代理是一種常見技巧,將監聽器掛在父層元素,由它統一處理子元素的事件。這種情境下,e.target 就非常重要。

<ul id="list">
<li data-id="1">Item 1</li>
<li data-id="2">Item 2</li>
<li data-id="3">Item 3</li>
</ul>

<script>
const list = document.getElementById('list');

list.addEventListener('click', (e) => {
console.log('target tag:', e.target.tagName);
console.log('currentTarget tag:', e.currentTarget.tagName);

if (e.target.tagName === 'LI') {
console.log('You clicked item with data-id:', e.target.dataset.id);
}
});
</script>

執行結果​

  • 點擊 Item 2 時:

    • e.target.tagName → LI
    • e.currentTarget.tagName → UL
    • 印出 "You clicked item with data-id: 2"

說明​

  • list(UL)負責監聽整個清單,這就是 e.currentTarget。
  • 實際點擊的 <li> 元素才是 e.target。
  • 藉由 e.target,我們能知道使用者點的是哪個清單項目,而不用對每個 <li> 綁定監聽器。

範例三:模擬 Vue 的 .self 行為​

在 Vue 中有一個 .self 修飾符,代表「只有點擊元素本身,而不是子元素時,事件才觸發」。這其實就是透過 e.target === e.currentTarget 判斷。

<div id="modal" style="width:300px; height:200px; background:rgba(0,0,0,0.5);">
<div style="width:100px; height:100px; background:white; margin:50px auto;"></div>
</div>

<script>
const modal = document.getElementById('modal');

modal.addEventListener('click', (e) => {
if (e.target === e.currentTarget) {
console.log('Clicked on modal background, close modal');
} else {
console.log('Clicked on inner content, do nothing');
}
});
</script>

說明​

  • 點擊外層灰色背景時:e.target === e.currentTarget → 成立 → 關閉彈窗。
  • 點擊白色內層時:e.target 為內層 div,而 e.currentTarget 為外層 → 不相等 → 不關閉。

這個邏輯就是框架中 .self 修飾符的實作原理。


總結​

  • e.target 與 e.currentTarget 都是事件物件的重要屬性。
  • e.target 表示事件來源,是使用者實際操作的元素。
  • e.currentTarget 表示事件處理者,是目前綁定監聽器的元素。
  • 理解這兩者的差別,對於處理事件冒泡、事件代理、以及控制複雜 UI 行為都至關重要。

當我們需要知道「使用者點了哪個子元素」時,請使用 e.target。當我們需要確保「正在處理事件的元素本身」時,請使用 e.currentTarget。

掌握這個觀念,能讓我們在撰寫事件監聽邏輯時更精準,避免誤判來源,也能設計出更乾淨與可維護的程式碼。

Git 指令學習筆記 | 學習筆記

· 閱讀時間約 6 分鐘
KD Chang
Builder, Curator & Lifelong Learner

Git 是現代軟體開發中最常使用的分散式版本控制系統,它幫助開發者管理源代碼的版本和協作。這篇教學將從基礎開始,介紹 Git 的常用命令並提供實際範例,幫助你熟悉如何使用 Git 來進行版本控制與協作。

1. 初始化 Git 倉庫 (git init)​

在專案資料夾中使用 git init 可以初始化一個新的 Git 倉庫,這樣就可以開始使用 Git 來管理專案中的檔案版本。

git init

執行後會在專案資料夾內建立一個 .git 的隱藏資料夾,這表示這個資料夾已經被 Git 所管理。

2. 查看當前狀態 (git status)​

在進行版本控制時,使用 git status 可以查看專案的當前狀態。這個命令會顯示哪些檔案已經被修改、哪些檔案尚未加入版本控制。

git status

這會列出所有變更過的檔案(包括已修改、尚未追蹤的檔案等)以及當前分支的狀態。

3. 新增檔案到暫存區 (git add)​

修改檔案後,必須先使用 git add 把檔案新增到暫存區,才可以進行提交。使用 git add 可以指定單一檔案或多個檔案,或者一次將所有檔案加入暫存區。

git add filename.txt

或將所有變更加入暫存區:

git add .

4. 提交變更 (git commit)​

將檔案從暫存區提交到本地版本庫使用 git commit。每次提交時,需要提供一個簡短的描述,解釋這次提交的內容。

git commit -m "新增使用者登錄功能"

5. 設定 Git 使用者資訊 (git config)​

在進行提交之前,必須設定 Git 的使用者名稱和電子郵件。這些資料會與每次提交關聯。

git config --global user.name "Your Name"
git config --global user.email "your.email@example.com"

這會把你的名稱和郵箱寫入全域設定,未來每次提交都會使用這些設定。

6. 查看提交歷史 (git log)​

使用 git log 可以查看專案的提交歷史。這會顯示每次提交的詳細信息,包括提交哈希值、作者、日期和提交訊息。

git log

如果希望更簡潔的輸出,可以使用 --oneline 參數:

git log --oneline

7. 恢復檔案 (git restore)​

如果你修改了一些檔案並且還沒有提交,可以使用 git restore 恢復檔案到上次提交的狀態,撤銷變更。

git restore filename.txt

如果想撤銷暫存區的變更,可以使用:

git restore --staged filename.txt

8. 追蹤檔案變動紀錄 (git blame)​

如果你想查看某個檔案每一行是誰在什麼時候修改的,可以使用 git blame。

git blame filename.txt

這會顯示每一行的修改者與修改時間,有助於了解程式碼的歷史。

9. 建立分支 (git branch)​

在 Git 中,分支讓你能夠並行地進行多個工作。你可以使用 git branch 查看目前所有的分支。

git branch

要創建新的分支:

git branch feature-login

10. 切換分支 (git switch)​

要切換到不同的分支,可以使用 git switch。例如,切換到 feature-login 分支:

git switch feature-login

如果你要創建並切換到一個新的分支,可以加上 -c 選項:

git switch -c feature-register

11. 切換提交紀錄或分支 (git checkout)​

雖然 git switch 是較新的命令,但仍然可以使用 git checkout 切換分支或還原檔案。要切換到某個分支:

git checkout feature-login

若要還原某個檔案到指定的提交:

git checkout <commit_hash> filename.txt

12. 合併分支 (git merge)​

將不同分支的更改合併回主分支可以使用 git merge。首先切換到目標分支(例如 main),然後執行合併操作:

git switch main
git merge feature-login

如果有衝突,Git 會提示你解決衝突。

13. 重新整理提交歷史 (git rebase)​

git rebase 用來重新整理提交歷史,將一個分支的更改重新應用到另一個分支上,使歷史更為線性。

git switch feature-login
git rebase main

這會將 feature-login 分支的變更,重新應用到 main 分支的最新提交上。

14. 重置提交 (git reset)​

如果你想取消最近一次的提交,可以使用 git reset。使用 --soft 保留檔案變更,--hard 則會刪除變更:

git reset --soft HEAD^

這會將上一個提交撤回,但保留檔案變更;若使用 --hard,則會完全撤回提交及變更。

15. 查看操作紀錄 (git reflog)​

git reflog 可以幫助你查看 HEAD 的操作紀錄,即便你進行了 reset 或 checkout 等操作。

git reflog

這個命令列出所有曾經指向 HEAD 的 commit 參考,並且可以用來恢復丟失的提交。

16. 暫存變更 (git stash)​

如果你正在進行某些變更,但又需要切換到其他分支處理緊急問題,可以使用 git stash 暫時儲存未提交的變更:

git stash

這會暫時保存變更,並恢復工作目錄到最後一次提交的狀態。待你完成緊急工作後,可以使用 git stash pop 恢復變更。

17. 選擇性地應用提交 (git cherry-pick)​

git cherry-pick 讓你可以選擇性地將某個提交應用到當前分支。例如,將 feature-login 分支的某個提交應用到 main 分支:

git cherry-pick <commit_hash>

這對於選擇性地將某些特定變更移動到其他分支非常有用。

18. 子模組 (git submodule)​

當專案中有其他專案作為依賴時,可以使用 Git 子模組來管理。首先,將子模組加入專案:

git submodule add <repository_url> path/to/submodule

這會在專案中加入一個指向其他 Git 倉庫的子模組。在克隆專案時,記得加上 --recurse-submodules 來獲取子模組的內容。


總結

以上介紹了 Git 中一些常用的命令,這些命令幾乎涵蓋了日常開發中的大部分需求。熟練掌握 Git 讓你在開發過程中更加高效,無論是單人開發還是團隊協作,都能大大提高工作流的順暢性。如果你還有進一步的需求或疑問,隨時可以深入學習 Git 的進階技巧。

Git 入門教學筆記:從零開始學版本控制 | 學習筆記

· 閱讀時間約 4 分鐘
KD Chang
Builder, Curator & Lifelong Learner

為什麼要學 Git?​

在軟體開發中,版本控制是一個不可或缺的工具。Git 是目前最流行的版本控制系統,它能幫助你:

  • 記錄每次修改的歷史紀錄
  • 回復到任一個過去的狀態
  • 多人協作開發不衝突
  • 管理分支進行實驗性開發

無論是獨立開發者、團隊協作,還是參與開源專案,Git 都是你的好夥伴。


一、安裝 Git​

macOS​

brew install git

Ubuntu / Debian​

sudo apt update
sudo apt install git

Windows​

請至官網下載安裝:https://git-scm.com


二、基本設定​

第一次使用 Git 前,請先設定你的名稱與 Email(這會記錄在每次提交的資訊中):

git config --global user.name "你的名字"
git config --global user.email "you@example.com"

你也可以檢查目前的設定:

git config --list

三、建立本地倉庫(repository)​

建立一個新資料夾並初始化 Git​

mkdir my-project
cd my-project
git init

此時 .git/ 目錄會被建立,代表你已經在這個資料夾啟用了版本控制。


四、版本控制的基本流程​

Git 的工作流程大致分為三個區域:

  1. 工作區(Working Directory):你實際修改的檔案
  2. 暫存區(Staging Area):準備提交的檔案
  3. 版本庫(Repository):正式提交的版本歷史

1. 查看狀態​

git status

2. 將檔案加入暫存區​

git add 檔名
# 或加入全部變動檔案
git add .

3. 提交版本(commit)​

git commit -m "加入了首頁 HTML 結構"

五、版本紀錄與回溯​

查看提交紀錄​

git log

如果想要簡潔查看

git log --oneline

回到先前的狀態(例如最近一次提交)​

git checkout HEAD^
# 回到上上一個版本
git checkout HEAD~2

但請注意這樣會進入「detached HEAD」狀態,只是暫時查看,不適合繼續開發。


六、分支操作(branch)​

分支是 Git 強大的一環,可以讓你同時開發多個功能而互不干擾。

建立新分支​

git branch feature-login

切換分支​

git checkout feature-login

或合併成一行:

git checkout -b feature-login

合併分支​

切回主分支(通常是 main 或 master):

git checkout main
git merge feature-login

七、遠端倉庫(Remote Repository)​

若你使用 GitHub、GitLab 等服務,可以將本地專案推送到遠端:

新增遠端倉庫​

git remote add origin https://github.com/yourname/yourproject.git

推送本地分支​

git push -u origin main

拉取遠端最新變更​

git pull origin main

八、.gitignore 檔案​

.gitignore 用來指定不想被 Git 管理的檔案,例如:

node_modules/
.env
*.log
.DS_Store

建立一個 .gitignore 檔案,放進你專案根目錄,就能避免把不該版本控制的東西送到遠端倉庫。


九、常見狀況與解法​

修改後不小心沒 add 就 commit 了?​

可用下列方式補上:

git add 新增的檔案
git commit --amend

想撤銷修改?​

還沒 add 的:

git checkout -- 檔案名

已經 add 了但還沒 commit:

git reset HEAD 檔案名

十、總結:從習慣開始,讓版本控制變簡單​

熟悉 Git 的過程一開始可能會覺得繁瑣,但當你習慣了之後,它會成為你每天工作中不可或缺的工具。建議你在小專案中練習基本指令,隨手記錄每次的變更,透過良好的 commit message,讓未來的你感謝現在有好好整理的你。

如果你希望與他人協作開發,Git 更能大幅簡化流程、避免衝突。下一步,你可以學習 Pull Request(PR)、Git Flow、Rebase 等進階技巧,逐步精進你的版本控制能力。

參考文件

  1. Conventional Commits
  2. 約定式提交
  3. [Git] Conventional Commits 規範性提交
  4. git reflog - 還原大招
  5. 為你自己學 Git

JavaScript this 使用情境入門教學筆記 | 學習筆記

· 閱讀時間約 3 分鐘
KD Chang
Builder, Curator & Lifelong Learner

在 JavaScript 中,箭頭函式(arrow function)與傳統函式在 this 的行為上有一些不同。箭頭函式不會創建自己的 this,而是繼承外部作用域的 this,這就是為什麼你會遇到 this 的問題。

傳統函式中的 this​

在傳統的函式中,this 會指向該函式被調用時的上下文。例如,當函式作為事件處理器時,this 會指向觸發事件的元素。

function regularFunction() {
console.log(this); // 'this' 是調用它的上下文
}

const obj = {
name: 'KD',
show: regularFunction
};

obj.show(); // 'this' 會指向 obj

箭頭函式中的 this​

在箭頭函式中,this 並不會綁定到函式的上下文,而是繼承自外部作用域。這通常會讓箭頭函式的 this 變得不同於你預期的結果。

例子:this 的問題​

假設有這樣的情境,當你在一個物件的方法中使用箭頭函式作為事件處理器,this 會指向外部作用域,而不是該物件本身。

const obj = {
name: 'KD',
show: function() {
// 使用傳統函式作為事件處理器
document.getElementById('btn').addEventListener('click', function() {
console.log(this); // 'this' 指向的是 'btn' 按鈕元素
});

// 使用箭頭函式作為事件處理器
document.getElementById('btn').addEventListener('click', () => {
console.log(this); // 'this' 指向外部作用域,即 'obj' 物件
});
}
};

obj.show();
  • 在傳統函式中,this 會指向觸發事件的 DOM 元素(這裡是 btn 按鈕)。
  • 在箭頭函式中,this 會指向外部作用域(這裡是 obj 物件),因為箭頭函式不會創建自己的 this。

解決方案:確保 this 正確指向​

如果你希望 this 指向物件本身,可以使用傳統函式或手動綁定 this。

使用傳統函式​

如果你希望在事件處理器中讓 this 指向物件本身,可以使用傳統函式,或者使用 bind 顯式綁定 this。

const obj = {
name: 'KD',
show: function() {
document.getElementById('btn').addEventListener('click', function() {
console.log(this); // 'this' 指向 obj
}.bind(this)); // 顯式綁定 'this' 到 obj
}
};

obj.show();

另一個選項:箭頭函式和外部 this​

如果你希望繼續使用箭頭函式,你可以將物件的 this 儲存到外部變數中,並在箭頭函式中引用它。

const obj = {
name: 'KD',
show: function() {
const self = this; // 保存物件的 'this'
document.getElementById('btn').addEventListener('click', () => {
console.log(self); // 使用外部的 'self',指向 obj
});
}
};

obj.show();

總結​

  • 傳統函式 會根據調用上下文決定 this 的值。
  • 箭頭函式 會繼承外部作用域的 this,不會創建自己的 this,這樣在某些情況下會導致 this 不如預期。
  • 如果你需要在事件處理器中使用物件的 this,可以選擇使用傳統函式或顯式綁定 this。

JavaScript 多事件處理綁定使用情境入門教學筆記 | 學習筆記

· 閱讀時間約 3 分鐘
KD Chang
Builder, Curator & Lifelong Learner

在 JavaScript 中,你可以通過事件處理的方式來為多個 input 元素綁定事件處理器。這樣做可以讓你在父容器上綁定一個事件處理器,並通過 event.target 確定觸發事件的具體 input 元素,而不需要為每個 input 元素單獨綁定事件。

事件委派的概念​

事件委派是一種常見的事件處理技術,它將事件綁定到父元素或容器上,然後通過 event.target 來確定哪個子元素觸發了事件。這種方式在動態生成的元素中非常有用,因為無論多少個元素,它們都會使用相同的事件處理器。

例子:為多個 input 元素使用事件委派​

假設有多個 input 元素,並且我們希望根據用戶在每個 input 中的輸入執行某些操作,可以像這樣使用 event.target:

<div id="inputContainer">
<input type="text" id="input1" />
<input type="text" id="input2" />
<input type="text" id="input3" />
</div>

<script>
// 綁定事件處理器到父容器
document.getElementById('inputContainer').addEventListener('input', function(event) {
// 檢查事件目標是否為 input 元素
if (event.target.tagName.toLowerCase() === 'input') {
console.log('觸發的 input 元素 ID:', event.target.id);
console.log('輸入的值:', event.target.value);
}
});
</script>

解釋:​

  1. 我們將 input 元素的 input 事件綁定到父容器 #inputContainer 上。
  2. 當任何一個 input 元素觸發 input 事件時,事件會冒泡到父容器,並且事件處理器會被執行。
  3. 在事件處理器中,我們使用 event.target 來確定是哪個 input 元素觸發了事件。event.target 會返回實際觸發事件的元素。
  4. 通過 event.target.id 和 event.target.value,我們可以獲取觸發事件的 input 元素的 ID 和輸入的值。

優點:​

  • 減少事件綁定數量:不需要為每個 input 元素單獨綁定事件,減少了冗餘代碼。
  • 動態元素支持:即使後來添加了新的 input 元素,父容器上的事件處理器也會自動處理新元素。

當 input 元素是動態創建時:​

如果你有動態創建的 input 元素,事件委派依然有效,因為事件處理器是綁定在父容器上的,而不需要直接綁定在每個 input 元素上。

// 假設需要動態創建 input 元素
const container = document.getElementById('inputContainer');

for (let i = 1; i <= 5; i++) {
const input = document.createElement('input');
input.type = 'text';
input.id = 'input' + i;
container.appendChild(input);
}

這樣,當你動態創建新的 input 元素時,父容器上的事件處理器會自動處理這些新的 input 元素。

JWT 使用好處入門教學筆記 | 學習筆記

· 閱讀時間約 3 分鐘
KD Chang
Builder, Curator & Lifelong Learner

前言​

JWT(JSON Web Token) 的好處在於它是輕量、跨平台、無狀態的驗證與授權機制,特別適合用於前後端分離、微服務架構與 API 驗證。以下是它的主要優點:

一、JWT 的 7 大好處​

1. 無狀態(Stateless)驗證​

  • JWT 本身就包含用戶資訊與簽名,不需要在伺服器端儲存 session。
  • 適合微服務或 Serverless 架構,因為伺服器不用記住誰是誰。

2. 跨平台、跨語言支援​

  • JWT 是一種標準格式(RFC 7519),可用於各種語言與框架(Node.js、Python、Go、Java、PHP、.NET 等)。
  • 非常適合前後端分離的架構,或多平台應用(Web + App)。

3. 傳遞資訊方便​

  • 可以在 payload 中攜帶使用者角色、權限、使用者 ID 等資料,前後端都可解碼取得。
  • 節省額外查詢資料庫的頻率(如登入後不再查資料庫即可知道使用者身份)。

4. 易於擴展與整合 OAuth​

  • JWT 是 OAuth 2.0 中 Access Token 的常見格式。
  • 整合第三方登入(如 Google、Facebook、GitHub)時很常使用 JWT 作為驗證令牌。

5. 高安全性(搭配正確實作)​

  • Token 是簽名過的,無法被竄改(除非持有密鑰)。
  • 可設定有效期限(exp),也支援附加自訂欄位。

6. 前端儲存與攜帶方便​

  • JWT 是字串格式,可透過:

    • Authorization header:Bearer <token>
    • Cookie(推薦設為 HttpOnly + Secure)
    • localStorage(需防範 XSS)

7. 支援單一登入(SSO)​

  • 多個子系統共用同一套 JWT 驗證邏輯,實現單一登入(Single Sign-On)。
  • 用於企業內部系統、微服務架構特別合適。

二、實際應用情境​

應用場景優勢
API 驗證無狀態、易於整合
單頁應用(SPA)可存取使用者資訊而不額外請求
手機 App 登入移動端儲存與驗證方便
微服務架構每個服務只需驗證簽章,無需共用 session
OAuth 第三方登入可當作 Access Token 與 ID Token

三、與 Session 的比較​

項目JWTSession
資料儲存位置前端(Token 傳遞)後端(記憶體/資料庫)
可擴充性高(無需共享記憶體)需維護集中式 Session 儲存
實作難度中(需處理 Token 過期、黑名單等)簡單(有框架支援)
安全性好(但需避免 XSS 與 Token 洩露)好(但可能遭 CSRF 攻擊)

四、常見誤解釐清​

常見誤解釐清
JWT 是加密的錯,預設只是簽名(不可竄改),不是加密(不可閱讀)
JWT 安全無敵錯,若密鑰外洩,任何人都能簽 Token。需配合 HTTPS 傳輸與妥善保護密鑰
JWT 不需要過期時間錯,若未設 exp,Token 可永久有效,極度危險

五、總結​

JWT 的優勢在於簡潔、無狀態、可擴展,特別適合 API 驗證與前後端分離架構。然而,要發揮 JWT 的最大效益並保障安全,開發者需了解其限制,搭配正確實作(如過期、黑名單、HTTPS、Refresh Token 管理等)。

React 前端整合 JWT(含 Refresh Token)入門教學筆記 | 學習筆記

· 閱讀時間約 4 分鐘
KD Chang
Builder, Curator & Lifelong Learner

前言​

當你要將 JWT 驗證整合至 React 前端,並搭配 Refresh Token 或整合 OAuth(如 Google 登入)流程,你需要考慮前端的存儲方式、token 的更新機制,以及第三方登入的流程銜接。

以下將分成三個部分講解:


一、React 前端整合 JWT(含 Refresh Token)

流程總覽:​

  1. 使用者輸入帳密登入,發送 /login 請求。

  2. 伺服器簽發兩種 token:

    • Access Token(短效,有效期如 15 分鐘)
    • Refresh Token(長效,有效期如 7 天)
  3. 前端儲存 Access Token(如在記憶體),Refresh Token 建議儲存在 HttpOnly Cookie。

  4. 當 Access Token 過期時,自動用 Refresh Token 換取新 Access Token。

  5. 使用者主動登出時,Refresh Token 一併清除。


伺服器回傳範例(登入成功):​

{
"accessToken": "xxxxx",
"expiresIn": 900
}

並透過 Set-Cookie 傳送 HttpOnly 的 Refresh Token:

Set-Cookie: refreshToken=xxxxx; HttpOnly; Path=/; Max-Age=604800;

React 實作要點​

// login.js
import axios from 'axios';

export async function login(username, password) {
const res = await axios.post(
'/api/login',
{ username, password },
{
withCredentials: true, // 允許 cookie 傳遞
}
);

localStorage.setItem('accessToken', res.data.accessToken);
}

// api.js
import axios from 'axios';

const api = axios.create({
baseURL: '/api',
withCredentials: true,
});

api.interceptors.request.use((config) => {
const token = localStorage.getItem('accessToken');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});

api.interceptors.response.use(
(res) => res,
async (err) => {
if (err.response.status === 401) {
try {
// 嘗試使用 Refresh Token 換 Access Token
const res = await axios.post('/api/refresh', {}, { withCredentials: true });
localStorage.setItem('accessToken', res.data.accessToken);

// 重試原本請求
err.config.headers.Authorization = `Bearer ${res.data.accessToken}`;
return axios(err.config);
} catch (e) {
// refresh 失敗,跳轉登入頁
window.location.href = '/login';
return Promise.reject(e);
}
}
return Promise.reject(err);
}
);

export default api;

登出​

export async function logout() {
await axios.post('/api/logout', {}, { withCredentials: true });
localStorage.removeItem('accessToken');
}

備註​

  • Refresh Token 儲存在瀏覽器的 HttpOnly Cookie,無法被 JavaScript 存取,提升安全性。
  • Access Token 儲存在記憶體或 localStorage(但 localStorage 易受 XSS 攻擊)。
  • 若要完全防止 CSRF,Refresh Token cookie 需搭配 SameSite 設定與 CSRF token。

二、OAuth 2.0 登入(以 Google 為例)

流程總覽​

  1. 前端點擊「使用 Google 登入」。
  2. 透過 Google OAuth 流程取得授權碼(或 id_token)。
  3. 前端將該 token 傳送到後端 /auth/google。
  4. 後端驗證 Google id_token,並簽發 JWT 給前端。

React 前端整合(Google 登入)​

使用 @react-oauth/google:

npm install @react-oauth/google
import { GoogleOAuthProvider, GoogleLogin } from '@react-oauth/google';

function App() {
return (
<GoogleOAuthProvider clientId="你的 Google Client ID">
<GoogleLogin
onSuccess={(credentialResponse) => {
// 將 id_token 傳送給後端
fetch('/api/auth/google', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ token: credentialResponse.credential }),
});
}}
onError={() => {
console.log('登入失敗');
}}
/>
</GoogleOAuthProvider>
);
}

後端處理(Node.js + google-auth-library)​

import { OAuth2Client } from 'google-auth-library';
import jwt from 'jsonwebtoken';

const client = new OAuth2Client(GOOGLE_CLIENT_ID);

router.post('/auth/google', async (req, res) => {
const { token } = req.body;

const ticket = await client.verifyIdToken({
idToken: token,
audience: GOOGLE_CLIENT_ID,
});

const payload = ticket.getPayload();

const jwtToken = jwt.sign({ userId: payload.sub, name: payload.name }, process.env.JWT_SECRET, {
expiresIn: '15m',
});

const refreshToken = jwt.sign({ userId: payload.sub }, process.env.JWT_REFRESH_SECRET, {
expiresIn: '7d',
});

res
.cookie('refreshToken', refreshToken, {
httpOnly: true,
maxAge: 7 * 24 * 60 * 60 * 1000,
})
.json({ accessToken: jwtToken });
});

三、小結

功能JWT + RefreshOAuth 2.0
是否需要帳密登入是否,透過第三方登入
Token 儲存Access Token: localStorage / memory
Refresh Token: HttpOnly Cookie
同上
適合對象自建會員系統使用 Google / Facebook / LINE 等快速登入
安全性良好,需搭配 HTTPS高,由 Google 等大廠管理
實作難度中等,需處理 Token 刷新邏輯中高,需處理外部驗證流程

延伸建議

  • 若你想使用前後端共用的 JWT 驗證邏輯,建議抽出 middleware 並集中處理。
  • 可搭配 jsonwebtoken、axios-auth-refresh 等工具。
  • 若前後端完全分離,建議使用跨網域 Cookie 搭配 SameSite=None; Secure。