chore(i18n): sync translations with latest source changes (chunk 9/20, 42 files)

pull/1668/head
localizeflow[bot] 2 months ago
parent befcf97143
commit aae8ad6035

@ -1,64 +1,247 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "2581528206a2a01c3a0b9c88e039b7bc",
"translation_date": "2025-10-03T08:56:11+00:00",
"original_hash": "d45ddcc54eb9232a76d08328b09d792e",
"translation_date": "2026-01-06T13:11:27+00:00",
"source_file": "1-getting-started-lessons/1-intro-to-programming-languages/README.md",
"language_code": "hk"
}
-->
# 程式語言與工具入門
# 程式語言與現代開發工具簡介
嗨,未來的開發者!👋 可不可以跟你說件每天都讓我雞皮疙瘩掉滿地的事?你即將發現,程式設計不只是關於電腦——它是擁有真正超能力,把你最狂野的想法實現出來的能力!
你知道那種用你最喜歡的 app所有操作都剛剛好完美連接的瞬間嗎當你按一個按鈕然後發生了某種完全神奇的事讓你忍不住想說「哇他們到底怎麼做到的」剛好某個跟你一樣的人——可能正坐在他最喜歡的咖啡店裡凌晨兩點喝著第三杯濃縮咖啡——寫了造就那魔法的程式碼。你將親眼見證的令人震驚的事是到了這節課結束你不僅會理解他們是怎麼做到的還會迫不及待想要自己試試看
聽著,如果你覺得程式設計現在很可怕,我完全可以理解。當我剛開始時,我真的以為你需要是某種數學天才,或者從五歲就開始寫程式。但徹底改變我觀念的是這件事:程式設計就像學一門語言溝通一樣。你從「你好」和「謝謝」學起,然後開始點咖啡,不知不覺你開始進行深刻的哲學討論!只不過在這案例裡,你是在和電腦對話。說真的?它們是你能找到的最有耐性的對話夥伴——它們從不責備你的錯誤,而是永遠準備好再試一次!
今天,我們將探索現代網頁開發令人驚嘆的工具,讓它不僅僅是可行,而是讓人嚴重上癮。我說的就是 Netflix、Spotify 和你喜歡的獨立應用工作室每天使用的同一套編輯器、瀏覽器和工作流程。最棒的是:這些專業級、業界標準的工具大多數都是完全免費的!
![Intro Programming](../../../../translated_images/webdev101-programming.d6e3f98e61ac4bff.hk.png)
> Sketchnote by [Tomomi Imura](https://twitter.com/girlie_mac)
```mermaid
journey
title 你今日嘅程式編寫旅程
section 發現
乜嘢係程式設計: 5: You
程式語言: 4: You
工具概覽: 5: You
section 探索
程式碼編輯器: 4: You
瀏覽器同開發者工具: 5: You
指令行: 3: You
section 練習
語言偵探: 4: You
工具探索: 5: You
社群連繫: 5: You
```
## 讓我們看看你已經知道些什麼!
在跳進有趣的內容前,我很好奇——你對程式設計這個世界已經知道多少?聽著,如果你看到這些問題心想「我根本完全沒概念」,那不只是可以,反而是完美!這表示你正處於正確位置。把這個小測驗想成運動前的暖身——我們只是熱身大腦肌肉!
[做課前測驗](https://forms.office.com/r/dru4TE0U9n?origin=lprLink)
## 我們即將一起展開的冒險
這節課涵蓋了程式語言的基礎知識。這裡提到的主題適用於大多數現代程式語言。在「工具介紹」部分,你將學習一些對開發者非常有用的軟件。
好啦,我真心為我們今天將探討的東西興奮到蹦蹦跳!說真的,我希望能看到當你理解這些概念的那張臉。這就是我們一起踏上的驚奇旅程:
![程式語言入門](../../../../translated_images/webdev101-programming.d6e3f98e61ac4bff0b27dcbf1c3f16c8ed46984866f2d29988929678b0058fde.hk.png)
> Sketchnote由 [Tomomi Imura](https://twitter.com/girlie_mac) 提供
- **什麼是程式設計(以及為什麼它超酷!)** 我們要了解程式碼如何成為背後無形魔法,驅動你周遭一切,從那個能準時知道是星期一早晨的鬧鐘,到完美推薦你 Netflix 節目的算法
- **程式語言和它們奇妙的個性** 想像走進一場派對,每個人都有完全不同的超能力和解決問題的方法。這就是程式語言的世界,你會很享受認識它們!
- **數位魔法的基本構件** 想像這是終極創意樂高組合套件。一旦你了解這些積木怎麼拼組,你會發現你實際上能打造出你想像的任何東西
- **讓你感覺像握到魔法棒的專業工具** 我不是誇張——這些工具真的會讓你覺得你有超能力,最棒的是?它們是專業人士每天使用的!
## 課前測驗
[課前測驗](https://forms.office.com/r/dru4TE0U9n?origin=lprLink)
> 💡 **重點是**:今天不要想著要全部記住!我現在只想讓你感受到對可能性的那股興奮。細節會隨著我們一起練習自然記住——真正的學習就是這樣發生的!
## 簡介
> 你也可以在 [Microsoft Learn](https://docs.microsoft.com/learn/modules/web-development-101/introduction-programming/?WT.mc_id=academic-77807-sagibbon) 上進行這堂課程!
在這節課中,我們將探討:
## 那麼究竟什麼是*程式設計*
- 什麼是程式設計?
- 程式語言的類型
- 程式的基本元素
- 專業開發者常用的軟件和工具
好,來回答百萬美元的問題:程式設計到底是什麼?
> 你可以在 [Microsoft Learn](https://docs.microsoft.com/learn/modules/web-development-101/introduction-programming/?WT.mc_id=academic-77807-sagibbon) 上學習這節課!
我來分享一個徹底改變我思考的故事。上週我試著向媽媽解釋如何使用我們的新智慧電視遙控器。結果我發現自己一直在說「按紅色的按鈕,不是大紅色的,是左邊那個小紅色的…不,是另一個左邊…好,現在按住兩秒,不是一秒,不是三秒…」聽起來很熟悉嗎?😅
## 什麼是程式設計?
這就是程式設計!它是對一個非常強大的東西,給出極度詳細、逐步的指令,而它需要所有步驟都說得非常清楚的藝術。只不過你不是在解釋給媽媽聽(她可以問「哪個紅色按鈕?!」),而是在解釋給電腦——電腦會精確執行你說的每一句話,即便你真正想說的不是這樣。
程式設計(也稱為編碼)是為電腦或移動設備編寫指令的過程。我們使用程式語言來編寫這些指令,然後由設備解讀這些指令。這些指令集可能有不同的名稱,例如 *程式*、*電腦程式*、*應用程式 (app)* 和 *可執行檔* 等。
第一次學到這點時讓我震撼的是電腦其實本質非常簡單。它們字面上只懂兩個東西——1 和 0基本上就是「是」和「否」或「開」和「關」。就這樣但神奇之處在於——我們不必像《駭客任務》裡那樣只說 1 和 0 。這時就是**程式語言**大顯身手的地方。它們就像世界上最棒的翻譯官,把你正常的人類思考完美地轉換成電腦語言
*程式* 可以是任何用程式碼編寫的東西;網站、遊戲和手機應用程式都是程式。雖然可以在不編寫程式碼的情況下創建程式,但其底層邏輯仍然需要由設備解讀,而這些邏輯通常是用程式碼編寫的。一個正在 *運行**執行* 程式碼的程式正在執行指令。你正在使用的設備正運行著一個程式,將這節課顯示在你的螢幕上。
而每天早上醒來仍讓我起雞皮疙瘩的是:你生活中所有數位東西都是由跟你一樣的人開始的,可能他們穿著睡衣,手裡拿著咖啡,在筆電前敲著程式碼。讓你看起來完美無瑕的 Instagram 濾鏡?有人寫了那程式碼。推推薦你發現新歌的算法?一位開發者設計了它。幫你和朋友分餐費用的 app沒錯有人想「這很煩我一定能解決它」結果…他們做到了
✅ 做一些研究:誰被認為是世界上第一位電腦程式設計師?
學程式不僅是學新技術——你是加入這個超棒的問題解決社群,他們每天的想法是,「如果我能做出些什麼讓別人的生活更好一點點呢?」說真的,還有比這更帥的事嗎
## 程式語言
**趣味知識探索**:有空時去查查世界上第一位程式設計師是誰?提示一下:她可能不是你預期的人!她的故事非常吸引人,而且證明了程式設計一直是關於創意解決問題和跳出框架思考。
程式語言使開發者能夠為設備編寫指令。設備只能理解二進制1和0而對於 *大多數* 開發者來說,這並不是一種高效的溝通方式。程式語言是人類與電腦之間溝通的工具。
### 🧠 **回顧時間:你感覺如何?**
程式語言有不同的格式可能用於不同的目的。例如JavaScript 主要用於網頁應用程式,而 Bash 主要用於操作系統。
**花點時間反思:**
- 「給電腦下指令」的概念現在對你來說有意義嗎?
- 你能想到想用程式自動化的日常任務嗎?
- 關於程式設計,你腦中冒出什麼問題?
*低階語言* 通常比 *高階語言* 需要更少的步驟來讓設備解讀指令。然而高階語言之所以受歡迎是因為它們的可讀性和支援性。JavaScript 被認為是一種高階語言。
> **記住**:如果某些概念現在還模糊是完全正常的。學程式就像學一門新語言——你的大腦需要時間建立神經通路。你做得很棒!
以下程式碼展示了使用高階語言JavaScript和低階語言ARM組合語言的差異。
## 程式語言就像不同口味的魔法
好啦,這聽起來怪怪的,但跟我走——程式語言很像不同類型的音樂。想想看:有浪漫即興的爵士樂、強而有力的搖滾、典雅嚴謹的古典樂和充滿創造力與表達的嘻哈。每種風格都有自己的氛圍、熱情粉絲社群,也適合不同心情和場合。
程式語言也是如此!你不會用同一種語言寫一款有趣的手機遊戲和處理大量氣候數據的程式,就像你不會在瑜伽課播放死亡金屬(嗯,大多數瑜伽課至少是這樣😄)。
每次想起這點,我都覺得超神奇:這些語言就像世界上最有耐性的、最聰明的口譯員坐在你旁邊。你以對你腦子自然的方式表達想法,它們處理把這些轉成電腦真正懂的 1 和 0。就像你有一位朋友同時流利「人類創意」和「電腦邏輯」——而且永不疲倦、不需要喝咖啡休息也不會因為你問第二次同樣問題而嘲笑你
### 熱門程式語言與用途
```mermaid
mindmap
root((程式語言))
Web Development
JavaScript
前端魔法
互動網站
TypeScript
JavaScript + 類型
企業應用
Data & AI
Python
數據科學
機器學習
自動化
R
統計學
研究
Mobile Apps
Java
安卓
企業
Swift
iOS
蘋果生態系統
Kotlin
現代安卓
跨平台
Systems & Performance
C++
遊戲
性能關鍵
Rust
記憶體安全
系統編程
Go
雲端服務
可擴展後端
```
| 語言 | 最適合的用途 | 為什麼受歡迎 |
|----------|----------|------------------|
| **JavaScript** | 網頁開發、使用者介面 | 在瀏覽器執行,驅動互動網站 |
| **Python** | 數據科學、自動化、AI | 易讀易學,擁有強大函式庫 |
| **Java** | 企業應用、Android 應用 | 跨平台,適合大型系統 |
| **C#** | Windows 應用、遊戲開發 | 微軟生態系統強力支援 |
| **Go** | 雲端服務、後端系統 | 快速、簡單,為現代運算設計 |
### 高階語言 vs 低階語言
說實話,這是我剛開始學程式時完全被弄得暈頭轉向的概念,所以我會分享讓我終於理解的比喻——希望也能幫助你!
想像你去到一個陌生國家,你不會說當地語言,而你急著要找廁所(大家都有這經驗吧?😅):
- **低階程式設計** 就像你學會當地方言,甚至能跟街角賣水果的老奶奶用文化暗號、地方俚語和只有在當地長大的人才懂的笑話聊天。超厲害而且效率超高……但前提是你必須流利!不然光找廁所就快瘋掉。
- **高階程式設計** 就像你有個超棒的當地朋友理解你。你只要用簡單的英文說「我很需要找廁所」,他就會做所有文化轉換,給你用你這個局外人腦袋能懂的指引。
用程式語言來說:
- **低階語言**(像組合語言或 C讓你能和電腦硬體進行非常細緻的對話但你必須用機器的思維方式——嗯可以說是大腦大轉彎
- **高階語言**(像 JavaScript、Python 或 C#)讓你用人的思考方式,而它們在背後處理所有機器語言的轉換。而且它們還有熱情的社群,有許多曾經是新手的人,真心想幫忙!
你猜我建議從哪開始?😉 高階語言就像有輔助輪,你會捨不得拆掉,因為它讓整個體驗愉快許多!
```mermaid
flowchart TB
A["👤 人類思考:<br/>'我想計算費波納契數列'"] --> B{選擇語言層級}
B -->|高階| C["🌟 JavaScript/Python<br/>易讀易寫"]
B -->|低階| D["⚙️ 組合語言/C<br/>直接硬體控制"]
C --> E["📝 撰寫fibonacci(10)"]
D --> F["📝 撰寫mov r0,#00<br/>sub r0,r0,#01"]
E --> G["🤖 電腦理解:<br/>翻譯器處理複雜性"]
F --> G
G --> H["💻 相同結果:<br/>0, 1, 1, 2, 3, 5, 8, 13..."]
style C fill:#e1f5fe
style D fill:#fff3e0
style H fill:#e8f5e8
```
### 讓我告訴你為什麼高階語言更友善
好,我馬上展示一個絕佳例子,說明我為何愛上高階語言。不過先請你答應我一件事。看到第一個程式碼範例時,別怕!看起來嚇人完全是我的重點!
我們會用兩種完全不同方式寫同樣的任務。兩個都產生所謂的費氏數列——這是一個美麗的數學模式每個數字都是前兩個數字之和0、1、1、2、3、5、8、13……趣味知識你會在大自然各處找到這個模式——向日葵種子漩渦、松果圖案、甚至星系形成方式
準備好看到差異了嗎?出發!
**高階語言JavaScript 對人類友善:**
```javascript
let number = 10
let n1 = 0, n2 = 1, nextTerm;
for (let i = 1; i <= number; i++) {
console.log(n1);
nextTerm = n1 + n2;
n1 = n2;
n2 = nextTerm;
// 第一步:基本斐波那契設置
const fibonacciCount = 10;
let current = 0;
let next = 1;
console.log('Fibonacci sequence:');
```
**這段程式碼做了什麼:**
- **宣告** 一個常數指定要產生多少 Fibonacci 數字
- **初始化** 兩個變數追蹤目前和下一個序列數字
- **設定** 起始值0 和 1定義 Fibonacci 模式
- **顯示** 標題訊息辨識輸出
```javascript
// 步驟 2使用迴圈生成序列
for (let i = 0; i < fibonacciCount; i++) {
console.log(`Position ${i + 1}: ${current}`);
// 計算序列中的下一個數字
const sum = current + next;
current = next;
next = sum;
}
```
```c
**分解運作過程:**
- **使用** `for` 迴圈走訪序列的每個位置
- **顯示** 每個數字及其位置,使用模板字串格式化
- **計算** 下一個 Fibonacci 數字為現值與次值相加
- **更新** 追蹤變數,以進入下一輪迴圈
```javascript
// 第三步:現代函數式方法
const generateFibonacci = (count) => {
const sequence = [0, 1];
for (let i = 2; i < count; i++) {
sequence[i] = sequence[i - 1] + sequence[i - 2];
}
return sequence;
};
// 使用範例
const fibSequence = generateFibonacci(10);
console.log(fibSequence);
```
**在上面,我們:**
- **創建** 使用現代箭頭函數語法的可重複使用函式
- **建構** 陣列儲存完整序列,而非逐個顯示
- **使用** 陣列索引由前值計算每個新數字
- **回傳** 完整序列以便程序其他部分彈性使用
**低階語言ARM 組合語言)– 對電腦友善:**
```assembly
area ascen,code,readonly
entry
code32
@ -83,137 +266,597 @@ back add r0,r1
end
```
相信或不信,*它們都在做同樣的事情*列印出前10個斐波那契數列
注意 JavaScript 幾乎像英文指令一樣易讀,而組合語言用神秘指令直接控制電腦處理器。兩者完成完全相同的任務,但高階語言對人類來說更容易理解、撰寫和維護
✅ 斐波那契數列 [定義](https://en.wikipedia.org/wiki/Fibonacci_number) 為一組數字其中每個數字是前兩個數字的總和從0和1開始。前10個斐波那契數列的數字是0, 1, 1, 2, 3, 5, 8, 13, 21 和 34。
**你會注意到的主要差異:**
- **可讀性**JavaScript 使用描述性名稱如 `fibonacciCount`,組合語言用難懂標籤如 `r0`、`r1`
- **註解**:高階語言鼓勵使用解釋性的註解,使程式碼本身成為自我說明
- **結構**JavaScript 的邏輯流程與人類逐步思考問題的方式相符合
- **維護**:根據不同需求更新 JavaScript 版本是直觀且清晰的
## 程式的基本元素
**關於費波那契數列**這個絕美的數字模式每個數字是前兩項的和0、1、1、2、3、5、8……幾乎在自然界到處可見你會在向日葵螺旋、松果排列、鳳梨貝殼的弧度甚至樹枝的生長方式中看到它。數學與程式碼如何幫助我們理解並重現自然用來創造美的模式實在令人驚嘆
程式中的單個指令稱為 *語句*,通常會有一個字符或行間距來標記指令的結束或 *終止*。程式如何終止因語言而異。
程式中的語句可能依賴於使用者或其他地方提供的數據來執行指令。數據可以改變程式的行為,因此程式語言提供了一種暫時存儲數據的方法,以便稍後使用。這些被稱為 *變數*。變數是指令,指示設備將數據存儲在其記憶體中。程式中的變數類似於代數中的變數,它們有唯一的名稱,其值可能隨時間改變。
## 讓魔法發生的基本元素
有些語句可能不會被設備執行。這通常是由開發者設計的,或者是由於意外錯誤而發生的。這種對應用程式的控制使其更加穩健和可維護。通常,這些控制變化發生在滿足某些條件時。現代程式設計中常用的一種語句是 `if..else` 語句。
好了,現在你已經見識過程式語言的實際運作,我們來拆解構成每一段程式碼的基本部分。把這些想成你最愛食譜中的關鍵材料──一旦瞭解每個元素的功能,你就能閱讀並撰寫幾乎任何語言的程式碼!
✅ 你將在後續課程中學習更多關於這類語句的知識。
這有點像學習程式設計的文法。還記得學校時學過名詞、動詞以及如何組成句子嗎?程式語言也有自己的文法,老實說,它比英文文法邏輯多也更寬容!😄
## 工具介紹
### 陳述句:逐步指令
[![工具介紹](https://img.youtube.com/vi/69WJeXGBdxg/0.jpg)](https://youtube.com/watch?v=69WJeXGBdxg "工具介紹")
先從 **陳述句** 開始──它們就像與電腦對話中的單句話。每句陳述告訴電腦要做一件具體的事,就像指示:「這裡左轉」、「紅燈停下」、「停在那個車位」。
> 🎥 點擊上方圖片觀看工具相關影片
我喜歡陳述句的是它們通常很易讀。看看這個:
在這部分,你將了解一些在開始專業開發旅程時可能非常有用的軟件。
```javascript
// 執行單一操作的基本語句
const userName = "Alex";
console.log("Hello, world!");
const sum = 5 + 3;
```
**開發環境** 是開發者在編寫軟件時經常使用的一組獨特工具和功能。其中一些工具已根據開發者的特定需求進行了定制,並可能隨著開發者工作重點的改變、個人項目或使用不同程式語言而改變。開發環境如同使用它的開發者一樣獨特。
**這段程式碼做了什麼:**
- **宣告**一個常數變數來存放使用者名稱
- **顯示**歡迎訊息到控制台輸出
- **計算**並儲存一個數學運算結果
### 編輯器
```javascript
// 與網頁互動的語句
document.title = "My Awesome Website";
document.body.style.backgroundColor = "lightblue";
```
編輯器是軟件開發中最重要的工具之一。編輯器是你編寫程式碼的地方,有時也是你運行程式碼的地方。
**一步一步看發生了什麼:**
- **修改**瀏覽器頁籤上顯示的網頁標題
- **變更**整個頁面內容的背景顏色
開發者依賴編輯器的幾個額外原因:
### 變數:程式的記憶體系統
- *除錯* 幫助逐行檢查程式碼,找出錯誤和問題。一些編輯器具有除錯功能;它們可以根據特定程式語言進行定制和添加。
- *語法高亮* 為程式碼添加顏色和文本格式,使其更易於閱讀。大多數編輯器允許定制語法高亮。
- *擴展和整合* 是專為開發者設計的工具,由開發者開發。這些工具並未內建於基礎編輯器中。例如,許多開發者會為程式碼添加文檔以解釋其工作原理。他們可能會安裝拼寫檢查擴展來幫助發現文檔中的拼寫錯誤。大多數擴展是針對特定編輯器使用的,而大多數編輯器都提供搜索可用擴展的方法。
- *定制化* 使開發者能夠創建符合自己需求的獨特開發環境。大多數編輯器都非常可定制,並且可能允許開發者創建自定義擴展。
說實話,**變數** 是我最喜歡教的概念之一,因為它們就像你每天都會用到的東西!
#### 流行的編輯器及網頁開發擴展
想像一下你的手機聯絡人列表。你不會記得每個人的電話號碼──而是將「媽媽」、「最好的朋友」或「凌晨兩點還送披薩的店」存起來,讓手機記住實際的號碼。變數就是這樣!它們是有標籤的容器,你的程式可以使用有意義的名稱存取資訊。
- [Visual Studio Code](https://code.visualstudio.com/?WT.mc_id=academic-77807-sagibbon)
- [Code Spell Checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker)
- [Live Share](https://marketplace.visualstudio.com/items?itemName=MS-vsliveshare.vsliveshare)
- [Prettier - Code formatter](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
- [Atom](https://atom.io/)
- [spell-check](https://atom.io/packages/spell-check)
- [teletype](https://atom.io/packages/teletype)
- [atom-beautify](https://atom.io/packages/atom-beautify)
- [Sublimetext](https://www.sublimetext.com/)
- [emmet](https://emmet.io/)
- [SublimeLinter](http://www.sublimelinter.com/en/stable/)
更酷的是:變數會隨著程式運行而改變(所以叫「變數」──你懂的吧?)。就像發現更好的披薩店時會更新聯絡資訊一樣,變數能隨著程式得到新資訊或情況改變而更新!
讓我示範這有多簡單又美妙:
### 瀏覽器
```javascript
// 第一步:建立基本變量
const siteName = "Weather Dashboard";
let currentWeather = "sunny";
let temperature = 75;
let isRaining = false;
```
**理解這些概念:**
- **存放**不變的數值到 `const` 變數(比如網站名稱)
- **使用** `let` 定義可變動的數值
- **賦予**不同的資料型態:字串(文字)、數字及布林值(真/假)
- **選擇**描述性的名稱說明變數內容
另一個重要工具是瀏覽器。網頁開發者依賴瀏覽器來查看程式碼在網頁上的運行效果。瀏覽器還用於顯示編輯器中編寫的網頁的視覺元素例如HTML。
```javascript
// 第 2 步:使用物件來群組相關資料
const weatherData = {
location: "San Francisco",
humidity: 65,
windSpeed: 12
};
```
許多瀏覽器都配備了 *開發者工具* (DevTools),其中包含一組有用的功能和信息,幫助開發者收集和捕獲有關其應用程式的重要信息。例如:如果網頁出現錯誤,有時知道錯誤發生的時間會很有幫助。瀏覽器中的開發者工具可以配置為捕獲這些信息。
**上面我們:**
- **建立**一個物件來群組相關的天氣資訊
- **整理**多筆資料放在同一個變數名稱下
- **用**鍵值對清楚標示每筆資訊
#### 流行的瀏覽器及開發者工具
```javascript
// 第3步使用及更新變數
console.log(`${siteName}: Today is ${currentWeather} and ${temperature}°F`);
console.log(`Wind speed: ${weatherData.windSpeed} mph`);
- [Edge](https://docs.microsoft.com/microsoft-edge/devtools-guide-chromium/?WT.mc_id=academic-77807-sagibbon)
- [Chrome](https://developers.google.com/web/tools/chrome-devtools/)
- [Firefox](https://developer.mozilla.org/docs/Tools)
// 更新可變變數
currentWeather = "cloudy";
temperature = 68;
```
### 命令行工具
**理解每個部分:**
- **顯示**內容使用帶有 `${}` 語法的模板字串
- **使用**點記法 (`weatherData.windSpeed`) 取物件屬性
- **更新**使用 `let` 宣告的變數以反映變化
- **結合**多個變數創造有意義的訊息
一些開發者更喜歡使用較少圖形化的界面來完成日常任務,並依賴命令行來實現這一點。編寫程式碼需要大量的打字工作,一些開發者更喜歡不打斷鍵盤上的工作流程。他們會使用鍵盤快捷鍵在桌面窗口之間切換、處理不同文件以及使用工具。大多數任務可以用鼠標完成,但使用命令行的一個好處是許多工作可以通過命令行工具完成,而無需在鼠標和鍵盤之間切換。命令行的另一個好處是它們是可配置的,你可以保存自定義配置,稍後更改,並導入到其他開發機器上。由於開發環境對每個開發者來說都非常獨特,有些人會避免使用命令行,有些人完全依賴它,有些人則喜歡兩者混合使用。
```javascript
// 第4步使用現代解構賦值以使代碼更清晰
const { location, humidity } = weatherData;
console.log(`${location} humidity: ${humidity}%`);
```
### 流行的命令行選項
**你需要知道的:**
- **從物件中解構賦值以擷取特定屬性**
- **創建與物件鍵相同名稱的新變數**
- **簡化程式碼,避免反覆使用點記法**
命令行選項會根據你使用的操作系統而有所不同。
### 控制流程:教你的程式思考
*💻 = 操作系統預裝。*
好了,這就是程式設計讓人驚嘆的地方!**控制流程** 就是教你的程式如何做出智慧決策,就像你每天不假思索地做的一樣。
#### Windows
想像一下:今天早上你可能心裡想「如果下雨,我就帶傘;如果很冷,我會穿外套;如果我遲到了,我會跳過早餐順路買杯咖啡。」你的大腦天然遵循這種如果──那麼的邏輯,天天做好幾十次!
- [Powershell](https://docs.microsoft.com/powershell/scripting/overview?view=powershell-7/?WT.mc_id=academic-77807-sagibbon) 💻
- [Command Line](https://docs.microsoft.com/windows-server/administration/windows-commands/windows-commands/?WT.mc_id=academic-77807-sagibbon) (也稱為CMD) 💻
- [Windows Terminal](https://docs.microsoft.com/windows/terminal/?WT.mc_id=academic-77807-sagibbon)
- [mintty](https://mintty.github.io/)
#### MacOS
這就是為什麼程式感覺聰明又活潑,而不只是乏味、可預測的腳本。它們真能看情況、評估狀況,並適當回應。就像給你的程式一顆能適應並做決定的大腦!
想看看這個邏輯怎麼精采運作?讓我示範:
```javascript
// 第一步:基本條件邏輯
const userAge = 17;
if (userAge >= 18) {
console.log("You can vote!");
} else {
const yearsToWait = 18 - userAge;
console.log(`You'll be able to vote in ${yearsToWait} year(s).`);
}
```
**這段程式碼做的事:**
- **檢查**使用者是否達到投票年齡要求
- **依條件結果執行**不同程式區塊
- **計算**並顯示距離投票資格還要多久如果未滿18歲
- **針對各情況提供**具體有用的回饋
```javascript
// 第 2 步:使用邏輯運算子設置多重條件
const userAge = 17;
const hasPermission = true;
if (userAge >= 18 && hasPermission) {
console.log("Access granted: You can enter the venue.");
} else if (userAge >= 16) {
console.log("You need parent permission to enter.");
} else {
console.log("Sorry, you must be at least 16 years old.");
}
```
**解析這裡發生的事:**
- **`&&`(且)運算子結合多個條件**
- **`else if` 建立多層條件階層處理多種情況**
- **最後用 `else` 處理所有剩餘可能狀況**
- **為每種情況提供明確可執行的回饋**
```javascript
// 第三步:使用三元運算子寫簡潔的條件判斷
const votingStatus = userAge >= 18 ? "Can vote" : "Cannot vote yet";
console.log(`Status: ${votingStatus}`);
```
**你需要記住:**
- **用三元運算子(`? :`)處理簡單的兩種條件**
- **先寫條件,接著 `?`,然後是條件成立結果,接著是 `:`,最後是不成立結果**
- **當要根據條件賦值時運用此模式**
```javascript
// 第4步處理多個特定情況
const dayOfWeek = "Tuesday";
switch (dayOfWeek) {
case "Monday":
case "Tuesday":
case "Wednesday":
case "Thursday":
case "Friday":
console.log("It's a weekday - time to work!");
break;
case "Saturday":
case "Sunday":
console.log("It's the weekend - time to relax!");
break;
default:
console.log("Invalid day of the week");
}
```
**這段程式碼完成的事:**
- **根據變數值匹配多個具體案例**
- **將類似案例(平日與週末)分組**
- **找到匹配時執行對應區塊**
- **包含 `default` 處理意外值**
- **`break` 阻止程式繼續執行後續案例**
> 💡 **現實世界類比**:把控制流程想成世界上最有耐心的 GPS 指路員。它可能說「如果主要幹道塞車,改走高速公路。如果高速公路施工,試試風景路線。」程式用完全相同的條件邏輯,智能地回應不同情況,總給使用者最佳體驗。
### 🎯 **概念檢視:基礎元素精通**
**讓我們來看看你基礎掌握得如何:**
- 你能用自己的話解釋變數和陳述句的不同嗎?
- 想一個現實例子,使用 if-then 決策(就像投票範例)
- 程式邏輯中,有什麼令你感到意外的事?
**快速信心提升:**
```mermaid
flowchart LR
A["📝 陳述<br/>(指令)"] --> B["📦 變量<br/>(儲存)"] --> C["🔀 控制流程<br/>(決策)"] --> D["🎉 工作程式!"]
style A fill:#ffeb3b
style B fill:#4caf50
style C fill:#2196f3
style D fill:#ff4081
```
**接著要探討的是**:我們將一起深入挖掘這些概念,展開一段令人興奮的旅程!現在只要感受未來諸多精彩可能帶來的興奮。隨著練習,特定技巧和方法自然會掌握──我保證這比你想像中還更有趣!
## 開發工具
說真的,這裡是我最興奮到快控制不住自己的一部分!🚀 我們將說說那些令人感覺像握到了數位太空船鑰匙的神奇工具。
你知道廚師手中完美平衡、彷彿化身為手的刀具嗎?或是音樂家那把一碰就奏出美妙音符的吉他?開發者也有我們自己的神奇工具,而下面這些會徹底顛覆你的想像──其中大多完全免費!
我自己坐立難安想跟你分享這些,因為它們徹底改變了我們打造軟體的方式。我們談的是由 AI 助理撰寫程式碼的工具(我不是開玩笑!)、可以從任何有 Wi-Fi 的地方建立整個應用的雲端環境,還有讓你程式宛如 X 光般透視的偵錯工具。
震撼的是:這些不是你用一陣子就會丟掉的「新手工具」,它們就是 Google、Netflix 還有你喜歡的獨立應用工作室活躍開發者此刻用的專業級工具。你會用它們感覺像個專家!
```mermaid
graph TD
A["💡 你的點子"] --> B["⌨️ 程式碼編輯器<br/>(VS Code)"]
B --> C["🌐 瀏覽器開發工具<br/>(測試與除錯)"]
C --> D["⚡ 命令列<br/>(自動化與工具)"]
D --> E["📚 文件說明<br/>(學習與參考)"]
E --> F["🚀 超讚的網頁應用!"]
B -.-> G["🤖 AI 助理<br/>(GitHub Copilot)"]
C -.-> H["📱 裝置測試<br/>(響應式設計)"]
D -.-> I["📦 套件管理器<br/>(npm, yarn)"]
E -.-> J["👥 社群<br/>(Stack Overflow)"]
style A fill:#fff59d
style F fill:#c8e6c9
style G fill:#e1f5fe
style H fill:#f3e5f5
style I fill:#ffccbc
style J fill:#e8eaf6
```
### 程式碼編輯器與整合開發環境:你的新數位好夥伴
談談程式碼編輯器──這可真快成為你最愛聚集的地方!把它想成你的程式碼聖地,你會花最多時間在這裡編寫和優化數位創作。
而且現代編輯器超魔法──它們不只是花俏的文本編輯器。它們就像全天候坐在你旁邊、無比聰明而且支持你寫程式的導師。它們會在你發現錯字之前找到它們,建議改善讓你看起來更厲害,還幫你理解每段程式碼在做什麼,其中一些甚至能預測你下一步打什麼與你的想法!
我還記得第一次發現自動完成功能──感覺彷彿生活在未來。你開始打字,編輯器就跳出來說:「诶,你是不是想用這個,剛好能幫你完成需要的功能?」就像有個心靈感應的程式碼夥伴!
**令這些編輯器不可思議的原因?**
現代程式碼編輯器提供一系列功能,設計來提升你的生產力:
- [Terminal](https://support.apple.com/guide/terminal/open-or-quit-terminal-apd5265185d-f365-44cb-8b09-71a064a42125/mac) 💻
- [iTerm](https://iterm2.com/)
- [Powershell](https://docs.microsoft.com/powershell/scripting/install/installing-powershell-core-on-macos?view=powershell-7/?WT.mc_id=academic-77807-sagibbon)
| 功能 | 功能說明 | 為何有用 |
|---------|--------------|--------------|
| **語法高亮** | 為程式碼不同部分著色 | 讓程式碼更易讀與找錯 |
| **自動完成** | 打字時推薦程式碼 | 加快寫程式速度並減少錯字 |
| **除錯工具** | 幫你找到並修正錯誤 | 節省無數除錯時間 |
| **擴充功能** | 新增專業化能力 | 讓你能依技術需求客製編輯器 |
| **AI 助手** | 建議程式碼與解說 | 加速學習與工作效率 |
#### Linux
> 🎥 **影片資源**:想實際看看這些工具的運作?請觀看這部 [Tools of the Trade video](https://youtube.com/watch?v=69WJeXGBdxg) 獲得完整介紹。
- [Bash](https://www.gnu.org/software/bash/manual/html_node/index.html) 💻
- [KDE Konsole](https://docs.kde.org/trunk5/en/konsole/konsole/index.html)
- [Powershell](https://docs.microsoft.com/powershell/scripting/install/installing-powershell-core-on-linux?view=powershell-7/?WT.mc_id=academic-77807-sagibbon)
#### 推薦給網頁開發者的編輯器
#### 流行的命令行工具
**[Visual Studio Code](https://code.visualstudio.com/?WT.mc_id=academic-77807-sagibbon)**(免費)
- 網頁開發者最火紅的編輯器
- 擁有豐富擴充生態系
- 內建終端機與 Git 整合
- **必裝擴充**
- [GitHub Copilot](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot) - AI 程式碼建議
- [Live Share](https://marketplace.visualstudio.com/items?itemName=MS-vsliveshare.vsliveshare) - 即時協作
- [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) - 自動格式化程式碼
- [Code Spell Checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker) - 抓出程式碼錯字
- [Git](https://git-scm.com/) (💻 在大多數操作系統上預裝)
- [NPM](https://www.npmjs.com/)
- [Yarn](https://classic.yarnpkg.com/en/docs/cli/)
**[JetBrains WebStorm](https://www.jetbrains.com/webstorm/)**(付費,學生免費)
- 進階除錯與測試工具
- 智慧程式碼補全
- 內建版本控制
### 文檔
**雲端 IDE**(多種定價)
- [GitHub Codespaces](https://github.com/features/codespaces) - 瀏覽器中完整 VS Code
- [Replit](https://replit.com/) - 非常適合學習與分享程式碼
- [StackBlitz](https://stackblitz.com/) - 即時全端網頁開發
當開發者想學習新事物時,他們通常會查閱文檔以了解如何使用它。開發者經常依賴文檔來指導他們正確使用工具和語言,並深入了解其工作原理。
> 💡 **入門建議**:先用 Visual Studio Code──它免費行業使用率高社群及教學資源豐富
#### 網頁開發相關的流行文檔
### 網頁瀏覽器:你的秘密開發實驗室
- [Mozilla Developer Network (MDN)](https://developer.mozilla.org/docs/Web),由 [Firefox](https://www.mozilla.org/firefox/) 瀏覽器的出版商Mozilla提供
- [Frontend Masters](https://frontendmasters.com/learn/)
- [Web.dev](https://web.dev),由 [Chrome](https://www.google.com/chrome/) 的出版商Google提供
- [Microsoft的開發者文檔](https://docs.microsoft.com/microsoft-edge/#microsoft-edge-for-developers),適用於 [Microsoft Edge](https://www.microsoft.com/edge)
- [W3 Schools](https://www.w3schools.com/where_to_start.asp)
準備好徹底被震撼了嗎!你知道一直以來用瀏覽器滑社交媒體和看影片,其實它們藏著一個巨大的秘密開發實驗室?
✅ 做一些研究:現在你已經了解了網頁開發者的環境,試著比較和對比它與網頁設計師的環境。
每次你在網頁上點右鍵選「檢查」元素,就是在開啟一個隱藏的開發者工具世界──它們強大到比我以前花大錢買的某些軟體還厲害。就像發現你平凡廚房後面藏著一座專業大廚實驗室的秘密通道!
第一次有人向我展示瀏覽器 DevTools 時,我花了差不多三個小時一直點來點去,然後不停地說:「等等,原來它仲可以做呢啲嘢?!」你可以即時修改任何網站,準確睇到每樣嘢加載嘅速度,測試你嘅網站喺唔同裝置上嘅效果,甚至可以好似專業人士咁除錯 JavaScript。真係令人嘆為觀止
**以下係瀏覽器成為你秘密武器嘅原因:**
當你建立網站或者網頁應用程式時,你需要睇下佢喺現實世界中嘅外觀同埋行為。瀏覽器唔單止係用嚟顯示你嘅作品,仲會提供有關效能、無障礙性同埋潛在問題嘅詳細反饋。
#### 瀏覽器開發者工具DevTools
現代瀏覽器包咗全面嘅開發套件:
| 工具類別 | 功能 | 使用範例 |
|---------------|--------------|------------------|
| **元素檢查器** | 即時檢視同編輯 HTML/CSS | 調整樣式,立即睇到結果 |
| **主控台** | 查看錯誤訊息同測試 JavaScript | 除錯問題同試驗代碼 |
| **網絡監控器** | 跟踪資源加載情況 | 優化效能同載入時間 |
| **無障礙檢查器** | 測試包容性設計 | 確保網站適合所有用戶使用 |
| **裝置模擬器** | 預覽不同屏幕尺寸效果 | 測試響應式設計無需多部裝置 |
#### 推薦開發用瀏覽器
- **[Chrome](https://developers.google.com/web/tools/chrome-devtools/)** - 業界標準 DevTools有豐富文檔
- **[Firefox](https://developer.mozilla.org/docs/Tools)** - 出色嘅 CSS Grid 同無障礙工具
- **[Edge](https://docs.microsoft.com/microsoft-edge/devtools-guide-chromium/?WT.mc_id=academic-77807-sagibbon)** - 基於 Chromium配合微軟開發資源
> ⚠️ **重要測試提示**:一定要喺多個瀏覽器上測試網站!喺 Chrome 完美運作嘅嘢,可能喺 Safari 或 Firefox 呈現唔同。專業開發者會喺所有主流瀏覽器測試,確保用戶體驗一致。
### 命令行工具:開發者超能力之門
好啦講真我哋依家誠實吓講命令行因為我想你聽聽一個真係明白你嘅人嘅心聲。初次見到佢嗰陣──只係一個可怕嘅黑色畫面同閃動字元──我真係以為「唔好唔得呢啲好似1980年代駭客電影入面先有嘅完全唔係我啱用嘅」😅
但我希望當年有人告訴我而家我就告訴你命令行唔驚人──其實好似直接同你嘅電腦對話咁。想像下叫外賣經過一個有圖有菜單嘅高級app好方便同埋行入你最愛嘅本地餐廳廚師只需你一句「驚喜點啲特別嘢畀我」就整返啱你嘅餐點兩者完全不一樣。
命令行係開發者感覺自己成為巫師嘅地方。你打幾個似乎好神奇嘅字(好啦,其實就係指令,但佢哋感覺好神奇!),按 enter嘭──你就建立咗整個項目結構從全球安裝強大工具或者把你嘅應用部署到互聯網畀數百萬人睇到。試過一次嗰種力量真係令人上癮
**點解命令行會成為你最愛嘅工具:**
雖然圖形介面好適合好多任務,但命令行擅長自動化、精確同速度。好多開發工具主要透過命令行介面操作,而學懂點高效使用可以大大提升你嘅生產力。
```bash
# 第一步:創建並進入專案目錄
mkdir my-awesome-website
cd my-awesome-website
```
**呢段代碼做緊啲咩:**
- **建立** 一個叫 "my-awesome-website" 嘅新目錄俾你嘅項目用
- **進入** 新建立嘅目錄開始工作
```bash
# 第2步使用 package.json 初始化項目
npm init -y
# 安裝現代開發工具
npm install --save-dev vite prettier eslint
npm install --save-dev @eslint/js
```
**一步步嘅流程說明:**
- **初始化** 使用 `npm init -y` 用預設設置建立新嘅 Node.js 項目
- **安裝** Vite 作為快速開發同生產構建嘅現代構建工具
- **添加** Prettier 用自動格式化代碼ESLint 用檢查代碼質量
- **用** `--save-dev` 標記佢哋係僅開發時依賴
```bash
# 第三步:建立項目結構及檔案
mkdir src assets
echo '<!DOCTYPE html><html><head><title>My Site</title></head><body><h1>Hello World</h1></body></html>' > index.html
# 啟動開發伺服器
npx vite
```
**上面我哋做咗:**
- **組織** 項目,建立源代碼同資源嘅獨立資料夾
- **生成** 一個基本嘅 HTML 文件,包含正確嘅文件結構
- **啟動** Vite 開發服務器,支持即時重新載入同模組熱替換
#### 網頁開發必備命令行工具
| 工具 | 作用 | 為何需要 |
|------|---------|-----------------|
| **[Git](https://git-scm.com/)** | 版本控制 | 追蹤更改,協作,備份工作 |
| **[Node.js & npm](https://nodejs.org/)** | JavaScript 執行環境及包管理 | 喺瀏覽器以外運行 JavaScript安裝現代開發工具 |
| **[Vite](https://vitejs.dev/)** | 構建工具及開發伺服器 | 極速開發支援模組熱替換 |
| **[ESLint](https://eslint.org/)** | 代碼質量 | 自動發現及修復 JavaScript 問題 |
| **[Prettier](https://prettier.io/)** | 代碼格式化 | 保持代碼格式一致且易讀 |
#### 平台特定選擇
**Windows:**
- **[Windows Terminal](https://docs.microsoft.com/windows/terminal/?WT.mc_id=academic-77807-sagibbon)** - 現代功能豐富嘅終端
- **[PowerShell](https://docs.microsoft.com/powershell/?WT.mc_id=academic-77807-sagibbon)** 💻 - 強大嘅腳本環境
- **[Command Prompt](https://docs.microsoft.com/windows-server/administration/windows-commands/?WT.mc_id=academic-77807-sagibbon)** 💻 - 傳統嘅 Windows 命令行
**macOS:**
- **[Terminal](https://support.apple.com/guide/terminal/)** 💻 - 內置終端應用
- **[iTerm2](https://iterm2.com/)** - 帶進階功能嘅增強終端
**Linux:**
- **[Bash](https://www.gnu.org/software/bash/)** 💻 - Linux 標準 shell
- **[KDE Konsole](https://docs.kde.org/trunk5/en/konsole/konsole/index.html)** - 進階終端模擬器
> 💻 = 作業系統預裝
> 🎯 **學習路線**:由基本命令開始學起,例如 `cd`(切換目錄)、`ls` 或 `dir`(列出檔案)、`mkdir`(建立資料夾)。練習現代工作流程常用命令,例如 `npm install`、`git status` 同 `code .`(喺 VS Code 開啟目前資料夾)。隨住習慣,會自然而然掌握更高級命令同自動化技巧。
### 文件:你永遠可用嘅學習導師
好啦,畀我講個秘密令你作為初學者心裡好啲:就算最有經驗嘅開發者,都花大量時間喺睇文件上。唔係因為佢哋唔識做──其實反而係智慧嘅象徵!
諗吓文件就似全天候全年無休嘅最有耐性、最識嘅老師。凌晨兩點卡住解決唔到嘅問題?文件會以暖心虛擬擁抱同你提供正確答案。想了解啲人人都講嘅新特性?文件會提供逐步教學。想明白點解件事係咁運作?嘩,文件就係你嗰個最終令你「啊哈!」嘅解說員!
有一樣完全改變咗我觀念嘅事情:網頁開發世界發展超級快,冇人(講真,冇一個人!)會把所有嘢記曬。我見過有 15 年經驗以上嘅資深開發者都要查基本語法,知唔知道?呢啲唔係丟人──係明智!唔係靠記憶,而係知道喺邊度好快搵到可靠答案,並且懂得應用。
**魔力就喺呢度:**
專業開發者大量時間用嚟睇文件──唔係因為唔識做,而係網頁開發環境變化太快,要持續學習先跟得上。好文件幫你明白唔單止「點用」,仲有「點解要用」同「幾時用」。
#### 必備文件資源
**[Mozilla Developer Network (MDN)](https://developer.mozilla.org/docs/Web)**
- 網絡技術文件嘅黃金標準
- 詳盡 HTML、CSS 同 JavaScript 指南
- 包含瀏覽器兼容性資訊
- 實用範例同互動演示
**[Web.dev](https://web.dev)** (由 Google 提供)
- 現代網頁開發最佳實踐
- 性能優化指南
- 無障礙同包容性設計原則
- 實踐案例研究
**[Microsoft Developer Documentation](https://docs.microsoft.com/microsoft-edge/#microsoft-edge-for-developers)**
- Edge 瀏覽器開發資源
- 漸進式網頁應用指南
- 跨平台開發洞見
**[Frontend Masters Learning Paths](https://frontendmasters.com/learn/)**
- 有系統嘅學習課程
- 業界專家視頻課程
- 實操編程練習
> 📚 **學習策略**:唔好死記硬背文件──要學識有效瀏覽。收藏常用參考資料,練習用搜尋功能迅速搵特定訊息。
### 🔧 **工具掌握檢查:你有咩共鳴?**
**花啲時間諗吓:**
- 你最期待先試邊個工具?(冇錯誤答案!)
- 命令行依然令你驚,定係想了解佢?
- 你可唔可以想像用瀏覽器 DevTools 去偷睇你最鍾意網站嘅背後?
```mermaid
pie title "開發者使用工具時間分佈"
"程式碼編輯器" : 40
"瀏覽器測試" : 25
"命令行" : 15
"閱讀文件" : 15
"除錯" : 5
```
> **有趣嘅見解**:大多數開發者約有 40% 嘅時間喺代碼編輯器度,但你有冇留意測試、學習同問題解決花咗幾多時間?編程唔單止係寫代碼——係塑造體驗!
**思考題**:諗吓有趣嘅嘢──你認為開發網站用嘅工具(開發)同用嚟設計網站外觀嘅工具(設計)會有咩分別?就好似建築師設計靚屋,跟承包商建嗰間屋一樣。兩個都重要,但要用唔同嘅工具箱!呢種思考會幫助你睇清網站點樣誕生嘅大局。
## GitHub Copilot Agent 挑戰 🚀
用 Agent 模式完成以下挑戰:
**描述:** 探索現代代碼編輯器或 IDE 嘅功能,展示佢點樣提升你作為網頁開發者嘅工作流程。
**提示:** 揀一個代碼編輯器或 IDE例如 Visual Studio Code、WebStorm 或雲端 IDE。列出三個幫助你更有效率寫代碼、除錯或維護代碼嘅功能或擴展。對每個功能提供簡短說明講解佢點樣增強你嘅工作效率。
---
## 🚀 挑戰
比較一些程式語言。JavaScript與Java有哪些獨特特性COBOL與Go又如何
**好啦,偵探,你準備好攪第一宗案未?**
## 課後測驗
[課後測驗](https://ff-quizzes.netlify.app/web/)
依家你有咗呢個超棒嘅基礎,我會畀你一個冒險,幫你見識到編程世界嘅多元同精采。聽好──呢個唔係寫代碼嘅挑戰,所以唔使擔心!想像自己係編程語言嘅偵探,展開你第一單振奮人心嘅案件!
## 回顧與自學
**你嘅任務,如果你願意接受:**
1. **成為語言探索者**:揀三種完全唔同領域嘅編程語言——可能一個用嚟建網站,一個做手機 app一個用嚟處理科學數據。找出同一個簡單任務喺佢哋嘅寫法。保證你會超乎想像佢哋雖然做同樣嘢表達可以咁唔同
研究一下程式設計師可用的不同語言。嘗試用一種語言寫一行程式碼,然後用另外兩種語言重寫它。你學到了什麼?
2. **揭開佢哋嘅故事**:每個語言有咩特色?有趣嘅係,每種語言背後都係因為有人覺得「有冇更好方法解決呢個具體問題?」你能唔能搵出嗰啲問題係咩?啲故事好精彩㗎!
3. **認識社群**:睇吓每個語言嘅社群係點樣熱情同支持成員。有啲有幾百萬名開發者分享知識,有啲雖然細細,但超級緊密而且支援力十足。睇吓佢哋性格點,好打開眼界!
4. **跟隨直覺**:而家邊個語言最吸引你?唔好擔心揀「完美」──聽住你直覺就得!無錯誤答案,將來你可以慢慢探索其他語言。
**額外偵探技巧**:試搵吓每個語言有咩大型網站或應用係用佢寫嘅。我保證你會震驚發現 Instagram、Netflix甚至係嗰個停唔到玩嘅手機遊戲係用邊種語言做嘅
> 💡 **記住**:你唔係想今日成為專家,只係想先認識吓個社區,之後再決定喺邊度安家。慢慢嚟,玩得開心,畀好奇心帶領你!
## 一齊慶祝你嘅發現!
嘩,你今日吸收咗好多精彩資訊!我非常興奮見到你同行呢個奇妙旅程嘅收穫。記住──呢唔係測驗唔使做到完美,反而係慶祝你認識到呢個奇妙世界嘅所有精彩嘢!
[參加課後測驗](https://ff-quizzes.netlify.app/web/)
## 評閱與自學
**慢慢探索,享受其中的樂趣!**
你今天已經學了很多內容,真值得驕傲!現在是最有趣的部分——探索那些激發你好奇心的主題。記住,這不是功課——這是一場冒險!
**深入了解讓你感興趣的內容:**
**親手玩玩程式語言:**
- 訪問 2-3 個引起你注意的官方網站。每個語言都有自己的個性和故事!
- 嘗試一些線上程式碼練習場,比如 [CodePen](https://codepen.io/)、[JSFiddle](https://jsfiddle.net/) 或 [Replit](https://replit.com/)。不要害怕嘗試——你不會壞掉什麼東西!
- 閱讀你最喜愛的語言是如何誕生的。認真說,有些語言的起源故事非常有趣,能幫助你理解語言為什麼會那樣運作。
**熟悉你新工具:**
- 如果還沒下載 Visual Studio Code就趕快下載吧——它免費你會愛上的
- 花幾分鐘瀏覽一下擴充套件市場。它就像你的程式碼編輯器的應用商店!
- 打開瀏覽器的開發者工具,隨便點點看看。不要擔心能不能理解全部內容——只是熟悉一下介面。
**加入社群:**
- 在 [Dev.to](https://dev.to/)、[Stack Overflow](https://stackoverflow.com/) 或 [GitHub](https://github.com/) 追蹤一些開發者社群。程式設計社群對新手非常友善!
- 在 YouTube 看一些適合初學者的程式教學影片。那裡有很多優秀的創作者,懂得新手的感受。
- 思考加入本地的見面會或線上社群。相信我,開發者們都很樂意幫助新手!
> 🎯 **聽著,請記得這點**:你不需要一夜之間成為程式高手!現在你只是開始認識這個你即將成為一份子的精彩新世界。慢慢來,享受這段旅程,並記得——每一位你敬佩的開發者,曾經都坐在你現在的位置上,感到興奮並可能有些不知所措。這非常正常,這表示你做得很對!
## 作業
[閱讀文檔](assignment.md)
[Reading the Docs](assignment.md)
> 注意:在選擇作業工具時,不要選擇上面已列出的編輯器、瀏覽器或命令行工具。
> 💡 **給你作業的小提醒**:我非常想看到你探索一些我們還沒提過的工具!跳過已討論過的編輯器、瀏覽器和指令列工具——這個世界上有一整個令人驚嘆的開發工具宇宙在等你去發現。尋找那些活躍維護且社群活躍、樂於助人的工具(這些通常有最棒的教學,當你卡關時也最有人幫忙)。
---
## 🚀 你的程式之旅時間表
### ⚡ **接下來 5 分鐘你可以做什麼**
- [ ] 收藏 2-3 個引起你注意的程式語言網站
- [ ] 如果還沒下載,立刻安裝 Visual Studio Code
- [ ] 打開瀏覽器的 DevTools (F12),隨便點點瀏覽任何網站
- [ ] 加入一個程式社群Dev.to、Reddit r/webdev 或 Stack Overflow
### ⏰ **接下來 1 小時你可以完成什麼**
- [ ] 完成課後小測驗並思考你的答案
- [ ] 設定 VS Code安裝 GitHub Copilot 擴充套件
- [ ] 線上用兩種不同程式語言試寫「Hello World」範例
- [ ] 觀看一支「開發者的一天」YouTube 影片
- [ ] 開始你的程式語言調查工作(挑戰中提到的)
### 📅 **你的一週冒險**
- [ ] 完成作業並探索 3 個新的開發工具
- [ ] 在社群媒體追蹤 5 位開發者或程式帳號
- [ ] 在 CodePen 或 Replit 嘗試建構一個小作品哪怕只是說「Hello, [你的名字]!」)
- [ ] 閱讀一篇開發者部落格文章,了解他人的程式歷程
- [ ] 參加一次線上見面會或觀看程式主題講座
- [ ] 使用線上教學開始學習你選擇的程式語言
### 🗓️ **你的一個月蛻變**
- [ ] 建造你的第一個小專案(就算是一個簡單的網頁也算!)
- [ ] 為開源專案做出貢獻(可以從文件修正開始)
- [ ] 輔導一名剛開始學程式的新手
- [ ] 建立你的開發者作品集網站
- [ ] 與當地開發者社群或讀書會建立連結
- [ ] 開始規劃下一個學習里程碑
### 🎯 **最終反思檢視**
**在繼續前,花點時間慶祝:**
- 今天有什麼程式相關的事讓你感到興奮?
- 你想先探索哪一個工具或概念?
- 你對開始這段程式之旅有什麼感覺?
- 你現在最想問開發者什麼問題?
```mermaid
journey
title 你的信心建立旅程
section 今天
好奇: 3: 你
不知所措: 4: 你
興奮: 5: 你
section 本週
探索: 4: 你
學習: 5: 你
聯繫: 4: 你
section 下個月
建立: 5: 你
自信: 5: 你
幫助他人: 5: 你
```
> 🌟 **記住**:每個專家都曾是初學者。每位資深開發者都曾經有和你一樣的感覺——興奮,可能有點不知所措,並且對未來充滿好奇。你有很棒的夥伴,這段旅程將會非常精彩。歡迎來到奇妙的程式世界!🎉
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。
本文件由 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於翻譯準確性,但請注意自動翻譯可能包含錯誤或不準確之處。原始文件的原文版本應視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們不對因使用本翻譯而引起的任何誤解或誤譯負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,31 +1,78 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "9e2f84e351a6fcb44bfc4066d98525f0",
"translation_date": "2025-10-03T08:56:49+00:00",
"original_hash": "17b8ec8e85d99e27dcb3f73842e583be",
"translation_date": "2026-01-06T13:14:37+00:00",
"source_file": "1-getting-started-lessons/1-intro-to-programming-languages/assignment.md",
"language_code": "hk"
}
-->
## 指示
# Assignment: Exploring Modern Web Development Tools
網頁開發者可能需要許多工具,這些工具列在 [MDN 客戶端工具文檔](https://developer.mozilla.org/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Overview) 中。請選擇 **三個工具**,這些工具 **未在本課程中涵蓋**(排除 [列出特定工具或參考課程內容]),解釋 **為什麼** 網頁開發者會使用每個工具,並找到符合每個類別的工具。對於每個工具,分享其官方文檔的鏈接(而非 MDN 上的示例)。
## Instructions
**格式:**
- 工具名稱
- 為什麼網頁開發者會使用它2-3 句)
- 文檔鏈接
The web development ecosystem includes hundreds of specialized tools that help developers build, test, and maintain applications efficiently. Your task is to research and understand tools that complement the ones covered in this lesson.
**字數要求:**
- 每個解釋應為 2-3 句。
**Your Mission:** Select **three tools** that are **not covered in this lesson** (avoid choosing code editors, browsers, or command line tools already listed). Focus on tools that solve specific problems in modern web development workflows.
## 評分標準
**For each tool, provide:**
卓越 | 合格 | 需要改進
--- | --- | --- |
解釋了為什麼網頁開發者會使用工具 | 解釋了如何使用,但未解釋為什麼開發者會使用工具 | 未提及開發者如何或為什麼使用工具 |
1. **Tool name and category** (e.g., "Figma - Design Tool" or "Jest - Testing Framework")
2. **Purpose and benefits** - Explain in 2-3 sentences why a web developer would use this tool and what problems it solves
3. **Official documentation link** - Provide a link to the tool's official documentation or website (not just tutorial sites)
4. **Real-world context** - Mention one way this tool fits into a professional development workflow
## Suggested Tool Categories
Consider exploring tools from these categories:
| Category | Examples | What They Do |
|----------|----------|--------------|
| **Build Tools** | Vite, Webpack, Parcel, esbuild | Bundle and optimize code for production with fast development servers |
| **Testing Frameworks** | Vitest, Jest, Cypress, Playwright | Ensure code works correctly and catch bugs before deployment |
| **Design Tools** | Figma, Adobe XD, Penpot | Create mockups, prototypes, and design systems collaboratively |
| **Deployment Platforms** | Netlify, Vercel, Cloudflare Pages | Host and distribute websites with automatic CI/CD |
| **Version Control** | GitHub, GitLab, Bitbucket | Manage code changes, collaboration, and project workflows |
| **CSS Frameworks** | Tailwind CSS, Bootstrap, Bulma | Accelerate styling with pre-built component libraries |
| **Package Managers** | npm, pnpm, Yarn | Install and manage code libraries and dependencies |
| **Accessibility Tools** | axe-core, Lighthouse, Pa11y | Test for inclusive design and WCAG compliance |
| **API Development** | Postman, Insomnia, Thunder Client | Test and document APIs during development |
## Format Requirements
**For each tool:**
```
### [Tool Name] - [Category]
**Purpose:** [2-3 sentences explaining why developers use this tool]
**Documentation:** [Official website/documentation link]
**Workflow Integration:** [1 sentence about how it fits into development process]
```
## Quality Guidelines
- **Choose current tools**: Select tools that are actively maintained and widely used in 2025
- **Focus on value**: Explain the specific benefits, not just what the tool does
- **Professional context**: Consider tools used by development teams, not just individual hobbyists
- **Diverse selection**: Pick tools from different categories to show breadth of the ecosystem
- **Modern relevance**: Prioritize tools that align with current web development trends and best practices
## Rubric
| Excellent | Good | Needs Improvement |
|-----------|------|-------------------|
| **Clearly explained why developers use each tool and what problems it solves** | **Explained what the tool does but missed some context about its value** | **Listed tools but didn't explain their purpose or benefits** |
| **Provided official documentation links for all tools** | **Provided mostly official links with 1-2 tutorial sites** | **Relied mainly on tutorial sites rather than official documentation** |
| **Selected current, professionally-used tools from diverse categories** | **Selected good tools but limited variety in categories** | **Selected outdated tools or only from one category** |
| **Demonstrated understanding of how tools fit into development workflows** | **Showed some understanding of professional context** | **Focused only on tool features without workflow context** |
> 💡 **Research Tip**: Look for tools mentioned in job postings for web developers, check popular developer surveys, or explore the dependencies used by successful open-source projects on GitHub!
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件已使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,請注意自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。
本文件使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們力求準確,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的本地語言版本應被視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用本翻譯而引起的任何誤解或誤讀概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,343 +1,777 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "ea65b75e488aa33a3cc5cb1c6c3f047a",
"translation_date": "2025-10-03T13:42:49+00:00",
"original_hash": "5c383cc2cc23bb164b06417d1c107a44",
"translation_date": "2026-01-06T13:14:50+00:00",
"source_file": "1-getting-started-lessons/2-github-basics/README.md",
"language_code": "hk"
}
-->
# GitHub 簡介
本課程涵蓋 GitHub 的基礎知識,這是一個用於托管和管理代碼變更的平台。
嗨,未來的開發者!👋 準備好加入全球數百萬程式員的行列了嗎?我非常興奮地向你介紹 GitHub——把它想像成程式員的社交媒體平台不同的是我們分享的不是午餐照而是程式碼一起打造令人驚嘆的作品
![GitHub 簡介](../../../../translated_images/webdev101-github.8846d7971abef6f947909b4f9d343e2a23778aa716ca6b9d71df7174ee5009ac.hk.png)
> [Tomomi Imura](https://twitter.com/girlie_mac) 的手繪筆記
這點真的讓我震撼不已:你手機上的每個應用程式、你瀏覽的每個網站,以及你將學習使用的大多數工具,都是由團隊開發者在像 GitHub 這樣的平台上共同協作完成的。你喜愛的音樂應用程式?可能就有人跟你一樣貢獻了程式碼。那款玩不膩的遊戲?沒錯,可能就是用 GitHub 協作建造出來的。現在,你也將學會如何成為這個精彩社群的一員!
## 課前測驗
[課前測驗](https://ff-quizzes.netlify.app)
我知道剛開始可能會覺得很多——說真的,我還記得第一次看到 GitHub 頁面時想:「這到底是什麼意思?」但事實是:每個開發者都是從你現在的狀態開始的。結束這堂課時,你會有自己的 GitHub 倉庫(把它想像成你個人在雲端的專案展示台),你會知道如何儲存工作、與他人分享,甚至參與數百萬人使用的專案。
## 簡介
我們會一步步一起走這趟旅程,沒有急躁,沒有壓力——只有你我和一些超酷的工具,它們將成為你新的好朋友!
在本課程中,我們將學習:
![Intro to GitHub](../../../../translated_images/webdev101-github.8846d7971abef6f9.hk.png)
> Sketchnote by [Tomomi Imura](https://twitter.com/girlie_mac)
- 如何追蹤你在本地機器上的工作
- 與他人合作完成項目
- 如何為開源軟件做出貢獻
```mermaid
journey
title 你今日的 GitHub 冒險
section 設置
Install Git: 4: You
Create Account: 5: You
First Repository: 5: You
section 精通 Git
Local Changes: 4: You
Commits & Pushes: 5: You
Branching: 4: You
section 合作
Fork Projects: 4: You
Pull Requests: 5: You
Open Source: 5: You
```
## 課前小測驗
[Pre-lecture quiz](https://ff-quizzes.netlify.app)
## 介紹
在我們深入真正令人興奮的內容之前,先讓你的電腦準備好 GitHub 的魔法吧!想像這就像在創作傑作之前整理藝術工具——有了合適的工具,一切都會變得更順暢、更有趣。
我會親自引導你完成每個設定步驟,我保證並沒有看起來那麼可怕。如果一開始有些地方不太懂,那完全正常!我還記得設定第一個開發環境時,感覺像在解讀古埃及象形文字。每個開發者都曾經跟你現在一樣,不知道自己有沒有做對。劇透一下:你正在學習,已經做對了!🌟
本課將涵蓋:
- 追蹤你在電腦上的工作
- 與他人合作專案
- 如何貢獻開源軟體
### 先決條件
在開始之前,你需要檢查是否已安裝 Git。在終端中輸入
先讓你的電腦準備好 GitHub 的魔法吧!別擔心--這個設定只需做一次,之後你的程式之旅都將順利進行。
好了,讓我們從基礎開始!首先,我們需要確認你的電腦上是否已經安裝了 Git。Git 基本上就像一個超聰明的助手,記錄你對程式碼的每個變更,比起每兩秒猛按 Ctrl+S我們都做過好太多了
在終端機輸入這行魔法指令,看看你的電腦是否有安裝 Git
`git --version`
如果尚未安裝 Git請[下載 Git](https://git-scm.com/downloads)。然後在終端中設置你的本地 Git 配置檔案:
* `git config --global user.name "your-name"`
* `git config --global user.email "your-email"`
如果還沒安裝,別擔心!到 [download Git](https://git-scm.com/downloads) 下載安裝就好。裝好之後,我們要正式介紹你和 Git 認識一下:
> 💡 **初次設定**:這些指令會告訴 Git 你是誰。這資訊會附加在你每次提交commit所以請選擇你願意公開分享的名字和郵件。
```bash
git config --global user.name "your-name"
git config --global user.email "your-email"
```
要檢查 Git 是否已配置,可以輸入:
`git config --list`
要檢查 Git 是否已設好,可以輸入:
```bash
git config --list
```
你還需要一個 GitHub 帳戶、一個代碼編輯器(例如 Visual Studio Code並打開你的終端或命令提示符
你還需要有 GitHub 帳號、程式碼編輯器(如 Visual Studio Code還有打開終端機或命令提示字元)。
前往 [github.com](https://github.com/) 創建帳戶(如果尚未創建),或登錄並完善你的個人資料。
造訪 [github.com](https://github.com/),還沒帳號就註冊,或登入並完善個人資料。
✅ GitHub 不是世界上唯一的代碼倉庫;還有其他選擇,但 GitHub 是最知名的。
💡 **現代提示**:考慮設定 [SSH 金鑰](https://docs.github.com/en/authentication/connecting-to-github-with-ssh) 或使用 [GitHub CLI](https://cli.github.com/) 來無密碼更輕鬆認證。
✅ GitHub 並非唯一的程式碼倉庫,市場上還有其他選擇,但 GitHub 最出名。
### 準備工作
你需要在本地機器(筆記本或 PC上準備一個包含代碼項目的文件夾以及 GitHub 上的一個公共倉庫,這將作為如何為他人的項目做出貢獻的示例。
你需要在本機(筆電或電腦)準備一個有程式碼的資料夾,還有 GitHub 上一個公開倉庫,作為示範如何貢獻他人專案的例子
---
### 保護你的程式碼安全
## 代碼管理
說點安全性的事情——別擔心,不會嚇到你!把安全習慣想成鎖車或鎖房門。這些簡單習慣會成為你的第二天性,保護你辛苦的作品。
假設你在本地有一個代碼項目文件夾,並希望使用 Git版本控制系統開始追蹤你的進度。有些人將使用 Git 比作寫給未來自己的情書。幾天、幾周或幾個月後閱讀你的提交信息,你將能回憶起為什麼做出某個決定,或者“回滾”某個更改——前提是你寫了好的“提交信息”。
我們會從一開始就展示如何用現代且安全的方式與 GitHub 合作。這樣你能培養好習慣,伴隨你整個程式人生。
使用 GitHub 時,重要的是遵守安全最佳實務:
| 安全範疇 | 最佳實務 | 重要原因 |
|---------------|---------------|----------------|
| **身份驗證** | 使用 SSH 金鑰或個人存取權杖 | 密碼安全性較差並逐漸淘汰 |
| **雙重認證** | 啟用 GitHub 帳號 2FA | 增加帳號安全防護層 |
| **倉庫安全** | 永遠不提交敏感資訊 | API 金鑰與密碼不該放公開倉庫 |
| **相依管理** | 啟用 Dependabot 更新 | 保持依賴套件安全與最新 |
> ⚠️ **重要安全提醒**:切勿將 API 金鑰、密碼或其他敏感資訊提交到任何倉庫。使用環境變數與 `.gitignore` 檔案來保護敏感資料。
**現代認證設定:**
```bash
# 產生 SSH 金鑰(現代 ed25519 演算法)
ssh-keygen -t ed25519 -C "your_email@example.com"
# 設定 Git 使用 SSH
git remote set-url origin git@github.com:username/repository.git
```
> 💡 **專業提示**SSH 金鑰讓你不必重複輸入密碼,比傳統認證更安全。
---
## 像專業人士般管理你的程式碼
好了,這裡開始真正有趣了!🎉 我們將學習如何像專家一樣追蹤與管理程式碼,坦白說,這是我最喜歡教的主題之一,因為它會改變你的開發方式。
想像你正在寫一個精彩故事想要記錄每個稿本、每個絕妙修訂還有每個「等等這主意太棒了」的瞬間。Git 就是為你的程式碼做到這件事!它就像一個不可思議的時光筆記本,記錄所有細節——每一次敲擊鍵盤、每個修正、每個「哎呀,壞了!」的時刻,你都能立刻回到過去。
說實話——剛開始可能會有點難懂。我當年也想:「為什麼不能像平常一樣直接存檔呢?」但相信我,一旦你「察覺」了 Git 的妙用(一定會的!),你會有「以前怎麼沒用過它?」的靈光一閃。就像一輩子走路,忽然發現自己會飛一樣!
假設你本機有一個資料夾,裡面放著程式碼專案,你想開始用 git版本控制系統追蹤進度。有人說用 git 有點像寫給未來自己的情書。數天、數週、數月後,再回頭看你的 commit 訊息,你能回憶為何改動,或「還原」變更--前提是你有寫好 commit 訊息。
```mermaid
flowchart TD
A[📁 你的專案檔案] --> B{是否為 Git 倉庫?}
B -->|否| C[git init]
B -->|是| D[進行更改]
C --> D
D --> E[git add .]
E --> F["git commit -m '訊息'"]
F --> G[git push]
G --> H[🌟 GitHub 上的代碼!]
H --> I{想要協作嗎?}
I -->|是| J[分叉並克隆]
I -->|否| D
J --> K[建立分支]
K --> L[進行更改]
L --> M[提取請求]
M --> N[🎉 貢獻中!]
style A fill:#fff59d
style H fill:#c8e6c9
style N fill:#ff4081,color:#fff
```
### 任務:建立你的第一個倉庫!
> 🎯 **你的任務(我超級期待!)**:我們要一起建立你的第一個 GitHub 倉庫完成後你會有屬於自己的網路小天地存放程式碼並完成第一次「commit」開發者專用語用聰明方式保存工作成果
>
> 這真的是特別的時刻——你即將正式加入全球開發者社群!我依然記得第一次創建倉庫時的興奮,心想「哇,我真的做到了!」
### 任務:創建倉庫並提交代碼
咱們一步步共赴這場冒險。每個步驟都慢慢來,不用急,保證你絕對懂。記得,每位你認識的程式達人都曾跟你一樣,正準備創建他們的第一個倉庫。多酷啊!
> 查看視頻
> 觀看影片
>
> [![Git 和 GitHub 基礎視頻](https://img.youtube.com/vi/9R31OUPpxU4/0.jpg)](https://www.youtube.com/watch?v=9R31OUPpxU4)
> [![Git and GitHub basics video](https://img.youtube.com/vi/9R31OUPpxU4/0.jpg)](https://www.youtube.com/watch?v=9R31OUPpxU4)
1. **在 GitHub 上創建倉庫**。在 GitHub.com 的倉庫標籤中,或在右上角的導航欄中,找到 **新建倉庫** 按鈕。
1. 為你的倉庫(文件夾)命名。
1. 選擇 **創建倉庫**。
**一起動手做:**
1. **導航到你的工作文件夾**。在終端中,切換到你希望開始追蹤的文件夾(也稱為目錄)。輸入:
1. **在 GitHub 建立你的倉庫**。前往 GitHub.com找那個明亮的綠色 **New** 按鈕(或右上角的 **+** 號),點擊並選擇 **New repository**
以下是操作步驟:
1. 幫你的倉庫取個名字──取一個對你有意義的!
1. 可以加個描述(讓別人知道專案是什麼)
1. 決定是公開(大家都能看到)還是私密(只限你)
1. 建議勾選新增 README 檔案——這是你專案的封面頁
1. 點擊 **Create repository**,慶祝一下-你剛創建了第一個倉庫!🎉
2. **進入你的專案資料夾**。打開終端機(別怕,真的沒那麼可怕!)。我們要指示電腦專案檔案位置。輸入以下指令:
```bash
cd [name of your folder]
```
1. **初始化 Git 倉庫**。在你的項目中輸入:
**我們這樣做是因為:**
- 其實是在告訴電腦「嘿,帶我到專案資料夾」
- 這就像在桌面打開指定資料夾,但用文字指令執行
- 把 `[name of your folder]` 換成你專案資料夾的真實名稱
3. **將資料夾變成 Git 倉庫**。魔法就在此發生!打:
```bash
git init
```
1. **檢查狀態**。要檢查倉庫的狀態,輸入:
**這步驟的意義(很酷吧!):**
- Git 在你的專案中建立一個隱藏的 `.git` 資料夾,你看不到它,但它存在著!
- 你的資料夾現在成為可追蹤所有變更的「倉庫」
- 就像賦予資料夾超能力,能記住一切
4. **查看目前狀態**。看看 Git 對你的專案「怎麼看」:
```bash
git status
```
輸出的內容可能如下所示:
**讀懂 Git 講了什麼:**
你可能會看到像這樣的東西:
```output
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
(use "git restore <file>..." to discard changes in working directory)
modified: file.txt
modified: file2.txt
```
通常,`git status` 命令會告訴你哪些文件已準備好保存到倉庫,或者哪些文件有更改需要持久化。
**別慌!這是什麼意思:**
- 紅色的檔案代表有變更但還沒準備好要儲存
- 綠色的檔案(如果有看到)代表已準備好儲存
- Git 很貼心,告訴你下一步該怎麼做
> 💡 **專業提示**`git status` 是你最好的朋友!任何時候搞不懂狀況就用它。它就像問 Git 「嘿,情況怎樣?」
1. **添加所有文件進行追蹤**
這也稱為暫存文件/將文件添加到暫存區。
5. **準備好要儲存的檔案**(稱為「暫存」):
```bash
git add .
```
`git add` 加上 `.` 參數表示追蹤所有文件和更改。
**我們剛做了什麼:**
- 告訴 Git 「我想把這資料夾裡的所有檔案都加入下一次儲存」
- `.` 就像說「資料夾裡所有的東西」
- 現在檔案都「暫存」了,準備下一步
1. **選擇性添加文件進行追蹤**
**想挑選性加入?** 你也可以只加入特定檔案:
```bash
git add [file or folder name]
```
當你不希望一次提交所有文件時,這可以幫助我們僅添加選定的文件到暫存區。
**為什麼會想這樣做?**
- 有時候你想一併保存相關修改
- 幫助你把工作化整為零
- 更容易理解什麼時候改了什麼
1. **取消暫存所有文件**
**改主意了?** 沒問題!可以這樣取消暫存:
```bash
# 取消暫存所有項目
git reset
# 只取消暫存一個檔案
git reset [file name]
```
此命令幫助我們一次取消暫存所有文件。
別擔心-這不會刪除你的檔案,只是把檔案從「準備儲存」的列表移除
1. **取消暫存特定文件**
```bash
git reset [file or folder name]
```
此命令幫助我們一次僅取消暫存特定文件,這些文件我們不希望包含在下一次提交中。
1. **持久化你的工作**。此時你已將文件添加到所謂的暫存區Git 正在追蹤你的文件。要使更改永久化,你需要提交文件。提交代表倉庫歷史中的保存點。輸入以下命令創建提交:
6. **正式永久保存你的工作**(做出第一個 commit
```bash
git commit -m "first commit"
```
此命令提交所有文件,並添加信息“首次提交”。未來的提交信息應更具描述性,以傳達你所做更改的類型。
**🎉 恭喜你!完成你的第一次 commit**
**剛發生了什麼:**
- Git 在這一刻幫你把所有暫存檔案「拍了一張快照」
- commit 訊息「first commit」說明這次保存的意義
- Git 給予這張快照一個獨特 ID讓你能隨時找到它
- 你正式開始追蹤專案歷史!
> 💡 **未來 commit 訊息**下次提交時訊息要更具描述性不要只寫「updated stuff」試試「Add contact form to homepage」或「Fix navigation menu bug」。未來的你會感謝現在用心的你
1. **將本地 Git 倉庫與 GitHub 連接**。本地 Git 倉庫在你的機器上很好,但最終你可能希望將文件備份到某個地方,並邀請其他人與你一起工作。一個很好的地方就是 GitHub。記住我們已經在 GitHub 上創建了一個倉庫,所以唯一需要做的就是將本地 Git 倉庫與 GitHub 連接。`git remote add` 命令可以完成這項工作。輸入以下命令:
7. **連接本機專案到 GitHub**。目前專案只在你電腦裡,我們要連到 GitHub 倉庫,才能分享給全世界!
> 注意,在輸入命令之前,前往你的 GitHub 倉庫頁面找到倉庫 URL。你將在以下命令中使用它。將 ```https://github.com/username/repository_name.git``` 替換為你的 GitHub URL。
先去你的 GitHub 倉庫頁面,複製 URL 回來,然後打:
```bash
git remote add origin https://github.com/username/repository_name.git
```
(把這個 URL 換成你實際的倉庫網址!)
此命令創建了一個名為“origin”的遠程連接指向你之前創建的 GitHub 倉庫。
**我們做了什麼:**
- 我們已建立你本地專案和你的 GitHub 倉庫之間的連接
- 「origin」只是你 GitHub 倉庫的暱稱——就像把聯絡人加到你的電話裡一樣
- 現在本地的 Git 知道該把你的程式碼傳送到哪裡,當你準備好分享時
1. **將本地文件推送到 GitHub**。到目前為止,你已經在本地倉庫和 GitHub 倉庫之間建立了連接。現在使用以下命令 `git push` 將這些文件推送到 GitHub
💡 **更簡易的方法**:如果你已安裝 GitHub CLI可以用一條指令完成這些動作
```bash
gh repo create my-repo --public --push --source=.
```
> 注意,你的分支名稱可能默認不同於 ```main```。
8. **將你的程式碼傳送到 GitHub**(重要時刻!):
```bash
git push -u origin main
```
此命令將你的“main”分支中的提交推送到 GitHub。在命令中包含 `-u` 設置 `upstream` 分支,建立本地分支與遠程分支之間的鏈接,這樣以後你可以簡單地使用 git push 或 git pull而無需指定分支名稱。Git 將自動使用 upstream 分支,你以後不需要在命令中明確指定分支名稱。
**🚀 就是現在!你要將程式碼上傳到 GitHub 了!**
**發生了什麼:**
- 你的提交從電腦傳到 GitHub
- `-u` 旗標建立永久連接,未來推送更方便
- 「main」是你的主要分支名稱就像主資料夾
- 之後你只要打 `git push` 就能上傳!
💡 **小提示**如果你的分支叫別的名字例如「master」請用那個名稱。可用 `git branch --show-current` 確認。
2. **添加更多更改**。如果你希望繼續進行更改並將其推送到 GitHub你只需使用以下三個命令
9. **你的全新日常編程節奏**(這會讓你上癮!):
從此以後,當你對專案作出改動時,有一個簡單的三步舞:
```bash
git add .
git commit -m "type your commit message here"
git commit -m "describe what you changed"
git push
```
> 提示,你可能還希望採用 `.gitignore` 文件,以防止你不希望追蹤的文件出現在 GitHub 上——例如存儲在同一文件夾中的筆記文件,但不適合放在公共倉庫中。你可以在 [.gitignore 模板](https://github.com/github/gitignore) 中找到 `.gitignore` 文件的模板。
**這會成為你的編程節奏:**
- 作出很棒的程式碼改動 ✨
- 用 `git add` 登記改動「Git請注意這些改動
- 用 `git commit` 並附上描述訊息儲存改動(未來的你會感謝現在的你!)
- 用 `git push` 和世界分享你的改動 🚀
- 不斷重複——認真說,這會像呼吸一樣自然!
我喜歡這個工作流程,因為它就像遊戲裡有多個儲存點。喜歡剛才的改動?就 commit想試些冒險的東西沒問題——如果出錯隨時可以回到最後的提交狀態
> 💡 **小貼士**:你也可能想用 `.gitignore` 檔,避免不想追蹤的檔案出現在 GitHub 上——像存在同資料夾裡但不適合放公開倉庫的筆記檔。能從 [.gitignore templates](https://github.com/github/gitignore) 找到範本,或用 [gitignore.io](https://www.toptal.com/developers/gitignore) 自製。
### 🧠 **第一次倉庫提交:感覺如何?**
**花點時間慶祝並反思:**
- 第一次看到程式碼出現在 GitHub 時是什麼感覺?
- 哪一步最令你困惑,哪一步出乎意料地簡單?
- 你能用自己的話解釋 `git add`、`git commit` 和 `git push` 的差別嗎?
```mermaid
stateDiagram-v2
[*] --> LocalFiles: 建立專案
LocalFiles --> Staged: git add .
Staged --> Committed: git commit
Committed --> GitHub: git push
GitHub --> [*]: 成功!🎉
note right of Staged
檔案準備好儲存
end note
note right of Committed
快照已建立
end note
```
> **記住**:即使是有經驗的開發者,有時也會忘記正確指令。讓這流程變成肌肉記憶需要練習——你做得很棒!
#### 現代 Git 工作流程
考慮採用以下現代規範:
#### 提交信息
- **Conventional Commits**:用標準化的提交訊息格式,如 `feat:`、`fix:`、`docs:` 等。了解更多:[conventionalcommits.org](https://www.conventionalcommits.org/)
- **Atomic commits**:每個 commit 只代表一個邏輯修改
- **Frequent commits**:經常、用描述性訊息提交,避免一次大量、少次提交
一個好的 Git 提交主題行應完成以下句子:
如果應用,此提交將 <你的主題行>
#### Commit 訊息
主題行使用命令式現在時:“更改”而不是“已更改”或“更改中”。
在主題行中使用命令式現在時,在正文(可選)中也使用命令式現在時。正文應包括更改的動機,並與之前的行為形成對比。你是在解釋“為什麼”,而不是“如何”。
一個優秀的 Git commit 主旨句應完成以下句子:
If applied, this commit will <你的主旨句>
✅ 花幾分鐘瀏覽 GitHub。你能找到一個非常好的提交信息嗎你能找到一個非常簡短的提交信息嗎你認為在提交信息中傳達哪些信息最重要和最有用
主旨用命令式、現在時用「change」非「changed」或「changes」。
正文(可選)也用命令式、現在時,應說明為何改動並和之前行為對比。是解釋「為什麼」不是「怎麼做」。
### 任務:協作
✅ 花幾分鐘逛逛 GitHub。能找到非常好的 commit 訊息嗎?或非常簡短的訊息?你覺得 commit 訊息中最重要且有用的資訊是什麼?
將項目放到 GitHub 上的主要原因是使得能夠與其他開發者協作。
## 與他人合作(有趣的部分!)
## 與他人合作完成項目
戴好帽子,因為這正是 GitHub 魔法開始的地方!🪄 你已掌握管理自己程式碼,但現在我們要進入我最愛的部分——與來自世界各地的優秀開發者合作。
> 查看視頻
想像一下:明天醒來時,你發現有人在東京幫你改進了程式碼;有人在柏林修復你卡住的 bug下午時聖保羅的一位開發者加入了你從未想過的功能。這不是科幻——在 GitHub 世界裡,這就是日常!
讓我最興奮的是,你即將學到的協作技能,就是 Google、微軟以及你喜歡的初創公司每天使用的 EXACT 工作流程。你不只是在學一個酷工具——你在學讓全球軟體世界協同運作的祕密語言。
說真的,一旦你體驗到有人合併你的第一個 Pull Request 的快感,就會懂為什麼開發者對開源充滿熱情。就像參與全球最大、最具創意的團隊專案!
> 參考影片
>
> [![Git 和 GitHub 基礎視頻](https://img.youtube.com/vi/bFCM-PC3cu8/0.jpg)](https://www.youtube.com/watch?v=bFCM-PC3cu8)
> [![Git and GitHub basics video](https://img.youtube.com/vi/bFCM-PC3cu8/0.jpg)](https://www.youtube.com/watch?v=bFCM-PC3cu8)
放到 GitHub 的主要原因是為了方便與其他開發者合作。
```mermaid
flowchart LR
A[🔍 尋找專案] --> B[🍴 分叉儲存庫]
B --> C[📥 複製到本地]
C --> D[🌿 建立分支]
D --> E[✏️ 進行更改]
E --> F[💾 提交更改]
F --> G[📤 推送分支]
G --> H[🔄 建立拉取請求]
H --> I{維護者審核}
I -->|✅ 核准| J[🎉 合併!]
I -->|❓ 要求修改| K[📝 更新內容]
K --> F
J --> L[🧹 清理分支]
style A fill:#e3f2fd
style J fill:#e8f5e8
style L fill:#fff3e0
```
在你的倉庫中,前往 `Insights > Community` 查看你的專案如何符合社區建議標準。
想讓你的倉庫看起來專業且讓人感到歡迎嗎?到你的倉庫點擊 `Insights > Community`。這個很棒的功能會告訴你專案在 GitHub 社區認為「良好倉庫實踐」方面的表現。
> 🎯 **讓你的專案閃耀**:一個組織良好且文件齊全的倉庫就像乾淨、招人的店面。它告訴大家你很在乎作品,也讓其他人想投入貢獻!
**以下讓倉庫變優秀:**
| 應加入什麼 | 為何重要 | 帶給你的效果 |
|-------------|-----------|---------------|
| **描述** | 第一印象很重要! | 讓人第一時間知道你的專案做什麼 |
| **README** | 專案的門面 | 就像迎新導覽給新訪客 |
| **貢獻指南** | 表示歡迎協助 | 讓人明白如何協助你 |
| **行為守則** | 創造友善環境 | 讓每個人都感覺被歡迎參與 |
| **授權條款** | 法務清楚 | 讓他人知道如何使用你的程式碼 |
| **安全政策** | 展現負責任 | 表現專業作風 |
> 💡 **專家秘訣**GitHub 提供這些檔案的範本。建立新倉庫時勾選即可自動生成。
**探索現代 GitHub 功能:**
在你的倉庫中,導航到 `Insights > Community`,查看你的項目如何符合推薦的社區標準。
🤖 **自動化與 CI/CD**
- **GitHub Actions** 自動化測試與部署
- **Dependabot** 依賴自動更新
以下是一些可以改善你的 GitHub 倉庫的事項:
- **描述**。你是否為項目添加了描述?
- **README**。你是否添加了 READMEGitHub 提供了撰寫 [README](https://docs.github.com/articles/about-readmes/?WT.mc_id=academic-77807-sagibbon) 的指導。
- **貢獻指南**。你的項目是否有 [貢獻指南](https://docs.github.com/articles/setting-guidelines-for-repository-contributors/?WT.mc_id=academic-77807-sagibbon)
- **行為準則**。是否有 [行為準則](https://docs.github.com/articles/adding-a-code-of-conduct-to-your-project/)
- **許可證**。或許最重要的是,是否有 [許可證](https://docs.github.com/articles/adding-a-license-to-a-repository/)
💬 **社群與專案管理:**
- **GitHub Discussions** 問題之外的社群討論空間
- **GitHub Projects** 看板式專案管理
- **分支保護規則** 強制程式碼品質標準
所有這些資源都將有助於新團隊成員的入門。而這些通常是新貢獻者在查看你的代碼之前會先查看的內容,以了解你的項目是否值得他們投入時間。
✅ README 文件雖然需要花時間準備,但經常被忙碌的維護者忽視。你能找到一個特別詳細的 README 示例嗎?注意:有一些[工具可以幫助創建好的 README](https://www.makeareadme.com/),你可能會想嘗試一下。
這些資源能幫助新團隊成員加入時更快上手。通常新貢獻者在看程式碼前會先看這些,判斷專案是否值得他們花時間
### 任務:合併代碼
✅ README 檔雖然準備起來花時間,但常被忙碌的維護者忽略。能找到特別有描述性的範例嗎?備註:有一些[工具幫助創建優質 README](https://www.makeareadme.com/)值得嘗試。
貢獻文檔幫助人們為項目做出貢獻。它解釋了你希望的貢獻類型以及流程如何運作。貢獻者需要完成一系列步驟才能為你的 GitHub 倉庫做出貢獻:
### 任務:合併一些程式碼
1. **Fork 你的倉庫**。你可能希望人們 _fork_ 你的項目。Fork 意味著在他們的 GitHub 個人資料中創建你的倉庫的副本。
1. **克隆**。之後,他們會將項目克隆到本地機器。
1. **創建分支**。你會希望他們為自己的工作創建一個 _分支_
1. **專注於一個區域的更改**。要求貢獻者一次專注於一件事——這樣你合併他們工作的可能性更高。想像一下,他們修復了一個 bug添加了一個新功能並更新了幾個測試——如果你只希望或只能實施其中的 2 個或 1 個更改呢?
貢獻文件幫助他人參與專案。說明你找什麼樣的貢獻及流程如何。貢獻者需經過一系列步驟,才能在 GitHub 貢獻給你的倉庫:
✅ 想像一個分支在編寫和交付良好代碼中特別重要的情況。你能想到哪些使用場景?
> 注意,成為你希望看到的改變,為自己的工作創建分支。你所做的任何提交都將在你當前“檢出”的分支上進行。使用 `git status` 查看當前分支。
1. **Fork 你的倉庫** 大概你會希望大家先對你的專案 _fork_。Fork 是建立你倉庫的複製品在他們的 GitHub 帳號上。
1. **Clone**。接著他們會 clone 專案到本地機器。
1. **建立分支**。你會想請他們為他們的工作建立一個 _branch_
1. **專注於一件事**。請貢獻者一次專注於一個改動內容——這樣你合併他們工作的機率比較高。想像他們修 bug、新增功能、更新多項測試——如果你只能接受其中兩項或一項怎麼辦
讓我們來看看貢獻者的工作流程。假設貢獻者已經 _fork__克隆_ 了倉庫,因此他們在本地機器上有一個準備好工作的 Git 倉庫:
✅ 想像在寫和交付優質程式碼時,分支扮演關鍵角色。你能想到哪些應用場景?
1. **創建分支**。使用命令 `git branch` 創建一個分支,該分支將包含他們打算貢獻的更改:
> 註:成為你想看到的改變,也建立分支管理自己的工作吧。你所做的 commit 預設是在你當前「checkout」的分支上。用 `git status` 查看你在哪個分支。
讓我們來看一下貢獻者的工作流程。假設貢獻者已經 _fork__clone_ 好 repo且本地已有 Git 環境:
1. **建立分支**。用命令 `git branch` 建立一個準備提交改動的分支:
```bash
git branch [branch-name]
```
1. **切換到工作分支**。使用 `git switch` 切換到指定分支並更新工作目錄:
> 💡 **現代做法**:你也可以用一條命令同時建立並切換分支:
```bash
git switch -c [branch-name]
```
1. **切換到工作分支**。用 `git switch` 切換到剛建立的分支並更新工作目錄:
```bash
git switch [branch-name]
```
1. **進行工作**。此時你可以添加更改。不要忘記使用以下命令告訴 Git
> 💡 **現代提示**`git switch` 是改用於切分支的更清晰替代 `git checkout`,對初學者更安全。
1. **開始工作**。此時你要新增改動。別忘告訴 Git命令如下
```bash
git add .
git commit -m "my changes"
```
確保你為提交取一個好的名字,這對你自己以及你幫助的倉庫維護者都很重要。
> ⚠️ **提交訊息品質**:務必為 commit 取個好名字,無論是為自己還是你協助的 repo 維護者。具體說明你改了什麼!
1. **將你的工作與 `main` 分支合併**。某個時候你完成了工作,並希望將你的工作與 `main` 分支的工作合併。`main` 分支可能在此期間發生了更改,因此請確保首先使用以下命令更新到最新版本:
1. **合併你的工作到 `main` 分支**。完成工作後,想結合你的工作和 `main` 分支的內容。`main` 可能已有更新,先用以下命令拉最新版本:
```bash
git switch main
git pull
```
此時你需要確保任何 _衝突_Git 無法輕易合併更改的情況)發生在你的工作分支中。因此,運行以下命令
現在你要確保任何 _衝突_,即 Git 不知如何 _合併_ 的情況,都處理在你的工作分支。執行
```bash
git switch [branch_name]
git merge main
```
`git merge main` 命令將把 `main` 中的所有更改帶入你的分支。希望你可以直接繼續。如果不行VS Code 會告訴你 Git 在哪些地方“困惑”,你只需修改受影響的文件以確定哪個內容最準確
`git merge main` 會把 `main` 最新變更帶入你的分支。希望能直接繼續。如果不行VS Code 會告訴你 Git 混淆在哪裡,並讓你修改檔案指定正確版本
要切換到不同的分支,使用現代的 `git switch` 命令
💡 **現代替代法**:考慮用 `git rebase` 取得更乾淨的歷史
```bash
git switch [branch_name]
git rebase main
```
這會把你的提交依序放到最新的 main 上,形成線性歷史。
1. **將你的工作推送到 GitHub**。將你的工作推送到 GitHub 意味著兩件事:將你的分支推送到你的倉庫,然後打開一個 PRPull Request
1. **推送你的工作到 GitHub**。推送工作到 GitHub 具包含兩件事。先把分支推送到你的遠端 fork然後開 Pull Request
```bash
git push --set-upstream origin [branch-name]
```
上述命令會在你的 fork 倉庫中創建分支。
1. **開啟 PR**。接下來,你需要開啟一個 PRPull Request。你可以通過進入 GitHub 上的 fork 倉庫來完成這一步。在 GitHub 上,你會看到一個提示,詢問你是否要創建一個新的 PR點擊它後你會進入一個介面在那裡你可以修改提交訊息的標題並添加更合適的描述。現在你 fork 的倉庫的維護者會看到這個 PR_希望_ 他們會欣賞並 _合併_ 你的 PR。恭喜你現在你是一名貢獻者了太棒了:)
上述指令會在你的 forked repo 建立分支。
### 🤝 **協作技能檢測:準備好與別人合作了嗎?**
**來看看你對協作的感覺:**
- 對 fork 與 pull request 的概念是否明白?
- 有關使用分支工作,你還想練習什麼?
- 對貢獻別人專案感到多大信心?
```mermaid
mindmap
root((Git 協作))
Branching
Feature branches
Bug fix branches
Experimental work
Pull Requests
Code review
Discussion
Testing
Best Practices
Clear commit messages
Small focused changes
Good documentation
```
> **信心加油站**:你敬佩的每位開發者,在提出第一個 PR 時都曾緊張。GitHub 社區對新手非常友好!
1. **開 PR**。接著要開 pull request。到你 fork 的 repo 頁面上GitHub 會提示你是否建立新 PR點擊即可進入介面修改 commit 標題和敘述。原專案維護者會看到,並 _希望_ 會合併你的 PR。從此你成為一名貢獻者耶 :)
💡 **現代提示**:也可使用 GitHub CLI 建立 PR
```bash
gh pr create --title "Your PR title" --body "Description of changes"
```
1. **清理**。在成功合併 PR 後_清理_ 是一個良好的習慣。你需要清理本地分支以及你推送到 GitHub 的分支。首先,使用以下命令在本地刪除分支:
🔧 **PR 最佳實踐**
- 用關鍵字連結相關 issue如 "Fixes #123"
- UI 改動補充截圖
- 指定特定審查者
- 進行中可用草稿 PR
- 發出審查請求前確保所有 CI 通過
1. **清理**。成功合併 PR 後,進行 _清理_ 是良好做法。你需要清理本地分支和推送到 GitHub 的分支。首先,使用以下命令在本地刪除它:
```bash
git branch -d [branch-name]
```
接著,進入 GitHub 上的 fork 倉庫頁面,刪除你剛剛推送的遠端分支。
`Pull request`(拉取請求)這個詞聽起來有點奇怪,因為實際上你是想將你的更改推送到項目中。但維護者(項目擁有者)或核心團隊需要在合併到項目的 "main" 分支之前審核你的更改,因此實際上你是在向維護者請求一個更改的決定。
接著請到該 fork 的 GitHub 頁面,刪除你剛剛推送過去的遠端分支。
Pull request 是一個用於比較和討論分支中引入的差異的地方,這裡可以進行審核、評論、集成測試等操作。一個好的 pull request 大致遵循與提交訊息相同的規則。例如,當你的工作解決了一個問題時,你可以在 issue tracker 中引用該問題。這可以通過使用 `#` 後跟問題號來完成,例如 `#97`
`Pull request`拉取請求聽起來怪怪的因為你真正想做的是把變更推送到專案裡。但維護者專案擁有者或核心團隊需要先審核你的變更才會將它合併進專案的「main」分支所以你其實是在請求維護者做變更決定
🤞希望所有檢查都通過,並且項目擁有者合併你的更改到項目中🤞
拉取請求是用來比較和討論分支所引入差異的地方,可以包含審核、評論、整合測試等等。一個好的拉取請求大致遵循和提交說明相同的規則。如果你的工作是修復某個問題,可以加上 issue 的參考,格式是 `#` 後面接問題號碼。例如 `#97`
更新你當前本地工作分支,使其包含 GitHub 上對應遠端分支的所有新提交:
🤞希望所有檢查通過,且專案擁有者能將你的變更合併進專案🤞
用以下指令更新你當前的本地工作分支,將 GitHub 上對應遠端分支的所有新提交拉下來:
`git pull`
## 如何為開源項目做貢獻
## 貢獻開源專案(你改變世界的機會!)
準備好迎接讓你驚嘆的新世界了嗎?🤯 來談談如何參與開源貢獻吧 —— 光是想著能和你分享這些內容我就起雞皮疙瘩了!
這是你成為真正偉大事物一部分的機會。想像一下,改善數百萬開發者每天使用的工具,或者修正你朋友們都愛用的應用程式的錯誤。這不只是一場夢想 —— 這就是開源貢獻的精髓!
讓我每次想到都感到顫慄的是:你所學的每個工具 —— 你的程式碼編輯器、我們將探索的框架,甚至你正在閱讀本文的瀏覽器 —— 都是由像你一樣的人首次貢獻開始。那位打造你最愛 VS Code 擴充功能的優秀開發者?他們曾經也是充滿忐忑地按下「建立拉取請求」按鈕的初學者,就像你現在一樣。
最美好的部分是開源社群就像網路上最大的群體擁抱。大多數專案都積極尋找新手並且有標示為「good first issue」的議題就是專門為你這樣的初學者準備維護者看到新貢獻者時會非常興奮因為他們記得自己也從第一次踏出第一步開始。
```mermaid
flowchart TD
A[🔍 探索 GitHub] --> B[🏷️ 尋找 "good first issue"]
B --> C[📖 閱讀貢獻指南]
C --> D[🍴 分叉倉庫]
D --> E[💻 設置本地環境]
E --> F[🌿 建立功能分支]
F --> G[✨ 做出你的貢獻]
G --> H[🧪 測試你的更改]
H --> I[📝 撰寫清晰提交訊息]
I --> J[📤 推送並建立 PR]
J --> K[💬 參與反饋交流]
K --> L[🎉 合併成功!你成為了貢獻者!]
L --> M[🌟 尋找下一個議題]
style A fill:#e1f5fe
style L fill:#c8e6c9
style M fill:#fff59d
```
你不只是學寫程式,更是在準備加入一個每天早上醒來就思考「我們怎麼讓數位世界變得更好一點?」的全球創作者大家庭。歡迎加入!🌟
首先,讓我們在 GitHub 上找到一個你感興趣並希望對其進行更改的倉庫(或 **repo**)。你需要將其內容複製到你的電腦上。
首先,我們來找一個你感興趣並想貢獻變更的 GitHub 儲存庫repo。你需要將它的內容複製到你的電腦
✅ 尋找 "適合初學者" 的倉庫的一個好方法是 [通過標籤 'good-first-issue' 進行搜索](https://github.blog/2020-01-22-browse-good-first-issues-to-start-contributing-to-open-source/)。
找到「新手友好」的 repo 一個好方法是[使用 'good-first-issue' 標籤搜尋](https://github.blog/2020-01-22-browse-good-first-issues-to-start-contributing-to-open-source/)。
![將倉庫複製到本地](../../../../translated_images/clone_repo.5085c48d666ead57664f050d806e325d7f883be6838c821e08bc823ab7c66665.hk.png)
![Copy a repo locally](../../../../translated_images/clone_repo.5085c48d666ead57.hk.png)
有幾種方法可以複製代碼。一種方法是使用 HTTPS、SSH 或 GitHub CLI命令行界面來 "克隆" 倉庫的內容。
複製代碼有好幾種方式。最常見的是使用 HTTPS、SSH 或 GitHub CLI命令列工具「克隆」該儲存庫內容。
打開終端,像這樣克隆倉庫:
`git clone https://github.com/ProjectURL`
打開你的終端機並這樣克隆儲存庫:
```bash
# 使用 HTTPS
git clone https://github.com/ProjectURL
要開始處理該項目,切換到正確的文件夾:
# 使用 SSH需要設定 SSH 金鑰)
git clone git@github.com:username/repository.git
# 使用 GitHub CLI
gh repo clone username/repository
```
要在專案內工作,切換到正確資料夾:
`cd ProjectURL`
你還可以使用 [Codespaces](https://github.com/features/codespaces)GitHub 的嵌入式代碼編輯器/雲開發環境)或 [GitHub Desktop](https://desktop.github.com/) 打開整個項目。
你也可以開啟整個專案:
- **[GitHub Codespaces](https://github.com/features/codespaces)** - GitHub 的雲端開發環境,可在瀏覽器中使用 VS Code
- **[GitHub Desktop](https://desktop.github.com/)** - 用於 Git 操作的圖形使用者介面應用程式
- **[GitHub.dev](https://github.dev)** - 任何 GitHub repo 按下 `.` 鍵即可在瀏覽器開啟 VS Code
- 搭配 GitHub 拉取請求擴充功能的 VS Code
最後,你可以下載壓縮檔形式的代碼。
最後,你也可以下載一個壓縮文件夾來獲取代碼。
### 關於 GitHub 的更多有趣事
### 關於 GitHub 的一些有趣事實
你可以對任何公開 repo 加星標、訂閱觀察和/或「fork」它們。加星的 repo 可以在右上角的下拉選單找到,這就像書籤,但用於程式碼。
你可以為 GitHub 上的任何公共倉庫加星標、關注或 "fork"。你可以在右上角的下拉菜單中找到你加星標的倉庫。這就像為代碼添加書籤一樣。
專案有譯題追蹤器,通常是在 GitHub 的「Issues」標籤頁除非另有說明用來討論專案相關問題。拉取請求標籤是用來討論和審查正在進行的改動
項目通常有一個問題追蹤器,大多數情況下在 GitHub 的 "Issues" 標籤中,除非另有說明。在這裡,人們討論與項目相關的問題。而 Pull Requests 標籤則是人們討論和審核正在進行的更改的地方。
有些專案還會在論壇、郵件列表或聊天頻道(像 Slack、Discord 或 IRC進行討論
項目可能還會有論壇、郵件列表或聊天頻道(如 Slack、Discord 或 IRC進行討論。
🔧 **現代 GitHub 功能**
- **GitHub Discussions** - 內建的社群討論區
- **GitHub Sponsors** - 財務支持維護者
- **Security tab** - 漏洞報告與安全公告
- **Actions tab** - 查看自動化工作流程及 CI/CD 管線
- **Insights tab** - 關於貢獻者、提交和專案健康度的分析
- **Projects tab** - GitHub 內建的專案管理工具
✅ 瀏覽一下你的新 GitHub 倉庫,嘗試一些操作,比如編輯設置、為倉庫添加信息,或者創建一個項目(比如看板)。你可以做很多事情!
四處逛逛你的新 GitHub repo試試修改設定、添加資訊、建立專案如看板、設定 GitHub Actions 來自動化。你能做的事非常多
---
## 🚀 挑戰
與朋友合作處理彼此的代碼。共同創建一個項目fork 代碼,創建分支,並合併更改。
好了,現在是測試你閃亮新 GitHub 超能力的時刻了!🚀 這項挑戰會讓一切以最令人滿足的方式連結起來:
找一個朋友(或那位一直問你在做什麼「電腦東西」的家人)一起展開協同編程冒險吧!這就是魔法所在 —— 建立一個專案,讓他們 fork建立一些分支像專業人士一樣合併變更。
說實話 —— 你們絕對會笑翻(尤其是當你們試圖同時更改同一行),可能還會被搞得抓頭困惑,但你一定會體驗到那些令人驚豔的「啊哈!」時刻,讓所有學習都變得值得。此外,與人分享第一次成功合併有著特別的意義 —— 它像是在慶祝你已走多遠!
## 課後測驗
[課後測驗](https://ff-quizzes.netlify.app/web/en/)
還沒有編程夥伴完全沒問題GitHub 社群熱情友善他們都記得當初自己也是新手。找標記「good first issue」的儲存庫 —— 這基本上是在說「嘿,新手,來和我們一起學吧!」多棒啊!
## 回顧與自學
## 課後小測
[Post-lecture quiz](https://ff-quizzes.netlify.app/web/en/)
閱讀更多關於 [如何為開源軟件做貢獻](https://opensource.guide/how-to-contribute/#how-to-submit-a-contribution) 的內容。
## 複習並持續學習
[Git 速查表](https://training.github.com/downloads/github-git-cheat-sheet/)
呼!🎉 看你多棒 —— 你剛剛像冠軍一樣征服了 GitHub 基礎!如果你現在覺得腦子有點爆炸,那是很正常,說實話這也是好事。你剛剛學到的工具,是我當初花了好幾週才習慣的
多練習多實踐。GitHub 提供了很棒的學習路徑:[skills.github.com](https://skills.github.com)
Git 和 GitHub 非常強大(真的很強),我認識的每位開發者 —— 包括現在看起來像魔法師的那些 —— 都必須練習和摸索一段時間才有突破。你能順利完成這堂課,代表你已經踏上掌握開發者工具箱中最重要工具的旅程。
- [GitHub 的第一周](https://skills.github.com/#first-week-on-github)
以下是一些超棒的資源,幫助你練習並變得更棒:
你還可以找到更多進階課程。
- [貢獻開源軟體指南](https://opensource.guide/how-to-contribute/#how-to-submit-a-contribution) —— 你改變世界的路線圖
- [Git 速查表](https://training.github.com/downloads/github-git-cheat-sheet/) —— 方便快速查詢!
別忘了:練習促成進步,而非完美!你使用 Git 和 GitHub 越多就越自然。GitHub 創建了很多有趣的互動課程,讓你在安全環境中練習:
- [GitHub 入門](https://github.com/skills/introduction-to-github)
- [使用 Markdown 交流](https://github.com/skills/communicate-using-markdown)
- [GitHub Pages](https://github.com/skills/github-pages)
- [管理合併衝突](https://github.com/skills/resolve-merge-conflicts)
**想更冒險一點?試試這些現代工具:**
- [GitHub CLI 文件](https://cli.github.com/manual/) —— 讓你感覺像指令列大師
- [GitHub Codespaces 文件](https://docs.github.com/en/codespaces) —— 雲端程式編輯體驗!
- [GitHub Actions 文件](https://docs.github.com/en/actions) —— 自動化一切
- [Git 最佳實踐](https://www.atlassian.com/git/tutorials/comparing-workflows) —— 升級你的工作流程
## GitHub Copilot Agent 挑戰 🚀
使用 Agent 模式完成以下挑戰:
**說明:** 建立一個合作式網頁開發專案,展示你在本課程學到的完整 GitHub 工作流程。這個挑戰幫助你練習儲存庫建立、協作特性,以及現代 Git 工作流程,在實際場景中操練。
**提示:** 建立一個新的公開 GitHub 儲存庫名為「Web Development Resources網頁開發資源」。儲存庫應包含結構良好的 README.md列出有用的網頁開發工具和資源依照不同類別HTML、CSS、JavaScript 等)組織。按社群標準設定儲存庫,包括授權、貢獻指南和行為準則。至少建立兩個功能分支:一個新增 CSS 資源、另一個新增 JavaScript 資源。分別在各分支做有描述的提交,然後發出拉取請求合併回 main。啟用 GitHub 功能,例如 Issues、Discussions並建立基本的 GitHub Actions 工作流程做自動化檢查。
## 作業
完成 [GitHub 的第一周課程](https://skills.github.com/#first-week-on-github)
你的任務(如果你願意接受):完成 GitHub Skills 的 [GitHub 入門](https://github.com/skills/introduction-to-github) 課程。這個互動課程讓你在安全且有指導的環境中練習所學。完成後還能獲得酷炫徽章!🏅
**想挑戰更多?**
- 為 GitHub 帳號設定 SSH 認證(不用再輸入密碼!)
- 嘗試使用 GitHub CLI 執行日常 Git 操作
- 建立一個有 GitHub Actions 工作流程的儲存庫
- 嘗試用雲端編輯器 GitHub Codespaces 開啟本儲存庫
---
## 🚀 你的 GitHub 精通時間表
### ⚡ **接下來 5 分鐘你可以做的事**
- [ ] 給這個儲存庫和另外三個你感興趣的專案加星標
- [ ] 為你的 GitHub 帳戶設定雙重身份驗證
- [ ] 為你第一個儲存庫創建簡單的 README
- [ ] 追蹤 5 位令你受啟發的開發者
### 🎯 **接下來一小時你可以完成的目標**
- [ ] 完成課後小測並反思你的 GitHub 旅程
- [ ] 設定 SSH 金鑰,實現免密碼登入 GitHub
- [ ] 做出你的第一個有意義的提交並寫好提交訊息
- [ ] 探索 GitHub「Explore」分頁發掘熱門專案
- [ ] 練習 fork 一個儲存庫並做小改動
### 📅 **你的一週 GitHub 冒險**
- [ ] 完成 GitHub Skills 課程GitHub 入門、Markdown
- [ ] 對開源專案提出第一個拉取請求
- [ ] 設置 GitHub Pages 網站展示你的作品
- [ ] 參與你感興趣專案的 GitHub Discussions
- [ ] 建立具社群標準README、授權等的儲存庫
- [ ] 嘗試使用 GitHub Codespaces 進行雲端開發
### 🌟 **你的一月蛻變**
- [ ] 對 3 個不同開源項目做出貢獻
- [ ] 輔導一位 GitHub 新手(回饋社群!)
- [ ] 設定 GitHub Actions 自動化工作流程
- [ ] 建立展示 GitHub 貢獻的作品集
- [ ] 參加 Hacktoberfest 或類似社群活動
- [ ] 成為自己開源專案的維護者,讓他人貢獻
### 🎓 **最終 GitHub 精通回顧**
**慶祝你走過的路:**
- 你最喜歡使用 GitHub 的哪個部分?
- 哪個協作功能最讓你興奮?
- 你現在對參與開源貢獻有多大信心?
- 你想要貢獻的第一個專案是什麼?
```mermaid
journey
title 你的 GitHub 信心之旅
section 今日
緊張: 3: 你
好奇: 4: 你
興奮: 5: 你
section 本週
練習中: 4: 你
貢獻中: 5: 你
聯繫中: 5: 你
section 下個月
合作中: 5: 你
領導中: 5: 你
啟發他人: 5: 你
```
> 🌍 **歡迎加入全球開發者社群!** 你現在擁有和全球數百萬開發者合作的工具。你的第一次貢獻看似微小,但別忘了 —— 每個重大開源專案都是從某個人首次提交開始的。問題不是你是否能產生影響,而是什麼令人驚嘆的專案將率先受益於你獨特的視角!🚀
記住:每位專家都曾是新手。你可以的!💪
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。
本文件由 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們雖力求準確,但請注意自動翻譯可能存在錯誤或不準確之處。原始文件的母語版本應視為權威來源。對於重要資訊,建議採用專業人工翻譯。因使用本翻譯所產生之任何誤解或誤讀,我們概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,27 +1,261 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "a258597a194e77d4fd469b3cd976b29e",
"translation_date": "2025-08-23T23:26:38+00:00",
"original_hash": "e6d0f456dfc22afb41bbdefeb5ec179d",
"translation_date": "2026-01-06T13:10:46+00:00",
"source_file": "1-getting-started-lessons/3-accessibility/assignment.md",
"language_code": "hk"
}
-->
# 分析一個無法訪問的網站
# 全面網站無障礙測試報告
## 指引
找出一個你認為無法訪問的網站,並制定一個行動計劃來改善其可訪問性。
你的第一個任務是識別這個網站,詳細說明你認為它無法訪問的原因(不使用分析工具),然後提交給 Lighthouse 進行分析。將分析結果保存為 PDF並列出一個包含至少十點的詳細計劃說明如何改善該網站。
在此作業中,你將運用所學原則與技術,對一個真實網站進行專業級的無障礙測試。這個實作經驗將加深你對無障礙障礙及解決方案的理解。
## 測試網站可訪問性的表格
選擇一個看似存在無障礙問題的網站──這比分析已完美無瑕的網站更有學習價值。合適的對象包括較舊的網站、複雜的網頁應用程式或包含豐富多媒體內容的網站。
| 標準 | 優秀 | 合格 | 需要改善 |
|----------|-----------|----------|----------------|
| | 缺少 <10% | 20% | 50% |
### 階段一:策略性人工評估
----
學生報告包括描述網站無法訪問的段落、Lighthouse 報告的 PDF 截圖、一份包含十點改善建議的清單,以及詳細說明如何改善的內容
在使用自動化工具之前,先進行全面的人工評估。此以使用者為中心的方法常能揭露工具遺漏的問題,並幫助你理解真實使用者體驗。
**🔍 重要評估標準:**
**導覽與結構:**
- 能否僅使用鍵盤Tab、Shift+Tab、Enter、空格、方向鍵瀏覽整個網站
- 所有互動元素是否有清晰焦點指示?
- 標題結構H1-H6是否形成邏輯的內容大綱
- 是否有跳過連結可迅速跳轉至主要內容?
**視覺無障礙:**
- 全站色彩對比是否足夠(普通文字最低 4.5:1
- 網站是否僅用顏色傳達重要資訊?
- 所有圖片是否有適當替代文字?
- 放大至 200% 時版面是否仍維持功能性?
**內容與溝通:**
- 是否有「點此」或含糊的連結文字?
- 不依賴視覺提示是否能理解內容與功能?
- 表格欄位是否有適當標籤和分組?
- 錯誤訊息是否清楚且有幫助?
**互動元素:**
- 所有按鈕和表格控制項是否能只用鍵盤操作?
- 動態內容變化是否有向螢幕閱讀器宣告?
- 模態對話框和複雜元件是否遵循正確無障礙模式?
📝 **紀錄你的發現**,並附具體範例、截圖與頁面 URL。記下問題與做得好的地方。
### 階段二:全面自動化測試
接著使用業界標準的無障礙測試工具,驗證並擴充你的人工評估結果。不同工具有不同強項,結合多種工具可以覆蓋更全面。
**🛠️ 必要測試工具:**
1. **Lighthouse 無障礙審查**Chrome/Edge 開發者工具內建)
- 多頁面測試
- 聚焦特定指標和改進建議
- 紀錄無障礙分數及違規項目
2. **axe DevTools**(瀏覽器擴充套件,業界標準)
- 比 Lighthouse 更細緻的問題偵測
- 提供具體修正程式碼示例
- 依 WCAG 2.1 標準測試
3. **WAVE 無障礙評估工具**(瀏覽器擴充)
- 以視覺方式展示無障礙特性
- 標示錯誤與正面特點
- 有助於了解頁面結構
4. **色彩對比分析工具**
- WebAIM Contrast Checker 用於特定色彩組合
- 瀏覽器擴充可做全頁分析
- 依 WCAG AA 與 AAA 標準測試
**🎧 實際輔助技術測試:**
- **螢幕閱讀器測試**:使用 NVDAWindows、VoiceOverMac、TalkBackAndroid
- **僅鍵盤導覽**:拔掉滑鼠,完全鍵盤操作
- **縮放測試**200% 及 400% 縮放層級下測試功能
- **語音控制測試**:若有,嘗試語音導覽工具
**📊 系統化結果紀錄**,建立主控試算表包含:
- 問題描述與位置
- 嚴重程度(關鍵/高/中/低)
- 違反的 WCAG 成功準則
- 偵測工具
- 截圖與證據
### 階段三:詳細發現文件
撰寫專業無障礙測試報告,展現你對技術問題及其對人的影響的理解。
**📋 必需報告章節:**
1. **執行摘要**1 頁)
- 網站 URL 及簡介
- 整體無障礙成熟度
- 三大關鍵問題
- 對障礙者影響預估
2. **方法論**(½ 頁)
- 測試方法與工具
- 評估頁面與裝置/瀏覽器組合
- 依據標準WCAG 2.1 AA
3. **詳細發現**2-3 頁)
- 依 WCAG 原則分類問題(可感知、可操作、可理解、穩健)
- 附截圖及具體範例
- 記錄正向無障礙特性
- 與自動化工具結果交叉參照
4. **用戶影響評估**1 頁)
- 發現問題對不同障礙者影響
- 描述實際使用場景
- 商業影響法律風險、SEO、用戶擴展
**📸 證據蒐集:**
- 無障礙違規截圖
- 問題操作流程螢幕錄影
- 工具報告PDF 存檔)
- 展示問題的程式碼示例
### 階段四:專業修正方案
擬訂策略性且有優先順序的無障礙修正計畫,展示你具備作為專業網頁開發者處理真實商務限制的能力。
**🎯 提出具體改善建議(至少 10 項問題):**
**針對每項問題提供:**
- **問題描述**:清楚說明問題及影響
- **WCAG 參考**違反的具體成功準則例如「2.4.4 連結目的(上下文)- A 級」)
- **用戶影響**:對不同障礙者的影響
- **解決方案**:具體程式碼修改、設計調整或流程改善
- **優先級**:關鍵(阻礙基本使用)/高(重大障礙)/中(使用度問題)/低(優化)
- **實施難度**:時間/複雜度評估(快速完成/中等努力/重大重構)
- **測試驗證**:如何驗證修復有效
**改善範例條目:**
```
Issue: Generic "Read more" link text appears 8 times on homepage
WCAG Reference: 2.4.4 Link Purpose (In Context) - Level A
User Impact: Screen reader users cannot distinguish between links when viewed in link list
Solution: Replace with descriptive text like "Read more about sustainability initiatives"
Priority: High (major navigation barrier)
Effort: Low (30 minutes to update content)
Testing: Generate link list with screen reader - each link should be meaningful standalone
```
**📈 策略實施階段:**
- **階段 10-2 週)**:修復阻礙基本功能的關鍵問題
- **階段 21-2 個月)**:高優先改善,提升使用體驗
- **階段 33-6 個月)**:中優先優化與流程改進
- **階段 4持續進行**:持續監控與改進
## 評分標準
你的無障礙測試將以技術準確度與專業呈現度評分:
| 項目 | 優秀 (90-100%) | 良好 (80-89%) | 滿意 (70-79%) | 需要改進 (<70%) |
|----------|-------------------|---------------|---------------------|------------------------|
| **人工測試深度** | 全面涵蓋所有 POUR 原則,細節觀察與真實使用者場景完整 | 大部分無障礙區域覆蓋,明確發現並含部分用戶影響分析 | 基本關鍵區域評估,觀察適中 | 測試有限,觀察膚淺且用戶影響不足 |
| **工具使用與分析** | 有效運用全部必需工具,交叉參照、清晰證據、並分析限制 | 多數工具使用良好,含文檔與交叉參考,證據適當 | 必需工具使用,基本文檔與證據 | 工具運用不足、文檔不佳或無證據 |
| **問題識別與分類** | 識別超過 15 個問題,依嚴重度正確分類,展現深厚理解 | 識別 10-14 問題,多數準則涵蓋且分類良好 | 識別 7-9 問題,基本 WCAG 涵蓋與分類 | 識別少於 7 問題,範圍有限或分類不良 |
| **解決方案品質與可行性** | 超過 10 項具體可行方案,準確 WCAG 引用,實施評估與驗證方法完善 | 8-9 項較完善方案,引用大致準確,實施詳情良好 | 6-7 項基本方案,細節一般,實施方法合理 | 少於 6 項方案或細節不足,實施不切實際 |
| **專業溝通** | 報告組織優良、文字清楚、含摘要,專業用語適當,符合作業標準 | 組織良好、文筆尚佳,含大部分必要章節、更適用口吻 | 組織可接受、文筆合格,含基本必需章節 | 組織不佳、文字不清或缺少關鍵章節 |
| **實務應用** | 展現商業影響、法律考量、用戶多元及實際挑戰理解 | 具有實務應用良好理解及商業脈絡 | 基本實務應用認識 | 實務應用連結有限 |
## 進階挑戰選項
**🚀 針對想挑戰更多的學生:**
- **比較分析**:針對 2-3 個競爭網站做無障礙成熟度比較
- **行動無障礙聚焦**:使用 Android TalkBack 或 iOS VoiceOver 深入探討行動特定無障礙問題
- **國際視角**研究並運用不同國家之無障礙標準EN 301 549、Section 508、ADA
- **無障礙聲明審核**:評估網站現有無障礙聲明(若有)與你的檢測結果相符度
## 提交項目
提交一份專業全面的無障礙測試報告,展現完整分析及實務實施規劃:
**📄 最終報告需求:**
1. **執行摘要**1 頁)
- 網站概覽與無障礙成熟度評估
- 主要發現摘要及商業影響
- 推薦優先行動
2. **方法論與範圍**1 頁)
- 測試方法、使用工具與評估標準
- 評測頁面/區段與限制說明
- 遵循標準框架WCAG 2.1 AA
3. **詳細發現報告**3-4 頁)
- 人工測試觀察與用戶場景
- 自動工具結果與交叉參考
- 按 WCAG 原則分組問題及證據
- 發現的正面無障礙特質
4. **策略修正計畫**3-4 頁)
- 優先改善建議(至少 10 項)
- 實施時間表與努力評估
- 成功度量與驗證方法
- 長期無障礙維護策略
5. **輔助證據**(附錄)
- 無障礙違規與測試工具截圖
- 展示問題與解決方案的程式碼範例
- 工具報告與審查摘要
- 螢幕閱讀器測試筆記或錄音
**📊 格式需求:**
- **文件格式**PDF專業呈現
- **字數**2,500-3,500 字(不含附錄與截圖)
- **視覺元素**:全篇包含截圖、圖表與範例
- **引用**:適當引述 WCAG 指南與無障礙資源
**💡 卓越提點:**
- 使用專業報告格式,標題與樣式一致
- 含目錄方便導覽
- 技術準確與商業語言平衡
- 展現技術實施與用戶影響雙重理解
## 學習成果
完成本全面無障礙測試後,你將具備重要專業技能:
**🎯 技術能力:**
- **無障礙測試精通**:熟練業界標準人工與自動測試方法
- **WCAG 應用**:實務應用網頁內容無障礙指引於真實案例
- **輔助技術理解**:實戰螢幕閱讀器與鍵盤導航體驗
- **問題-解決映射**:識別障礙並制定具體可行的改善策略
**💼 專業技能:**
- **技術溝通**:撰寫專業無障礙報告,面向多元利害關係人
- **策略規劃**:基於用戶影響和實施可行性,優先排序無障礙改善
- **品質保證**:理解作為開發流程一環的無障礙測試
- **風險評估**:認識法律、商業與倫理面向的無障礙合規
**🌍 包容性設計心態:**
- **用戶同理心**:深入理解多元用戶需求與輔助技術互動
- **普及設計原則**:明白無障礙設計惠及所有用戶,不僅限於殘障者
- **持續改進**:建立無障礙持續評估與優化架構
- **倡導能力**:未來專案與團隊推廣無障礙最佳實踐的自信
**🚀 職涯準備:**
本作業模擬真實無障礙顧問案,提供呈現作品集的經驗,彰顯:
- 系統性問題解決流程
- 注重技術細節與商業影響
- 清晰傳達複雜技術概念
- 理解網頁開發法律與倫理責任
完成後,你將能在任何網頁開發職務中,有意義地參與無障礙推動,成為包容設計實踐的積極倡議者。
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為具權威性的來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。
本文件是使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們力求準確,但請注意自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要資訊,建議尋求專業人工翻譯。本公司不對因使用本翻譯而產生的任何誤解或錯誤詮釋承擔責任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

File diff suppressed because it is too large Load Diff

@ -1,215 +1,618 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "b95fdd8310ef467305015ece1b0f9411",
"translation_date": "2025-08-29T15:05:33+00:00",
"original_hash": "672b0bb6e8b431075f3bdb7130590d2d",
"translation_date": "2026-01-06T12:16:34+00:00",
"source_file": "2-js-basics/1-data-types/README.md",
"language_code": "hk"
}
-->
# JavaScript 基礎:數據類型
# JavaScript 基礎:資料類型
![JavaScript Basics - Data types](../../../../translated_images/webdev101-js-datatypes.4cc470179730702c.hk.png)
> 筆記作者:[Tomomi Imura](https://twitter.com/girlie_mac)
```mermaid
journey
title 你的 JavaScript 資料類型冒險
section 基礎
變量與常數: 5: 你
聲明語法: 4: 你
賦值概念: 5: 你
section 核心類型
數字與數學: 4: 你
字串與文字: 5: 你
布林與邏輯: 4: 你
section 應用知識
類型轉換: 4: 你
實際範例: 5: 你
最佳實踐: 5: 你
```
資料類型是你在每個編寫的 JavaScript 程式中都會遇到的基本概念之一。想像資料類型就像古代亞歷山卓圖書管理員使用的檔案系統——他們有特定的地方放詩歌、數學和歷史記錄的卷軸。JavaScript 用不同類別的資料來組織資訊,類似這種方法。
在本課中,我們將探討讓 JavaScript 運作的核心資料類型。你會學習如何處理數字、文字、真/假值,並了解選擇正確類型為何對你的程式至關重要。這些概念一開始可能看起來抽象,但透過練習,它們將變成你的第二天性。
![JavaScript 基礎 - 數據類型](../../../../translated_images/webdev101-js-datatypes.4cc470179730702c756480d3ffa46507f746e5975ebf80f99fdaaf1cff09a7f4.hk.png)
> [Tomomi Imura](https://twitter.com/girlie_mac) 的手繪筆記
理解資料類型會讓 JavaScript 其他部分變得更加清晰。正如建築師在建造大教堂之前需要了解不同的建材,這些基礎會支撐你日後所建構的所有東西。
## 課前測驗
[課前測驗](https://ff-quizzes.netlify.app/web/)
這節課涵蓋了 JavaScript 的基礎知識,這是一種為網頁提供互動性的語言。
> 你可以在 [Microsoft Learn](https://docs.microsoft.com/learn/modules/web-development-101-variables/?WT.mc_id=academic-77807-sagibbon) 上學習這節課!
[![變數](https://img.youtube.com/vi/JNIXfGiDWM8/0.jpg)](https://youtube.com/watch?v=JNIXfGiDWM8 "JavaScript 中的變數")
[![JavaScript 中的數據類型](https://img.youtube.com/vi/AWfA95eLdq8/0.jpg)](https://youtube.com/watch?v=AWfA95eLdq8 "JavaScript 中的數據類型")
> 🎥 點擊上方圖片觀看有關變數和數據類型的影片
讓我們從變數和填充它們的數據類型開始吧!
本課涵蓋 JavaScript 的基礎,這是為網頁提供互動性的語言。
> 你可以在 [Microsoft Learn](https://docs.microsoft.com/learn/modules/web-development-101-variables/?WT.mc_id=academic-77807-sagibbon) 上學習這課!
[![Variables](https://img.youtube.com/vi/JNIXfGiDWM8/0.jpg)](https://youtube.com/watch?v=JNIXfGiDWM8 "Variables in JavaScript")
[![Data Types in JavaScript](https://img.youtube.com/vi/AWfA95eLdq8/0.jpg)](https://youtube.com/watch?v=AWfA95eLdq8 "Data Types in JavaScript")
> 🎥 點擊上方圖片觀看有關變數與資料類型的影片
讓我們先從變數和用來裝載它們的資料類型開始!
```mermaid
mindmap
root((JavaScript 數據))
Variables
let myVar
const PI = 3.14
var oldStyle
Primitive Types
number
42
3.14
-5
string
"Hello"
'World'
`Template`
boolean
true
false
undefined
null
Operations
Arithmetic
+ - * / %
String Methods
連接
模板字串
Type Conversion
隱式
顯式
```
## 變數
變數用來存儲值,這些值可以在你的代碼中使用並更改。
變數是程式設計中的基本組成元素。就像中世紀煉金術士用來儲存不同物質的標記瓶子,變數讓你能儲存資訊,並給它一個描述性的名稱,方便日後引用。需要記錄某人的年齡嗎?就存入名為 `age` 的變數。想追蹤使用者名稱?就存在 `userName` 變數中。
我們會專注於 JavaScript 中建立變數的現代方法。你在這裡學到的技術代表了語言演進多年來程式社群所發展的最佳實踐。
創建和**聲明**一個變數的語法是 **[關鍵字] [名稱]**,它由以下兩部分組成:
建立並**宣告**變數的語法是 **[關鍵字] [名稱]**,由兩部分組成:
- **關鍵字**。關鍵字可以是 `let``var`
- **關鍵字**。使用 `let` 表示可變變數,或 `const` 表示不變值。
- **變數名稱**,這是你自行選擇的描述性名稱。
`let` 關鍵字是在 ES6 中引入的,為變數提供了所謂的 _區塊作用域_。建議使用 `let` 而不是 `var`。我們會在後續部分更深入地討論區塊作用域。
- **變數名稱**,這是你自己選擇的一個名稱。
`let` 是 ES6 引入的關鍵字,賦予你的變數所謂的 _區塊作用域_。建議你用 `let``const` 取代舊的 `var` 關鍵字。我們將在後續章節更深入講解區塊作用域。
### 任務 - 使用變數
1. **聲明一個變數**。讓我們使用 `let` 關鍵字聲明一個變數:
1. **宣告變數**。我們先來建立第一個變數:
```javascript
let myVariable;
```
現在 `myVariable` 已經使用 `let` 關鍵字聲明了,但目前還沒有賦值。
**這樣做的效果:**
- 這告訴 JavaScript 建立一個名為 `myVariable` 的儲存空間
- JavaScript 在記憶體中分配空間給此變數
- 變數目前沒有值undefined
1. **賦值**。使用 `=` 運算符,後接期望的值,將值存儲到變數中。
2. **賦予它一個值**。現在給變數放入一個值:
```javascript
myVariable = 123;
```
> 注意:在本課中,`=` 的使用表示我們使用的是 "賦值運算符",用於將值設置給變數。它並不表示等於。
**指派的工作原理:**
- `=` 運算子將值 123 指派給變數
- 變數現在包含這個值,不再是 undefined
- 你可以在程式中用 `myVariable` 參考這個值
現在 `myVariable` 已經被*初始化*為值 123。
> 注意:本課中使用的 `=` 是「指派運算子」,用於設定變數值,並非等號
1. **重構**。用以下語句替換你的代碼。
3. **智慧地合併步驟**。實際上,我們可以將兩步合成一步:
```javascript
let myVariable = 123;
```
上述操作稱為 _顯式初始化_,即在聲明變數的同時賦值。
**此方式較有效率:**
- 你同時宣告變數並賦值,一行搞定
- 這是開發者的標準作法
- 減少程式碼長度,同時保持清晰易懂
1. **更改變數值**。以下方式更改變數值:
4. **改變值**。若想儲存不同的數字怎麼辦?
```javascript
myVariable = 321;
```
一旦變數被聲明,你可以在代碼中的任何地方使用 `=` 運算符和新值來更改它的值。
**了解重新指派:**
- 變數現在包含 321取代了之前的 123
- 先前的值被覆蓋——變數一次只存一個值
- 此可變性是用 `let` 宣告變數的主要特性
✅ 試試看!你可以直接在瀏覽器中編寫 JavaScript。打開瀏覽器窗口並導航到開發者工具。在控制台中你會看到一個提示符輸入 `let myVariable = 123`,按回車,然後輸入 `myVariable`。會發生什麼?注意,你會在後續課程中學到更多這些概念。
✅ 試試看!你可以直接在瀏覽器中寫 JavaScript。打開瀏覽器開發者工具進入控制台console輸入 `let myVariable = 123`,按下 Enter再輸入 `myVariable`。會發生什麼事情?後續課程還會學到更多這些概念。
## 常量
### 🧠 **變數掌握檢測:熟悉度測驗**
常量的聲明和初始化與變數的概念相同,但使用的是 `const` 關鍵字。常量通常用全大寫字母聲明。
**來測試你對變數的理解:**
- 你能解釋宣告和賦值變數的差別嗎?
- 如果你在宣告之前使用變數,會發生什麼?
- 什麼情況下會選擇用 `let` 而不是 `const` 宣告變數?
```mermaid
stateDiagram-v2
[*] --> Declared: let myVar
Declared --> Assigned: myVar = 123
Assigned --> Reassigned: myVar = 456
Assigned --> [*]: 變數已準備好!
Reassigned --> [*]: 更新了數值
note right of Declared
變數存在但
無值(未定義)
end note
note right of Assigned
變數包含
數值 123
end note
```
> **快速提示**:把變數想成有標籤的儲存盒。你先建立盒子(`let`),放入東西(`=`),然後可以在需要時替換內容!
## 常數
有時你需要儲存一個在程式執行期間永遠不會改變的資訊。想像常數就像歐幾里得在古希臘建立的數學原則——一旦被證明並記錄下來,它們就固定不變,供未來所有參考。
常數類似於變數,但有一個重要限制:一旦你給它們值,就不能改變。這種不可變性防止程式中關鍵值被意外修改。
宣告和初始化常數的概念與變數相同,除了使用 `const` 關鍵字。常數通常採用大寫字母命名。
```javascript
const MY_VARIABLE = 123;
```
常量與變數類似,但有兩個例外:
**此程式碼做了什麼:**
- **建立** 名為 `MY_VARIABLE` 的常數,值為 123
- **使用** 大寫的命名慣例
- **防止** 未來更改該值
常數有兩個主要規則:
- **必須立即給它一個值** — 不允許空白常數!
- **永遠不能更改該值** — 嘗試改變會導致錯誤。來看範例:
- **必須有值**。常量必須初始化,否則運行代碼時會出錯。
- **引用不可更改**。一旦初始化,常量的引用不能更改,否則運行代碼時會出錯。我們來看兩個例子:
- **簡單值**。以下是不允許的:
**簡單值** - 以下是不允許的:
```javascript
const PI = 3;
PI = 4; // not allowed
PI = 4; // 不允許
```
**需要記住的是:**
- **嘗試** 重新指派常數會造成錯誤
- **保護** 重要值免於意外變更
- **確保** 值在程式中保持一致
- **對象引用受保護**。以下是不允許的:
**物件參考被保護** - 以下是不允許的:
```javascript
const obj = { a: 3 };
obj = { b: 5 } // not allowed
obj = { b: 5 } // 不允許
```
- **對象值未受保護**。以下是允許的:
**了解這些概念:**
- **阻止** 用新物件取代整個物件
- **保護** 原始物件的參考不變
- **維持** 記憶體中物件的一致身份
**物件內容不被保護** - 以下是允許的:
```javascript
const obj = { a: 3 };
obj.a = 5; // allowed
obj.a = 5; // 允許
```
上述代碼更改了對象的值,但沒有更改引用本身,因此是允許的。
**解析發生了什麼:**
- **修改** 物件內部屬性值
- **保留** 相同的物件參考
- **顯示** 物件內容可以更改,而參考保持不變
> 注意,`const` 意味著參考受保護免於重新指派,但該值本身並不 _不可變_,特別是當它是物件等複雜結構時,內容可以更改。
> 注意,`const` 表示引用受到重新賦值的保護。然而,值並不是 _不可變_ 的,尤其是當它是像對象這樣的複雜結構時,值仍然可以更改。
## 資料類型
## 數據類型
JavaScript 將資訊組織成不同類別,稱為資料類型。這個概念類似古代學者分類知識的方式——亞里斯多德區分不同的推理類型,知道邏輯原則不能同樣套用於詩歌、數學和自然哲學。
變數可以存儲多種不同類型的值,例如數字和文本。這些不同類型的值被稱為**數據類型**。數據類型是軟件開發中的重要部分,因為它幫助開發者決定代碼應該如何編寫以及軟件應該如何運行。此外,一些數據類型具有獨特的功能,可以幫助轉換或提取值中的附加信息
資料類型很重要因為不同的運算需對應不同的資訊類型。就像你不能在人的名字上做數學運算或對數學公式做字母排序一樣JavaScript 也需要適合的資料類型以避免錯誤並提升可靠度
✅ 數據類型也被稱為 JavaScript 的數據原始類型因為它們是語言提供的最低級別的數據類型。JavaScript 有 7 種原始數據類型string、number、bigint、boolean、undefined、null 和 symbol。花點時間想像一下這些原始類型可能代表什麼。什麼是 `zebra``0` 呢?`true` 呢?
變數可以存儲許多不同類型的值,比如數字和文字。這些不同類型的值稱為**資料類型**。資料類型在軟體開發中非常重要,幫助開發者決定如何撰寫程式碼和軟體的運作方式。此外,有些資料類型具有獨特的功能,幫助轉換或提取額外資訊。
### 數字
✅ 資料類型亦稱為 JavaScript 原始資料類型,因為它們是語言提供的最低階資料類型。共有 7 種原始類型string、number、bigint、boolean、undefined、null 與 symbol。花點時間想像這些原始類型各代表什麼。`zebra` 是什麼?`0` 呢?`true` 又是什麼?
在上一節中,`myVariable` 的值是一個數字數據類型。
### 數字Numbers
`let myVariable = 123;`
數字是 JavaScript 最直觀的資料類型。無論你處理的是整數例如 42、小數例如 3.14,或是負數例如 -5JavaScript 都能統一處理。
變數可以存儲所有類型的數字,包括小數或負數。數字還可以與算術運算符一起使用,這部分內容會在[下一節](../../../../2-js-basics/1-data-types)中介紹。
還記得前面存入的 123 嗎?那實際上是數字資料類型:
```javascript
let myVariable = 123;
```
### 算術運算符
**主要特徵:**
- JavaScript 自動辨識數字值
- 可用變數執行數學運算
- 不需明確宣告資料類型
在執行算術操作時,可以使用多種類型的運算符,以下列出了一些:
變數可以存所有類型數字,包括小數和負數。數字也能跟數學運算子搭配使用,後面[數學運算子章節](../../../../2-js-basics/1-data-types)會介紹。
| 符號 | 描述 | 範例 |
| ---- | --------------------------------------------------------------------- | --------------------------------- |
| `+` | **加法**:計算兩個數字的和 | `1 + 2 // 預期答案是 3` |
| `-` | **減法**:計算兩個數字的差 | `1 - 2 // 預期答案是 -1` |
| `*` | **乘法**:計算兩個數字的積 | `1 * 2 // 預期答案是 2` |
| `/` | **除法**:計算兩個數字的商 | `1 / 2 // 預期答案是 0.5` |
| `%` | **取餘**:計算兩個數字相除後的餘數 | `1 % 2 // 預期答案是 1` |
```mermaid
flowchart LR
A["🔢 數字"] --> B[" 加法"]
A --> C[" 減法"]
A --> D["✖️ 乘法"]
A --> E["➗ 除法"]
A --> F["📊 餘數 %"]
B --> B1["1 + 2 = 3"]
C --> C1["5 - 3 = 2"]
D --> D1["4 * 3 = 12"]
E --> E1["10 / 2 = 5"]
F --> F1["7 % 3 = 1"]
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
style E fill:#e0f2f1
style F fill:#fce4ec
```
### 數學運算子
✅ 試試看!在瀏覽器的控制台中嘗試一個算術操作。結果是否讓你感到驚訝?
數學運算子讓你在 JavaScript 中執行算術計算。這些運算子遵循數學家數百年來的慣例——正如發展代數符號的作家如 Al-Khwarizmi 所使用的符號。
### 字串
這些運算子跟傳統數學相同:加號作加法,減號作減法,依此類推。
字串是位於單引號或雙引號之間的一組字符。
以下是部分可用運算子:
- `'這是一個字串'`
- `"這也是一個字串"`
- `let myString = '這是一個存儲在變數中的字串值';`
| 符號 | 說明 | 範例 |
| ------ | ------------------------------------------------------------------- | ------------------------------- |
| `+` | **加法**:計算兩數之和 | `1 + 2 // 預期結果是 3` |
| `-` | **減法**:計算兩數之差 | `1 - 2 // 預期結果是 -1` |
| `*` | **乘法**:計算兩數之積 | `1 * 2 // 預期結果是 2` |
| `/` | **除法**:計算兩數之商 | `1 / 2 // 預期結果是 0.5` |
| `%` | **餘數**:計算兩數相除的餘數 | `1 % 2 // 預期結果是 1` |
記得在寫字串時使用引號,否則 JavaScript 會認為它是一個變數名稱。
✅ 試試看吧!在瀏覽器控制台中嘗試一個數學運算。結果會不會讓你驚訝?
### 格式化字串
### 🧮 **數學技能檢測:自信計算**
字串是文本,有時需要進行格式化。
**測試你對算術的理解:**
- `/`(除法)和 `%`(餘數)有什麼差別?
- 你能預測 `10 % 3` 的值嗎?(提示:不是 3.33…)
- 程式中為什麼會需要餘數運算子?
要**連接**兩個或多個字串(即將它們拼接在一起),可以使用 `+` 運算符。
```mermaid
pie title "JavaScript 數字運算使用率"
"加法 (+)" : 35
"減法 (-)" : 20
"乘法 (*)" : 20
"除法 (/)" : 15
"餘數 (%)" : 10
```
> **實務小知識**:餘數運算子(%)非常適合用來檢查數字是奇數或偶數、創造模式,或在陣列中循環使用!
### 字串Strings
在 JavaScript 中,文字資料用字串表示。字串一詞來自把多個字元串連起來的概念,就如同中世紀修道院的抄寫員將字母連成詞句一樣。
字串是網頁開發的基礎。網頁上所有顯示的文字——使用者名稱、按鈕標籤、錯誤訊息、內容——都是字串資料。理解字串對於建立功能性使用者介面至關重要。
字串是由位於單引號或雙引號之間的字元組成。
```javascript
'This is a string'
"This is also a string"
let myString = 'This is a string value stored in a variable';
```
**理解這些概念:**
- **使用** 單引號 `'` 或雙引號 `"` 來定義字串
- **儲存** 可包含字母、數字及符號的文字資料
- **賦值** 給變數以供後續使用
- **需要** 引號以區分文字與變數名稱
寫字串時記得使用引號,否則 JavaScript 會把它當成變數名。
```mermaid
flowchart TD
A["📝 字串"] --> B["單引號"]
A --> C["雙引號"]
A --> D["模板字面量"]
B --> B1["'Hello World'"]
C --> C1["\"Hello World\""]
D --> D1["`Hello \${name}`"]
E["字串操作"] --> F["串接"]
E --> G["模板插入"]
E --> H["長度及方法"]
F --> F1["'Hello' + ' ' + 'World'"]
G --> G1["`Hello \${firstName} \${lastName}`"]
H --> H1["myString.length"]
style A fill:#e3f2fd
style E fill:#fff3e0
style D fill:#e8f5e8
style G fill:#e8f5e8
```
### 字串格式化
字串操作讓你可以結合文字元素、插入變數,並建立能依程式狀態動態變化的內容。此技巧使你能用程式方式組成文字。
常常你需要將多個字串串接起來——這稱為字串串接concatenation
要**串接**兩個或多個字串,或將它們連接在一起,請使用 `+` 運算子。
```javascript
let myString1 = "Hello";
let myString2 = "World";
myString1 + myString2 + "!"; //HelloWorld!
myString1 + " " + myString2 + "!"; //Hello World!
myString1 + ", " + myString2 + "!"; //Hello, World!
myString1 + myString2 + "!"; //哈囉世界!
myString1 + " " + myString2 + "!"; //哈囉 世界!
myString1 + ", " + myString2 + "!"; //哈囉,世界!
```
✅ 為什麼在 JavaScript 中 `1 + 1 = 2`,但 `'1' + '1' = 11`?想一想。那 `'1' + 1` 呢?
**逐步說明,發生了什麼:**
- **使用** `+` 運算子結合多個字串
- **直接** 將字串連接在一起,第一個範例中不加空格
- **加入** 字串間的空格字元 `" "` 以提升可讀性
- **插入** 標點符號如逗號,讓格式正確
✅ 為什麼在 JavaScript 中 `1 + 1 = 2`,但 `'1' + '1' = 11`?想想看。那 `'1' + 1` 又是什麼呢?
**模板字串**是格式化字串的另一種方式,不同於引號,它使用的是反引號。任何非純文本的內容必須放在 `${ }` 的佔位符中,包括可能是字串的變數。
**模板字面量**是格式化字串的另一種方式,不過不是用引號,而是用反引號。任何非純文字的東西必須放入佔位符 `${}`,包括可能是字串的變數。
```javascript
let myString1 = "Hello";
let myString2 = "World";
`${myString1} ${myString2}!` //Hello World!
`${myString1}, ${myString2}!` //Hello, World!
`${myString1} ${myString2}!` //哈囉,世界!
`${myString1}, ${myString2}!` //哈囉,世界!
```
你可以使用任一方法來實現格式化目標,但模板字串會保留所有空格和換行。
**來了解每個部分:**
- **使用** 反引號 `` ` `` 來建立模板字面量,而非一般引號
- **直接嵌入** 變數,使用 `${}` 佔位符語法
- **完全保留** 空格和格式
- **提供** 更乾淨的方式來建立含有變數的複雜字串
你可以用任一方法達到格式需求,但模板字面量會尊重所有空格和斷行。
✅ 你何時會使用模板字面量而非普通字串?
### 🔤 **字串掌握檢測:文字操作信心**
**評估你的字串技巧:**
- 你能解釋為什麼 `'1' + '1'``'11'` 而非 `2` 嗎?
- 哪種字串方法你覺得較易閱讀:串接還是模板字面量?
- 如果忘記在字串周圍加引號會怎樣?
```mermaid
stateDiagram-v2
[*] --> PlainText: "Hello"
[*] --> Variable: name = "Alice"
PlainText --> Concatenated: + " " + name
Variable --> Concatenated
PlainText --> Template: `Hello ${name}`
Variable --> Template
Concatenated --> Result: "Hello Alice"
Template --> Result
note right of Concatenated
傳統方法
更繁瑣
end note
note right of Template
現代 ES6 語法
更簡潔及易讀
end note
```
> **專家小提示**:模板字面量通常更適合複雜的字串構建,因為它們更易讀,且能漂亮地處理多行字串!
✅ 什麼時候會使用模板字串而不是普通字串?
### 布林值 (Booleans)
### 布林值
布林值代表最簡單的資料型態:它只能有兩個值之一——`true` 或 `false`。這個二元邏輯系統可追溯到十九世紀數學家 George Boole 的布林代數。
布林值只有兩個值:`true` 或 `false`。布林值可以幫助決定在滿足某些條件時應該運行哪些代碼行。在許多情況下,[運算符](../../../../2-js-basics/1-data-types)會幫助設置布林值,並且你會經常看到和編寫變數在初始化或更新其值時使用運算符。
雖然簡單,布林值對程式邏輯非常重要。它們讓你的程式根據條件做決策——例如使用者是否已登入,按鈕是否被點擊,或者是否符合某些條件
- `let myTrueBool = true`
- `let myFalseBool = false`
布林值只能是兩種:`true` 或 `false`。布林值有助於決定在特定條件下哪些程式碼應該執行。在多數情況下,[運算子](../../../../2-js-basics/1-data-types)會協助設定布林值,你也會經常看到使用運算子來初始化或更新變數值。
✅ 如果一個變數被認為是 "truthy",則它會評估為布林值 `true`。有趣的是,在 JavaScript 中,[所有值默認為 truthy除非被定義為 falsy](https://developer.mozilla.org/docs/Glossary/Truthy)。
```javascript
let myTrueBool = true;
let myFalseBool = false;
```
**以上範例中,我們:**
- **建立** 一個儲存布林值 `true` 的變數
- **展示** 如何儲存布林值 `false`
- **使用** 精確的關鍵字 `true``false` (不需要加引號)
- **準備** 這些變數在條件語句中使用
✅ 變數若評估結果為布林 `true`即可視為「truthy」。有趣的是在 JavaScript 中,[所有值除了被定義為 falsy 外都是 truthy](https://developer.mozilla.org/docs/Glossary/Truthy)。
```mermaid
flowchart LR
A["🔘 布林值"] --> B["true"]
A --> C["false"]
D["真值"] --> D1["'hello'"]
D --> D2["42"]
D --> D3["[]"]
D --> D4["{}"]
E["假值"] --> E1["false"]
E --> E2["0"]
E --> E3["''"]
E --> E4["null"]
E --> E5["undefined"]
E --> E6["NaN"]
style B fill:#e8f5e8
style C fill:#ffebee
style D fill:#e3f2fd
style E fill:#fff3e0
```
### 🎯 **布林邏輯檢測:決策技能**
**測試你的布林理解:**
- 你為什麼認為 JavaScript 除了 `true``false`還有「truthy」和「falsy」值
- 你能預測以下哪項是 falsy 嗎:`0`、`"0"`、`[]`、`"false"`
- 布林值在控制程式流程方面有什麼用處?
```mermaid
pie title "常用布林值用例"
"條件邏輯" : 40
"用戶狀態" : 25
"功能開關" : 20
"驗證" : 15
```
> **記住**:在 JavaScript 中只有6個值是 falsy`false`、`0`、`""`、`null`、`undefined` 和 `NaN`。其餘都是 truthy
---
## 📊 **你的資料型別工具包總結**
```mermaid
graph TD
A["🎯 JavaScript 數據類型"] --> B["📦 變數"]
A --> C["🔢 數字"]
A --> D["📝 字串"]
A --> E["🔘 布林值"]
B --> B1["let 可變"]
B --> B2["const 不可變"]
C --> C1["42, 3.14, -5"]
C --> C2["+ - * / %"]
D --> D1["'引號' 或 \\\"引號\\\""]
D --> D2["`模板字面量`"]
E --> E1["true 或 false"]
E --> E2["真值 vs 假值"]
F["⚡ 主要概念"] --> F1["類型對操作很重要"]
F --> F2["JavaScript 是動態類型"]
F --> F3["變數可改變類型"]
F --> F4["命名區分大小寫"]
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
style E fill:#e0f2f1
style F fill:#fce4ec
```
## GitHub Copilot Agent 挑戰 🚀
使用 Agent 模式完成以下挑戰:
**說明:** 建立一個個人資訊管理器,展示你課程中學到的所有 JavaScript 資料型別,並處理實際生活中的資料場景。
**命題:** 建立一個 JavaScript 程式,創建一個包含以下內容的使用者個人資料物件:姓名 (字串)、年齡 (數字)、是否為學生狀態 (布林值)、最愛顏色陣列,以及含有街道、城市和郵遞區號屬性的地址物件。包括顯示個人資料與更新各欄位的函式。務必展示字串串接、模板字面量、年齡的算術運算、和學生狀態的布林邏輯。
瞭解更多 [agent 模式](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode)。
## 🚀 挑戰
JavaScript 因其在處理數據類型時偶爾會出現令人驚訝的行為而聞名。研究一下這些 "陷阱"。例如:大小寫敏感可能會讓你吃虧!在控制台中試試看:`let age = 1; let Age = 2; age == Age`(結果是 `false`——為什麼?)。你還能找到哪些陷阱?
JavaScript 有些行為容易讓開發者措手不及。這是一個經典範例:在瀏覽器主控台輸入 `let age = 1; let Age = 2; age == Age`,並觀察結果。它回傳 `false` — 你能判斷原因嗎?
這只是眾多 JavaScript 行為之一。熟悉這些特性將幫助你寫出更可靠的程式,並更有效地除錯。
## 課後測驗
[課後測驗](https://ff-quizzes.netlify.app)
## 複習與自學
看看[這份 JavaScript 練習題列表](https://css-tricks.com/snippets/javascript/),試著完成一題。你學到了什麼?
看看[這份 JavaScript 練習清單](https://css-tricks.com/snippets/javascript/),試試其中一個。你學到了什麼?
## 作業
[數據類型練習](assignment.md)
[資料型別練習](assignment.md)
## 🚀 你的 JavaScript 資料型別精通時間表
### ⚡ **未來 5 分鐘可做到的事**
- [ ] 開啟瀏覽器主控台並建立3個不同資料型別的變數
- [ ] 嘗試挑戰:`let age = 1; let Age = 2; age == Age`,想清楚為什麼結果是 false
- [ ] 練習使用你的名字和喜歡的數字進行字串串接
- [ ] 測試將數字加到字串上會發生什麼
### 🎯 **本小時可完成的目標**
- [ ] 完成課後測驗並回顧任何困惑的概念
- [ ] 建立一個簡易計算機,能加減乘除兩個數字
- [ ] 使用模板字面量建立簡單的名字格式化程式
- [ ] 探索 `==``===` 比較運算子的差異
- [ ] 練習不同資料型別間的轉換
### 📅 **你這週的 JavaScript 基礎內容**
- [ ] 有信心且有創意地完成作業
- [ ] 建立一個包含所有學習過資料型別的個人資料物件
- [ ] 練習 [CSS-Tricks 的 JavaScript 練習](https://css-tricks.com/snippets/javascript/)
- [ ] 使用布林邏輯建立簡單表單驗證器
- [ ] 試玩陣列和物件資料型別(後續課程預告)
- [ ] 加入 JavaScript 社群,並詢問關於資料型別的問題
### 🌟 **你的月度轉變計劃**
- [ ] 將資料型別知識整合至較大型的程式專案
- [ ] 瞭解何時以及為何在實際應用中使用各種資料型別
- [ ] 幫助其他初學者理解 JavaScript 基礎
- [ ] 建置管理不同用戶資料類型的小型應用
- [ ] 探索進階資料型別概念,如型態強制轉換與嚴格相等
- [ ] 為開源 JavaScript 專案貢獻文件改進
### 🧠 **最終資料型別精通檢測**
**慶祝你的 JavaScript 基礎:**
- 哪種資料型別的行為最讓你驚訝?
- 你解釋變數與常數的能力有多自在?
- 關於 JavaScript 的型別系統,你發現最有趣的是什麼?
- 你想像用這些基礎可以建立什麼現實世界的應用?
```mermaid
journey
title 你的 JavaScript 信心之旅
section 今天
困惑: 3: 你
好奇: 4: 你
興奮: 5: 你
section 本週
練習中: 4: 你
理解中: 5: 你
建構中: 5: 你
section 下個月
解決問題: 5: 你
教導他人: 5: 你
實際專案: 5: 你
```
> 💡 **你已建立基礎!** 理解資料型別就像學習字母表,為寫故事打下基礎。你未來寫的每個 JavaScript 程式都會用到這些基本概念。你現在擁有了創建互動網站、動態應用程式,以及以程式碼解決現實問題的基石。歡迎來到 JavaScript 的奇妙世界! 🎉
---
**免責聲明**
此文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原始語言的文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解讀概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件係使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 所翻譯。雖然我們致力於準確性,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們不對因使用此翻譯而產生的任何誤解或誤釋承擔責任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,25 +1,103 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "de0ec12c337394806425c7fd2f003b62",
"translation_date": "2025-10-03T08:55:20+00:00",
"original_hash": "6fd645e97c48cd5eb5a3d290815ec8b5",
"translation_date": "2026-01-06T12:18:33+00:00",
"source_file": "2-js-basics/1-data-types/assignment.md",
"language_code": "hk"
}
-->
# 資料類型練習
# Data Types Practice: E-commerce Shopping Cart
## 指引
## Instructions
假設你正在建立一個購物車。撰寫有關完成購物體驗所需資料類型的文件。對於每種資料類型,解釋如何以及為什麼使用它,並提供一個範例。六種 JavaScript 資料類型分別是String、Number、Boolean、Null、Undefined 和 Object。
Imagine you are building a modern e-commerce shopping cart system. This assignment will help you understand how different JavaScript data types work together to create real-world applications.
## 評分標準
### Your Task
標準 | 卓越 | 合格 | 需要改進
--- | --- | --- | --- |
資料類型 | 六種資料類型均列出,詳細探討並附有範例 | 探討四種資料類型並有部分解釋 | 探討兩種資料類型且解釋不足 |
Create a comprehensive analysis of how you would use JavaScript data types in a shopping cart application. For each of the seven primitive data types and objects, you need to:
1. **Identify** the data type and its purpose
2. **Explain** why this data type is the best choice for specific shopping cart features
3. **Provide** realistic code examples showing the data type in action
4. **Describe** how this data type interacts with other parts of the shopping cart
### Required Data Types to Cover
**Primitive Data Types:**
- **String**: Product names, descriptions, user information
- **Number**: Prices, quantities, tax calculations
- **Boolean**: Item availability, user preferences, cart status
- **Null**: Intentionally empty values (like missing discount codes)
- **Undefined**: Uninitialized values or missing data
- **Symbol**: Unique identifiers (advanced use)
- **BigInt**: Large financial calculations (advanced use)
**Reference Types:**
- **Object**: Product details, user profiles, cart contents
- **Array**: List of products, order history, categories
### Example Format for Each Data Type
For each data type, structure your response like this:
```markdown
## [Data Type Name]
**Purpose in Shopping Cart:** [Explain what this data type does]
**Why This Type:** [Explain why this is the best choice]
**Code Example:**
```javascript
// Your realistic code example here
```
**Real-world Usage:** [Describe how this would work in practice]
**Interactions:** [Explain how this data type works with others]
```
### Bonus Challenges
1. **Type Coercion**: Show an example where JavaScript automatically converts between data types in your shopping cart (e.g., string "5" + number 10)
2. **Data Validation**: Demonstrate how you would check if user input is the correct data type before processing
3. **Performance Considerations**: Explain when you might choose one data type over another for performance reasons
### Submission Guidelines
- Create a markdown document with clear headings for each data type
- Include working JavaScript code examples
- Use realistic e-commerce scenarios in your examples
- Explain your reasoning clearly for beginners to understand
- Test your code examples to ensure they work correctly
## Rubric
| Criteria | Exemplary (90-100%) | Proficient (80-89%) | Developing (70-79%) | Needs Improvement (Below 70%) |
|----------|---------------------|---------------------|---------------------|------------------------------|
| **Data Type Coverage** | All 7 primitive types and objects/arrays covered with detailed explanations | 6-7 data types covered with good explanations | 4-5 data types covered with basic explanations | Fewer than 4 data types or minimal explanations |
| **Code Examples** | All examples are realistic, working, and well-commented | Most examples work and are relevant to e-commerce | Some examples work but may be generic | Code examples are incomplete or non-functional |
| **Real-world Application** | Clearly connects each data type to practical shopping cart features | Good connection to e-commerce scenarios | Some connection to shopping cart context | Limited real-world application demonstrated |
| **Technical Accuracy** | All technical information is correct and demonstrates deep understanding | Most technical information is accurate | Generally accurate with minor errors | Contains significant technical errors |
| **Communication** | Explanations are clear, beginner-friendly, and well-organized | Good explanations that are mostly clear | Explanations are understandable but may lack clarity | Explanations are unclear or poorly organized |
| **Bonus Elements** | Includes multiple bonus challenges with excellent execution | Includes one or more bonus challenges well done | Attempts bonus challenges with mixed success | No bonus challenges attempted |
### Learning Objectives
By completing this assignment, you will:
- ✅ **Understand** the seven JavaScript primitive data types and their uses
- ✅ **Apply** data types to real-world programming scenarios
- ✅ **Analyze** when to choose specific data types for different purposes
- ✅ **Create** working code examples that demonstrate data type usage
- ✅ **Explain** technical concepts in beginner-friendly language
- ✅ **Connect** fundamental programming concepts to practical applications
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件已使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。
本文件由 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻譯而成。雖然我們力求準確,但請注意自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。如涉及重要資訊,建議使用專業人工翻譯。本公司不就使用此翻譯而產生的任何誤解或誤譯承擔責任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,45 +1,105 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "92e136090efc4341b1d51c37924c1802",
"translation_date": "2025-08-29T15:04:26+00:00",
"original_hash": "71f7d7dafa1c7194d79ddac87f669ff9",
"translation_date": "2026-01-06T12:14:28+00:00",
"source_file": "2-js-basics/2-functions-methods/README.md",
"language_code": "hk"
}
-->
# JavaScript 基礎:方法與函數
![JavaScript 基礎 - 函數](../../../../translated_images/webdev101-js-functions.be049c4726e94f8b7605c36330ac42eeb5cd8ed02bcdd60fdac778174d6cb865.hk.png)
> 手繪筆記由 [Tomomi Imura](https://twitter.com/girlie_mac) 提供
## 課前測驗
[課前測驗](https://ff-quizzes.netlify.app)
當我們在撰寫程式碼時,總是希望程式碼能夠易於閱讀。雖然這聽起來有點違背直覺,但事實上,程式碼被閱讀的次數遠多於被撰寫的次數。開發者工具箱中的一個核心工具,就是**函數**,它能幫助我們確保程式碼的可維護性。
[![方法與函數](https://img.youtube.com/vi/XgKsD6Zwvlc/0.jpg)](https://youtube.com/watch?v=XgKsD6Zwvlc "方法與函數")
> 🎥 點擊上方圖片觀看有關方法與函數的影片。
> 你可以在 [Microsoft Learn](https://docs.microsoft.com/learn/modules/web-development-101-functions/?WT.mc_id=academic-77807-sagibbon) 上學習這一課!
![JavaScript Basics - Functions](../../../../translated_images/webdev101-js-functions.be049c4726e94f8b.hk.png)
> 繪圖筆記由 [Tomomi Imura](https://twitter.com/girlie_mac) 製作
```mermaid
journey
title 你的 JavaScript 函式冒險
section 基礎
函式語法: 5: You
呼叫函式: 4: You
參數與引數: 5: You
section 進階概念
回傳值: 4: You
預設參數: 5: You
函式組合: 4: You
section 現代 JavaScript
箭頭函式: 5: You
匿名函式: 4: You
高階函式: 5: You
```
## 課前小測驗
[課前小測驗](https://ff-quizzes.netlify.app)
反覆撰寫相同程式碼是程式設計中最常見的挫折之一。函數解決此問題,讓你可以將程式碼封裝成可重複使用的區塊。你可以將函數視為讓亨利福特的組裝生產線變革性的標準化零件——一旦你建立了可靠的元件,就可以在需要的地方使用它,而無需重新從零開始構建。
函數允許你將程式碼片段打包,並在程式中多次重用。與其到處複製貼上相同邏輯,不如建立函數並在需要時呼叫。這種方法能保持程式碼整潔,也更容易做出更新。
在本課程中,你將學習如何創建自己的函數、將資訊傳遞給函數並取得有用的回傳結果。你將發現函數和方法的差異,學習現代語法用法,並了解函數如何與其他函數搭配使用。我們將一步步建立這些概念。
[![Methods and Functions](https://img.youtube.com/vi/XgKsD6Zwvlc/0.jpg)](https://youtube.com/watch?v=XgKsD6Zwvlc "Methods and Functions")
> 🎥 點擊上方圖片可以觀看關於方法與函數的教學影片。
> 你也可以在 [Microsoft Learn](https://docs.microsoft.com/learn/modules/web-development-101-functions/?WT.mc_id=academic-77807-sagibbon) 上參加這堂課!
```mermaid
mindmap
root((JavaScript 函數))
Basic Concepts
Declaration
傳統語法
箭頭函數語法
Calling
使用括號
需用括號
Parameters
Input Values
多個參數
預設值
Arguments
傳入值
可為任意類型
Return Values
Output Data
return 陳述式
離開函數
Use Results
儲存於變數
函數鏈結
Advanced Patterns
Higher-Order
函數作為參數
回調函數
Anonymous
不需名稱
內聯定義
```
## 函數
從本質上來說,函數是一段可以按需執行的程式碼。這非常適合需要多次執行相同任務的情境;與其在多個地方重複相同的邏輯(這樣會在需要更新時變得困難),不如將邏輯集中在一個地方,並在需要執行操作時呼叫它——甚至可以從其他函數中呼叫函數!
函數是一段自包含的程式碼區塊,用來完成特定任務。它封裝你需要執行的邏輯,隨時可以執行。
與其在程式中重複書寫相同的程式碼,不如將它封裝成函數,並在需要時呼叫。此作法讓你的程式碼保持乾淨,也能更輕鬆維護。試想,如果你需要改變散佈於 20 個不同位置的邏輯,會有多困難。
函數的另一個重要特性是可以為其命名。雖然這看似微不足道,但函數名稱提供了一種快速記錄程式碼片段的方式。你可以將其想像成按鈕上的標籤。如果我點擊一個標有「取消計時器」的按鈕,我就知道它會停止計時。
為函數取有描述性的名稱非常重要。命名良好的函數能清楚傳達其目的——看到 `cancelTimer()` 就馬上知道它的功能,就像標示清楚的按鈕可以告訴你點擊後會發生什麼一樣
## 建立與呼叫函數
函數的語法如下所示:
讓我們看看如何建立函數。語法遵循固定的模式
```javascript
function nameOfFunction() { // function definition
// function definition/body
function nameOfFunction() { // 函數定義
// 函數定義/函數體
}
```
如果我想建立一個函數來顯示問候語,它可能看起來像這樣:
拆解說明:
- `function` 關鍵字告訴 JavaScript「嘿我正在創建一個函數
- `nameOfFunction` 是你為函數取的描述性名稱
- 括號 `()` 可放入參數(稍後會講)
- 花括號 `{}` 裡包含你在呼叫函數時要執行的程式碼
讓我們來寫個簡單的問候函數,看看範例:
```javascript
function displayGreeting() {
@ -47,28 +107,57 @@ function displayGreeting() {
}
```
每當我們想要呼叫(或執行)函數時,只需使用函數名稱並加上 `()`。值得注意的是我們的函數可以在定義之前或之後被呼叫JavaScript 編譯器會幫你找到它。
此函數將在控制台列印 "Hello, world!"。定義完成後,你可以重複多次呼叫它。
執行或「呼叫」函數的方法是寫函數名稱加上括號。JavaScript 允許你在呼叫前或後定義函數——它的執行順序會自動處理。
```javascript
// calling our function
// 呼叫我們的函數
displayGreeting();
```
> **NOTE:** 有一種特殊類型的函數稱為 **方法**,你可能已經在使用了!事實上,我們在上面的範例中使用 `console.log` 時就看到了它。方法與函數的區別在於,方法是附加到某個物件上的(例如我們的範例中的 `console`),而函數則是獨立的。許多開發者會將這兩個術語交替使用。
執行此行程式碼,即會執行 `displayGreeting` 函數內所有程式碼,並在瀏覽器控制台顯示 "Hello, world!"。你可以多次呼叫此函數。
### 🧠 **函數基礎檢測:建立你的第一個函數**
**讓我們檢查你對基礎函數的理解:**
- 你能解釋為何在函數定義要使用花括號 `{}` 嗎?
- 如果寫 `displayGreeting` 而不加括號會怎樣?
- 為什麼有時候要多次呼叫同一個函數?
```mermaid
flowchart TD
A["✏️ 定義函數"] --> B["📦 打包代碼"]
B --> C["🏷️ 給它命名"]
C --> D["📞 需要時呼叫"]
D --> E["🔄 隨處重用"]
F["💡 好處"] --> F1["無需重複編寫代碼"]
F --> F2["容易維護"]
F --> F3["結構清晰"]
F --> F4["測試更簡單"]
style A fill:#e3f2fd
style E fill:#e8f5e8
style F fill:#fff3e0
```
> **注意:** 在這些課程中,你一直在使用 **方法**。`console.log()` 是一個方法——基本上是屬於 `console` 物件的函數。關鍵差異在於方法綁定於物件,而函數是獨立存在。許多開發者在日常對話中常互用這兩個詞。
### 函數的最佳實踐
### 函數最佳實踐
在建立函數時,有幾個最佳實踐需要記住:
以下是寫出優秀函數的小技巧
- 一如既往,使用描述性的名稱,這樣你就知道函數的用途
- 使用 **camelCasing**(駝峰式命名法)來組合單詞
- 讓函數專注於執行特定任務
- 給函數取清晰且描述性名稱——未來的自己會感謝你!
- 使用 **camelCase** 命名多字函數(例如 `calculateTotal`,而非 `calculate_total`
- 保持每個函數專注完成一件事
## 傳遞資訊給函數
為了讓函數更具重用性,你通常會希望將資訊傳遞給它。如果我們考慮上面提到的 `displayGreeting` 範例,它只會顯示 **Hello, world!**,這並不是一個非常實用的函數。如果我們希望它更靈活,例如允許指定要問候的人的名字,我們可以新增一個**參數**。參數(有時也稱為**引數**)是傳遞給函數的額外資訊。
我們剛剛的 `displayGreeting` 函數很有限——它只能對所有人顯示 "Hello, world!"。參數使函數變得更靈活有用
參數在定義部分列於括號內,並以逗號分隔,如下所示:
**參數** 就像佔位符,每次使用函數時你可填入不同數值。如此一來,同一函數可針對不同資訊運作。
當你定義函數時,將參數列在括號中,多個參數用逗號分隔:
```javascript
function name(param, param2, param3) {
@ -76,7 +165,9 @@ function name(param, param2, param3) {
}
```
我們可以更新 `displayGreeting`,使其接受一個名字並顯示出來。
每個參數就像佔位符,當呼叫函數時會提供實際值,填入這些位置。
讓我們改寫問候函數,接受一個名字參數:
```javascript
function displayGreeting(name) {
@ -85,16 +176,44 @@ function displayGreeting(name) {
}
```
當我們想要呼叫函數並傳遞參數時,只需在括號內指定它。
注意我們使用反引號(`` ` ``)和 `${}` 直接將 `name` 插入訊息中——這種方式叫做模板字串,是建立帶有變數字串的好工具。
呼叫函數時,我們可以傳入任何名字:
```javascript
displayGreeting('Christopher');
// displays "Hello, Christopher!" when run
// 執行時顯示「Hello, Christopher!」
```
JavaScript 將字串 `'Christopher'` 指派給 `name` 參數,並建立個人化訊息 "Hello, Christopher!"
```mermaid
flowchart LR
A["🎯 函數調用"] --> B["📥 參數"]
B --> C["⚙️ 函數主體"]
C --> D["📤 結果"]
A1["displayGreeting('Alice')"] --> A
B1["name = 'Alice'"] --> B
C1["模板字串\n\`Hello, \${name}!\`"] --> C
D1["'Hello, Alice!'"] --> D
E["🔄 參數類型"] --> E1["字串"]
E --> E2["數字"]
E --> E3["布林值"]
E --> E4["物件"]
E --> E5["函數"]
style A fill:#e3f2fd
style C fill:#e8f5e8
style D fill:#fff3e0
style E fill:#f3e5f5
```
## 預設值
我們可以通過新增更多參數來讓函數更靈活。但如果我們不希望每個值都必須指定呢?以我們的問候語範例為例,我們可以將名字設為必填(因為我們需要知道要問候誰),但允許問候語本身根據需要進行自定義。如果有人不想自定義,我們可以提供一個預設值。要為參數提供預設值,我們可以像為變數設置值一樣進行設置——`parameterName = 'defaultValue'`。完整範例如下:
如果我們想讓參數可選怎麼辦?這時預設值非常有用!
假設我們想讓使用者能自訂問候語,但若沒指定,就使用 "Hello" 這個備用詞彙。你可用等號設定預設值,就像設定變數一樣:
```javascript
function displayGreeting(name, salutation='Hello') {
@ -102,29 +221,63 @@ function displayGreeting(name, salutation='Hello') {
}
```
當我們呼叫函數時,可以決定是否為 `salutation` 設置值。
這裡 `name` 仍是必填,`salutation` 如果沒給值則默認為 `'Hello'`
現在我們可用兩種方式呼叫這個函數:
```javascript
displayGreeting('Christopher');
// displays "Hello, Christopher"
// 顯示「Hello, Christopher」
displayGreeting('Christopher', 'Hi');
// displays "Hi, Christopher"
// 顯示「Hi, Christopher」
```
## 返回值
第一次呼叫未指定問候詞JavaScript 使用預設的 "Hello"。第二次呼叫提供自訂的 "Hi"。這種彈性使函數適應不同場景。
### 🎛️ **參數掌握檢測:讓函數靈活運用**
**測試你對參數的理解:**
- 參數與引數有什麼差異?
- 為什麼預設值在實務編程中很有用?
- 如果傳入比參數多的引數,會發生什麼?
```mermaid
stateDiagram-v2
[*] --> NoParams: function greet() {}
[*] --> WithParams: function greet(name) {}
[*] --> WithDefaults: function greet(name, greeting='Hi') {}
NoParams --> Static: 輸出永遠相同
WithParams --> Dynamic: 隨輸入改變
WithDefaults --> Flexible: 可選擇性自訂
Static --> [*]
Dynamic --> [*]
Flexible --> [*]
note right of WithDefaults
最靈活的方法
向後相容
end note
```
> **專家提示**:預設參數讓函數更友善。使用者可以用合理預設值快速上手,仍可依需求自訂!
## 回傳值
到目前為止,我們建立的函數會將輸出直接顯示在 [console](https://developer.mozilla.org/docs/Web/API/console) 上。有時這正是我們想要的,特別是當我們建立的函數會呼叫其他服務時。但如果我想建立一個輔助函數來執行計算並將值返回,以便在其他地方使用呢?
到目前為止,我們的函數只在控制台輸出訊息,但如果你想讓函數計算結果並回傳呢?
我們可以通過使用**返回值**來實現。返回值是由函數返回的,可以像儲存字串或數字這樣的靜態值一樣儲存在變數中。
這就是**回傳值**的用處。函數不只是輸出,也能把值回傳給呼叫它的程式,你可以存進變數或在其他地方使用
如果函數返回某些內容,則使用關鍵字 `return`。`return` 關鍵字需要一個值或引用作為返回內容,如下所示:
回傳值用 `return` 關鍵字,後面接你想回傳的內容
```javascript
return myVariable;
```
```
重要一點:當函數執行到 `return`,會立刻停止並把該值送回呼叫者。
我們可以建立一個函數來生成問候語訊息並將值返回給呼叫者:
改寫問候函數,讓它回傳訊息而非直接列印
```javascript
function createGreetingMessage(name) {
@ -133,35 +286,81 @@ function createGreetingMessage(name) {
}
```
當呼叫這個函數時,我們會將返回的值儲存在變數中。這與我們將變數設置為靜態值(例如 `const name = 'Christopher'`)的方式非常相似。
現在函數不列印,只建立訊息並回傳。
使用回傳值,就像其他值一樣存入變數:
```javascript
const greetingMessage = createGreetingMessage('Christopher');
```
## 函數作為函數的參數
`greetingMessage` 現在包含 "Hello, Christopher",你可用它顯示在網頁、寄信,或者傳給其他函數。
```mermaid
flowchart TD
A["🔧 函數處理"] --> B{"return 陳述式?"}
B -->|是| C["📤 回傳值"]
B -->|否| D["📭 回傳未定義"]
C --> E["💾 存入變數"]
C --> F["🔗 在運算式中使用"]
C --> G["📞 傳給函數"]
D --> H["⚠️ 通常無用"]
I["📋 回傳值用途"] --> I1["計算結果"]
I --> I2["驗證輸入"]
I --> I3["轉換資料"]
I --> I4["建立物件"]
style C fill:#e8f5e8
style D fill:#ffebee
style I fill:#e3f2fd
```
### 🔄 **回傳值檢測:取得結果**
**評估你對回傳值的理解:**
- 函數執行到 `return` 後,後面的程式會怎樣?
- 為什麼回傳值比只列印控制台更好?
- 函數可以回傳不同型態的值(字串、數字、布林)嗎?
```mermaid
pie title "常見返回值類型"
"字串" : 30
"數字" : 25
"物件" : 20
"布林值" : 15
"陣列" : 10
```
> **關鍵洞察**:回傳值使函數更靈活,因為呼叫者決定如何使用結果。這讓程式更模組化、重複使用更方便!
## 函數作為參數傳入函數
隨著你在程式設計生涯中的進步,你會遇到接受函數作為參數的函數。這個巧妙的技巧通常用於我們無法確定某件事何時會發生或完成,但我們知道需要在某件事發生時執行操作的情況。
函數可以當作參數傳給其他函數。雖然這聽起來複雜,但這是一個強大的特性,可實現靈活程式設計模式
例如,[setTimeout](https://developer.mozilla.org/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout) 會啟動一個計時器,並在計時完成後執行程式碼。我們需要告訴它我們想執行什麼程式碼。這聽起來像是函數的完美應用場景!
這種用法非常常見,當你想說「當某事發生,做[另一件事]」時。譬如「計時結束時,執行這段程式碼」或「使用者點按鈕時,呼叫此函數」。
如果你執行以下程式碼3 秒後你會看到訊息 **3 seconds has elapsed**
看看 `setTimeout`,它是內建函數,會等待一段時間後執行程式碼。我們需要告訴它要執行什麼程式碼——這正是傳入函數的好例子!
試試看這段程式碼——3 秒後會看到訊息:
```javascript
function displayDone() {
console.log('3 seconds has elapsed');
}
// timer value is in milliseconds
// 計時器數值以毫秒為單位
setTimeout(displayDone, 3000);
```
注意我們傳入 `displayDone`(沒有括號)給 `setTimeout`。我們不是自己呼叫函數,而是把函數交給 `setTimeout`告訴它「3 秒後呼叫它。」
### 匿名函數
讓我們再看看我們建立的內容。我們建立了一個只會使用一次的具名函數。隨著應用程式變得更複雜,我們可能會建立許多只會被呼叫一次的函數。這並不理想。事實證明,我們並不總是需要提供名稱!
有時你只需要一個函數做一件事,不想特別命名。想想看——如果只用一次,為什麼要多個名稱搞亂程式碼?
當我們將函數作為參數傳遞時,可以跳過事先建立函數,直接在參數中建立它。我們使用相同的 `function` 關鍵字,但直接在參數中構建函數。
JavaScript 允許你創建 **匿名函數**——沒有名字的函數,直接寫在需要它的地方
讓我們重寫上述程式碼,使用匿名函數:
下面是用匿名函數改寫計時例子
```javascript
setTimeout(function() {
@ -169,13 +368,15 @@ setTimeout(function() {
}, 3000);
```
如果你執行我們的新程式碼,你會注意到結果相同。我們建立了一個函數,但不需要給它命名!
效果相同,但函數直接定義在 `setTimeout` 裡,省掉了另写一個函數宣告。
### 箭頭函數
### Fat Arrow 函數
現代 JavaScript 有更精簡寫函數的方法,稱為 **箭頭函數**。符號是 `=>`(像個箭頭,你懂的),非常受開發者歡迎。
許多程式語言(包括 JavaScript中常見的一個快捷方式是使用所謂的**箭頭函數**或**胖箭頭函數**。它使用一個特殊的符號 `=>`,看起來像一個箭頭——因此得名!通過使用 `=>`,我們可以省略 `function` 關鍵字。
箭頭函數省略 `function` 關鍵字,寫起來更簡潔
讓我們再一次重寫程式碼,使用胖箭頭函數
這是我們計時例子改用箭頭函數寫法
```javascript
setTimeout(() => {
@ -183,28 +384,200 @@ setTimeout(() => {
}, 3000);
```
### 何時使用哪種策略
現在你已經看到了三種將函數作為參數的方式,可能會想知道何時使用哪一種。如果你知道函數會被多次使用,那麼就按正常方式建立它。如果只會在一個地方使用,通常最好使用匿名函數。至於使用胖箭頭函數還是傳統的 `function` 語法,則取決於你,但你會發現大多數現代開發者更偏好使用 `=>`
`()` 是放參數的地方(這例子是空的),接著是箭頭 `=>`,最後是用花括號包住的函數內容。兩者功能相同,但語法更簡潔。
```mermaid
flowchart LR
A["📝 函數寫法"] --> B["傳統"]
A --> C["箭頭函數"]
A --> D["匿名函數"]
B --> B1["function name() {}"]
B --> B2["提升作用域"]
B --> B3["有名字"]
C --> C1["const name = () => {}"]
C --> C2["簡潔語法"]
C --> C3["現代風格"]
D --> D1["function() {}"]
D --> D2["無名稱"]
D --> D3["一次性使用"]
E["⏰ 什麼時候用"] --> E1["傳統:可重複使用函數"]
E --> E2["箭頭函數:簡短回呼函數"]
E --> E3["匿名函數:事件處理器"]
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
style E fill:#e0f2f1
```
### 何時使用哪種寫法
什麼時候該用哪種方法呢?實用指南是:若需多次使用函數,給它命名並獨立定義;若只用一次,可考慮匿名函數。箭頭函數和傳統語法都是可行選擇,但箭頭函數已成現代 JavaScript 代碼庫的主流。
### 🎨 **函數風格掌握檢測:挑選適合語法**
**測試你的語法理解:**
- 何時你會偏好箭頭函數而非傳統函數語法?
- 匿名函數的主要優勢是什麼?
- 你能想出命名函數比匿名函數更好的情境嗎?
```mermaid
quadrantChart
title 函數選擇決策矩陣
x-axis 簡單 --> 複雜
y-axis 一次性使用 --> 可重用
quadrant-1 箭頭函數
quadrant-2 有名函數
quadrant-3 匿名函數
quadrant-4 傳統函數
Event Handlers: [0.3, 0.2]
Utility Functions: [0.7, 0.8]
Callbacks: [0.2, 0.3]
Class Methods: [0.8, 0.7]
Mathematical Operations: [0.4, 0.6]
```
> **現代趨勢**:箭頭函數因為語法簡潔,逐漸成為許多開發者的首選,但傳統函數依然有其存在價值!
---
## 🚀 挑戰
你能用一句話說明函數與方法的區別嗎?試試看吧!
你能用一句話說明函數與方法的差異嗎?試試看!
## GitHub Copilot Agent 挑戰 🚀
## 課後測驗
[課後測驗](https://ff-quizzes.netlify.app)
使用 Agent 模式完成以下挑戰:
**說明:** 建立一個數學函數的工具庫,示範本課涵蓋的不同函數概念,包括參數、預設值、回傳值與箭頭函數。
**提示:** 建立一個名為 `mathUtils.js` 的 JavaScript 檔案,包含以下函數:
1. 一個函數 `add`,接收兩個參數並回傳它們的和
2. 一個帶有預設參數值的 `multiply` 函數(第二個參數預設為 1
3. 一個箭頭函數 `square`,接收一個數字並回傳其平方
4. 一個函數 `calculate`,接收另一個函數參數與兩個數字,將函數套用到兩數字上
5. 展示呼叫每個函數的適當測試案例
在此處了解更多有關 [agent 模式](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) 。
## 課後小測驗
[課後小測驗](https://ff-quizzes.netlify.app)
## 複習與自學
值得[深入了解箭頭函數](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Functions/Arrow_functions),因為它們在程式碼中越來越常見。練習撰寫一個函數,然後用這種語法重寫它。
值得[多讀一些關於箭頭函數](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Functions/Arrow_functions),因為它們越來越多於程式碼中使用。練習編寫一個函數,然後用這語法重寫
## 作業
[函數的樂趣](assignment.md)
[Fun with Functions](assignment.md)
---
## 🧰 **你的 JavaScript 函數工具箱總結**
```mermaid
graph TD
A["🎯 JavaScript 函式"] --> B["📋 函式宣告"]
A --> C["📥 參數"]
A --> D["📤 回傳值"]
A --> E["🎨 現代語法"]
B --> B1["function name() {}"]
B --> B2["具描述性的命名"]
B --> B3["可重用的程式區塊"]
C --> C1["輸入資料"]
C --> C2["預設值"]
C --> C3["多重參數"]
D --> D1["return 陳述句"]
D --> D2["結束函式"]
D --> D3["傳回資料"]
E --> E1["箭頭函式: () =>"]
E --> E2["匿名函式"]
E --> E3["高階函式"]
F["⚡ 主要優點"] --> F1["程式碼重用"]
F --> F2["更佳的組織"]
F --> F3["更容易測試"]
F --> F4["模組化設計"]
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
style E fill:#e0f2f1
style F fill:#fce4ec
```
---
## 🚀 你的 JavaScript 函數掌握時間軸
### ⚡ **接下來 5 分鐘你可以做到**
- [ ] 寫一個簡單函數回傳你最喜歡的數字
- [ ] 建立一個有兩個參數回傳相加結果的函數
- [ ] 嘗試將傳統函數轉換為箭頭函數語法
- [ ] 練習挑戰:解釋函數與方法的不同
### 🎯 **你今小時可以完成的事**
- [ ] 完成課後小測驗並複習任何令人困惑的概念
- [ ] 建立 GitHub Copilot 挑戰中的數學公用程式庫
- [ ] 創建一個使用另一個函數作為參數的函數
- [ ] 練習撰寫帶有預設參數的函數
- [ ] 嘗試函數回傳值中使用模板字串
### 📅 **你為期一週的函數精通計劃**
- [ ] 以創意完成「Fun with Functions」作業
- [ ] 將一些你已有的重複程式碼重構成可重用函數
- [ ] 僅使用函數(不使用全局變數)建置一個簡易計算器
- [ ] 練習使用箭頭函數搭配陣列方法如 `map()``filter()`
- [ ] 建立一組用於常見任務的工具函數集合
- [ ] 研習高階函數與函數式程式設計觀念
### 🌟 **你為期一個月的轉變規劃**
- [ ] 精通進階函數概念如閉包與作用域
- [ ] 建立一個嚴重使用函數組合的專案
- [ ] 透過改進函數文件來貢獻開源
- [ ] 教導他人函數與不同語法風格
- [ ] 探索 JavaScript 的函數式程式設計範式
- [ ] 建立一個個人可重用函數庫以用於未來專案
### 🏆 **最終函數冠軍檢閱**
**慶祝你對函數的精通:**
- 到目前為止,哪個函數是你創建得最有用的?
- 學習函數如何改變你對程式碼組織的想法?
- 你偏好哪種函數語法?為什麼?
- 你會寫函數解決什麼真實世界的問題?
```mermaid
journey
title 你的函數信心演變
section 今日
對語法感到困惑: 3: You
理解基礎: 4: You
編寫簡單函數: 5: You
section 本週
使用參數: 4: You
返回值: 5: You
現代語法: 5: You
section 下個月
函數合成: 5: You
進階模式: 5: You
教導他人: 5: You
```
> 🎉 **你已經精通了程式設計中最強大的概念之一!** 函數是大型程式的基礎組件。你將會建立的每個應用程式都會使用函數來組織、重用和架構程式碼。你現在了解如何將邏輯包裝成可重用的組件,讓你成為更有效率和更具生產力的程式設計師。歡迎來到模組化程式設計的世界! 🚀
---
**免責聲明**
此文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原始語言的文件作為權威來源。對於關鍵資訊,建議使用專業的人工作業翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋不承擔責任。
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件由 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於確保翻譯的準確性,但請注意自動翻譯可能存在錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要資訊,建議採用專業人工翻譯。我們對因使用本翻譯而產生的任何誤解或誤譯概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,25 +1,82 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "8973f96157680a13e9446e4bb540ee57",
"translation_date": "2025-08-23T22:42:56+00:00",
"original_hash": "8328f58f4593b4671656ff8f4b2edbd9",
"translation_date": "2026-01-06T12:16:19+00:00",
"source_file": "2-js-basics/2-functions-methods/assignment.md",
"language_code": "hk"
}
-->
# 玩轉函數
# 函數樂趣
## 指
## 指
創建不同的函數,包括會返回值的函數和不會返回值的函數
在這個作業中,你將練習創建不同類型的函數,以加強你對 JavaScript 函數、參數、預設值和回傳語句的理解
試試看能否創建一個同時包含普通參數和帶有預設值參數的函數。
建立一個名為 `functions-practice.js` 的 JavaScript 檔案,並實作以下函數:
### 第一部分:基本函數
1. **建立一個名為 `sayHello` 的函數**,不接受任何參數,並只在控制台輸出 "Hello!"。
2. **建立一個名為 `introduceYourself` 的函數**,接受一個 `name` 參數,並在控制台輸出類似 "Hi, my name is [name]" 的訊息。
### 第二部分:帶有預設參數的函數
3. **建立一個名為 `greetPerson` 的函數**,接受兩個參數:`name`(必需)和 `greeting`(可選,預設為 "Hello")。函數應該在控制台輸出類似 "[greeting], [name]!" 的訊息。
### 第三部分:回傳值的函數
4. **建立一個名為 `addNumbers` 的函數**,接受兩個參數(`num1` 和 `num2`)並回傳它們的和。
5. **建立一個名為 `createFullName` 的函數**,接受 `firstName``lastName` 參數,並回傳組合後的全名字串。
### 第四部分:綜合應用
6. **建立一個名為 `calculateTip` 的函數**,接受兩個參數:`billAmount`(必需)和 `tipPercentage`(可選,預設為 15。函數應該計算並回傳小費金額。
### 第五部分:測試你的函數
新增函數呼叫來測試每個函數,並使用 `console.log()` 顯示結果。
**範例測試呼叫:**
```javascript
// 在此測試你的函數
sayHello();
introduceYourself("Sarah");
greetPerson("Alex");
greetPerson("Maria", "Hi");
const sum = addNumbers(5, 3);
console.log(`The sum is: ${sum}`);
const fullName = createFullName("John", "Doe");
console.log(`Full name: ${fullName}`);
const tip = calculateTip(50);
console.log(`Tip for $50 bill: $${tip}`);
```
## 評分標準
| 評分標準 | 卓越 | 合格 | 需要改進 |
| -------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------ | ----------------- |
| | 提供的解決方案包含兩個或以上表現良好的函數,並且參數多樣化 | 提供的解決方案包含一個函數,並且參數較少 | 解決方案有錯誤 |
| 評分項目 | 優秀 | 及格 | 需改進 |
| -------- | --------- | -------- | ----------------- |
| **函數建立** | 6 個函數皆正確實作,符合語法與命名慣例 | 4-5 個函數正確實作,語法有輕微問題 | 3 個或更少函數有正確實作,或語法錯誤嚴重 |
| **參數與預設值** | 正確使用必需參數、可選參數及預設值 | 正確使用參數,但預設值有問題 | 參數實作錯誤或缺漏 |
| **回傳值** | 需回傳的函數皆正確回傳,不需回傳的函數僅執行動作 | 大部分回傳正確,有少許問題 | 回傳語句問題嚴重 |
| **程式碼品質** | 程式碼乾淨、有良好組織、有意義的變數名稱和適當縮排 | 程式碼可運作,但組織或清晰度可改善 | 程式碼難讀或結構不佳 |
| **測試** | 所有函數皆有適當測試並清楚顯示結果 | 大部分函數有測試 | 測試函數有限或錯誤 |
## 額外挑戰(選做)
如果你想更進一步挑戰自己:
1. **建立其中一個函數的箭頭函數版本**
2. **建立一個接受另一個函數作為參數的函數**(如課程中 `setTimeout` 範例)
3. **加入輸入驗證**,確保函數能優雅處理無效輸入
---
> 💡 **小提示**記得開啟你瀏覽器的開發者控制台F12以查看 `console.log()` 的輸出!
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。
本文件乃使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 所翻譯。儘管我們力求準確,但請注意自動翻譯可能包含錯誤或不準確之處。原始文件之原文版本應被視為權威來源。對於重要資訊,建議採用專業人工翻譯。本公司不對因使用本翻譯所引致的任何誤解或曲解承擔責任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,183 +1,477 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "f7009631b73556168ca435120a231c98",
"translation_date": "2025-08-29T15:05:09+00:00",
"original_hash": "c688385d15dd3645e924ea0ffee8967f",
"translation_date": "2026-01-06T12:18:41+00:00",
"source_file": "2-js-basics/3-making-decisions/README.md",
"language_code": "hk"
}
-->
# JavaScript 基礎:作出決策
![JavaScript 基礎 - 作出決策](../../../../translated_images/webdev101-js-decisions.69e1b20f272dd1f0b1cb2f8adaff3ed2a77c4f91db96d8a0594132a353fa189a.hk.png)
![JavaScript Basics - Making decisions](../../../../translated_images/webdev101-js-decisions.69e1b20f272dd1f0.hk.png)
> Sketchnote 由 [Tomomi Imura](https://twitter.com/girlie_mac) 製作
```mermaid
journey
title 你的 JavaScript 決策冒險
section 基礎
布林值: 5: 你
比較運算子: 4: 你
邏輯思考: 5: 你
section 基本決策
If 陳述句: 4: 你
If-Else 邏輯: 5: 你
Switch 陳述句: 4: 你
section 進階邏輯
邏輯運算子: 5: 你
複雜條件: 4: 你
三元運算式: 5: 你
```
你有沒有想過應用程式如何作出智慧決策?比如導航系統如何選擇最快路線,或者恆溫器如何決定何時開啟暖氣?這就是程式設計中決策的基本概念。
正如查爾斯·巴貝奇的分析機設計用以根據條件執行不同的操作序列,現代 JavaScript 程式需要根據不同情況做出選擇。這種分支和決策的能力讓靜態程式碼轉變為回應式且智能的應用。
> 由 [Tomomi Imura](https://twitter.com/girlie_mac) 繪製的筆記圖
在這堂課中,你將學習如何在程式中實作條件邏輯。我們將探討條件語句、比較運算符和邏輯表達式,使你的程式能評估情況並作出適當回應。
## 課前測驗
[課前測驗](https://ff-quizzes.netlify.app/web/quiz/11)
作出決策並控制程式碼執行的順序,能讓你的程式碼更具重用性和穩健性。本節將介紹在 JavaScript 中控制資料流的語法,以及當與布林值結合使用時的重要性。
能夠做出決策並控制程式流程是程式設計的基本面向。本節涵蓋如何使用布林值與條件邏輯控制 JavaScript 程式的執行路徑
[![作出決策](https://img.youtube.com/vi/SxTp8j-fMMY/0.jpg)](https://youtube.com/watch?v=SxTp8j-fMMY "作出決策")
[![Making Decisions](https://img.youtube.com/vi/SxTp8j-fMMY/0.jpg)](https://youtube.com/watch?v=SxTp8j-fMMY "Making Decisions")
> 🎥 點擊上方圖片觀看有關作出決策的影片。
> 你可以在 [Microsoft Learn](https://docs.microsoft.com/learn/modules/web-development-101-if-else/?WT.mc_id=academic-77807-sagibbon) 上學習這節課!
## 布林值簡介
布林值只有兩個可能的值:`true` 或 `false`。布林值用於決定在特定條件下應執行哪些程式碼。
你可以這樣設定布林值為 true 或 false
`let myTrueBool = true`
`let myFalseBool = false`
> 你也可以在 [Microsoft Learn](https://docs.microsoft.com/learn/modules/web-development-101-if-else/?WT.mc_id=academic-77807-sagibbon) 上學習此課程!
```mermaid
mindmap
root((決策制定))
Boolean Logic
true/false
比較結果
邏輯表達式
Conditional Statements
if statements
單一條件
程式碼執行
if-else
兩個路徑
替代動作
switch
多個選項
清晰結構
Operators
Comparison
=== !== < > <= >=
價值關係
Logical
&& || !
結合條件
Advanced Patterns
Ternary
? : 語法
內嵌決策
Complex Logic
嵌套條件
多重標準
```
## 布林值簡短回顧
✅ 布林值是以英國數學家、哲學家和邏輯學家 George Boole (18151864) 的名字命名的。
在探索決策前,讓我們回顧前一課提過的布林值。此值以數學家喬治·布爾命名,代表二元狀態——`true` 或 `false`,無模糊空間,沒有中間值
## 比較運算符與布林值
這些二元值構成所有計算邏輯的基礎。你程式做出的每個決策最終都會被簡化為布林評估。
運算符用於通過比較來評估條件,並生成布林值。以下是一些常用的運算符列表。
建立布林變數相當簡單:
| 符號 | 描述 | 範例 |
| ----- | ------------------------------------------------------------------------------------------------------------------------------------------ | ------------------- |
| `<` | **小於**:比較兩個值,若左邊的值小於右邊,則返回布林值 `true` | `5 < 6 // true` |
| `<=` | **小於或等於**:比較兩個值,若左邊的值小於或等於右邊,則返回布林值 `true` | `5 <= 6 // true` |
| `>` | **大於**:比較兩個值,若左邊的值大於右邊,則返回布林值 `true` | `5 > 6 // false` |
| `>=` | **大於或等於**:比較兩個值,若左邊的值大於或等於右邊,則返回布林值 `true` | `5 >= 6 // false` |
| `===` | **嚴格相等**:比較兩個值,若左右兩邊的值相等且類型相同,則返回布林值 `true` | `5 === 6 // false` |
| `!==` | **不相等**:比較兩個值,返回與嚴格相等運算符相反的布林值 | `5 !== 6 // true` |
```javascript
let myTrueBool = true;
let myFalseBool = false;
```
✅ 在瀏覽器的控制台中寫一些比較來檢查你的知識。是否有任何返回的數據讓你感到驚訝?
此程式建立兩個顯式布林值變數。
✅ 布林值名稱來自英國數學家、哲學家及邏輯學家喬治·布爾18151864
## 比較運算子與布林值
在實務中,你很少會手動設布林值。相反地,你會透過評估條件來產生它們:「這個數字是否大於那個?」或者「這些值相等嗎?」
比較運算子讓你進行這些評估。它們比較值並根據運算元間的關係回傳布林結果。
| 符號 | 說明 | 範例 |
| ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
| `<` | **小於**:比較兩個值,當左邊值小於右邊時回傳 `true` 布林類型 | `5 < 6 // true` |
| `<=` | **小於或等於**:比較兩個值,當左邊值小於或等於右邊時回傳 `true` | `5 <= 6 // true` |
| `>` | **大於**:比較兩個值,當左邊值大於右邊時回傳 `true` | `5 > 6 // false` |
| `>=` | **大於或等於**:比較兩個值,當左邊值大於或等於右邊時回傳 `true` | `5 >= 6 // false` |
| `===` | **嚴格相等**:比較兩值且值與資料型態相同時回傳 `true` | `5 === 6 // false` |
| `!==` | **不等於**:比較兩值並回傳與嚴格相等比較相反的布林值 | `5 !== 6 // true` |
✅ 在瀏覽器主控台動手寫些比較來測試你的理解,有沒有什麼回傳結果讓你感到意外?
```mermaid
flowchart LR
A["🔢 數值"] --> B["⚖️ 比較"]
B --> C["✅ 布林結果"]
D["5"] --> E["< 6"]
E --> F["真"]
G["10"] --> H["=== '10'"]
H --> I["假"]
J["'hello'"] --> K["!== 'world'"]
K --> L["真"]
M["📋 運算子類型"] --> M1["相等:=== !=="]
M --> M2["關係:< > <= >="]
M --> M3["嚴格與寬鬆"]
style A fill:#e3f2fd
style C fill:#e8f5e8
style M fill:#fff3e0
```
### 🧠 **比較運算邏輯測驗:理解布林邏輯**
**測試你對比較的理解:**
- 你認為為什麼通常偏好使用 `===`(嚴格相等)而不是 `==`(寬鬆相等)?
- 你能預測 `5 === '5'``5 == '5'` 的回傳結果嗎?
- `!==``!=` 有什麼差異?
```mermaid
stateDiagram-v2
[*] --> Comparison: 兩個值
Comparison --> StrictEqual: === 或 !==
Comparison --> Relational: < > <= >=
StrictEqual --> TypeCheck: 檢查類型和數值
Relational --> NumberCompare: 轉換為數字
TypeCheck --> BooleanResult: true 或 false
NumberCompare --> BooleanResult
note right of StrictEqual
首選方法
不進行類型轉換
end note
note right of Relational
適合範圍判斷
數值比較
end note
```
> **專家提示**:除非特別需要型別轉換,否則使用 `===``!==` 來比較相等性,以避免意外行為!
## If 語句
`if` 語句會在條件為 true 時執行其區塊內的程式碼。
`if` 語句就像在程式碼裡問問題:「如果這個條件成立,就執行這件事。」這應該是你在 JavaScript 中做決策最重要的工具。
它的工作原理如下:
```javascript
if (condition) {
//Condition is true. Code in this block will run.
// 條件為真。此區塊內的代碼將會執行。
}
```
邏輯運算符通常用於構成條件。
條件放在括號內,如果為 `true`JavaScript 就會執行大括號內的程式碼。若為 `false`,則跳過整個區塊。
你通常會使用比較運算子來建立這些條件。來看看一個實例:
```javascript
let currentMoney;
let laptopPrice;
let currentMoney = 1000;
let laptopPrice = 800;
if (currentMoney >= laptopPrice) {
//Condition is true. Code in this block will run.
// 條件為真。此區塊內的代碼將會執行。
console.log("Getting a new laptop!");
}
```
因為 `1000 >= 800` 評估為 `true`,所以區塊內的程式碼會執行,控制台顯示 "Getting a new laptop!"。
```mermaid
flowchart TD
A["🚀 程式開始"] --> B{"💰 currentMoney >= laptopPrice"}
B -->|true| C["🎉 '買新手提電腦!'"]
B -->|false| D["⏭️ 跳過程式碼區塊"]
C --> E["📋 繼續程式"]
D --> E
F["📊 If 陳述式結構"] --> F1["if (condition) {"]
F1 --> F2[" // 如果為真要執行的程式"]
F2 --> F3["}"]
style B fill:#fff3e0
style C fill:#e8f5e8
style D fill:#ffebee
style F fill:#e3f2fd
```
## If..Else 語句
`else` 語句會在條件為 false 時執行其區塊內的程式碼。它是 `if` 語句的可選部分。
但是如果你想讓程式在條件為 `false` 時做不同的事呢?這時就用到 `else`——它就像備案。
`else` 語句讓你說:「如果這個條件不成立,改做這件事。」
```javascript
let currentMoney;
let laptopPrice;
let currentMoney = 500;
let laptopPrice = 800;
if (currentMoney >= laptopPrice) {
//Condition is true. Code in this block will run.
// 條件為真。此區塊內的程式碼將會執行。
console.log("Getting a new laptop!");
} else {
//Condition is false. Code in this block will run.
// 條件為假。此區塊內的程式碼將會執行。
console.log("Can't afford a new laptop, yet!");
}
```
✅ 在瀏覽器控制台中運行這段程式碼以及以下程式碼來測試你的理解。更改 `currentMoney``laptopPrice` 變數的值,觀察 `console.log()` 的返回結果。
因為 `500 >= 800``false`JavaScript 會跳過第一個區塊,執行 `else` 區塊。你會在控制台看到 "Can't afford a new laptop, yet!"。
✅ 在瀏覽器主控台測試這段程式碼以及下方程式碼。透過改變 currentMoney 和 laptopPrice 變數的值,看看控制台輸出有何不同。
### 🎯 **If-Else 邏輯檢核:分支路徑**
**評估你的條件邏輯理解:**
- 如果 `currentMoney` 恰好等於 `laptopPrice`,程式會怎麼執行?
- 你能想到什麼現實情景中 if-else 邏輯特別有用嗎?
- 你會如何擴展此邏輯來處理多個價位範圍?
```mermaid
flowchart TD
A["🔍 評估條件"] --> B{"條件為真?"}
B -->|是| C["📤 執行 IF 區塊"]
B -->|否| D["📥 執行 ELSE 區塊"]
C --> E["✅ 採取一條路徑"]
D --> E
F["🌐 實際範例"] --> F1["用戶登入狀態"]
F --> F2["年齡驗證"]
F --> F3["表單驗證"]
F --> F4["遊戲狀態變更"]
style B fill:#fff3e0
style C fill:#e8f5e8
style D fill:#e3f2fd
style F fill:#f3e5f5
```
> **關鍵洞見**If-else 確保執行路徑唯一,保證程式對任何條件都有明確回應!
## Switch 語句
`switch` 語句用於根據不同的條件執行不同的操作。使用 `switch` 語句可以選擇執行多個程式碼區塊中的一個。
有時候你需要將一個值與多個選項比較。雖然可以用多個 `if..else` 串接,但寫起來會很難管理。`switch` 語句提供更清晰的結構來處理多個離散值。
這個概念類似早期電話交換機中的機械切換系統——一個輸入值決定執行哪條路徑。
```javascript
switch (expression) {
case x:
// code block
// 程式碼區塊
break;
case y:
// code block
// 程式碼區塊
break;
default:
// code block
// 程式碼區塊
}
```
其結構如下:
- JavaScript 執行表達式一次
- 搜尋每個 `case` 以尋找匹配
- 找到匹配時執行該代碼塊
- `break` 告訴 JavaScript 停止並跳出 switch
- 若沒有匹配,執行 `default` 區塊(若有)
```javascript
// program using switch statement
let a = 2;
// 使用 switch 陳述式處理星期幾的程式
let dayNumber = 2;
let dayName;
switch (a) {
switch (dayNumber) {
case 1:
a = "one";
dayName = "Monday";
break;
case 2:
a = "two";
dayName = "Tuesday";
break;
case 3:
dayName = "Wednesday";
break;
default:
a = "not found";
dayName = "Unknown day";
break;
}
console.log(`The value is ${a}`);
console.log(`Today is ${dayName}`);
```
✅ 在瀏覽器控制台中運行這段程式碼以及以下程式碼來測試你的理解。更改變數 `a` 的值,觀察 `console.log()` 的返回結果。
## 邏輯運算符與布林值
在此範例中JavaScript 發現 `dayNumber``2`,找到相符的 `case 2`,設定 `dayName` 為 "Tuesday",然後跳出 switch。結果在控制台印出 "Today is Tuesday"。
```mermaid
flowchart TD
A["📥 switch(表達式)"] --> B["🔍 評估一次"]
B --> C{"匹配情況 1"}
C -->|是| D["📋 執行情況 1"]
C -->|否| E{"匹配情況 2"}
E -->|是| F["📋 執行情況 2"]
E -->|否| G{"匹配情況 3"}
G -->|是| H["📋 執行情況 3"]
G -->|否| I["📋 執行預設"]
D --> J["🛑 中斷"]
F --> K["🛑 中斷"]
H --> L["🛑 中斷"]
J --> M["✅ 離開 switch"]
K --> M
L --> M
I --> M
style A fill:#e3f2fd
style B fill:#fff3e0
style M fill:#e8f5e8
```
✅ 在瀏覽器主控台測試這段程式碼以及下方程式碼。改變變數 a 的值看看控制台印出了什麼。
決策可能需要多個比較,並可以通過邏輯運算符串聯起來生成布林值。
### 🔄 **Switch 語句掌握:多重選擇**
| 符號 | 描述 | 範例 |
| ----- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| `&&` | **邏輯 AND**:比較兩個布林表達式。僅當兩邊都為 true 時返回 true | `(5 > 6) && (5 < 6 ) //一邊為 false另一邊為 true。返回 false` |
| `\|\|` | **邏輯 OR**:比較兩個布林表達式。只要有一邊為 true 就返回 true | `(5 > 6) \|\| (5 < 6) //一邊為 false另一邊為 true。返回 true` |
| `!` | **邏輯 NOT**:返回布林表達式的相反值 | `!(5 > 6) // 5 不大於 6但 "!" 會返回 true` |
**測試你對 switch 理解:**
- 如果忘了寫 `break` 會發生什麼事?
- 何時你會用 `switch` 而不是多個 `if-else`
- 即使你以為已覆蓋所有可能性,為什麼 `default` 仍然有用?
## 使用邏輯運算符的條件與決策
```mermaid
pie title "何時使用每種決策結構"
"簡單的 if-else" : 40
"複雜的 if-else 連鎖" : 25
"Switch 陳述式" : 20
"三元運算子" : 15
```
> **最佳實踐**:當比較單一變數對多個特定值時使用 `switch`。範圍檢查或複雜條件則用 `if-else`
## 邏輯運算子與布林值
複雜決策通常需要同時評估多個條件。就像布林代數讓數學家能結合邏輯表達式一樣,程式設計提供邏輯運算子來連接多個布林條件。
這些運算子讓你能結合簡單真偽判斷,寫出複雜條件邏輯。
| 符號 | 說明 | 範例 |
| ------ | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------ |
| `&&` | **邏輯且 AND**:比較兩個布林表達式。只有兩邊都為真時回傳真 | `(5 > 3) && (5 < 10) // 兩邊都為真。回傳真` |
| `\|\|` | **邏輯或 OR**:比較兩個布林表達式。至少一方為真時回傳真 | `(5 > 10) \|\| (5 < 10) // 一方為假另一方為真。回傳真` |
| `!` | **邏輯非 NOT**:回傳布林表達式的相反值 | `!(5 > 10) // 5 不大於 10"!" 使其變成真` |
這些運算子讓你用很有用的方式結合條件:
- AND (`&&`) 代表兩個條件都必須成立
- OR (`||`) 代表至少一個條件成立
- NOT (`!`) 把真變假,假變真
```mermaid
flowchart LR
A["🔗 邏輯運算子"] --> B["&& 且 (AND)"]
A --> C["|| 或 (OR)"]
A --> D["! 非 (NOT)"]
B --> B1["兩者皆須為真"]
B --> B2["true && true = true"]
B --> B3["true && false = false"]
C --> C1["至少一個為真"]
C --> C2["true || false = true"]
C --> C3["false || false = false"]
D --> D1["反轉數值"]
D --> D2["!true = false"]
D --> D3["!false = true"]
E["🌍 實際範例"] --> E1["年齡 >= 18 且 有駕照"]
E --> E2["是週末 || 是假日"]
E --> E3["未登入"]
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
style E fill:#e0f2f1
```
## 結合邏輯運算子的條件與決策
邏輯運算符可以用於在 `if..else` 語句中構成條件。
我們用一個較實際的範例來看看這些邏輯運算子:
```javascript
let currentMoney;
let laptopPrice;
let laptopDiscountPrice = laptopPrice - laptopPrice * 0.2; //Laptop price at 20 percent off
let currentMoney = 600;
let laptopPrice = 800;
let laptopDiscountPrice = laptopPrice - (laptopPrice * 0.2); // 筆記型電腦價格打八折
if (currentMoney >= laptopPrice || currentMoney >= laptopDiscountPrice) {
//Condition is true. Code in this block will run.
// 條件為真。此區塊內的程式碼會執行。
console.log("Getting a new laptop!");
} else {
//Condition is true. Code in this block will run.
// 條件為假。此區塊內的程式碼會執行。
console.log("Can't afford a new laptop, yet!");
}
```
### 否定運算符
此例中:我們計算 20% 折扣價640接著評估是否有足夠資金支付原價或折扣價。因 600 低於 640條件會是 `false` 吗?(譯者註:請以原文程式碼檢視詳細結果)
### 🧮 **邏輯運算子檢核:結合條件**
**測試你對邏輯運算子的理解:**
- 在 `A && B` 表達式中,如果 A 為假會怎樣B 還會不會被評估?
- 你能想到什麼情況需同時使用三種運算子 (`&&, ||, !`)
- `!user.isActive``user.isActive !== true` 有什麼差別?
```mermaid
stateDiagram-v2
[*] --> EvaluateA: A && B
EvaluateA --> CheckB: A 為真
EvaluateA --> ReturnFalse: A 為假
CheckB --> ReturnTrue: B 為真
CheckB --> ReturnFalse: B 為假
[*] --> EvaluateC: A || B
EvaluateC --> ReturnTrue: A 為真
EvaluateC --> CheckD: A 為假
CheckD --> ReturnTrue: B 為真
CheckD --> ReturnFalse: B 為假
note right of EvaluateA
短路求值:
如果 A 為假B 永遠不會被檢查
end note
```
> **效能提示**JavaScript 使用「短路評估」——在 `A && B` 中,如果 A 為假B 就不會被評估。善用此特性!
### 否定運算子
到目前為止,你已經看到如何使用 `if...else` 語句來創建條件邏輯。任何進入 `if` 的內容都需要評估為 true 或 false。通過使用 `!` 運算符,你可以**否定**表達式。它看起來像這樣:
有時候反過來思考更容易。比如不是問「使用者有沒有登入?」而是「使用者沒有登入嗎?」驚嘆號 (`!`) 運算子會幫你反轉邏輯。
```javascript
if (!condition) {
// runs if condition is false
// 當條件為假時執行
} else {
// runs if condition is true
// 當條件為真時執行
}
```
### 三元運算符
`!` 運算子就像說「相反的…」——如果是 `true`,它變 `false`,反之亦然。
### 三元運算子
`if...else` 不是表達決策邏輯的唯一方式。你還可以使用一種稱為三元運算符的東西。其語法如下:
對簡單條件賦值JavaScript 提供了 **三元運算子**。這種簡潔語法讓你能在一行寫條件,適合根據條件賦予兩種值之一。
```javascript
let variable = condition ? <return this if true> : <return this if false>
let variable = condition ? returnThisIfTrue : returnThisIfFalse;
```
以下是一個更具體的例子:
它讀起來像個問題:「這個條件成立嗎?若是,用這個值。否則用那個值。」
下面是一個更具體例子:
```javascript
let firstNumber = 20;
@ -185,15 +479,11 @@ let secondNumber = 10;
let biggestNumber = firstNumber > secondNumber ? firstNumber : secondNumber;
```
✅ 花點時間多讀幾遍這段程式碼。你理解這些運算符是如何工作的嗎?
上述程式碼的意思是:
✅ 花點時間多讀幾遍這段程式碼。你理解這些運算子是如何運作的嗎?
- 如果 `firstNumber` 大於 `secondNumber`
- 那麼將 `firstNumber` 賦值給 `biggestNumber`
- 否則將 `secondNumber` 賦值給 `biggestNumber`
這行程式碼的意思是:「`firstNumber` 是否大於 `secondNumber`?是的話,`biggestNumber` 等於 `firstNumber`;否則等於 `secondNumber`。」
三元運算符只是以下程式碼的簡潔寫法:
三元運算子其實是這段傳統 `if..else` 程式碼的簡短寫法:
```javascript
let biggestNumber;
@ -204,29 +494,179 @@ if (firstNumber > secondNumber) {
}
```
兩種寫法結果相同。三元運算子簡潔,傳統 if-else 可能在複雜條件下較易讀。
```mermaid
flowchart LR
A["🤔 三元運算子"] --> B["條件 ?"]
B --> C["條件為真 :"]
C --> D["條件為假"]
E["📝 傳統 If-Else"] --> F["if (條件) {"]
F --> G[" 返回 條件為真"]
G --> H["} else {"]
H --> I[" 返回 條件為假"]
I --> J["}"]
K["⚡ 何時使用"] --> K1["簡單賦值"]
K --> K2["短條件"]
K --> K3["內嵌決策"]
K --> K4["返回語句"]
style A fill:#e3f2fd
style E fill:#fff3e0
style K fill:#e8f5e8
```
---
## 🚀 挑戰
創建一個程式,首先使用邏輯運算符編寫,然後使用三元運算符重寫。你更喜歡哪種語法?
先用邏輯運算子寫一段程式,再改寫成三元運算子語法。你偏好使用哪種語法?
---
## 課後測驗
## GitHub Copilot Agent 挑戰 🚀
使用 Agent 模式完成以下挑戰:
**說明:** 創建一個完整的成績計算器,展示本課的多種決策概念,包括 if-else 語句、switch 語句、邏輯運算子和三元運算子。
**指示:** 撰寫 JavaScript 程式輸入學生分數0-100依據以下標準判斷字母等級
- A: 90-100
- B: 80-89
- C: 70-79
- D: 60-69
- F: 60 以下
[課後測驗](https://ff-quizzes.netlify.app/web/quiz/12)
需求:
1. 使用 if-else 語句判斷字母成績
2. 使用邏輯運算子檢查學生是否通過grade >= 60且有榮譽grade >= 90
3. 使用 switch 陳述式為每個等第提供具體反饋
4. 使用三元運算子判斷學生是否符合下一課程資格grade >= 70
5. 包含輸入驗證以確保分數介於 0 到 100 之間
使用各種分數測試你的程式,包括邊界條件如 59、60、89、90 及無效輸入。
在此了解更多有關 [agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode)。
## 課後小測驗
[課後小測驗](https://ff-quizzes.netlify.app/web/quiz/12)
## 複習與自學
閱讀更多有關用戶可用的運算符 [在 MDN 上](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators)。
閱讀更多使用者可用的各種運算子,[請見 MDN](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators)。
瀏覽 Josh Comeau 的精彩 [運算符查詢工具](https://joshwcomeau.com/operator-lookup/)
瀏覽 Josh Comeau 精彩的 [operator lookup](https://joshwcomeau.com/operator-lookup/)
## 作業
[運算符](assignment.md)
[運算子](assignment.md)
---
## 🧠 **你的決策工具箱總結**
```mermaid
graph TD
A["🎯 JavaScript 判斷"] --> B["🔍 布林邏輯"]
A --> C["📊 條件語句"]
A --> D["🔗 邏輯運算子"]
A --> E["⚡ 進階模式"]
B --> B1["true/false 值"]
B --> B2["比較運算子"]
B --> B3["真值概念"]
C --> C1["if 語句"]
C --> C2["if-else 鏈"]
C --> C3["switch 語句"]
D --> D1["&& (且)"]
D --> D2["|| (或)"]
D --> D3["! (非)"]
E --> E1["三元運算子"]
E --> E2["短路評估"]
E --> E3["複雜條件"]
F["💡 重要原則"] --> F1["明確易讀條件"]
F --> F2["一致比較風格"]
F --> F3["正確運算子優先權"]
F --> F4["高效評估順序"]
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
style E fill:#e0f2f1
style F fill:#fce4ec
```
---
## 🚀 你的 JavaScript 決策掌握時間表
### ⚡ **接下來 5 分鐘你可以做的事**
- [ ] 在瀏覽器主控台練習比較運算子
- [ ] 撰寫簡單的 if-else 陳述式檢查你的年齡
- [ ] 嘗試挑戰:使用三元運算子改寫 if-else
- [ ] 測試不同「真值」與「假值」的行為
### 🎯 **這小時內你能完成的目標**
- [ ] 完成課後小測驗並複習不熟悉的概念
- [ ] 建置 GitHub Copilot 挑戰的綜合成績計算機
- [ ] 為真實情境(如穿衣選擇)製作簡單決策樹
- [ ] 練習使用邏輯運算子結合多重條件
- [ ] 嘗試各種用途的 switch 陳述式
### 📅 **一週內的邏輯掌握計畫**
- [ ] 完成具創意範例的運算子作業
- [ ] 架設使用多種條件結構的迷你測驗應用
- [ ] 建立能檢查多項輸入條件的表單驗證器
- [ ] 練習 Josh Comeau 的 [operator lookup](https://joshwcomeau.com/operator-lookup/) 練習題
- [ ] 重構現有程式碼以使用更合適的條件結構
- [ ] 研讀短路評估與效能影響
### 🌟 **一個月的轉變計劃**
- [ ] 精通複雜的巢狀條件並維持程式可讀性
- [ ] 建置具精緻決策邏輯的應用程式
- [ ] 透過改進既有專案的條件邏輯參與開源貢獻
- [ ] 教導他人不同條件結構及使用時機
- [ ] 探索函式化程式設計在條件邏輯中的應用
- [ ] 建立個人條件判斷最佳實務參考指南
### 🏆 **決策大師最終檢閱**
**慶祝你的邏輯思維精通:**
- 你成功實作過最複雜的決策邏輯是什麼?
- 哪種條件結構對你而言最自然?為什麼?
- 學習邏輯運算子如何改變你的問題解決方式?
- 哪項現實世界的應用最能受益於精巧的決策邏輯?
```mermaid
journey
title 你的邏輯思維演進
section 今天
布林混淆: 3: You
If-Else 理解: 4: You
運算子識別: 5: You
section 本週
複雜條件: 4: You
Switch 精通: 5: You
邏輯組合: 5: You
section 下個月
高級模式: 5: You
效能意識: 5: You
教導他人: 5: You
```
> 🧠 **你已掌握數位決策的藝術!** 每一個互動式應用都依賴條件邏輯,才能智能回應用戶動作與變化的狀況。你現在懂得如何讓程式思考、評估並選擇適當回應。這個邏輯基礎將驅動你所建造的每一個動態應用!🎉
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
此文件已使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋不承擔責任。
本文件乃使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於確保準確性,但請注意自動翻譯可能存在錯誤或不準確之處。原始語言版本的文件應被視為具權威性的資料來源。對於重要資訊,建議尋求專業人工翻譯。我們不對使用本翻譯所引致的任何誤解或誤釋承擔責任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,52 +1,120 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "bf62b82567e6f9bdf4abda9ae0ccb64a",
"translation_date": "2025-08-23T22:39:37+00:00",
"original_hash": "ffe366b2d1f037b99fbadbe1dc81083d",
"translation_date": "2026-01-06T12:20:58+00:00",
"source_file": "2-js-basics/3-making-decisions/assignment.md",
"language_code": "hk"
}
-->
# 運算符
# Making Decisions: Student Grade Processor
## 指引
## Learning Objectives
試試運用運算符。以下是一個你可以實現的程式建議:
在這個作業中,你將透過建構一個處理不同評分系統中學生成績的程式,練習本課所學的決策製定概念。你將使用 `if...else` 陳述式、比較運算子及邏輯運算子以判斷哪些學生通過了課程。
你有一組來自兩個不同評分系統的學生。
## The Challenge
### 第一個評分系統
你為一所剛與另一機構合併的學校工作。現在你需要處理來自兩個完全不同評分系統的學生成績,並判斷哪些學生是及格的。這是練習條件邏輯的絕佳機會!
第一個評分系統的分數範圍是1到5其中3分或以上表示通過課程。
### Understanding the Grading Systems
### 第二個評分系統
#### First Grading System (Numeric)
- 成績以數字 1-5 表示
- **及格分數**3 及以上3、4 或 5
- **不及格分數**:低於 31 或 2
另一個評分系統的分數包括 `A, A-, B, B-, C, C-`,其中 `A` 是最高分,`C` 是最低的及格分數。
#### Second Grading System (Letter Grades)
- 成績使用字母:`A`、`A-`、`B`、`B-`、`C`、`C-`
- **及格成績**`A`、`A-`、`B`、`B-`、`C`、`C-`(此系統列出的所有成績均為及格)
- **注意**:此系統不包含像 `D``F` 這類不及格成績
### 任務
### Your Task
給定以下陣列 `allStudents`,表示所有學生及其分數,構建一個新的陣列 `studentsWhoPass`,包含所有通過的學生。
> TIP使用 for-loop 和 if...else 以及比較運算符:
給定以下的陣列 `allStudents` ,表示所有學生及其成績,請建立一個新陣列 `studentsWhoPass` ,其中包含根據各自評分系統判斷及格的所有學生。
```javascript
let allStudents = [
'A',
'B-',
1,
4,
5,
2
]
'A', // 字母等級 - 通過
'B-', // 字母等級 - 通過
1, // 數字等級 - 不及格
4, // 數字等級 - 通過
5, // 數字等級 - 通過
2 // 數字等級 - 不及格
];
let studentsWhoPass = [];
```
## 評分標準
### Step-by-Step Approach
1. **設置迴圈** 遍歷 `allStudents` 陣列中的每項成績
2. **檢查成績類型**(是數字還是字串?)
3. **套用適當的評分系統規則**
- 若是數字:檢查成績是否 >= 3
- 若是字串:檢查是否為有效的及格字母成績之一
4. **將及格的成績** 加入 `studentsWhoPass` 陣列
### Helpful Code Techniques
使用本課中提到的這些 JavaScript 概念:
- **typeof 運算子**:使用 `typeof grade === 'number'` 來檢查是否為數字成績
- **比較運算子**:使用 `>=` 比較數字成績
- **邏輯運算子**:使用 `||` 來檢查多個字母成績條件
- **if...else 陳述式**:處理不同評分系統
- **陣列方法**:使用 `.push()` 將通過的成績加入新陣列
### Expected Output
執行程式後,`studentsWhoPass` 應該包含:`['A', 'B-', 4, 5]`
**這些成績為何能通過:**
- `'A'``'B-'` 是有效的字母成績(此系統所有字母成績均及格)
- `4``5` 是數字成績且 >= 3
- `1``2` 失敗,因它們是數字成績且 < 3
## Testing Your Solution
使用不同情境測試你的程式碼:
```javascript
// 使用不同的分數組合進行測試
let testGrades1 = ['A-', 3, 'C', 1, 'B'];
let testGrades2 = [5, 'A', 2, 'C-', 4];
// 你的解決方案應該能適用於任何有效分數的組合
```
## Bonus Challenges
完成基本作業後,嘗試以下擴充任務:
1. **新增驗證**:檢查無效成績(例如負數或無效字母)
2. **計算統計**:計算及格與不及格學生人數
3. **成績轉換**將所有成績轉換成單一數字系統A=5、B=4、C=3 等)
## Rubric
| Criteria | Exemplary (4) | Proficient (3) | Developing (2) | Beginning (1) |
|----------|---------------|----------------|----------------|---------------|
| **Functionality** | 程式正確辨別兩系統所有及格成績 | 程式可運作但有些小問題或邊界狀況 | 程式部分運作但存在邏輯錯誤 | 程式有嚴重錯誤或無法執行 |
| **Code Structure** | 程式碼簡潔、有組織且適當使用 if...else 邏輯 | 結構良好,條件陳述適當 | 結構尚可但有組織問題 | 結構差,邏輯難以理解 |
| **Use of Concepts** | 有效運用比較運算子、邏輯運算子與條件陳述式 | 良好使用課程概念但有少許不足 | 部分使用課程概念,缺少重要部分 | 使用課程概念有限 |
| **Problem Solving** | 清楚理解問題並提出優雅解決方案 | 問題解決思路良好且邏輯紮實 | 合理的解決思路,但有些混亂 | 解決方法不清楚,未展現理解 |
## Submission Guidelines
1. **徹底測試你的程式碼**,使用提供的範例
2. **添加註解**,說明邏輯,特別是條件陳述部分
3. **核對輸出**是否符合預期結果:`['A', 'B-', 4, 5]`
4. **考慮邊界狀況**,如空陣列或意外的資料型別
> 💡 **專家小提示**:從簡單開始!先讓基本功能正常運作,再加入更複雜的功能。記得,本課的目標是練習決策邏輯並運用你所學的工具。
| 標準 | 優秀表現 | 合格表現 | 需要改進 |
| -------- | ---------------------------- | ---------------------------- | ---------------------------- |
| | 提供完整的解決方案 | 提供部分解決方案 | 提供有錯誤的解決方案 |
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。
本文件由 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們力求準確,請注意自動翻譯可能包含錯誤或不準確之處。原文文件以其母語版本為準。如涉及重要資訊,建議尋求專業人工翻譯。本公司不對因使用本翻譯所引致的任何誤解或曲解承擔責任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,147 +1,750 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "9029f96b0e034839c1799f4595e4bb66",
"translation_date": "2025-08-29T15:06:10+00:00",
"original_hash": "1710a50a519a6e4a1b40a5638783018d",
"translation_date": "2026-01-06T12:21:18+00:00",
"source_file": "2-js-basics/4-arrays-loops/README.md",
"language_code": "hk"
}
-->
# JavaScript 基礎:陣列與迴圈
![JavaScript 基礎 - 陣列](../../../../translated_images/webdev101-js-arrays.439d7528b8a294558d0e4302e448d193f8ad7495cc407539cc81f1afe904b470.hk.png)
> Sketchnote by [Tomomi Imura](https://twitter.com/girlie_mac)
![JavaScript Basics - Arrays](../../../../translated_images/webdev101-js-arrays.439d7528b8a29455.hk.png)
> 筆記作者:[Tomomi Imura](https://twitter.com/girlie_mac)
```mermaid
journey
title 你的陣列與迴圈歷險
section 陣列基礎
建立陣列: 5: 你
存取元素: 4: 你
陣列方法: 5: 你
section 迴圈精通
For 迴圈: 4: 你
While 迴圈: 5: 你
現代語法: 4: 你
section 資料處理
陣列 + 迴圈: 5: 你
實際應用: 4: 你
效能優化: 5: 你
```
## 課前測驗
[課前測驗](https://ff-quizzes.netlify.app/web/quiz/13)
這節課涵蓋了 JavaScript 的基礎知識,這是一種為網頁提供互動性的語言。在這節課中,你將學習有關陣列和迴圈的知識,這些工具用於操作數據。
有沒有想過網站如何追蹤購物車的商品或顯示你的好友清單?這就是陣列與迴圈發揮作用的地方。陣列就像數位容器,可以存放多個資訊項目,而迴圈則讓你有效率地處理所有資料,避免重複撰寫程式碼。
這兩個觀念共同構成了處理程式中資訊的基礎。你將學會如何從手動撰寫每一步,轉變為創造聰明且高效率的程式碼,快速處理上百甚至上千個項目。
學完本課程後,你將能了解如何用少量程式碼完成複雜資料處理任務。讓我們來探索這些重要的程式設計概念。
[![Arrays](https://img.youtube.com/vi/1U4qTyq02Xw/0.jpg)](https://youtube.com/watch?v=1U4qTyq02Xw "Arrays")
[![Loops](https://img.youtube.com/vi/Eeh7pxtTZ3k/0.jpg)](https://www.youtube.com/watch?v=Eeh7pxtTZ3k "Loops")
> 🎥 點擊上述圖片觀看關於陣列與迴圈的影片。
> 你也可以在 [Microsoft Learn](https://docs.microsoft.com/learn/modules/web-development-101-arrays/?WT.mc_id=academic-77807-sagibbon) 參加這堂課程!
```mermaid
mindmap
root((數據處理))
Arrays
Structure
方括號語法
零起始索引
動態大小
Operations
push/pop
shift/unshift
indexOf/includes
Types
數字陣列
字串陣列
混合類型
Loops
For Loops
計數迭代
陣列處理
可預測流程
While Loops
基於條件
未知迭代次數
用戶輸入
Modern Syntax
for...of
forEach
函數式方法
Applications
Data Analysis
統計
過濾
轉換
User Interfaces
清單
功能表
圖庫
```
## 陣列
[![陣列](https://img.youtube.com/vi/1U4qTyq02Xw/0.jpg)](https://youtube.com/watch?v=1U4qTyq02Xw "陣列")
把陣列想像成數位檔案櫃——不像一個抽屜只能放一份文件,你可以在一個結構化的容器中組織多個相關項目。在程式語言中,陣列讓你能把多項資訊集中存放於一個有序的套件。
[![迴圈](https://img.youtube.com/vi/Eeh7pxtTZ3k/0.jpg)](https://www.youtube.com/watch?v=Eeh7pxtTZ3k "迴圈")
無論你在建立相片集、管理待辦事項清單,或是追蹤遊戲中的高分,陣列都提供了資料組織的基礎。讓我們看看它們的運作方式。
> 🎥 點擊上方圖片觀看有關陣列和迴圈的影片。
✅ 陣列就在我們周遭!你能舉出生活中陣列的例子嗎?例如太陽能電池板陣列?
> 你可以在 [Microsoft Learn](https://docs.microsoft.com/learn/modules/web-development-101-arrays/?WT.mc_id=academic-77807-sagibbon) 上學習這節課!
### 建立陣列
## 陣列
建立陣列非常簡單——只要用中括號!
處理數據是任何程式語言中的常見任務,而當數據以結構化格式(例如陣列)組織時,這項任務會變得更加簡單。使用陣列時,數據以類似清單的結構存儲。陣列的一大優勢是你可以在一個陣列中存儲不同類型的數據。
```javascript
// 空陣列 - 就好似一個空購物車等待加入商品
const myArray = [];
```
✅ 陣列在我們周圍隨處可見!你能想到一個現實生活中的陣列例子嗎,例如太陽能板陣列?
**這裡發生了什麼?**
你用中括號 `[]` 建立了一個空容器。就像一個空的圖書架,準備好放進你想整理的書本。
陣列的語法是一對方括號。
你也可以直接在建立的時候填入初始值:
```javascript
let myArray = [];
```
// 你的雪糕店口味菜單
const iceCreamFlavors = ["Chocolate", "Strawberry", "Vanilla", "Pistachio", "Rocky Road"];
這是一個空陣列,但陣列可以在宣告時已經填充數據。陣列中的多個值用逗號分隔。
// 用戶的個人資料資訊(混合不同類型的數據)
const userData = ["John", 25, true, "developer"];
```javascript
let iceCreamFlavors = ["Chocolate", "Strawberry", "Vanilla", "Pistachio", "Rocky Road"];
// 你最喜愛課程的考試分數
const scores = [95, 87, 92, 78, 85];
```
**有趣的觀察:**
- 你可以在同一個陣列裡存放文字、數字,甚至是 true/false 值
- 每項目以逗號分隔,非常簡單!
- 陣列非常適合把相關資訊整理在一起
```mermaid
flowchart LR
A["📦 陣列"] --> B["建立 [ ]"]
A --> C["儲存多個項目"]
A --> D["由索引存取"]
B --> B1["const arr = []"]
B --> B2["const arr = [1,2,3]"]
C --> C1["數字"]
C --> C2["字串"]
C --> C3["布林值"]
C --> C4["混合類型"]
D --> D1["arr[0] = 第一個"]
D --> D2["arr[1] = 第二個"]
D --> D3["arr[2] = 第三個"]
E["📊 陣列索引"] --> E1["索引 0: 第一個"]
E --> E2["索引 1: 第二個"]
E --> E3["索引 2: 第三個"]
E --> E4["索引 n-1: 最後一個"]
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
style E fill:#e0f2f1
```
### 陣列索引
陣列中的值會被分配一個獨特的值,稱為 **索引**,這是一個基於其距離陣列開頭的整數。在上面的例子中,字串值 "Chocolate" 的索引是 0而 "Rocky Road" 的索引是 4。使用索引和方括號可以檢索、更改或插入陣列中的值。
這點一開始可能會覺得奇怪:陣列裡的項目編號從 0 開始,而非 1。這種從零開始的編號方式源於電腦記憶體的運作原理是電腦語言如 C 語言)早期慣例。陣列中每個位置都有自己的位址編號,稱為**索引**
✅ 你是否感到驚訝,陣列的索引是從零開始的?在某些程式語言中,索引是從 1 開始的。這背後有一段有趣的歷史,你可以在 [Wikipedia](https://en.wikipedia.org/wiki/Zero-based_numbering) 上閱讀。
| 索引 | 值 | 說明 |
|-------|-------|-------------|
| 0 | "Chocolate" | 第一個元素 |
| 1 | "Strawberry" | 第二個元素 |
| 2 | "Vanilla" | 第三個元素 |
| 3 | "Pistachio" | 第四個元素 |
| 4 | "Rocky Road" | 第五個元素 |
✅ 陣列從零開始編號會讓你感到驚訝嗎?有些程式語言從 1 開始編號。這背後有有趣故事,你可以在[維基百科](https://en.wikipedia.org/wiki/Zero-based_numbering)閱讀了解。
**存取陣列元素:**
```javascript
let iceCreamFlavors = ["Chocolate", "Strawberry", "Vanilla", "Pistachio", "Rocky Road"];
iceCreamFlavors[2]; //"Vanilla"
const iceCreamFlavors = ["Chocolate", "Strawberry", "Vanilla", "Pistachio", "Rocky Road"];
// 使用括號標記法存取個別元素
console.log(iceCreamFlavors[0]); // "Chocolate" - 第一個元素
console.log(iceCreamFlavors[2]); // "Vanilla" - 第三個元素
console.log(iceCreamFlavors[4]); // "Rocky Road" - 最後一個元素
```
你可以利用索引來更改值,例如:
**解說:**
- **使用**中括號搭配索引號碼來取得元素
- **回傳**該位置上所儲存的值
- **從** 0 開始編號,第一個元素索引是 0
**修改陣列元素:**
```javascript
iceCreamFlavors[4] = "Butter Pecan"; //Changed "Rocky Road" to "Butter Pecan"
// 更改現有值
iceCreamFlavors[4] = "Butter Pecan";
console.log(iceCreamFlavors[4]); // "牛油胡桃"
// 在結尾新增元素
iceCreamFlavors[5] = "Cookie Dough";
console.log(iceCreamFlavors[5]); // "曲奇麵糰"
```
你也可以在指定索引處插入新值,例如:
**上例中,我們:**
- **將**索引 4 處的元素從 "Rocky Road" 改為 "Butter Pecan"
- **新增**了一個索引 5 的元素 "Cookie Dough"
- **新增**元素時,陣列長度會自動擴展
### 陣列長度與常用方法
陣列內建屬性和方法,讓你更輕鬆地操作資料。
**取得陣列長度:**
```javascript
iceCreamFlavors[5] = "Cookie Dough"; //Added "Cookie Dough"
const iceCreamFlavors = ["Chocolate", "Strawberry", "Vanilla", "Pistachio", "Rocky Road"];
console.log(iceCreamFlavors.length); // 5
// 長度會隨陣列變動自動更新
iceCreamFlavors.push("Mint Chip");
console.log(iceCreamFlavors.length); // 6
```
✅ 更常見的方式是使用陣列操作符,例如 array.push(),來向陣列中添加值。
**重點摘要:**
- **回傳**陣列中元素的總數
- **元素增加或刪除時,長度會自動更新**
- **可用於迴圈或驗證元素數量**
要知道陣列中有多少項,可以使用 `length` 屬性。
**重要的陣列方法:**
```javascript
let iceCreamFlavors = ["Chocolate", "Strawberry", "Vanilla", "Pistachio", "Rocky Road"];
iceCreamFlavors.length; //5
const fruits = ["apple", "banana", "orange"];
// 新增元素
fruits.push("grape"); // 新增到尾端: ["apple", "banana", "orange", "grape"]
fruits.unshift("strawberry"); // 新增到開頭: ["strawberry", "apple", "banana", "orange", "grape"]
// 移除元素
const lastFruit = fruits.pop(); // 移除並返回 "grape"
const firstFruit = fruits.shift(); // 移除並返回 "strawberry"
// 尋找元素
const index = fruits.indexOf("banana"); // 返回 1 ( "banana" 的位置)
const hasApple = fruits.includes("apple"); // 返回 true
```
✅ 試試看!使用你的瀏覽器控制台創建並操作一個你自己設計的陣列。
**這些方法的作用:**
- `push()` 往陣列尾端新增元素,`unshift()` 往陣列前端新增元素
- `pop()` 從陣列尾端移除元素,`shift()` 從陣列前端移除元素
- `indexOf()` 找出元素索引,`includes()` 檢查元素是否存在
- 回傳相關值,如被移除的元素或索引位置
✅ 試試看!打開瀏覽器的控制台,創建並操作屬於你自己的陣列。
### 🧠 **陣列基礎檢核:組織你的資料**
**挑戰你的陣列概念:**
- 為什麼陣列的計數是從 0 開始,而不是從 1
- 如果你試著存取不存在的索引(像是在五元素陣列中用 `arr[100]`)會發生什麼?
- 你能想到三個現實世界中陣列的應用情境嗎?
```mermaid
stateDiagram-v2
[*] --> EmptyArray: const arr = []
EmptyArray --> WithItems: 新增元素
WithItems --> Accessing: 使用索引
Accessing --> Modifying: 更改數值
Modifying --> Processing: 使用方法
WithItems --> WithItems: push(), unshift()
Processing --> Processing: pop(), shift()
note right of Accessing
零基索引
arr[0] = 第一個元素
end note
note right of Processing
內建方法
動態操作
end note
```
> **實際應用觀察**:陣列在程式設計中無處不在!社群動態、購物車、相簿、播放清單歌曲 - 這些背後都是陣列!
## 迴圈
迴圈允許我們執行重複或 **迭代** 的任務可以節省大量時間和代碼。每次迭代可以在變數、值和條件上有所不同。JavaScript 中有不同類型的迴圈,它們之間有些微差異,但基本上都做同樣的事情:迭代數據。
想像狄更斯小說中學生懲罰要在黑板上不停抄寫句子。試想你若能簡單指示「把這句話寫 100 次」,然後自動完成。這正是迴圈在程式中的作用。
迴圈就像是勤快又不會疲倦的助手,能無誤地重複執行任務。無論你要檢查購物車每個商品,還是顯示相簿全部照片,迴圈都能有效處理重複動作。
JavaScript 提供多種迴圈形式。讓我們逐一瞭解它們,以及適合何時使用。
```mermaid
flowchart TD
A["🔄 迴圈類型"] --> B["For 迴圈"]
A --> C["While 迴圈"]
A --> D["For...of 迴圈"]
A --> E["forEach 方法"]
B --> B1["已知次數"]
B --> B2["基於計數器"]
B --> B3["for(init; 條件; 增量)"]
C --> C1["未知次數"]
C --> C2["基於條件"]
C --> C3["while(條件)"]
D --> D1["現代 ES6+"]
D --> D2["陣列迭代"]
D --> D3["for(item of 陣列)"]
E --> E1["函式風格"]
E --> E2["陣列方法"]
E --> E3["array.forEach(回呼函數)"]
F["⏰ 何時使用"] --> F1["For計數、索引"]
F --> F2["While用戶輸入、搜尋"]
F --> F3["For...of簡單迭代"]
F --> F4["forEach函式式編程"]
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
style E fill:#e0f2f1
style F fill:#fce4ec
```
### For 迴圈
`for` 迴圈需要三個部分來進行迭代:
- `counter` 一個變數,通常初始化為一個數字,用於計算迭代次數
- `condition` 使用比較運算符的表達式,當結果為 `false` 時迴圈停止
- `iteration-expression` 在每次迭代結束時執行,通常用於更改計數器的值
`for` 迴圈就像設定計時器一樣——你確定想執行幾次。它十分有組織且可預期,特別適合操作陣列或計數任務。
**For 迴圈結構:**
| 元件 | 目的 | 範例 |
|-----------|---------|----------|
| **初始化** | 設定起點 | `let i = 0` |
| **條件** | 何時繼續 | `i < 10` |
| **增量** | 如何更新 | `i++` |
```javascript
// Counting up to 10
// 從0數到9
for (let i = 0; i < 10; i++) {
console.log(i);
console.log(`Count: ${i}`);
}
// 更實用的例子:處理分數
const testScores = [85, 92, 78, 96, 88];
for (let i = 0; i < testScores.length; i++) {
console.log(`Student ${i + 1}: ${testScores[i]}%`);
}
```
✅ 在瀏覽器控制台中運行這段代碼。當你對計數器、條件或迭代表達式進行小改動時會發生什麼?你能讓它倒著運行,創建一個倒數嗎?
**一步步發生什麼:**
- **初始化**計數器變數 `i` 為 0
- **條件判斷**在每次迴圈開始前:`i < 10`
- **條件為真時**執行程式區塊
- **每次迴圈後**用 `i++` 增加 `i` 的值 1
- **當條件不成立(`i` 達 10時**停止
✅ 在瀏覽器控制台執行此程式。若你更改計數器、條件或增量部分,會有什麼變化?你能控制使它倒數嗎?
### 🗓️ **For 迴圈精通檢核:有掌控力的重複**
**考考你對 for 迴圈的理解:**
- for 迴圈有哪三個部分?它們分別負責什麼?
- 如何反向遍歷陣列?
- 忘記寫增量部分 (`i++`) 會怎樣?
```mermaid
flowchart TD
A["🚀 開始 For 迴圈"] --> B["初始化: let i = 0"]
B --> C{"條件: i < array.length?"}
C -->|true| D["執行程式區塊"]
D --> E["遞增: i++"]
E --> C
C -->|false| F["✅ 離開迴圈"]
G["📋 常見範例"] --> G1["for(let i=0; i<n; i++)"]
G --> G2["for(let i=n-1; i>=0; i--)"]
G --> G3["for(let i=0; i<arr.length; i+=2)"]
style A fill:#e3f2fd
style F fill:#e8f5e8
style G fill:#fff3e0
```
> **迴圈智慧**for 迴圈最適合你確定需要執行多少次的場合。它是陣列處理最常見的選擇!
### While 迴圈
`for` 迴圈的語法不同,`while` 迴圈只需要一個條件,當條件變為 `false` 時迴圈停止。迴圈中的條件通常依賴於其他值,例如計數器,並且必須在迴圈中進行管理。計數器的初始值必須在迴圈外創建,並且任何滿足條件的表達式(包括更改計數器)都必須在迴圈內維護。
`while` 迴圈就像說「繼續做這件事直到...」——你可能不知道究竟會跑幾次,但知道何時停下。它非常適用向使用者重複要求輸入直到符合條件,或搜尋資料直到找到目標。
**While 迴圈特性:**
- **只要條件為真**就會持續執行
- **需要自行管理**計數器變數
- **條件判斷**在每次迴圈前執行
- **若條件不會成假值,可能造成無限迴圈**
```javascript
//Counting up to 10
// 基本計數範例
let i = 0;
while (i < 10) {
console.log(i);
i++;
console.log(`While count: ${i}`);
i++; // 唔好忘記遞增!
}
// 更實用嘅例子:處理用戶輸入
let userInput = "";
let attempts = 0;
const maxAttempts = 3;
while (userInput !== "quit" && attempts < maxAttempts) {
userInput = prompt(`Enter 'quit' to exit (attempt ${attempts + 1}):`);
attempts++;
}
if (attempts >= maxAttempts) {
console.log("Maximum attempts reached!");
}
```
✅ 為什麼你會選擇使用 for 迴圈而不是 while 迴圈17K 名觀眾在 StackOverflow 上有同樣的疑問,一些意見可能 [對你有趣](https://stackoverflow.com/questions/39969145/while-loops-vs-for-loops-in-javascript)。
**看懂這些範例:**
- **計數器 `i`** 在迴圈內手動遞增
- **避免**無限迴圈
- **案例示範** 使用者輸入及有限次數嘗試
- **安全機制**避免永遠執行的狀況
### ♾️ **While 迴圈智慧檢核:條件驅動的重複**
**挑戰你對 while 迴圈的理解:**
- 使用 while 迴圈最大危險是什麼?
- 什麼時候會選擇 while 迴圈而非 for 迴圈?
- 要怎麼防止無限迴圈?
```mermaid
flowchart LR
A["🔄 While vs For"] --> B["While 迴圈"]
A --> C["For 迴圈"]
B --> B1["不確定的迭代次數"]
B --> B2["條件驅動"]
B --> B3["用戶輸入,搜尋"]
B --> B4["⚠️ 風險:無限迴圈"]
C --> C1["已知的迭代次數"]
C --> C2["計數器驅動"]
C --> C3["陣列處理"]
C --> C4["✅ 安全:可預測結束"]
D["🛡️ 安全提示"] --> D1["總是修改條件變數"]
D --> D2["包含跳出條件"]
D --> D3["設定最大迭代限制"]
style A fill:#e3f2fd
style B fill:#fff3e0
style C fill:#e8f5e8
style D fill:#ffebee
```
> **安全第一**while 迴圈強大但需要謹慎管理條件。務必確保迴圈條件最終會變成假!
### 現代迴圈替代寫法
JavaScript 有更現代的迴圈語法,使程式碼更易讀且錯誤率降低。
**For...of 迴圈 (ES6+):**
```javascript
const colors = ["red", "green", "blue", "yellow"];
// 現代方法 - 更乾淨和更安全
for (const color of colors) {
console.log(`Color: ${color}`);
}
// 與傳統的 for 迴圈比較
for (let i = 0; i < colors.length; i++) {
console.log(`Color: ${colors[i]}`);
}
```
**for...of 的優點:**
- **免除**索引管理和 off-by-one 錯誤
- **直接**取得陣列元素
- **提升**程式碼清晰度並簡化語法
**forEach 方法:**
```javascript
const prices = [9.99, 15.50, 22.75, 8.25];
// 使用 forEach 實現函數式編程風格
prices.forEach((price, index) => {
console.log(`Item ${index + 1}: $${price.toFixed(2)}`);
});
// 使用箭頭函數搭配 forEach 進行簡單操作
prices.forEach(price => console.log(`Price: $${price}`));
```
**forEach 需知:**
- **對陣列中每個元素執行函式**
- **函式參數包含元素值與索引**
- **無法中途停止(不像傳統迴圈)**
- **回傳undefined不產生新陣列**
✅ 你為什麼會選擇 for 迴圈還是 while 迴圈?在 StackOverflow 上有 1.7 萬人討論這問題,裡面的一些意見[可能讓你感興趣](https://stackoverflow.com/questions/39969145/while-loops-vs-for-loops-in-javascript)。
### 🎨 **現代迴圈語法檢核:擁抱 ES6+**
**評估你對現代 JavaScript 的認識:**
- `for...of` 比傳統 for 迴圈有什麼優勢?
- 什麼時候還是會選擇傳統 for 迴圈?
- `forEach``map` 有什麼不同?
```mermaid
quadrantChart
title 迴圈選擇指南
x-axis 傳統 --> 現代
y-axis 簡單 --> 複雜
quadrant-1 現代 複雜
quadrant-2 傳統 複雜
quadrant-3 傳統 簡單
quadrant-4 現代 簡單
傳統 For: [0.2, 0.7]
While 迴圈: [0.3, 0.6]
For...of: [0.8, 0.3]
forEach: [0.9, 0.4]
陣列方法: [0.8, 0.8]
```
> **現代趨勢**:像 `for...of``forEach` 是 ES6+ 迴圈語法的主流,更簡潔且錯誤少!
## 迴圈與陣列
陣列通常與迴圈一起使用,因為大多數條件需要陣列的長度來停止迴圈,而索引也可以作為計數器的值。
將陣列與迴圈結合能創造強大的資料處理能力。這組合是許多程式任務的根本,從顯示清單到統計計算都用得到。
**傳統陣列處理:**
```javascript
let iceCreamFlavors = ["Chocolate", "Strawberry", "Vanilla", "Pistachio", "Rocky Road"];
const iceCreamFlavors = ["Chocolate", "Strawberry", "Vanilla", "Pistachio", "Rocky Road"];
// 傳統 for 迴圈方法
for (let i = 0; i < iceCreamFlavors.length; i++) {
console.log(iceCreamFlavors[i]);
} //Ends when all flavors are printed
console.log(`Flavor ${i + 1}: ${iceCreamFlavors[i]}`);
}
// 現代 for...of 方法
for (const flavor of iceCreamFlavors) {
console.log(`Available flavor: ${flavor}`);
}
```
✅ 在瀏覽器控制台中嘗試迭代一個你自己設計的陣列。
**了解各種做法:**
- 透過陣列長度決定迴圈範圍
- 傳統 for 迴圈使用索引存取元素
- for...of 迴圈直接存取元素
- 每個元素處理一次
**實用數據處理範例:**
```javascript
const studentGrades = [85, 92, 78, 96, 88, 73, 89];
let total = 0;
let highestGrade = studentGrades[0];
let lowestGrade = studentGrades[0];
// 使用單一迴圈處理所有成績
for (let i = 0; i < studentGrades.length; i++) {
const grade = studentGrades[i];
total += grade;
if (grade > highestGrade) {
highestGrade = grade;
}
if (grade < lowestGrade) {
lowestGrade = grade;
}
}
const average = total / studentGrades.length;
console.log(`Average: ${average.toFixed(1)}`);
console.log(`Highest: ${highestGrade}`);
console.log(`Lowest: ${lowestGrade}`);
```
**本程式運作方式:**
- 初始化總和與最大值/最小值追蹤變數
- 以單一高效迴圈處理所有成績
- 累積計算平均所需總分
- 遍歷過程中更新最高與最低分數
- 迴圈結束時計算統計結果
✅ 在瀏覽器控制台嘗試跑自己的陣列迴圈練習。
```mermaid
flowchart TD
A["📦 陣列資料"] --> B["🔄 迴圈處理"]
B --> C["📈 結果"]
A1["[85, 92, 78, 96, 88]"] --> A
B --> B1["計算總和"]
B --> B2["尋找最小/最大值"]
B --> B3["計算條件數量"]
B --> B4["資料轉換"]
C --> C1["平均: 87.8"]
C --> C2["最高分: 96"]
C --> C3["及格數量: 5/5"]
C --> C4["等級分數"]
D["⚡ 處理模式"] --> D1["累加 (總和)"]
D --> D2["比較 (最小/最大)"]
D --> D3["過濾 (條件)"]
D --> D4["映射 (轉換)"]
style A fill:#e3f2fd
style B fill:#fff3e0
style C fill:#e8f5e8
style D fill:#f3e5f5
```
---
## GitHub Copilot Agent 挑戰 🚀
使用 Agent 模式完成以下挑戰:
**描述:** 建立一個綜合資料處理函式,結合陣列與迴圈來分析資料集並產生有意義的洞察。
**提示:** 建立一個名為 `analyzeGrades` 的函式,該函式接受一個包含學生分數物件的陣列(每個物件含有 name 和 score 屬性),並回傳一個含有統計資料的物件,包括最高分、最低分、平均分數、及格學生數(分數 >= 70、和超過平均分數的學生名字陣列。請在解答中至少使用兩種不同的迴圈類型。
詳細了解 [agent 模式](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode)。
## 🚀 挑戰
JavaScript 提供了幾個現代陣列方法,可以取代特定任務的傳統循環。探索 [forEach](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach)、[for-of](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/for...of)、[map](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/map)、[filter](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) 和 [reduce](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce)。
除了 for 和 while 迴圈之外,還有其他方法可以迭代陣列,例如 [forEach](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach)、[for-of](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/for...of) 和 [map](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/map)。使用其中一種技術重寫你的陣列迴圈。
**你的挑戰:** 使用至少三種不同的陣列方法重構學生成績範例。注意使用現代 JavaScript 語法後,程式碼變得多麼乾淨且易讀
## 課後測驗
[課後測驗](https://ff-quizzes.netlify.app/web/quiz/14)
## 回顧與自學
JavaScript 中的陣列附帶許多方法,這些方法對數據操作非常有用。[閱讀這些方法](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array),並嘗試在你自己設計的陣列上使用一些方法(例如 push、pop、slice 和 splice
## 複習與自我學習
JavaScript 中的陣列附帶了許多方法,對於資料操作非常有用。[閱讀這些方法](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) 並嘗試一些(如 push、pop、slice 和 splice在你自己創建的陣列上。
## 作業
[迭代陣列](assignment.md)
[循環陣列](assignment.md)
---
## 📊 **你的陣列與循環工具總結**
```mermaid
graph TD
A["🎯 陣列與迴圈精通"] --> B["📦 陣列基礎"]
A --> C["🔄 迴圈類型"]
A --> D["🔗 資料處理"]
A --> E["🎨 現代技巧"]
B --> B1["建立: [ ]"]
B --> B2["索引: arr[0]"]
B --> B3["方法: push, pop"]
B --> B4["屬性: length"]
C --> C1["For: 固定次數"]
C --> C2["While: 條件式"]
C --> C3["For...of: 直接存取"]
C --> C4["forEach: 函式式"]
D --> D1["統計計算"]
D --> D2["資料轉換"]
D --> D3["篩選與搜尋"]
D --> D4["即時處理"]
E --> E1["箭頭函式"]
E --> E2["方法串接"]
E --> E3["解構賦值"]
E --> E4["模板字串"]
F["💡 主要優點"] --> F1["高效資料處理"]
F --> F2["減少程式重複"]
F --> F3["可擴展解決方案"]
F --> F4["更清晰語法"]
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
style E fill:#e0f2f1
style F fill:#fce4ec
```
---
## 🚀 你的陣列與循環精通時間軸
### ⚡ **接下來 5 分鐘可以做什麼**
- [ ] 建立你最喜愛電影的陣列並存取特定元素
- [ ] 撰寫一個從 1 數到 10 的 for 迴圈
- [ ] 嘗試課堂上的現代陣列方法挑戰
- [ ] 在瀏覽器控制台練習陣列索引
### 🎯 **這小時能達成的目標**
- [ ] 完成課後測驗並複習任何有挑戰的概念
- [ ] 建立 GitHub Copilot 挑戰中的綜合成績分析器
- [ ] 創建一個簡單的購物車,能新增和移除商品
- [ ] 練習不同迴圈類型的轉換
- [ ] 嘗試 `push`、`pop`、`slice` 和 `splice` 等陣列方法
### 📅 **你一週的資料處理旅程**
- [ ] 完成「循環陣列」作業,並加入創意強化
- [ ] 使用陣列和循環建立待辦清單應用程式
- [ ] 創建一個簡單的數據統計計算器
- [ ] 練習 [MDN 陣列方法](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)
- [ ] 建立照片集或音樂播放清單介面
- [ ] 探索函式式編程中的 `map`、`filter` 和 `reduce`
### 🌟 **你一個月的轉變**
- [ ] 精通進階陣列操作與效能優化
- [ ] 建立完整的數據可視化儀表板
- [ ] 參與開源專案中涉及資料處理的貢獻
- [ ] 用實務範例教導他人陣列與循環
- [ ] 創建個人可重複使用的資料處理函式庫
- [ ] 探索基於陣列的演算法和資料結構
### 🏆 **最終資料處理冠軍回顧**
**慶祝你對陣列和循環的精通:**
- 你學會的最實用的陣列操作是什麼,對真實世界應用最有幫助?
- 哪種迴圈型態你覺得最自然,為什麼?
- 理解陣列和循環後,你組織資料的方式有什麼改變?
- 下一個你想挑戰的複雜資料處理任務是什麼?
```mermaid
journey
title 你的數據處理演進
section 今日
陣列困惑: 3: 你
迴圈基礎: 4: 你
索引理解: 5: 你
section 本週
方法掌握: 4: 你
高效處理: 5: 你
現代語法: 5: 你
section 下月
複雜演算法: 5: 你
性能優化: 5: 你
教學他人: 5: 你
```
> 📦 **你已經解鎖了資料組織與處理的力量!** 陣列與循環是幾乎所有你會建立的應用程式的基礎。從簡單清單到複雜資料分析,你現在擁有高效且優雅處理資訊的工具。每個動態網站、行動應用程式和資料驅動的應用都依賴這些基本概念。歡迎來到可擴充資料處理的世界! 🎉
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。
本文件由 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 自動翻譯而成。儘管我們盡力確保準確性,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應視為權威來源。對於關鍵資訊,建議採用專業人工翻譯。我們對因使用本翻譯而引起的任何誤解或誤釋概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,25 +1,124 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "8b2381170bd0fd2870f5889bb8620f02",
"translation_date": "2025-08-23T22:45:37+00:00",
"original_hash": "8abcada0534e0fb3a7556ea3c5a2a8a4",
"translation_date": "2026-01-06T12:23:34+00:00",
"source_file": "2-js-basics/4-arrays-loops/assignment.md",
"language_code": "hk"
}
-->
# 遍歷陣列
# 陣列與迴圈練習
## 指示
建立一個程式列出1到20之間的每第三個數字並將其打印到控制台
完成以下練習以練習使用陣列和迴圈。每個練習以課程中的概念為基礎,並鼓勵你應用不同的迴圈類型和陣列方法
> 提示使用for迴圈並修改迴圈的迭代表達式
### 練習 1數字模式生成器
建立一個程式,列出 1 到 20 之間每隔 3 個的數字並輸出到控制台。
**需求:**
- 使用帶有自訂遞增值的 `for` 迴圈
- 以使用者友善的格式顯示數字
- 加上描述性註解說明你的邏輯
**預期輸出:**
```
3, 6, 9, 12, 15, 18
```
> **提示:** 修改 for 迴圈中的迭代表達式以跳過數字。
### 練習 2陣列分析
建立一個至少包含 8 個不同數字的陣列,並撰寫函式來分析資料。
**需求:**
- 建立一個名為 `numbers` 的陣列,包含至少 8 個值
- 撰寫函式 `findMaximum()` 回傳最高數字
- 撰寫函式 `findMinimum()` 回傳最低數字
- 撰寫函式 `calculateSum()` 回傳所有數字的總和
- 測試每個函式並顯示結果
**額外挑戰:** 建立一個函式找出陣列中的第二高數字。
### 練習 3字串陣列處理
建立你最喜愛的電影/書籍/歌曲陣列,練習不同迴圈類型。
**需求:**
- 建立一個含有至少 5 個字串值的陣列
- 使用傳統的 `for` 迴圈以編號格式顯示項目1. 項目名稱)
- 使用 `for...of` 迴圈以大寫顯示項目
- 使用 `forEach()` 方法計算並顯示總字元數
**範例輸出:**
```
Traditional for loop:
1. The Matrix
2. Inception
3. Interstellar
For...of loop (uppercase):
THE MATRIX
INCEPTION
INTERSTELLAR
Character count:
Total characters across all titles: 42
```
### 練習 4資料篩選進階
建立一個處理代表學生的物件陣列的程式。
**需求:**
- 建立一個包含至少 5 個學生物件的陣列,物件屬性為 `name`、`age`、`grade`
- 使用迴圈找出年齡 18 歲以上的學生
- 計算所有學生的平均成績
- 建立一個只包含成績高於 85 分學生的新陣列
**範例結構:**
```javascript
const students = [
{ name: "Alice", age: 17, grade: 92 },
{ name: "Bob", age: 18, grade: 84 },
// 新增更多學生...
];
```
## 測試你的程式
測試方式:
1. 在瀏覽器控制台執行每個練習
2. 確認輸出與預期結果相符
3. 使用不同資料集測試
4. 確認程式能處理邊界情況(空陣列、單一元素)
## 提交規範
請包含以下內容:
- 每個練習皆附有良好註解的 JavaScript 程式碼
- 顯示程式執行畫面截圖或文字輸出
- 簡述你為何在每項任務選擇該類型迴圈
## 評分標準
| 評分標準 | 表現卓越 | 表現尚可 | 需要改進 |
| -------- | --------------------------------------- | ------------------------ | ------------------------------ |
| | 程式運行正確且有註解 | 程式沒有註解 | 程式不完整或有錯誤 |
| 評分標準 | 優異 (3 分) | 尚可 (2 分) | 需改進 (1 分) |
| -------- | ----------- | ---------- | -------------- |
| **功能性** | 完成所有練習且包含額外挑戰 | 完成所有必要練習並且正確 | 部分練習未完成或有錯誤 |
| **程式碼品質** | 程式碼乾淨且結構良好,變數命名具描述性 | 程式碼能運作但可更精簡 | 程式碼混亂或難以理解 |
| **註解** | 詳盡註解,說明邏輯與決策 | 有基本註解 | 註解非常少或沒有註解 |
| **迴圈使用** | 適當使用多種迴圈類型以展示理解 | 迴圈使用正確但種類有限 | 迴圈使用不當或效率差 |
| **測試** | 有多種情境的完整測試證據 | 有基本測試證據 | 幾乎沒有測試證據 |
## 反思問題
完成練習後,請思考:
1. 你覺得哪種迴圈最自然且為什麼?
2. 使用陣列時遇到哪些挑戰?
3. 這些技能如何應用於真實世界的網頁開發專案?
4. 若要優化效能,你會怎麼調整程式碼?
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。
本文件由 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 所翻譯。儘管我們致力於確保準確性,但請注意,自動翻譯可能存在錯誤或不準確之處。原始文件的母語版本應視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們不對因使用本翻譯而引起的任何誤解或誤釋承擔責任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,84 +1,168 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "89f7f9f800ce7c9f149e98baaae8491a",
"translation_date": "2025-08-29T15:06:31+00:00",
"original_hash": "3fcfa99c4897e051b558b5eaf1e8cc74",
"translation_date": "2026-01-06T12:59:30+00:00",
"source_file": "3-terrarium/1-intro-to-html/README.md",
"language_code": "hk"
}
-->
# 玻璃缸項目第一部分HTML 簡介
![HTML 簡介](../../../../translated_images/webdev101-html.4389c2067af68e98280c1bde52b6c6269f399eaae3659b7c846018d8a7b0bbd9.hk.png)
> [Tomomi Imura](https://twitter.com/girlie_mac) 的手繪筆記
## 課前測驗
[課前測驗](https://ff-quizzes.netlify.app/web/quiz/15)
# Terrarium Project Part 1: 介紹 HTML
```mermaid
journey
title 你的 HTML 學習旅程
section 基礎
Create HTML file: 3: Student
Add DOCTYPE: 4: Student
Structure document: 5: Student
section 內容
Add metadata: 4: Student
Include images: 5: Student
Organize layout: 5: Student
section 語意
Use proper tags: 4: Student
Enhance accessibility: 5: Student
Build terrarium: 5: Student
```
![Introduction to HTML](../../../../translated_images/webdev101-html.4389c2067af68e98.hk.png)
> Sketchnote 由 [Tomomi Imura](https://twitter.com/girlie_mac) 製作
HTML或稱超文字標記語言是你曾造訪過的每個網站的基礎。將 HTML 想像成提供網頁結構的骨架——它定義內容的位置、組織方式以及每個部分的意義。雖然稍後 CSS 會為你的 HTML 加上顏色與版面配置JavaScript 則會讓它具有互動性,但 HTML 提供了讓一切成為可能的必要結構。
在這堂課中,你將為虛擬的玻璃花房介面建立 HTML 結構。這個實作專案會教你基本的 HTML 概念,同時打造出視覺吸引人的內容。你將學到如何使用語義化元素整理內容、操作圖片,並為互動式網頁應用程式奠定基礎。
完成本課程後,你會有一個能在有組織的欄位中顯示植物圖片的 HTML 頁面,並準備好在下一課進行樣式設計。別擔心它一開始看起來很簡單——這正是 HTML 在 CSS 加入視覺裝飾前該有的樣子。
```mermaid
mindmap
root((HTML 基礎知識))
Structure
DOCTYPE 宣告
HTML 元素
Head 部分
Body 內容
Elements
標籤與屬性
自我關閉標籤
巢狀元素
區塊元素與行內元素
Content
文字元素
圖像
容器 (div)
清單
Semantics
有意義的標籤
無障礙功能
螢幕閱讀器
SEO 好處
Best Practices
適當巢狀
合法標記
描述性替代文字
有組織的結構
```
## 課前小測驗
> 查看影片
[課前小測驗](https://ff-quizzes.netlify.app/web/quiz/15)
> 📺 **觀看學習**:看看這支有用的影片概覽
>
> [![Git 和 GitHub 基礎影片](https://img.youtube.com/vi/1TvxJKBzhyQ/0.jpg)](https://www.youtube.com/watch?v=1TvxJKBzhyQ)
### 簡介
> [![HTML Fundamentals Video](https://img.youtube.com/vi/1TvxJKBzhyQ/0.jpg)](https://www.youtube.com/watch?v=1TvxJKBzhyQ)
HTML全名為超文本標記語言HyperText Markup Language是網頁的「骨架」。如果說 CSS 是為 HTML「穿衣打扮」而 JavaScript 則是讓它「活起來」,那麼 HTML 就是網頁應用程式的身體。HTML 的語法甚至反映了這個概念因為它包含了「head」頭部、「body」身體和「footer」頁腳標籤。
## 設定你的專案
在這節課中,我們將使用 HTML 來設計虛擬玻璃缸介面的「骨架」。它將包含一個標題和三個欄位:左右兩側的欄位放置可拖動的植物,中間的區域則是實際的玻璃缸。到本課結束時,你將能看到欄位中的植物,但介面可能會看起來有點奇怪;別擔心,在下一節課中,我們會用 CSS 來美化介面。
深入 HTML 程式碼之前,讓我們先為你的玻璃花房專案建立一個適當的工作空間。從一開始就建立有條理的檔案結構,是個非常重要的習慣,對你的整個網頁開發旅程都會有幫助
### 任務
### 任務:建立你的專案結構
在你的電腦上建立一個名為「terrarium」的資料夾並在裡面新增一個名為「index.html」的檔案。你可以在 Visual Studio Code 中完成這個操作:打開一個新的 VS Code 視窗,點擊「開啟資料夾」,然後導航到你新建的資料夾。接著,在 Explorer 面板中點擊小的「檔案」按鈕,創建新檔案:
![VS Code 的 Explorer](../../../../translated_images/vs-code-index.e2986cf919471eb984a0afef231380c8b132b000635105f2397bd2754d1b689c.hk.png)
或者
使用以下指令在 Git Bash 中操作:
* `mkdir terrarium`
* `cd terrarium`
* `touch index.html`
* `code index.html``nano index.html`
> index.html 檔案告訴瀏覽器它是資料夾中的預設檔案;例如,`https://anysite.com/test` 可能是基於一個名為 `test` 的資料夾結構,裡面包含 `index.html`URL 中不一定會顯示 `index.html`
---
你將為玻璃花房專案建立一個專用資料夾,並加入你的第一個 HTML 檔案。以下是兩種可採用的方法:
## DocType 和 html 標籤
**方案一:使用 Visual Studio Code**
1. 開啟 Visual Studio Code
2. 點擊「檔案」→「開啟資料夾」,或使用 `Ctrl+K, Ctrl+O`Windows/Linux`Cmd+K, Cmd+O`Mac
3. 建立一個名為 `terrarium` 的新資料夾並選取它
4. 在檔案總管側邊欄,點擊「新增檔案」圖示
5. 將檔案命名為 `index.html`
HTML 檔案的第一行是其 DocType。雖然有點令人驚訝但這行必須放在檔案的最頂部它告訴舊版瀏覽器需要以標準模式渲染頁面遵循當前的 HTML 規範。
![VS Code Explorer showing new file creation](../../../../translated_images/vs-code-index.e2986cf919471eb9.hk.png)
> 提示:在 VS Code 中,你可以將滑鼠懸停在標籤上,查看來自 MDN 參考指南的相關資訊。
**方案二:使用終端機指令**
```bash
mkdir terrarium
cd terrarium
touch index.html
code index.html
```
第二行應該是 `<html>` 標籤的開啟標籤,接著是它的關閉標籤 `</html>`。這些標籤是介面的根元素。
**這些指令的作用如下:**
- **建立** 一個名為 `terrarium` 的新資料夾作為你的專案
- **切換** 到 terrarium 目錄
- **建立** 一個空的 `index.html` 檔案
- **在 Visual Studio Code 開啟** 該檔案以進行編輯
> 💡 **小秘訣**:檔案名稱 `index.html` 在網頁開發中非常特別。當有人訪問一個網站時,瀏覽器會自動尋找並顯示 `index.html` 作為預設頁面。這意味著像 `https://mysite.com/projects/` 的網址會自動呈現 `projects` 資料夾內的 `index.html`,無需在網址中指定檔案名稱。
## 理解 HTML 文件結構
每個 HTML 文件都遵循特定的結構,瀏覽器需要理解這個結構才能正確顯示網頁。將此結構想像為正式信件——它包含必要的元素且按照特定順序排列,有助於收件者(在這裡是瀏覽器)正確處理內容。
```mermaid
flowchart TD
A["<!DOCTYPE html>"] --> B["<html>"]
B --> C["<head>"]
C --> D["<title>"]
C --> E["<meta charset>"]
C --> F["<meta viewport>"]
B --> G["<body>"]
G --> H["<h1> 標題"]
G --> I["<div> 容器"]
G --> J["<img> 圖片"]
style A fill:#e1f5fe
style B fill:#f3e5f5
style C fill:#fff3e0
style G fill:#e8f5e8
```
讓我們先加上每個 HTML 文件都需要的基本基礎。
### 任務
### DOCTYPE 宣告與根元素
在你的 `index.html` 檔案頂部新增以下內容:
任何 HTML 檔案的前兩行,就像是文件對瀏覽器的「介紹」
```HTML
```html
<!DOCTYPE html>
<html></html>
```
✅ DocType 可以通過查詢字串設置不同的模式:[Quirks 模式和標準模式](https://developer.mozilla.org/docs/Web/HTML/Quirks_Mode_and_Standards_Mode)。這些模式用於支援一些現在幾乎不再使用的舊版瀏覽器(如 Netscape Navigator 4 和 Internet Explorer 5。你可以使用標準的 DocType 聲明。
**這段程式碼的作用是:**
- **宣告** 文件型態為 HTML5透過 `<!DOCTYPE html>`
- **建立** 根 `<html>` 元素包裹所有頁面內容
- **確立** 現代網頁標準,確保瀏覽器正確呈現
- **保證** 不同瀏覽器和裝置顯示一致
---
> 💡 **VS Code 小貼士**:在 VS Code 中將滑鼠移到任何 HTML 標籤上,即可看到 MDN Web Docs 提供的相關資訊,包括使用範例和瀏覽器支援狀況。
> 📚 **深入了解**DOCTYPE 宣告能防止瀏覽器進入「怪異模式」quirks mode該模式是為了支援非常古老的網站。現代網頁開發使用簡單的 `<!DOCTYPE html>` 宣告來確保[符合標準的呈現](https://developer.mozilla.org/docs/Web/HTML/Quirks_Mode_and_Standards_Mode)。
### 🔄 **教學檢視點**
**暫停思考**:繼續之前,請確保你理解:
- ✅ 為什麼每個 HTML 文件需要 DOCTYPE 宣告
- ✅ `<html>` 根元素包含什麼
- ✅ 這種結構如何幫助瀏覽器正確呈現頁面
## 文件的「head」
**快速自我檢測**:你能用自己的話說明「符合標準的呈現」是什麼意思嗎?
HTML 文件的「head」區域包含有關網頁的重要資訊也稱為[元數據](https://developer.mozilla.org/docs/Web/HTML/Element/meta)。在我們的例子中,我們需要告訴將渲染此頁面的網頁伺服器以下四件事:
## 新增必要的文件 Metadata
- 頁面的標題
- 頁面的元數據,包括:
- 字元集,告訴頁面使用的字元編碼
- 瀏覽器資訊,包括 `x-ua-compatible`,表示支援 IE=edge 瀏覽器
- 關於視窗在載入時應如何行為的資訊。將視窗的初始縮放設置為 1可以控制頁面首次載入時的縮放級別。
HTML 文件的 `<head>` 部分包含瀏覽器和搜尋引擎需要的重要資訊,但訪客不會直接在頁面上看到。可以把它想成「幕後」資料,幫助你的網頁運作正常,並在不同裝置和平台上正確顯示。
### 任務
這些 Metadata 告訴瀏覽器如何呈現頁面、要使用哪種字元編碼、以及如何處理不同螢幕尺寸——這些都是打造專業且無障礙網頁的關鍵。
`<html>` 標籤的開啟和關閉標籤之間新增一個「head」區塊。
### 任務:新增文件 Head
在你的 `<html>` 開始和結束標籤之間,插入這段 `<head>`
```html
<head>
@ -89,17 +173,28 @@ HTML 文件的「head」區域包含有關網頁的重要資訊也稱為[元
</head>
```
✅ 如果你將視窗的 meta 標籤設置為這樣:`<meta name="viewport" content="width=600">`,會發生什麼?閱讀更多關於[視窗](https://developer.mozilla.org/docs/Web/HTML/Viewport_meta_tag)的內容。
**解釋每個元素的作用:**
- **設定** 頁面標題,會顯示在瀏覽器分頁標籤和搜尋結果中
- **指定** UTF-8 字元編碼,確保文字在全球各地正確顯示
- **確保** 支援現代版 Internet Explorer
- **設定** 響應式設計的 viewport符合裝置寬度
- **控制** 初始縮放比例,使內容以自然大小呈現
---
> 🤔 **思考**:如果你設定一個 viewport meta 標籤像 `<meta name="viewport" content="width=600">`,會發生什麼事?這會強制頁面寬度永遠是 600 像素,破壞響應式設計!想了解更多關於[正確設定 viewport](https://developer.mozilla.org/docs/Web/HTML/Viewport_meta_tag)的內容。
## 建立文件主體
## 文件的 `body`
`<body>` 元素包含所有你網頁中可見的內容——使用者會看到和互動的所有東西。雖然 `<head>` 是給瀏覽器的指令,`<body>` 是實際內容:文字、圖片、按鈕和其他組成使用者介面的元素。
### HTML 標籤
讓我們加上 `body` 結構,並了解 HTML 標籤如何合作,創造有意義的內容。
在 HTML 中,你可以在 .html 檔案中新增標籤來創建網頁的元素。每個標籤通常都有開啟和關閉標籤,例如:`<p>hello</p>` 表示一段文字。通過在 `<html>` 標籤對內新增一組 `<body>` 標籤來創建介面的主體;你的標記現在看起來像這樣:
### 理解 HTML 標籤結構
### 任務
HTML 使用成對標籤來定義元素。大部分標籤有開始標籤例如 `<p>`,和結束標籤例如 `</p>`,中間夾帶內容:`<p>Hello, world!</p>`。這會建立一個包含「Hello, world!」文字的段落元素。
### 任務:新增 Body 元素
更新你的 HTML 檔案,加入 `<body>` 元素:
```html
<!DOCTYPE html>
@ -114,100 +209,179 @@ HTML 文件的「head」區域包含有關網頁的重要資訊也稱為[元
</html>
```
現在,你可以開始構建你的頁面。通常,你會使用 `<div>` 標籤來創建頁面中的不同元素。我們將創建一系列 `<div>` 元素來包含圖片。
**完整結構提供:**
- **建立** 基本的 HTML5 文件框架
- **包含** 必要的 Metadata 以供瀏覽器正確呈現
- **創造** 空白的 body 以容納可見內容
- **採用** 現代網頁開發最佳實務
現在,你準備好加入玻璃花房的可見元素了。我們將使用 `<div>` 元素作為容器,組織不同區塊的內容,並使用 `<img>` 元素來顯示植物圖片。
### 圖片
### 操作圖片與版面容器
有一個 HTML 標籤不需要關閉標籤,那就是 `<img>` 標籤,因為它有一個 `src` 屬性,包含頁面渲染該項目所需的所有資訊。
圖片在 HTML 中是特別的,使用「自我封閉」標籤。與像 `<p></p>` 這類包裹內容的元素不同,`<img>` 標籤本身帶有所需的所有資訊,用屬性如 `src` 指定圖片路徑,`alt` 屬性做無障礙使用說明
在你的應用程式中創建一個名為 `images` 的資料夾,並將 [source code folder](../../../../3-terrarium/solution/images) 中的所有圖片14 張植物圖片)新增到該資料夾中。
加入圖片到 HTML 之前,你需要先透過建立 images 資料夾並放入植物圖像,整理專案檔案
### 任務
**首先,準備你的圖片:**
1. 在你的玻璃花房專案資料夾中建立一個叫 `images` 的資料夾
2. 從[解答資料夾](../../../../3-terrarium/solution/images)下載植物圖片(共 14 張)
3. 將所有植物圖片複製到你新建立的 `images` 資料夾
`<body></body>` 標籤之間新增這些植物圖片,分成兩列:
### 任務:建立植物顯示版面
現在在你的 `<body></body>` 標籤之間,加入兩欄排列的植物圖片:
```html
<div id="page">
<div id="left-container" class="container">
<div class="plant-holder">
<img class="plant" alt="plant" id="plant1" src="./images/plant1.png" />
<img class="plant" alt="plant" id="plant1" src="../../../../translated_images/plant1.d87946a2ca70cc43.hk.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant2" src="./images/plant2.png" />
<img class="plant" alt="plant" id="plant2" src="../../../../translated_images/plant2.8daa1606c9c1ad89.hk.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant3" src="./images/plant3.png" />
<img class="plant" alt="plant" id="plant3" src="../../../../translated_images/plant3.8b0d484381a2a2a7.hk.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant4" src="./images/plant4.png" />
<img class="plant" alt="plant" id="plant4" src="../../../../translated_images/plant4.656e16ae1df37be2.hk.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant5" src="./images/plant5.png" />
<img class="plant" alt="plant" id="plant5" src="../../../../translated_images/plant5.2b41b9355f11ebcc.hk.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant6" src="./images/plant6.png" />
<img class="plant" alt="plant" id="plant6" src="../../../../translated_images/plant6.3d1827d03b656994.hk.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant7" src="./images/plant7.png" />
<img class="plant" alt="plant" id="plant7" src="../../../../translated_images/plant7.8152c302ac97f621.hk.png" />
</div>
</div>
<div id="right-container" class="container">
<div class="plant-holder">
<img class="plant" alt="plant" id="plant8" src="./images/plant8.png" />
<img class="plant" alt="plant" id="plant8" src="../../../../translated_images/plant8.38d6428174ffa850.hk.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant9" src="./images/plant9.png" />
<img class="plant" alt="plant" id="plant9" src="../../../../translated_images/plant9.f0e38d3327c37fc2.hk.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant10" src="./images/plant10.png" />
<img class="plant" alt="plant" id="plant10" src="../../../../translated_images/plant10.b159d6d6e985595f.hk.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant11" src="./images/plant11.png" />
<img class="plant" alt="plant" id="plant11" src="../../../../translated_images/plant11.2a03a1c2ec8ea84e.hk.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant12" src="./images/plant12.png" />
<img class="plant" alt="plant" id="plant12" src="../../../../translated_images/plant12.60e9b53e538fbaf3.hk.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant13" src="./images/plant13.png" />
<img class="plant" alt="plant" id="plant13" src="../../../../translated_images/plant13.07a51543c820bcf5.hk.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant14" src="./images/plant14.png" />
<img class="plant" alt="plant" id="plant14" src="../../../../translated_images/plant14.6e486371ba7d36ba.hk.png" />
</div>
</div>
</div>
```
> 注意Spans vs. Divs。Divs 被認為是「區塊」元素,而 Spans 是「行內」元素。如果你將這些 divs 轉換為 spans會發生什麼
使用這些標記後,植物現在會顯示在螢幕上。它看起來很糟糕,因為它們還沒有使用 CSS 進行樣式設置,我們會在下一節課中處理這個問題。
每張圖片都有替代文字alt text即使你無法看到或渲染圖片這些文字也會顯示出來。這是一個重要的屬性對於無障礙設計非常重要。在未來的課程中我們會更多地學習無障礙設計目前請記住alt 屬性為圖片提供了替代資訊當使用者因某些原因無法查看圖片時例如網速慢、src 屬性出錯,或使用者使用螢幕閱讀器),這些資訊會顯示出來。
✅ 你是否注意到每張圖片都有相同的 alt 標籤?這是好的做法嗎?為什麼?你能改進這段程式碼嗎?
---
## 語義化標記
**一步步來看這段程式碼的作用:**
- **建立** 一個主頁面容器,`id="page"`,包裹所有內容
- **設置** 兩個欄位容器:`left-container` 和 `right-container`
- **將 7 個植物放在左欄7 個植物放在右欄**
- **將每張植物圖片放入 `plant-holder` div 以便個別定位**
- **套用一致的 class 名稱,方便下一課的 CSS 樣式設計**
- **為每張植物圖片指定獨特的 ID方便日後 JavaScript 互動**
- **正確指向 images 資料夾中的檔案路徑**
> 🤔 **思考**:你會注意到所有圖片的 alt 屬性都是「plant」這對無障礙使用不是理想的做法。螢幕閱讀器使用者會聽到「plant」重複 14 次,卻不知道每張圖片是什麼特定植物。你能想出更好、更加描述性的 alt 文字嗎?
> 📝 **HTML 元素類型**`<div>` 元素屬於「區塊級」元素,佔滿整行寬度,而 `<span>` 是「內嵌」元素,只佔必要寬度。你覺得如果將這些 `<div>` 改成 `<span>` 會發生什麼事?
### 🔄 **教學檢視點**
**結構理解**:花點時間檢視你的 HTML 結構:
- ✅ 你能辨識版面中的主要容器嗎?
- ✅ 是否理解為什麼每張圖片需要獨特的 ID
- ✅ 你會如何描述 `plant-holder` div 的用途?
**視覺檢查**:開啟你的 HTML 檔案於瀏覽器中,你應該會看到:
- 簡單的植物圖片清單
- 兩欄排列顯示圖片
- 簡約,未經樣式設計的版面
**請記得**:這種樸素外觀正是 HTML 在加入 CSS 樣式前應該的樣子!
加入這些標記後,植物會呈現在螢幕上,雖然還未經修飾,這正是下一課 CSS 將要處理的。現在你擁有堅實的 HTML 基礎,正確組織內容並符合無障礙最佳實務。
## 使用語義化 HTML 提升無障礙
語義化 HTML 是根據元素的意義和用途挑選 HTML 標籤,而不只是依外觀。當你選用語義化標記時,你是在向瀏覽器、搜尋引擎和輔助技術(例如螢幕閱讀器)傳達內容的結構與意涵。
```mermaid
flowchart TD
A[需要加入內容嗎?] --> B{什麼類型?}
B -->|主標題| C["<h1>"]
B -->|副標題| D["<h2>, <h3>, 等等"]
B -->|段落| E["<p>"]
B -->|清單| F["<ul>, <ol>"]
B -->|導覽| G["<nav>"]
B -->|文章| H["<article>"]
B -->|區段| I["<section>"]
B -->|通用容器| J["<div>"]
C --> K[螢幕閱讀器會讀作主標題]
D --> L[建立適當的標題階層]
E --> M[提供適當的文字間距]
F --> N[啟用清單導覽捷徑]
G --> O[標示導覽地標]
H --> P[標示獨立內容]
I --> Q[群組相關內容]
J --> R[只有在沒有語義標籤適用時使用]
style C fill:#4caf50
style D fill:#4caf50
style E fill:#4caf50
style F fill:#4caf50
style G fill:#2196f3
style H fill:#2196f3
style I fill:#2196f3
style J fill:#ff9800
```
這個作法讓你網站對有障礙的使用者更友善,也幫助搜尋引擎更好理解你的內容。這是現代網頁開發的基本原則,為所有人創造更佳體驗。
一般來說,在撰寫 HTML 時,最好使用有意義的「語義化」標記。這是什麼意思?它意味著你應該使用 HTML 標籤來表示它們設計用於的數據類型或交互。例如,頁面上的主要標題文字應使用 `<h1>` 標籤。
### 新增語義化頁面標題
在你的開啟 `<body>` 標籤正下方新增以下內容:
讓我們為玻璃花房頁面加上一個適當標題。將此行插入在你的開頭 `<body>` 標籤後
```html
<h1>My Terrarium</h1>
```
使用語義化標記,例如將標題設為 `<h1>`,將無序列表渲染為 `<ul>`,有助於螢幕閱讀器導航頁面。一般來說,按鈕應該寫成 `<button>`,列表應該寫成 `<li>`。雖然可以使用特別樣式的 `<span>` 元素加上點擊處理器來模仿按鈕,但對於殘障使用者來說,使用技術來確定頁面上的按鈕位置並與之交互會更方便。因此,盡量使用語義化標記。
**為何語義化標記很重要:**
- **幫助** 螢幕閱讀器使用者導航和理解頁面結構
- **提升** 搜尋引擎排名SEO明確內容階層
- **加強** 視障或認知障礙使用者的無障礙體驗
- **創造** 跨所有裝置與平台更佳的使用者體驗
- **遵循** 網頁標準和專業開發最佳實務
**語義化與非語義化選擇範例:**
✅ 查看螢幕閱讀器[如何與網頁互動](https://www.youtube.com/watch?v=OUDV1gqs9GA)。你能理解為什麼非語義化標記可能會讓使用者感到沮喪嗎?
| 目的 | ✅ 語義化選擇 | ❌ 非語義化選擇 |
|---------|----------------------------|-------------------------------|
| 主標題 | `<h1>Title</h1>` | `<div class="big-text">Title</div>` |
| 導航欄 | `<nav><ul><li></li></ul></nav>` | `<div class="menu"><div></div></div>` |
| 按鈕 | `<button>Click me</button>` | `<span onclick="...">Click me</span>` |
| 文章內容 | `<article><p></p></article>` | `<div class="content"><div></div></div>` |
## 玻璃缸
> 🎥 **現場示範**:觀看[螢幕閱讀器如何與網頁互動](https://www.youtube.com/watch?v=OUDV1gqs9GA)以理解語義化標記對無障礙的重要性。注意正確的 HTML 結構如何幫助使用者有效導航。
介面的最後一部分涉及創建將被樣式化為玻璃缸的標記。
## 建立玻璃花房容器
### 任務:
現在讓我們新增玻璃花房本身的 HTML 結構——那個將用來放置植物的玻璃容器。這部分示範一個重要概念HTML 提供結構,但沒有 CSS 樣式,這些元素暫時不會可見。
在最後一個 `</div>` 標籤上方新增以下標記:
玻璃花房標記使用描述性的 class 名稱,方便下一課的 CSS 樣式設計既直覺又易維護。
### 任務:新增玻璃花房結構
將此標記插入在最後一個 `</div>` 標籤之前(即頁面容器關閉標籤前):
```html
<div id="terrarium">
@ -221,29 +395,200 @@ HTML 文件的「head」區域包含有關網頁的重要資訊也稱為[元
</div>
```
✅ 即使你新增了這些標記到螢幕上,你仍然看不到任何渲染。為什麼?
**了解這個玻璃花房結構的含義:**
- **建立** 一個主要的玻璃花房容器,並附加獨特 ID 以便樣式設計
- **定義**每個視覺組件的獨立元素(頂部、牆壁、土壤、底部)
- **包含**用於玻璃反射效果(光澤元素)的嵌套元素
- **使用**清晰指出每個元素用途的描述性類名
- **準備**結構以便 CSS 造出玻璃植物箱的外觀
> 🤔 **注意到了嗎?**:即使你已加入這些標記,頁面上卻看不到任何新東西!這正好說明了 HTML 提供結構,而 CSS 提供外觀。這些 `<div>` 元素存在,但還沒有視覺樣式——下一課會教你如何加上!
```mermaid
flowchart TD
A[HTML 文件] --> B[文件頭]
A --> C[文件主體]
B --> D[標題元素]
B --> E[Meta 字符集]
B --> F[Meta 視窗設定]
C --> G[主標題]
C --> H[頁面容器]
H --> I[左邊容器帶 7 株植物]
H --> J[右邊容器帶 7 株植物]
H --> K[生態瓶結構]
style A fill:#e1f5fe
style B fill:#fff3e0
style C fill:#e8f5e8
style H fill:#f3e5f5
```
### 🔄 **教學檢視**
**掌握 HTML 結構**:在繼續進行前,請確保你能:
- ✅ 解釋 HTML 結構與視覺外觀的差異
- ✅ 識別語意與非語意的 HTML 元素
- ✅ 描述正確標記如何提升無障礙使用
- ✅ 認識完整文件樹狀結構
**測試你的理解**:嘗試在瀏覽器中禁用 JavaScript 並移除 CSS開啟你的 HTML 檔案。這能讓你看到純粹的語意結構。
---
## 🚀挑戰
## GitHub Copilot Agent 挑戰
使用 Agent 模式完成以下挑戰:
**描述:** 創建一個語意化的 HTML 結構,用於可加入植物箱專案的植物護理指南部分。
**提示:** 建立一個語意化 HTML 區塊包含主標題「Plant Care Guide」及三個子區塊標題分別是「Watering」、「Light Requirements」和「Soil Care」每個子區塊包含一段植物護理資訊。使用適當的語意 HTML 標籤如 `<section>`、`<h2>`、`<h3>` 和 `<p>`,妥善組織內容。
進一步了解 [agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode)。
## 探索 HTML 歷史挑戰
HTML 中有一些「舊」標籤仍然很有趣,雖然你不應該在標記中使用已棄用的標籤,例如[這些標籤](https://developer.mozilla.org/docs/Web/HTML/Element#Obsolete_and_deprecated_elements)。不過,你能使用舊的 `<marquee>` 標籤讓 h1 標題水平滾動嗎?(如果你這麼做,記得之後將其移除)
**學習網頁演變**
自 1990 年 Tim Berners-Lee 在 CERN 創造首個網頁瀏覽器後HTML 已大幅演進。部分舊標籤如 `<marquee>` 目前已被淘汰,因為它們與現代無障礙標準及響應式設計不兼容。
**嘗試這個實驗:**
1. 臨時用 `<marquee>` 標籤包裹 `<h1>` 標題:`<marquee><h1>My Terrarium</h1></marquee>`
2. 在瀏覽器打開頁面,觀察滾動效果
3. 思考這個標籤被淘汰的原因(提示:考慮用戶體驗與無障礙)
4. 移除 `<marquee>`,恢復語意標記
**反思問題:**
- 滾動標題可能如何影響視力障礙或對動態影響敏感的用戶?
- 有哪些現代 CSS 技巧能更無障礙地實現類似視覺效果?
- 為什麼使用當代網頁標準比使用淘汰元素更重要?
深入了解更多關於[過時及淘汰的 HTML 元素](https://developer.mozilla.org/docs/Web/HTML/Element#Obsolete_and_deprecated_elements),理解網頁標準如何演進改善用戶體驗。
## 課後測驗
[課後測驗](https://ff-quizzes.netlify.app/web/quiz/16)
## 回顧與自學
## 複習與自學
**深化你的 HTML 知識**
HTML 作為網頁基礎超過 30 年,從簡單的文件標記語言發展成為構建互動應用的複雜平台。理解這演變能幫助你欣賞現代網頁標準並做出更好的開發決策。
**推薦學習路徑:**
1. **HTML 歷史與演變**
- 研究從 HTML 1.0 到 HTML5 的時間軸
- 探究某些標籤為何被淘汰(無障礙、行動友善、維護性)
- 研究新興 HTML 功能與提案
2. **語意化 HTML 深入**
- 學習完整的[HTML5 語意元素清單](https://developer.mozilla.org/docs/Web/HTML/Element)
- 練習判斷何時使用 `<article>`、`<section>`、`<aside>` 和 `<main>`
- 瞭解提升無障礙的 ARIA 屬性
3. **現代網頁開發**
- 探索 [建立響應式網站](https://docs.microsoft.com/learn/modules/build-simple-website/?WT.mc_id=academic-77807-sagibbon)Microsoft Learn
- 理解 HTML 如何與 CSS 及 JavaScript 整合
- 學習網頁效能與 SEO 最佳實踐
**反思問題:**
- 你發現了哪些已被淘汰的 HTML 標籤,為什麼它們被移除?
- 未來 HTML 版本有哪些新功能被提議?
- 語意化 HTML 如何促進無障礙與 SEO
### ⚡ **接下來 5 分鐘可做的事**
- [ ] 開啟 DevToolsF12並檢查你喜愛網站的 HTML 結構
- [ ] 建立簡單 HTML 檔案,包含基本標籤:`<h1>`、`<p>` 和 `<img>`
- [ ] 使用 W3C HTML 校驗器驗證你的 HTML
- [ ] 嘗試在 HTML 中加入註解 `<!-- comment -->`
### 🎯 **這小時可完成的目標**
- [ ] 完成課後測驗並複習語意 HTML 概念
- [ ] 用正確 HTML 結構建立關於你的簡單網頁
- [ ] 試驗不同標題層級與文字格式標籤
- [ ] 加入圖片與連結練習多媒體整合
- [ ] 研究你尚未嘗試的 HTML5 功能
### 📅 **你的週度 HTML 進階之路**
- [ ] 使用語意標記完成植物箱專案練習
- [ ] 建立使用 ARIA 標籤與角色的無障礙網頁
- [ ] 練習建立含多種輸入類型的表單
- [ ] 探索 HTML5 API如 localStorage 或地理定位
- [ ] 研讀響應式 HTML 範式與行動優先設計
- [ ] 複習其他開發者的 HTML 程式碼與最佳實踐
### 🌟 **你這月的網頁基礎進程**
- [ ] 建立展示 HTML 能力的作品集網站
- [ ] 學習使用如 Handlebars 的 HTML 模板技術
- [ ] 透過改善 HTML 文件為開源專案貢獻
- [ ] 精通進階 HTML 概念如自訂元素
- [ ] 整合 HTML 與 CSS 框架及 JavaScript 函式庫
- [ ] 指導其他學習 HTML 基礎的人
## 🎯 你的 HTML 精通時間線
```mermaid
timeline
title HTML 學習進度
section 基礎 (5 分鐘)
文件結構: DOCTYPE 聲明
: HTML 根元素
: Head 與 Body 理解
section 元資料 (10 分鐘)
基本 Meta 標籤: 字元編碼
: 視窗配置
: 瀏覽器相容性
section 內容創建 (15 分鐘)
圖像整合: 正確檔案路徑
: 替代文字重要性
: 自閉合標籤
section 佈局組織 (20 分鐘)
容器策略: 使用 Div 元素建立結構
: 類別與 ID 命名
: 嵌套元素階層
section 語意掌握 (30 分鐘)
有意義的標記: 標題階層
: 螢幕閱讀器導航
: 可及性最佳實踐
section 進階概念 (1 小時)
HTML5 功能: 現代語意元素
: ARIA 屬性
: 效能考量
section 專業技能 (1 週)
代碼組織: 檔案結構範例
: 容易維護的標記
: 團隊合作
section 專家等級 (1 個月)
現代網頁標準: 漸進式強化
: 跨瀏覽器相容性
: HTML 規範更新
```
### 🛠️ 你的 HTML 工具包總結
HTML 是「經得起考驗」的建構系統,幫助網頁發展成今天的樣子。通過學習一些舊標籤和新標籤,了解它的歷史。你能找出為什麼某些標籤被棄用,而某些標籤被新增嗎?未來可能會引入哪些標籤?
完成本課程後,你已具備:
- **文件結構**:完整的 HTML5 基礎和正確 DOCTYPE
- **語意標記**:增強無障礙與 SEO 的意義明確標籤
- **圖片整合**:妥善組織文件與替代文字使用
- **版面容器**:策略性利用帶描述性類名的 div
- **無障礙意識**:理解螢幕閱讀器導航原理
- **現代標準**:掌握目前 HTML5 實務與被淘汰標籤知識
- **專案基礎**:為 CSS 樣式與 JavaScript 互動打下堅實基礎
了解更多關於為網頁和行動裝置構建網站的內容,請參考 [Microsoft Learn](https://docs.microsoft.com/learn/modules/build-simple-website/?WT.mc_id=academic-77807-sagibbon)。
**下一步**:你的 HTML 結構已準備好進行 CSS 樣式設計!你建立的語意基礎會讓下一課更容易理解
## 作業
[練習你的 HTML構建一個部落格模型](assignment.md)
[練習你的 HTML建立一個部落格範本](assignment.md)
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
此文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議使用專業的人工作業翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤詮釋概不負責。
本文件是使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們盡力確保準確性,但請注意自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要資料,建議採用專業人工翻譯。我們對因使用本翻譯所引致的任何誤解或誤釋概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,53 +1,154 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "5a764667bbe82aa72ac0a67f4c97ff4a",
"translation_date": "2025-10-03T08:57:05+00:00",
"original_hash": "650e63282e1dfa032890fcf5c1c4119d",
"translation_date": "2026-01-06T13:01:24+00:00",
"source_file": "3-terrarium/1-intro-to-html/assignment.md",
"language_code": "hk"
}
-->
# HTML 練習作業:建立部落格模擬版面
# HTML 練習作業:建立博客模型
## 目標
## 學習目標
設計並手寫 HTML 結構,用於個人部落格首頁。本練習旨在幫助你練習語義化 HTML、版面規劃以及程式碼組織
透過設計和編寫完整的博客首頁結構,應用你的 HTML 知識。這個實作作業將加強語意化 HTML 概念、無障礙最佳實踐以及專業的程式碼組織技能,這些都是你在整個網頁開發旅程中會使用到的
## 指示
**完成此作業後,你將能夠:**
- 練習在編碼前規劃網站佈局
- 適當應用語意化的 HTML 元素
- 創建無障礙且結構良好的標記
- 培養帶有註解和組織的專業編碼習慣
1. **設計你的部落格模擬版面**
- 繪製部落格首頁的視覺模擬版面。包含主要區塊,例如:頁首、導航欄、主要內容、側邊欄以及頁尾。
- 你可以使用紙張繪製並掃描你的草圖,或者使用數位工具(例如 Figma、Adobe XD、Canva甚至 PowerPoint
## 專案需求
2. **識別 HTML 元素**
- 列出你計劃用於每個區塊的 HTML 元素(例如:`<header>`、`<nav>`、`<main>`、`<article>`、`<aside>`、`<footer>`、`<section>`、`<h1>``<h6>`、`<p>`、`<img>`、`<ul>`、`<li>`、`<a>` 等)。
### 第 1 部分:設計規劃(視覺模型)
3. **撰寫 HTML 標記**
- 手寫你的模擬版面的 HTML。專注於語義化結構和最佳實踐。
- 至少包含 10 個不同的 HTML 元素。
- 添加註解以解釋你的選擇和結構。
**創建你的博客首頁視覺模型,內容包括:**
- 含網站標題和導覽的頁首
- 主要內容區域,至少有 2-3 篇博客文章預覽
- 側邊欄,包含額外資訊(關於區塊、最新文章、分類)
- 含聯絡資訊或連結的頁尾
4. **提交你的作品**
- 上傳你的草圖/模擬版面以及 HTML 文件。
- 可選提供簡短的反思23 句),說明你的設計決策。
**模型創建方式選項:**
- **手繪草圖**:使用紙張和鉛筆,然後拍照或掃描你的設計
- **數位工具**Figma、Adobe XD、Canva、PowerPoint 或任何繪圖應用程式
- **線框工具**Balsamiq、MockFlow 或類似線框軟體
**在你的模型區塊上標註** 你計畫使用的 HTML 元素(例如:「頁首 - `<header>`」、「博客文章 - `<article>`」)。
### 第 2 部分HTML 元素規劃
**建立一個清單,將你的模型中每個區塊對應到特定的 HTML 元素:**
```
Example:
- Site Header → <header>
- Main Navigation → <nav> with <ul> and <li>
- Blog Post → <article> with <h2>, <p>, <time>
- Sidebar → <aside> with <section> elements
- Page Footer → <footer>
```
**必須包含的元素:**
你的 HTML 必須包含此列表中至少 10 個不同的語意元素:
- `<header>`, `<nav>`, `<main>`, `<article>`, `<section>`, `<aside>`, `<footer>`
- `<h1>`, `<h2>`, `<h3>`, `<p>`, `<ul>`, `<li>`, `<a>`
- `<img>`, `<time>`, `<blockquote>`, `<strong>`, `<em>`
### 第 3 部分HTML 實作
**依照以下標準編寫你的博客首頁:**
1. **文件結構**:包含適當的 DOCTYPE、html、head 和 body 元素
2. **語意標記**:依元素原始用途使用 HTML 元素
3. **無障礙設計**:圖片需附有適切的 alt 文字,連結文字需具意義
4. **程式碼品質**:使用一致縮排與有意義的註解
5. **內容**:包含真實的博客內容(可使用佔位文字)
**範例 HTML 結構:**
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Personal Blog</title>
</head>
<body>
<!-- Main site header -->
<header>
<h1>My Blog Title</h1>
<nav>
<!-- Navigation menu -->
</nav>
</header>
<!-- Main content area -->
<main>
<!-- Blog posts go here -->
</main>
<!-- Sidebar content -->
<aside>
<!-- Additional information -->
</aside>
<!-- Site footer -->
<footer>
<!-- Footer content -->
</footer>
</body>
</html>
```
### 第 4 部分:反思
**撰寫簡短反思3-5 句)說明:**
- 你最有信心使用哪些 HTML 元素?
- 在規劃或編碼時遇到哪些挑戰?
- 語意化 HTML 如何幫助你組織內容?
- 下一個 HTML 專案你會做哪些不同的事情?
## 提交清單
**提交前,請確認你已完成:**
- [ ] 有標註 HTML 元素的視覺模型
- [ ] 完整且有正確文件結構的 HTML 檔案
- [ ] 使用至少 10 種不同的語意 HTML 元素且適當運用
- [ ] 有意義的註解解釋你的程式碼結構
- [ ] 合法的 HTML 語法(可在瀏覽器測試)
- [ ] 回答指示問題的反思文字
## 評分標準
| 評分項目 | 優秀 | 合格 | 需改進 |
|------------------|------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------|--------------------------------------------------------------------------------|
| 視覺模擬版面 | 清晰、詳細的模擬版面,標註區塊並具有周到的版面設計 | 基本模擬版面,部分區塊有標註 | 模擬版面簡陋或不清晰;缺乏區塊標註 |
| HTML 元素 | 使用 10 個以上語義化 HTML 元素;展現結構和最佳實踐的理解 | 使用 59 個 HTML 元素;部分語義化結構 | 使用少於 5 個元素;缺乏語義化結構 |
| 程式碼品質 | 組織良好、易讀的程式碼,包含註解;遵循 HTML 標準 | 程式碼大致組織良好;註解較少 | 程式碼混亂;缺乏註解 |
| 反思 | 深刻反思設計選擇和挑戰 | 基本反思 | 無反思或缺乏相關性 |
| 評量標準 | 優秀 (4) | 精通 (3) | 發展中 (2) | 初學 (1) |
|----------|------------|------------|-------------|----------|
| **規劃與設計** | 詳細且標註完善的模型,顯示對佈局和 HTML 語意結構的清楚理解 | 清楚的模型,大部分區塊標註適當 | 基本模型,有部分標註,呈現一般理解 | 模型不完整或不清楚,缺乏適當區塊識別 |
| **語意 HTML 使用** | 適當使用 10 個以上語意元素,展現對 HTML 結構及無障礙深刻理解 | 正確使用 8-9 個語意元素,展現良好標記理解 | 使用 6-7 個語意元素,對適當使用有些混淆 | 使用少於 6 個元素,或語意元素誤用 |
| **程式碼品質與組織** | 程式碼組織優良,縮排正確,有完整註解與完美的 HTML 語法 | 組織良好,縮排適當,有幫助的註解且語法有效 | 大致組織,部分註解,少量語法問題 | 組織差,註解極少,多處語法錯誤 |
| **無障礙與最佳實務** | 無障礙設計優良alt 文本具意義,標題階層適切,符合所有現代 HTML 最佳實務 | 無障礙功能良好,適當使用標題與 alt 文本,符合多數最佳實務 | 有些無障礙考量,基本 alt 文本與標題結構 | 無障礙功能有限,標題結構差,未遵循最佳實務 |
| **反思與學習** | 深刻且有見地的反思,展現對 HTML 概念的理解與學習過程的深入分析 | 良好的反思,展現對主要概念的理解與學習自覺 | 基本反思,對 HTML 概念或學習過程的見解有限 | 反思甚少或缺乏,對所學概念理解不足 |
## 學習資源
**重要參考資料:**
- [MDN HTML 元素參考](https://developer.mozilla.org/docs/Web/HTML/Element) - 完整的 HTML 元素指南
- [HTML5 語意元素](https://developer.mozilla.org/docs/Web/HTML/Element#content_sectioning) - 理解語意標記
- [網頁無障礙指引](https://www.w3.org/WAI/WCAG21/quickref/) - 創造無障礙網頁內容
- [HTML 驗證器](https://validator.w3.org/) - 檢查你的 HTML 語法
## 提示
**成功小撇步:**
- 編寫程式碼前先做好原型模型
- 使用瀏覽器開發者工具檢視 HTML 結構
- 在不同螢幕尺寸測試頁面(即使沒有 CSS
- 大聲朗讀你的 HTML檢查結構是否合邏輯
- 想想螢幕閱讀器如何解讀你的頁面結構
- 使用語義化 HTML 標籤以提升可訪問性和 SEO。
- 使用縮排和註解來組織你的程式碼。
- 參考 [MDN HTML 元素參考](https://developer.mozilla.org/en-US/docs/Web/HTML/Element) 以獲取指導。
- 思考你的版面如何在未來的作業中擴展或進行樣式設計。
> 💡 **記得**:此作業專注於 HTML 結構與語意。無需擔心視覺樣式——那是 CSS 的工作!你的頁面可能看起來樸素,但應該結構良好且有意義。
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。
本文件乃使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 所翻譯。儘管我們力求準確,請注意自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要資訊,建議採用專業人工翻譯。對於因使用本翻譯而引起的任何誤解或錯誤詮釋,我們概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,278 +1,727 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "acb5ae00cde004304296bb97da8ff4c3",
"translation_date": "2025-08-29T15:07:13+00:00",
"original_hash": "e39f3a4e3bcccf94639e3af1248f8a4d",
"translation_date": "2026-01-06T13:04:33+00:00",
"source_file": "3-terrarium/2-intro-to-css/README.md",
"language_code": "hk"
}
-->
# 玻璃缸項目第 2 部分CSS 簡介
![CSS 簡介](../../../../translated_images/webdev101-css.3f7af5991bf53a200d79e7257e5e450408d8ea97f5b531d31b2e3976317338ee.hk.png)
> 手繪筆記由 [Tomomi Imura](https://twitter.com/girlie_mac) 提供
# Terrarium Project Part 2: CSS 入門
```mermaid
journey
title 你的 CSS 樣式旅程
section 基礎
連結 CSS 檔案: 3: Student
理解層疊: 4: Student
學習繼承: 4: Student
section 選擇器
元素定位: 4: Student
類別模式: 5: Student
ID 特異性: 5: Student
section 版面配置
定位元素: 4: Student
建立容器: 5: Student
建造生態瓶: 5: Student
section 精修
新增視覺效果: 5: Student
響應式設計: 5: Student
玻璃反射: 5: Student
```
![Introduction to CSS](../../../../translated_images/webdev101-css.3f7af5991bf53a20.hk.png)
> Sketchnote 由 [Tomomi Imura](https://twitter.com/girlie_mac) 製作
還記得你的 HTML terrarium 看起來相當簡單嗎CSS 就是將那個普通結構變得視覺吸引的關鍵。
如果說 HTML 就像搭建房子的框架,那 CSS 就是讓它成為家的所有元素—牆面顏色、傢俱擺設、燈光,以及房間之間的流動。想想凡爾賽宮剛開始只是簡單的狩獵小屋,精心的裝飾和佈局使它成為世界上最壯麗的建築之一。
今天,我們會把你的 terrarium 從功能性變得精緻。你會學到如何精準定位元素,讓佈局對不同螢幕尺寸有反應,並創造出吸引網站使用者的視覺效果。
課程結束時,你會看到策略性的 CSS 樣式如何大幅提升你的專案。讓我們一起給你的 terrarium 加點風格吧。
```mermaid
mindmap
root((CSS 基礎知識))
Cascade
Specificity Rules[特定性規則]
Inheritance[繼承]
Priority Order[優先順序]
Conflict Resolution[衝突解決]
Selectors
Element Tags[元素標籤]
Classes (.class)[類別 (.class)]
IDs (#id)[ID (#id)]
Combinators[組合器]
Box Model
Margin[外距]
Border[邊框]
Padding[內距]
Content[內容]
Layout
Positioning[定位]
Display Types[顯示類型]
Flexbox[彈性盒]
Grid[網格]
Visual Effects
Colors[顏色]
Shadows[陰影]
Transitions[過渡]
Animations[動畫]
Responsive Design
Media Queries[媒體查詢]
Flexible Units[彈性單位]
Viewport Meta[檢視區元資料]
Mobile First[行動優先]
```
## 講前小測驗
## 課前測驗
[講前小測驗](https://ff-quizzes.netlify.app/web/quiz/17)
[課前測驗](https://ff-quizzes.netlify.app/web/quiz/17)
## CSS 基本入門
### 簡介
很多人認為 CSS 只是「讓東西漂亮」但它的用途遠不止於此。CSS 就像電影導演—不只控制外觀,還能決定移動方式、互動反應,以及如何適應不同情況。
CSS全名為層疊樣式表Cascading Style Sheets解決了網頁開發中的一個重要問題如何讓你的網站看起來更美觀。為應用程式添加樣式不僅能提升其可用性還能讓其外觀更吸引人此外你還可以使用 CSS 創建響應式網頁設計Responsive Web DesignRWD讓你的應用程式無論在什麼螢幕尺寸下都能保持良好的顯示效果。CSS 不僅僅是讓應用程式看起來更美觀它的規範還包括動畫和變形功能能為你的應用程式實現更複雜的互動效果。CSS 工作組負責維護當前的 CSS 規範,你可以在 [World Wide Web Consortium 的網站](https://www.w3.org/Style/CSS/members)上關注他們的工作。
現代 CSS 非常強大。你能撰寫自動調整手機、平板和桌面電腦布局的程式碼。還可以創造引導使用者注意力的流暢動畫。當一切協同運作時,效果令人驚豔
> 注意CSS 是一種不斷演進的語言,就像網頁上的其他技術一樣,並非所有瀏覽器都支持最新的規範部分。請隨時參考 [CanIUse.com](https://caniuse.com) 來檢查你的實現。
> 💡 **專業提示**CSS 持續進化,加入新功能與能力。使用新 CSS 功能前,務必查看 [CanIUse.com](https://caniuse.com) 以確認瀏覽器支援度
在本課中,我們將為線上玻璃缸添加樣式,並學習多個 CSS 概念:層疊、繼承、選擇器的使用、定位以及使用 CSS 構建佈局。在此過程中,我們將設計玻璃缸的佈局,並創建實際的玻璃缸。
**本課程目標:**
- **用現代 CSS 技術** 創造完整的 terrarium 視覺設計
- **探索** 基本概念,例如層疊、繼承和 CSS 選擇器
- **實作** 響應式定位與佈局策略
- **建構** terrarium 容器,運用 CSS 形狀與樣式
### 前置條件
### 先備條件
你應該已經完成了玻璃缸的 HTML 結構,並準備好進行樣式設計。
你應該已完成上堂課的 terrarium HTML 結構,並準備好進行樣式設定
> 查看影片
> 📺 **教學影片**:看看這個實用的影片導覽
>
> [![CSS Basics Tutorial](https://img.youtube.com/vi/6yIdOIV9p1I/0.jpg)](https://www.youtube.com/watch?v=6yIdOIV9p1I)
>
> [![Git 和 GitHub 基礎影片](https://img.youtube.com/vi/6yIdOIV9p1I/0.jpg)](https://www.youtube.com/watch?v=6yIdOIV9p1I)
### 設定你的 CSS 檔案
### 任務
開始設定樣式前,需將 CSS 連結到 HTML。這個連結告訴瀏覽器在哪裏尋找 terrarium 的樣式說明。
在你的玻璃缸資料夾中,創建一個名為 `style.css` 的新檔案。在 `<head>` 區域中匯入該檔案:
terrarium 資料夾中建立名為 `style.css` 的新檔案,接著在你的 HTML 文件 `<head>` 區段加入連結
```html
<link rel="stylesheet" href="./style.css" />
```
---
**此段程式碼功能說明:**
- **建立** HTML 與 CSS 檔案之間的連結
- **告訴**瀏覽器載入並套用 `style.css` 的樣式
- **使用** `rel="stylesheet"` 屬性表示這是 CSS 檔案
- **透過** `href="./style.css"` 指定檔案路徑
## 層疊
## 認識 CSS 層疊效果
層疊樣式表的核心概念是樣式的“層疊”,即樣式的應用是由其優先級決定的。由網站作者設置的樣式優先於瀏覽器設置的樣式。內聯樣式的優先級高於外部樣式表中的樣式。
你是否好奇為何稱 CSS 為「層疊」樣式表?樣式像瀑布一樣往下流,有時候彼此會衝突
### 任務
想像軍隊指揮系統—將軍命令「所有士兵穿綠色」但特定單位接到命令「典禮穿藍色制服」。更具體的指令會優先執行。CSS 也類似,了解此層級關係可讓除錯變得簡單。
為你的 `<h1>` 標籤添加內聯樣式 "color: red"
### 層疊優先權實驗
```HTML
我們來看層疊實際運作,先在 `<h1>` 標籤加入內嵌樣式:
```html
<h1 style="color: red">My Terrarium</h1>
```
然後,將以下代碼添加到你的 `style.css` 檔案中:
**此段程式碼功能說明:**
- **直接給予** `<h1>` 紅色文字,使用內嵌樣式
- **利用** `style` 屬性在 HTML 內嵌 CSS
- **創造** 最高優先權的樣式規則於該元素
```CSS
接著在 `style.css` 加入這條規則:
```css
h1 {
color: blue;
color: blue;
}
```
✅ 你的網頁應用程式中顯示了哪種顏色?為什麼?你能找到覆蓋樣式的方法嗎?什麼情況下你會想這樣做,或者不這樣做?
**上面的設定:**
- **定義** 針對所有 `<h1>` 元素的 CSS 規則
- **將** 文字色設為藍色,透過外部樣式表
- **優先權低** 於內嵌樣式
**知識檢測**:你的網站顯示哪個顏色?為何該顏色勝出?你能想到哪些情境會需要覆寫樣式嗎?
```mermaid
flowchart TD
A["瀏覽器遇到 h1 元素"] --> B{"檢查內聯樣式"}
B -->|找到| C["style='color: red'"]
B -->|無| D{"檢查 ID 規則"}
C --> E["套用紅色 (1000 分)"]
D -->|找到| F["#heading { color: green }"]
D -->|無| G{"檢查類別規則"}
F --> H["套用綠色 (100 分)"]
G -->|找到| I[".title { color: blue }"]
G -->|無| J{"檢查元素規則"}
I --> K["套用藍色 (10 分)"]
J -->|找到| L["h1 { color: purple }"]
J -->|無| M["使用瀏覽器預設"]
L --> N["套用紫色 (1 分)"]
style C fill:#ff6b6b
style F fill:#51cf66
style I fill:#339af0
style L fill:#9775fa
```
> 💡 **CSS 優先順序(由高到低):**
> 1. **內嵌樣式** (style 屬性)
> 2. **ID 選擇器** (#myId)
> 3. **類別** (.myClass) 和屬性選擇器
> 4. **元素選擇器** (h1, div, p)
> 5. **瀏覽器預設**
---
## CSS 繼承原理
## 繼承
CSS 繼承就像基因遺傳—元素會繼承父元素的某些屬性。如果你在 body 設定字型,所有內部文字自動使用該字型。這有點像哈布斯堡王朝家族特徵的明顯下巴一代代遺傳。
樣式可以從祖先元素繼承到後代元素,嵌套的元素會繼承其父元素的樣式。
但不是所有屬性都會繼承。字型和顏色會繼承,邊距和邊框等佈局屬性則不會。就像小孩可能遺傳父母的長相,但不見得穿父母的時尚風格
### 任務
### 觀察字體繼承
將 body 的字體設置為指定字體,並檢查嵌套元素的字體:
透過設定 `<body>` 的字型,來實作繼承效果
```CSS
```css
body {
font-family: helvetica, arial, sans-serif;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
```
打開瀏覽器的控制台,進入“元素”標籤,觀察 H1 的字體。它繼承了 body 的字體,這在瀏覽器中有所說明:
**說明:**
- **設定** 全頁字型,針對 `<body>` 元素
- **使用** 多種字型備援沾功能,增強瀏覽器的兼容性
- **採用** 現代系統字型,跨作業系統呈現佳
- **確保** 除非特別覆寫,所有子元素繼承此字型
![繼承的字體](../../../../translated_images/1.cc07a5cbe114ad1d4728c35134584ac1b87db688eff83cf75985cf31fe0ed95c.hk.png)
打開瀏覽器開發者工具F12切換至 Elements檢查 `<h1>`,可看到它繼承 body 的字型:
✅ 你能讓嵌套樣式繼承不同的屬性嗎?
![inherited font](../../../../translated_images/1.cc07a5cbe114ad1d.hk.png)
---
**實驗時間**:試著在 `<body>` 設定其他可繼承屬性如 `color`、`line-height`、`text-align`。你的標題和其他元素會發生什麼事?
> 📝 **可繼承屬性範例**`color`、`font-family`、`font-size`、`line-height`、`text-align`、`visibility`
>
> **不可繼承屬性範例**`margin`、`padding`、`border`、`width`、`height`、`position`
### 🔄 **教學檢核**
**CSS 基礎理解**:在學習選擇器前,確保你能:
- ✅ 解釋層疊和繼承的差異
- ✅ 預測字體優先權衝突中哪個會勝出
- ✅ 識別哪些屬性會繼承父元素
- ✅ 正確連結 CSS 檔案到 HTML
**快速測試**:若有以下樣式,`<div class="special">` 中的 `<h1>` 會是什麼顏色?
```css
div { color: blue; }
.special { color: green; }
h1 { color: red; }
```
*答案:紅色(元素選擇器直接針對 h1*
## 精通 CSS 選擇器
CSS 選擇器是針對特定元素應用樣式的方式。它就像給出精確方向,與其說「那棟房子」,不如說「楓樹街上紅門的藍色房子」。
## CSS 選擇器
CSS 有多種精確程度,選擇正確的選擇器就像挑選合適工具。有時你想給整區所有門塗色,有時只要針對一扇門。
### 標籤
### 元素選擇器(標籤
到目前為止,你的 `style.css` 檔案中只為少數標籤設置了樣式,應用程式看起來有些奇怪:
元素選擇器根據標籤名稱標的 HTML 元素,適合設定廣泛適用的基礎樣式
```CSS
```css
body {
font-family: helvetica, arial, sans-serif;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 0;
padding: 0;
}
h1 {
color: #3a241d;
text-align: center;
color: #3a241d;
text-align: center;
font-size: 2.5rem;
margin-bottom: 1rem;
}
```
這種為標籤設置樣式的方法可以控制單一元素,但你需要控制玻璃缸中多個植物的樣式。為此,你需要利用 CSS 選擇器。
**理解這些樣式:**
- **對整頁** 用 `body` 設定一致的字型排版
- **移除** 瀏覽器預設的邊距與內距,方便控制
- **為** 所有標題元素設定顏色、對齊與間距
- **使用** `rem` 單位,確保字型大小可延展且易讀
### ID
元素選擇器非常適合設定通用風格,但對於 terrarium 內部像植物這類個別元件,需要更細緻選擇器。
為左側和右側容器添加一些樣式。由於標記中只有一個左側容器和一個右側容器,因此它們被賦予了 ID。使用 `#` 來設置樣式:
### ID 選擇器:獨特元素
```CSS
ID 選擇器使用 `#`,針對具有特定 `id` 屬性的元素。因 ID 在頁面中應唯一,非常適合為獨特元素(如左、右兩邊的植物容器)設定樣式。
讓我們設計 terrarium 側邊容器的樣式,放置植物:
```css
#left-container {
background-color: #eee;
width: 15%;
left: 0px;
top: 0px;
position: absolute;
height: 100%;
padding: 10px;
background-color: #f5f5f5;
width: 15%;
left: 0;
top: 0;
position: absolute;
height: 100vh;
padding: 1rem;
box-sizing: border-box;
}
#right-container {
background-color: #eee;
width: 15%;
right: 0px;
top: 0px;
position: absolute;
height: 100%;
padding: 10px;
background-color: #f5f5f5;
width: 15%;
right: 0;
top: 0;
position: absolute;
height: 100vh;
padding: 1rem;
box-sizing: border-box;
}
```
在這裡,你使用絕對定位將這些容器放置在螢幕的最左側和最右側,並使用百分比設置其寬度,以便它們可以適應小型行動裝置螢幕。
**此段程式碼成果:**
- **用** `absolute` 定位把容器固定在左右兩側
- **使用** `vh`(視窗高度)單位製作響應式高度
- **設定** `box-sizing: border-box`,包含 padding 計算寬度
- **移除** `px` 單位的零值,使程式碼更乾淨
- **搭配** 柔和背景色,較不刺眼
✅ 這段代碼有些重複因此不符合“DRY”Don't Repeat Yourself不要重複自己的原則你能找到更好的方法來設置這些 ID 的樣式嗎?也許可以結合 ID 和類?你需要更改標記並重構 CSS
**程式碼質量挑戰**:此 CSS 違反 DRY不要重複自己原則你能用 ID 和類別混合重構它嗎?
**改良方法:**
```html
<div id="left-container" class="container"></div>
<div id="right-container" class="container"></div>
```
### 類別
```css
.container {
background-color: #f5f5f5;
width: 15%;
top: 0;
position: absolute;
height: 100vh;
padding: 1rem;
box-sizing: border-box;
}
在上面的例子中,你為螢幕上的兩個唯一元素設置了樣式。如果你希望樣式應用於螢幕上的多個元素,可以使用 CSS 類別。為左側和右側容器中的植物設置樣式。
#left-container {
left: 0;
}
注意HTML 標記中的每個植物都有 ID 和類別的組合。這裡的 ID 是供稍後添加的 JavaScript 使用,用於操作玻璃缸植物的位置。而類別則為所有植物提供了統一的樣式。
#right-container {
right: 0;
}
```
### 類別選擇器:可復用樣式
類別選擇器用 `.` 表示,適合將相同樣式套用到多個元素。與 ID 不同,類別可多處重複使用,非常適合樣式一致性的模式。
在 terrarium 裡每株植物需要共用樣式但位置不同。可用類別分享樣式ID 定位。
**這是每株植物的 HTML 結構:**
```html
<div class="plant-holder">
<img class="plant" alt="plant" id="plant1" src="./images/plant1.png" />
<img class="plant" alt="Decorative plant for terrarium" id="plant1" src="../../../../translated_images/plant1.d18b18ffe73da18f.hk.png" />
</div>
```
將以下代碼添加到你的 `style.css` 檔案中:
**重點說明:**
- **用** `class="plant-holder"` 統一容器樣式
- **用** `class="plant"` 統一植物圖像樣式與行為
- **用** 唯一 `id="plant1"` 作特定定位與 JavaScript 操作
- **使用** 描述性 alt 文字供輔助工具使用
接著把這些樣式加進 `style.css`
```CSS
```css
.plant-holder {
position: relative;
height: 13%;
left: -10px;
position: relative;
height: 13%;
left: -0.6rem;
}
.plant {
position: absolute;
max-width: 150%;
max-height: 150%;
z-index: 2;
position: absolute;
max-width: 150%;
max-height: 150%;
z-index: 2;
transition: transform 0.3s ease;
}
.plant:hover {
transform: scale(1.05);
}
```
在這段代碼中,值得注意的是相對定位和絕對定位的混合使用,我們會在下一節中詳細介紹。請注意高度是如何以百分比處理的:
**解析這些樣式:**
- **為** plant holder 設定相對定位,建立定位情境
- **每個** plant holder 高度 13%,確保所有植物垂直可見且不需捲動
- **輕微** 左移容器,更好對齊植物
- **植物** 利用最大寬高限制,自主縮放保持比例
- **使用** `z-index` 將植物置於 terrarium 其他元素之上
- **加入** 平滑變化的懸停效果,提升互動感
**批判思考**:為什麼同時要有 `.plant-holder``.plant`?若只用一個會怎樣?
> 💡 **設計模式**:容器 `.plant-holder` 控制佈局和定位,內容 `.plant` 控制外觀與縮放。分工讓程式碼更易維護與彈性。
## CSS 定位原理
CSS 定位就像舞台導演,指揮每個角色在哪站位、如何移動。有些角色遵循一般規則,有些角色需要特定定位才能呈現戲劇效果。
一旦明白定位,許多佈局問題都迎刃而解。想要不動的導覽列?定位辦得到。想要具體位置的提示工具?定位同樣沒問題。
### 五種定位值
```mermaid
quadrantChart
title CSS 定位策略
x-axis 文件流程 --> 從流程中移除
y-axis 靜態定位 --> 精確控制
quadrant-1 絕對定位
quadrant-2 固定定位
quadrant-3 靜態定位
quadrant-4 黏性定位
Static: [0.2, 0.2]
Relative: [0.3, 0.6]
Absolute: [0.8, 0.8]
Fixed: [0.9, 0.7]
Sticky: [0.5, 0.9]
```
| 定位值 | 行為 | 使用場合 |
|----------------|----------|----------|
| `static` | 預設流,忽略 top/left/right/bottom | 正常文件佈局 |
| `relative` | 相對本身正常位置定位 | 小幅調整,建立定位上下文 |
| `absolute` | 相對最近已定位祖先定位 | 精確擺放,疊加效果 |
| `fixed` | 相對視窗定位 | 固定導覽列、浮動元素 |
| `sticky` | 根據捲動切換相對與固定 | 捲動置頂標頭 |
### 我們的 terrarium 定位策略
terrarium 應用多種類型定位,做出目標佈局:
```css
/* Container positioning */
.container {
position: absolute; /* Removes from normal flow */
/* ... other styles ... */
}
你將植物容器的高度設置為 13%,這是一個不錯的數值,確保所有植物都能顯示在每個垂直容器中,而不需要滾動。
/* Plant holder positioning */
.plant-holder {
position: relative; /* Creates positioning context */
/* ... other styles ... */
}
你將植物容器向左移動,使植物在容器內更居中。由於圖片有大量透明背景,為了讓它們更容易拖動,需要向左移動以更好地適應螢幕。
/* Plant positioning */
.plant {
position: absolute; /* Allows precise placement within holder */
/* ... other styles ... */
}
```
然後,植物本身的最大寬度設置為 150%。這樣可以讓它隨著瀏覽器縮小而縮小。嘗試調整瀏覽器大小,植物會保持在容器內,但會縮小以適應。
**解讀定位策略:**
- **絕對定位容器** 脫離文件流,固定於螢幕邊緣
- **相對定位植物容器** 建立定位上下文,仍保留文件流
- **絕對定位植物** 可精準定位於其相對容器內
- **此組合** 可讓植物垂直堆疊,且可個別定位
> 🎯 **重要原因**`plant` 元素需絕對定位,才能在下一課實作拖拉功能。絕對定位將它們從正常佈局中抽離,使拖放互動成為可能。
**實驗時間**:嘗試替換定位值並觀察結果:
- 若將 `.container``absolute` 改為 `relative` 會怎樣?
- 如果 `.plant-holder` 使用 `absolute` 而不是 `relative`,佈局會有什麼變化?
- 當你將 `.plant` 改成 `relative` 定位時會發生什麼?
### 🔄 **教學檢查點**
**CSS 定位精通**:暫停確認你的理解:
- ✅ 你能解釋為什麼植物需要絕對定位來實現拖放嗎?
- ✅ 你了解相對容器如何創造定位上下文嗎?
- ✅ 為什麼側邊容器使用絕對定位?
- ✅ 如果完全移除定位聲明會發生什麼?
**現實世界的聯繫**:想想 CSS 定位如何映射現實世界的佈局:
- **Static**:書架上的書(自然排列)
- **Relative**:稍微移動書本,但保持位置
- **Absolute**:把書籤放在確切的頁數
- **Fixed**:粘著便條紙,在翻頁時保持可見
## 使用 CSS 建造生態瓶
現在我們將只用 CSS 建造一個玻璃罐—不需要圖片或繪圖軟件。
利用定位和透明度來創造逼真的玻璃、陰影與深度效果展示CSS的視覺能力。此技術類似包豪斯運動建築師用簡單幾何形體創造複雜美麗建築。了解這些原則後你將能認出許多網站設計背後的 CSS 技巧。
```mermaid
flowchart LR
A[罐蓋] --> E[完整生態瓶]
B[罐壁] --> E
C[泥土層] --> E
D[罐底] --> E
F[玻璃效果] --> E
A1["50% 寬度<br/>5% 高度<br/>頂部位置"] --> A
B1["60% 寬度<br/>80% 高度<br/>圓角<br/>0.5 透明度"] --> B
C1["60% 寬度<br/>5% 高度<br/>深啡色<br/>底層"] --> C
D1["50% 寬度<br/>1% 高度<br/>底部位置"] --> D
F1["細微陰影<br/>透明度<br/>Z 索引分層"] --> F
style E fill:#d1e1df,stroke:#3a241d
style A fill:#e8f5e8
style B fill:#e8f5e8
style C fill:#8B4513
style D fill:#e8f5e8
```
### 建造玻璃罐組件
還值得注意的是 z-index 的使用,它控制元素的相對高度(使植物位於容器之上,看起來像是放在玻璃缸內)。
讓我們逐一建造生態瓶罐的各部分。每個部分都使用絕對定位以及百分比大小以達到響應式設計:
✅ 為什麼需要同時使用植物容器和植物的 CSS 選擇器?
```css
.jar-walls {
height: 80%;
width: 60%;
background: #d1e1df;
border-radius: 1rem;
position: absolute;
bottom: 0.5%;
left: 20%;
opacity: 0.5;
z-index: 1;
box-shadow: inset 0 0 2rem rgba(0, 0, 0, 0.1);
}
## CSS 定位
.jar-top {
width: 50%;
height: 5%;
background: #d1e1df;
position: absolute;
bottom: 80.5%;
left: 25%;
opacity: 0.7;
z-index: 1;
border-radius: 0.5rem 0.5rem 0 0;
}
混合使用定位屬性(包括 static、relative、fixed、absolute 和 sticky 定位)可能有些棘手,但如果使用得當,可以很好地控制頁面上的元素。
.jar-bottom {
width: 50%;
height: 1%;
background: #d1e1df;
position: absolute;
bottom: 0;
left: 25%;
opacity: 0.7;
border-radius: 0 0 0.5rem 0.5rem;
}
.dirt {
width: 60%;
height: 5%;
background: #3a241d;
position: absolute;
border-radius: 0 0 1rem 1rem;
bottom: 1%;
left: 20%;
opacity: 0.7;
z-index: -1;
}
```
絕對定位的元素是相對於其最近的已定位祖先進行定位的,如果沒有已定位的祖先,則相對於文檔的 body 進行定位。
**了解生態瓶的構造:**
- **使用**百分比尺寸,實現在所有螢幕尺寸上的響應式縮放
- **絕對定位**元素以精確堆疊和對齊
- **應用**不同透明度值來創造玻璃的透光效果
- **實施**`z-index`分層,讓植物看起來在罐子內部
- **加入**微妙的盒陰影和圓角,讓外觀更真實
相對定位的元素是根據 CSS 指定的方向,從其初始位置進行調整。
### 使用百分比的響應式設計
在我們的範例中,`plant-holder` 是一個相對定位的元素,位於一個絕對定位的容器內。這種行為的結果是,側邊欄容器被固定在左右兩側,而植物容器則嵌套在側邊欄內,調整自身位置以在側邊欄內垂直排列植物。
注意所有尺寸都使用百分比,而非固定像素值:
> 植物本身也使用了絕對定位,這是為了讓它可以被拖動,你會在下一課中了解更多。
**為什麼這很重要:**
- **確保**生態瓶在任意螢幕尺寸上按比例縮放
- **維持**罐子組件之間的視覺關係
- **提供**從手機到大螢幕桌面一致的體驗
- **允許**設計適應變化,不破壞視覺佈局
✅ 嘗試切換側邊容器和植物容器的定位類型。會發生什麼?
### CSS 單位的運用
## CSS 佈局
我們使用 `rem` 單位為 border-radius 設定大小,這會相對於根字體大小縮放,創建更可及的設計,尊重使用者字體偏好。詳細可參考官方規範的 [CSS relative units](https://www.w3.org/TR/css-values-3/#font-relative-lengths)。
現在,你將使用所學知識,完全通過 CSS 構建玻璃缸本身!
**視覺實驗**:嘗試修改這些值並觀察效果:
- 將罐子透明度從 0.5 改為 0.8 — 玻璃外觀如何變化?
- 調整污土顏色從 `#3a241d` 改為 `#8B4513` — 視覺影響是什麼?
- 將污土的 `z-index` 改為 2 — 分層順序會怎樣?
首先,使用 CSS 將 `.terrarium` div 的子元素設置為圓角矩形:
### 🔄 **教學檢查點**
**CSS 視覺設計理解**:確認你對視覺 CSS 的掌握:
- ✅ 百分比尺寸如何創造響應式設計?
- ✅ 為什麼透明度能營造玻璃透光效果?
- ✅ `z-index` 在元素分層中扮演什麼角色?
- ✅ border-radius 如何塑造罐子形狀?
```CSS
.jar-walls {
height: 80%;
width: 60%;
background: #d1e1df;
border-radius: 1rem;
position: absolute;
bottom: 0.5%;
left: 20%;
opacity: 0.5;
z-index: 1;
}
**設計原則**:注意我們如何用簡單圖形構建複雜視覺:
1. **矩形** → **圓角矩形** → **罐子組件**
2. **純色** → **透明度** → **玻璃效果**
3. **單一元素** → **分層組合** → **3D 外觀**
.jar-top {
width: 50%;
height: 5%;
background: #d1e1df;
position: absolute;
bottom: 80.5%;
left: 25%;
opacity: 0.7;
z-index: 1;
}
---
.jar-bottom {
width: 50%;
height: 1%;
background: #d1e1df;
position: absolute;
bottom: 0%;
left: 25%;
opacity: 0.7;
}
## GitHub Copilot Agent 挑戰 🚀
.dirt {
width: 60%;
height: 5%;
background: #3a241d;
position: absolute;
border-radius: 0 0 1rem 1rem;
bottom: 1%;
left: 20%;
opacity: 0.7;
z-index: -1;
}
```
使用 Agent 模式完成以下挑戰:
注意這裡使用了百分比。如果縮小瀏覽器,你可以看到玻璃缸也會縮放。還要注意玻璃缸元素的寬度和高度百分比,以及每個元素如何絕對定位在中心,並固定在視窗底部
**說明:** 創建一個 CSS 動畫,讓生態瓶中的植物輕輕搖擺,模擬自然微風吹拂效果。這將幫助你練習 CSS 動畫、變形和關鍵影格,並提升生態瓶的視覺吸引力。
我們還使用了 `rem` 作為圓角半徑的單位,這是一種相對於字體大小的長度。可以在 [CSS 規範](https://www.w3.org/TR/css-values-3/#font-relative-lengths) 中了解更多關於這種相對測量的方法
**提示:** 為 `.plant` 類添加 CSS 關鍵影格動畫,使植物輕輕左右擺動。創建一個擺動動畫,使每株植物沿水平方向旋轉約 2-3 度,持續時間 3-4 秒,且動畫無限循環,並使用平滑的緩動函數。
✅ 嘗試更改玻璃缸的顏色和透明度與泥土的顏色和透明度。會發生什麼?為什麼?
在這了解更多[Agent 模式](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode)。
---
## 🚀 挑戰:加入玻璃反光效果
## 🚀挑戰
準備好用逼真的玻璃反光效果增強你的生態瓶了嗎?這技術將增加層次感與真實感。
為玻璃缸的左下角添加一個“氣泡”光澤,使其看起來更像玻璃。你將為 `.jar-glossy-long``.jar-glossy-short` 添加樣式,使其看起來像反射的光澤。效果如下:
你將創建細膩的高光,模擬光線如何在玻璃面反射。這類似文藝復興畫家 Jan van Eyck 利用光影與反射使繪畫中的玻璃呈現立體感。目標如下:
![完成的玻璃缸](../../../../translated_images/terrarium-final.2f07047ffc597d0a06b06cab28a77801a10dd12fdb6c7fc630e9c40665491c53.hk.png)
![finished terrarium](../../../../translated_images/terrarium-final.2f07047ffc597d0a.hk.png)
要完成課後測驗,請參考此學習模組:[使用 CSS 為 HTML 應用程式設計樣式](https://docs.microsoft.com/learn/modules/build-simple-website/4-css-basics/?WT.mc_id=academic-77807-sagibbon)
**你的挑戰:**
- **創建**細膩的白色或淺色橢圓形,用於玻璃反光
- **策略性定位**在罐子左側
- **應用**適當透明度和模糊效果,模擬真實光反射
- **使用**`border-radius` 創造自然泡泡狀形狀
- **嘗試**漸層或盒陰影以增強真實感
## 課後測驗
[課後測驗](https://ff-quizzes.netlify.app/web/quiz/18)
## 回顧與自學
## 擴展你的 CSS 知識
CSS 一開始可能會覺得複雜,但掌握這些核心概念將為進階技巧打下紮實基礎。
**接下來的 CSS 學習領域:**
- **Flexbox** - 簡化元素對齊和分佈
- **CSS Grid** - 強大工具,建造複雜佈局
- **CSS 變數** - 減少重複,提升維護性
- **響應式設計** - 確保網站適用不同螢幕尺寸
### 互動學習資源
用這些有趣的實作遊戲練習概念:
- 🐸 [Flexbox Froggy](https://flexboxfroggy.com/) - 通過挑戰精通 Flexbox
- 🌱 [Grid Garden](https://codepip.com/games/grid-garden/) - 種胡蘿蔔學 CSS Grid
- 🎯 [CSS Battle](https://cssbattle.dev/) - 編碼挑戰測試你的 CSS 技能
### 額外學習
欲獲得完整 CSS 基礎教學,完成這個 Microsoft Learn 課程:[使用 CSS 美化你的 HTML 應用程式](https://docs.microsoft.com/learn/modules/build-simple-website/4-css-basics/?WT.mc_id=academic-77807-sagibbon)
### ⚡ **接下來 5 分鐘你可以做**
- [ ] 開啟 DevTools使用 Elements 面板檢查任一網站 CSS 樣式
- [ ] 建立簡單 CSS 文件並連結至 HTML 頁面
- [ ] 嘗試用不同方式改色十六進位、RGB、命名顏色
- [ ] 練習盒模型,為 div 加入 padding 和 margin
### 🎯 **這小時你能完成的目標**
- [ ] 完成課後測驗並複習 CSS 基礎
- [ ] 用字型、顏色和間距美化你的 HTML 頁面
- [ ] 使用 flexbox 或 grid 建立簡單佈局
- [ ] 嘗試 CSS 過渡效果,實現流暢效果
- [ ] 利用媒體查詢練習響應式設計
### 📅 **你的 CSS 一週冒險**
- [ ] 完成生態瓶樣式作業並加添創意
- [ ] 精通 CSS Grid打造相片集佈局
- [ ] 學習 CSS 動畫讓設計生動起來
- [ ] 探索 Sass 或 Less 等 CSS 預處理器
- [ ] 研究設計原則並應用於 CSS
- [ ] 分析並重現你在線看到的有趣設計
### 🌟 **你的 CSS 一個月設計精通**
- [ ] 建立完整響應式網站設計系統
- [ ] 學習 CSS-in-JS 或類似 Tailwind 的功能型框架
- [ ] 為開源專案貢獻 CSS 優化
- [ ] 精通進階 CSS 概念,如客製屬性與內容隔離
- [ ] 創建可重用的組件庫與模組化 CSS
- [ ] 指導 CSS 初學者並分享設計心得
## 🎯 你的 CSS 精通時間表
```mermaid
timeline
title CSS 學習進度
section 基礎 (10分鐘)
檔案連結:將 CSS 連結到 HTML
:了解層疊規則
:學習繼承基礎
section 選擇器 (15分鐘)
目標元素:元素選擇器
:類別模式
ID 特異性
:組合器
section 盒模型 (20分鐘)
版面基礎:外邊距與內邊距
:邊框屬性
:內容尺寸
:盒模型行為
section 定位 (25分鐘)
元素位置:靜態與相對定位
:絕對定位
Z 索引層次
:響應單位
section 視覺設計 (30分鐘)
樣式掌握:顏色與透明度
:陰影與特效
:過渡效果
:變形屬性
section 響應式設計 (45分鐘)
多裝置支援:媒體查詢
:彈性版面
:行動優先
:視口最佳化
section 進階技術 (1週)
現代 CSSFlexbox 版面
CSS Grid 系統
:自訂屬性
:動畫關鍵影格
section 專業能力 (1個月)
CSS 架構:元件模式
:可維護程式碼
:效能優化
:跨瀏覽器相容性
```
### 🛠️ 你的 CSS 工具總結
完成本課後,你已掌握:
- **層疊理解**:樣式如何繼承與覆蓋
- **選擇器精通**:準確鎖定元素、類別與 ID
- **定位技巧**:策略性擺放與分層
- **視覺設計**:創造玻璃效果、陰影與透明度
- **響應式技術**:百分比佈局,適應任意螢幕
- **代碼組織**:整潔且易維護的 CSS 結構
- **現代實踐**:運用相對單位與可及設計模式
CSS 看似簡單但要為所有瀏覽器和螢幕尺寸完美設計應用程式樣式仍然面臨許多挑戰。CSS-Grid 和 Flexbox 是為了讓這項工作更結構化、更可靠而開發的工具。通過玩 [Flexbox Froggy](https://flexboxfroggy.com/) 和 [Grid Garden](https://codepip.com/games/grid-garden/) 來學習這些工具。
**下一步**你的生態瓶已有結構HTML與樣式CSS。最後一課將用 JavaScript 加入互動!
## 作業
@ -280,5 +729,7 @@ CSS 看似簡單,但要為所有瀏覽器和螢幕尺寸完美設計應用程
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
此文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原始語言的文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解讀概不負責。
本文件由 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於追求準確性,請注意自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於關鍵資訊,建議聘請專業人工翻譯。因使用本翻譯所產生的任何誤解或誤譯,我們概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,8 +1,8 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "a212cc22a18eddf9046b7a16dfbafd8b",
"translation_date": "2025-10-03T08:56:56+00:00",
"original_hash": "bee6762d4092a13fc7c338814963f980",
"translation_date": "2026-01-06T13:06:33+00:00",
"source_file": "3-terrarium/2-intro-to-css/assignment.md",
"language_code": "hk"
}
@ -11,31 +11,126 @@ CO_OP_TRANSLATOR_METADATA:
## 目標
重構「玻璃花園」項目,使用 **Flexbox****CSS Grid** 進行佈局。根據需要更新 HTML 和 CSS以實現現代化、響應式的設計。您不需要實現可拖動的元素——只需專注於佈局和樣式
將你的玻璃花房項目轉換為使用現代 CSS 佈局技術!將目前的絕對定位方式重構為使用 **Flexbox****CSS Grid**,以實現更易於維護且響應式的設計。此作業挑戰你在保持玻璃花房視覺吸引力的同時,應用現代 CSS 標準
## 指示
理解什麼時候以及如何使用不同的佈局方法是現代網頁開發的重要技能。此練習將傳統定位技術與當代 CSS 佈局系統連接起來。
1. **創建一個新版本** 的玻璃花園應用。更新標記和 CSS使用 Flexbox 或 CSS Grid 進行佈局。
2. **確保藝術和元素保持原樣**,與原始版本一致。
3. **在至少兩個不同的瀏覽器中測試您的設計**例如Chrome、Firefox、Edge
4. **截取您的玻璃花園在每個瀏覽器中的屏幕截圖**,以展示跨瀏覽器的兼容性。
5. **提交** 您更新的代碼和屏幕截圖。
## 作業指示
### 第一階段:分析與規劃
1. **檢視你目前的玻璃花房程式碼**-找出哪些元素目前使用絕對定位
2. **選擇你的佈局方法**-決定 Flexbox 或 CSS Grid 哪個更適合你的設計目標
3. **繪製你的新佈局結構草圖**-規劃容器與植物元素如何被組織
### 第二階段:實作
1. **在另一個資料夾中建立你的玻璃花房新版本**
2. **依需要更新 HTML 結構**以支援你選擇的佈局方法
3. **重構 CSS**,用 Flexbox 或 CSS Grid 取代絕對定位
4. **保持視覺一致性**-確保植物和玻璃罐顯示於相同位置
5. **實作響應式行為**-你的佈局應能優雅地適應不同螢幕尺寸
### 第三階段:測試與文件撰寫
1. **跨瀏覽器測試**-確保你的設計在 Chrome、Firefox、Edge 及 Safari 中正常運作
2. **響應式測試**-在手機、平板及桌面尺寸上檢視佈局
3. **文件撰寫**-於 CSS 加入說明你的佈局選擇的註解
4. **截圖**-擷取不同瀏覽器與螢幕尺寸中你的玻璃花房畫面
## 技術規範
### 佈局實作
- **選擇其中一個**:實作 Flexbox 或 CSS Grid相同元素不可兩者同時使用
- **響應式設計**:使用相對單位(`rem`、`em`、`%`、`vw`、`vh`)替代固定像素
- **可及性**:保持適當的語義化 HTML 結構與替代文字 (alt)
- **程式碼品質**:使用一致命名規則且有條理地組織 CSS
### 要使用的現代 CSS 功能
```css
/* Example Flexbox approach */
.terrarium-container {
display: flex;
flex-direction: column;
min-height: 100vh;
align-items: center;
justify-content: center;
}
.plant-containers {
display: flex;
justify-content: space-between;
width: 100%;
max-width: 1200px;
}
/* Example Grid approach */
.terrarium-layout {
display: grid;
grid-template-columns: 1fr 3fr 1fr;
grid-template-rows: auto 1fr;
min-height: 100vh;
gap: 1rem;
}
```
### 瀏覽器支援需求
- **Chrome/Edge**:最新兩個版本
- **Firefox**:最新兩個版本
- **Safari**:最新兩個版本
- **行動瀏覽器**iOS Safari, Chrome Mobile
## 交付項目
1. **更新過的 HTML 檔案**,結構更語義化
2. **重構後的 CSS 檔案**,使用現代佈局技術
3. **截圖集**,展示跨瀏覽器相容性:
- 桌面版 (1920x1080)
- 平板版 (768x1024)
- 手機版 (375x667)
- 至少兩款不同瀏覽器
4. **README.md 文件**,記錄:
- 你的佈局選擇Flexbox 還是 Grid及其理由
- 重構過程中面臨的挑戰
- 瀏覽器相容性說明
- 執行程式碼的說明
## 評分標準
| 評分項目 | 優秀表現 | 合格表現 | 需要改進 |
|------------|--------------------------------------------------------------------------|---------------------------------------|----------------------------------------|
| 佈局 | 完全使用 Flexbox 或 CSS Grid 進行重構;視覺效果吸引人且響應式 | 部分元素重構;部分使用 Flexbox 或 Grid | 很少或未使用 Flexbox 或 Grid佈局未更改 |
| 跨瀏覽器 | 提供多個瀏覽器的屏幕截圖;外觀一致 | 僅提供一個瀏覽器的屏幕截圖;有輕微不一致 | 未提供屏幕截圖或存在重大不一致 |
| 代碼質量 | HTML/CSS 乾淨且組織良好;有清晰的註解 | 有一定的組織;註解較少 | 代碼混亂;缺乏註解 |
| 評分項目 | 優異 (4) | 精通 (3) | 發展中 (2) | 初學 (1) |
|----------|----------|----------|------------|----------|
| **佈局實作** | 熟練使用 Flexbox/Grid 進階功能;完全響應式 | 正確實作並具有良好響應式行為 | 基本實作,有輕微響應式問題 | 佈局實作不完整或錯誤 |
| **程式碼品質** | 乾淨、條理分明的 CSS具意義的註解與一致命名 | 良好組織並附有部分註解 | 適當組織,註解較少 | 組織差,難以理解 |
| **跨瀏覽器相容性** | 在所有要求瀏覽器中完美一致,附截圖 | 良好相容,少量差異並有記錄 | 部分相容問題但不影響功能 | 重大相容問題或缺少測試 |
| **響應式設計** | 優秀的 mobile-first 策略,切換點順暢 | 良好響應性,切換點適當 | 基本響應功能,佈局有些問題 | 響應性有限或失效 |
| **文件撰寫** | 詳盡的 README含充分解釋與見解 | 良好文件,涵蓋所有必需項目 | 基本文件,解釋不足 | 文件不完整或缺失 |
## 參考資源
### 佈局方法指南
- 📖 [Flexbox 完整指南](https://css-tricks.com/snippets/css/a-guide-to-flexbox/)
- 📖 [CSS Grid 完整指南](https://css-tricks.com/snippets/css/complete-guide-grid/)
- 📖 [Flexbox vs Grid - 選擇正確工具](https://blog.webdevsimplified.com/2022-11/flexbox-vs-grid/)
### 瀏覽器測試工具
- 🛠️ [瀏覽器開發者工具響應模式](https://developer.chrome.com/docs/devtools/device-mode/)
- 🛠️ [Can I Use - 功能支援](https://caniuse.com/)
- 🛠️ [BrowserStack - 跨瀏覽器測試](https://www.browserstack.com/)
### 程式碼品質工具
- ✅ [CSS 驗證器](https://jigsaw.w3.org/css-validator/)
- ✅ [HTML 驗證器](https://validator.w3.org/)
- ✅ [WebAIM 對比度檢查器](https://webaim.org/resources/contrastchecker/)
## 額外挑戰
## 提示
🌟 **進階佈局**:於設計不同部分同時實作 Flexbox 和 Grid
🌟 **動畫整合**:加入搭配新佈局的 CSS 過渡或動畫
🌟 **暗黑模式**:實作基於 CSS 自訂屬性的主題切換器
🌟 **容器查詢**:使用現代容器查詢技巧以達元件級響應式
- 查看 [Flexbox](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) 和 [CSS Grid](https://css-tricks.com/snippets/css/complete-guide-grid/) 指南。
- 使用瀏覽器開發工具測試響應式設計。
- 在代碼中添加註解以提高清晰度。
> 💡 **記住**:目標不只是讓它能用,而是理解為何你選擇的佈局方法是這個設計挑戰的最佳解決方案!
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,請注意自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。
本文件經由 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意自動翻譯結果可能包含錯誤或不準確之處。原始文件的母語版本應被視為具權威性的來源。對於重要資訊,建議採用專業人工翻譯。我們不對因使用此翻譯而產生的任何誤解或誤釋承擔責任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,61 +1,205 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "61c14b27044861e5e69db35dd52c4403",
"translation_date": "2025-08-29T15:08:31+00:00",
"original_hash": "973e48ad87d67bf5bb819746c9f8e302",
"translation_date": "2026-01-06T13:01:48+00:00",
"source_file": "3-terrarium/3-intro-to-DOM-and-closures/README.md",
"language_code": "hk"
}
-->
# 玻璃花園項目第三部分DOM 操作與閉包
# Terrarium 項目第三部分DOM 操作與 JavaScript 閉包
```mermaid
journey
title 你的 JavaScript DOM 之旅
section 基礎
理解 DOM: 3: Student
學習閉包: 4: Student
連接元素: 4: Student
section 互動
設置拖曳事件: 4: Student
追蹤座標: 5: Student
處理移動: 5: Student
section 精進
新增清理功能: 4: Student
測試功能: 5: Student
完成生態箱: 5: Student
```
![DOM 與閉包](../../../../translated_images/webdev101-js.10280393044d7eaa.hk.png)
> Sketchnote 由 [Tomomi Imura](https://twitter.com/girlie_mac) 製作
歡迎來到網頁開發中最吸引人的部分之一——讓事物互動起來文件物件模型DOM就像你的 HTML 與 JavaScript 之間的橋樑,今天我們將利用它讓你的植物箱活起來。當 Tim Berners-Lee 發明第一台網頁瀏覽器時,他就想像了一個文件可以動態且互動的網路,而 DOM 讓這個願景成為可能。
我們還會探索 JavaScript 閉包,這聽起來一開始可能會令人害怕。你可以把閉包想成創造「記憶口袋」,讓你的函數能記住重要資訊。就像你植物箱中的每棵植物各自擁有自己的資料記錄以追蹤位置一樣。在這課結束時,你會明白閉包是多麼自然且實用。
我們要做的是這樣的:一個讓使用者可以將植物拖放到任意位置的植物箱。你將學會推動從拖放檔案上傳到互動遊戲的 DOM 操作技巧。讓我們來讓你的植物箱活起來。
```mermaid
mindmap
root((DOM & JavaScript))
DOM Tree
元素選擇
屬性存取
事件處理
動態更新
Events
指標事件
滑鼠事件
觸控事件
事件監聽器
Closures
私有變數
函數作用域
記憶體持久化
狀態管理
Drag & Drop
位置追蹤
坐標運算
事件生命週期
使用者互動
Modern Patterns
事件委派
效能
跨裝置
可達性
```
## 講課前測驗
[講課前測驗](https://ff-quizzes.netlify.app/web/quiz/19)
## 了解 DOM你通往互動網頁的門戶
文件物件模型DOM是 JavaScript 與 HTML 元素溝通的方式。當你的瀏覽器載入 HTML 頁面時,它會在記憶體中建立該頁面的結構化表示,那就是 DOM。可以把它想成一棵家譜樹頁面中的每個 HTML 元素都是家族成員,而 JavaScript 可以存取、修改或重新排列他們。
DOM 操作能將靜態網頁轉變成互動式網站。每次你看到按鈕在滑鼠懸停時改變顏色、內容在不重新載入頁面下更新或可以拖曳的元素,那就是 DOM 操作在運作。
```mermaid
flowchart TD
A["文件"] --> B["HTML"]
B --> C["標頭"]
B --> D["主體"]
C --> E["標題"]
C --> F["元標籤"]
D --> G["H1我的生態瓶"]
D --> H["區塊:頁面容器"]
H --> I["區塊:左側容器"]
H --> J["區塊:右側容器"]
H --> K["區塊:生態瓶"]
I --> L["植物元素 1-7"]
J --> M["植物元素 8-14"]
L --> N["圖像#plant1"]
L --> O["圖像#plant2"]
M --> P["圖像#plant8"]
M --> Q["圖像#plant9"]
style A fill:#e1f5fe
style B fill:#f3e5f5
style D fill:#e8f5e8
style H fill:#fff3e0
style N fill:#ffebee
style O fill:#ffebee
style P fill:#ffebee
style Q fill:#ffebee
```
![DOM 樹狀結構圖](../../../../translated_images/dom-tree.7daf0e763cbbba92.hk.png)
> DOM 與對應 HTML 標記的示意圖。來自 [Olfa Nasraoui](https://www.researchgate.net/publication/221417012_Profile-Based_Focused_Crawler_for_Social_Media-Sharing_Websites)
**以下使 DOM 強大的原因:**
- **提供** 一種結構化方式存取頁面上的任何元素
- **使得** 在不用重新整理的情況下動態更新內容成為可能
- **允許** 即時對使用者互動如點擊與拖曳做出回應
- **創造** 現代互動式網頁應用程式的基礎
## JavaScript 閉包:創造有組織且強大的程式碼
[JavaScript 閉包](https://developer.mozilla.org/docs/Web/JavaScript/Closures) 就像給函數自己的私人工作區,裡面有持續存在的記憶。想像達爾文在加拉巴哥群島上每種雀鳥根據環境發展出專門喙的故事——閉包也是類似,創造專門的函數,使其「記得」特定上下文,即使父函數已經完成執行。
在我們的植物箱中,閉包讓每棵植物能獨立記住自己的位置。這種模式在專業 JavaScript 開發中到處可見,是個非常寶貴的概念。
```mermaid
flowchart LR
A["dragElement(plant1)"] --> B["建立閉包"]
A2["dragElement(plant2)"] --> B2["建立閉包"]
B --> C["私有變數"]
B2 --> C2["私有變數"]
C --> D["pos1, pos2, pos3, pos4"]
C --> E["pointerDrag 函數"]
C --> F["elementDrag 函數"]
C --> G["stopElementDrag 函數"]
C2 --> D2["pos1, pos2, pos3, pos4"]
C2 --> E2["pointerDrag 函數"]
C2 --> F2["elementDrag 函數"]
C2 --> G2["stopElementDrag 函數"]
H["植物 1 記住它的位置"] --> B
H2["植物 2 記住它的位置"] --> B2
style B fill:#e8f5e8
style B2 fill:#e8f5e8
style C fill:#fff3e0
style C2 fill:#fff3e0
```
> 💡 **理解閉包**:閉包是 JavaScript 中一個重要主題,許多開發者使用多年後才完全掌握所有理論面。今天我們主要探討實務應用——你會自然看到閉包在我們建構互動功能時出現。當你看到它們如何解決實際問題,理解就會逐漸形成。
![DOM 和閉包](../../../../translated_images/webdev101-js.10280393044d7eaaec7e847574946add7ddae6be2b2194567d848b61d849334a.hk.png)
> [Tomomi Imura](https://twitter.com/girlie_mac) 的手繪筆記
![DOM 樹狀結構圖](../../../../translated_images/dom-tree.7daf0e763cbbba92.hk.png)
## 課前測驗
> DOM 與對應 HTML 標記的示意圖。來自 [Olfa Nasraoui](https://www.researchgate.net/publication/221417012_Profile-Based_Focused_Crawler_for_Social_Media-Sharing_Websites)
[課前測驗](https://ff-quizzes.netlify.app/web/quiz/19)
在本課中,我們將完成互動植物箱專案,寫出 JavaScript 讓使用者能操控頁面上的植物。
### 簡介
## 開始之前:為成功做準備
操作 DOM文件物件模型是網頁開發中的一個關鍵部分。根據 [MDN](https://developer.mozilla.org/docs/Web/API/Document_Object_Model/Introduction) 的定義:「文件物件模型 (DOM) 是一種數據表示形式,用於描述構成網頁結構和內容的物件。」由於操作 DOM 的挑戰,許多開發者選擇使用 JavaScript 框架來取代原生 JavaScript 進行 DOM 管理,但我們這次將嘗試自己動手完成!
你需要之前植物箱課程的 HTML 與 CSS 檔案——我們即將把靜態設計變成交互式。如果你是第一次加入,先完成那些課程會有重要的背景知識。
此外,本課程還會介紹 [JavaScript 閉包](https://developer.mozilla.org/docs/Web/JavaScript/Closures) 的概念。閉包可以理解為一個函數包裹著另一個函數,使內部函數能夠訪問外部函數的作用域。
以下是我們要做的事:
- **為所有植物實現流暢的拖放功能**
- **追蹤座標,讓植物記住自己的位置**
- **建立完整的互動介面,使用純 JavaScript**
- **用閉包模式寫出乾淨、有組織的程式碼**
> JavaScript 閉包是一個廣泛且複雜的主題。本課程僅涉及最基本的概念。在這個玻璃花園的代碼中,你會發現一個閉包:一個內部函數和一個外部函數的結構設計,使內部函數能夠訪問外部函數的作用域。若想深入了解其運作方式,請參考 [詳細文檔](https://developer.mozilla.org/docs/Web/JavaScript/Closures)。
## 設定你的 JavaScript 檔案
我們將使用閉包來操作 DOM。
讓我們開始建立讓你的植物箱互動的 JavaScript 檔案
可以將 DOM 想像成一棵樹,代表了網頁文件可以被操作的所有方式。各種 API應用程式介面被設計出來讓開發者可以使用自己選擇的程式語言來訪問 DOM並進行編輯、修改、重組或其他管理操作。
**步驟 1建立你的程式檔**
![DOM 樹結構表示](../../../../translated_images/dom-tree.7daf0e763cbbba9273f9a66fe04c98276d7d23932309b195cb273a9cf1819b42.hk.png)
在你的植物箱資料夾中,建立一個新檔案命名為 `script.js`
> DOM 與其對應的 HTML 標記的表示圖。來源:[Olfa Nasraoui](https://www.researchgate.net/publication/221417012_Profile-Based_Focused_Crawler_for_Social_Media-Sharing_Websites)
**步驟 2將 JavaScript 連結到你的 HTML**
在本課程中,我們將完成互動式玻璃花園項目,通過編寫 JavaScript 代碼,讓用戶能夠操作頁面上的植物。
你的 `index.html``<head>` 區塊加入這個 script 標籤:
### 前置條件
```html
<script src="./script.js" defer></script>
```
你應該已經完成了玻璃花園的 HTML 和 CSS 部分。在本課程結束時,你將能夠通過拖拽將植物移入或移出玻璃花園。
**`defer` 屬性的重要性:**
- **確保** JavaScript 等待所有 HTML 載入完成後才執行
- **避免** JavaScript 在元素尚未準備好時尋找它們而造成錯誤
- **保證** 所有植物元素都可被互動使用
- **比把 script 放到頁面底部有更好的效能**
### 任務
> ⚠️ **重要提醒**`defer` 屬性可以避免常見的時序問題。沒有它JavaScript 可能嘗試存取還沒載入的 HTML 元素,導致錯誤。
在你的玻璃花園資料夾中,創建一個名為 `script.js` 的新文件,並在 `<head>` 區域中引入該文件:
---
```html
<script src="./script.js" defer></script>
```
## 將 JavaScript 與 HTML 元素連結
> 注意:在將外部 JavaScript 文件引入 HTML 文件時,使用 `defer` 屬性,這樣可以確保 JavaScript 在 HTML 文件完全加載後才執行。你也可以使用 `async` 屬性,允許腳本在 HTML 文件解析時執行,但在我們的情況下,確保 HTML 元素完全可用以進行拖拽操作更為重要。
---
在讓元素可拖放前JavaScript 需要找到它們,在 DOM 中定位它們。可以把這看成圖書館的目錄系統——只要有目錄號碼,你就能準確找到想要的書。
## DOM 元素
我們會使用 `document.getElementById()` 來建立這連結。這就像有一個精確的檔案系統——你給出 ID它就能找到你 HTML 中的正確元素。
首先,你需要在 DOM 中創建對要操作的元素的引用。在我們的例子中,這些元素是目前位於側邊欄的 14 個植物。
### 讓所有植物都能拖曳
### 任務
在你的 `script.js` 檔案加入以下程式碼:
```html
```javascript
// 啟用所有14種植物的拖曳功能
dragElement(document.getElementById('plant1'));
dragElement(document.getElementById('plant2'));
dragElement(document.getElementById('plant3'));
@ -72,159 +216,556 @@ dragElement(document.getElementById('plant13'));
dragElement(document.getElementById('plant14'));
```
這裡發生了什麼?你正在引用文件,並在其 DOM 中查找具有特定 Id 的元素。還記得我們在 HTML 課程中為每個植物圖片分配了唯一的 Id例如 `id="plant1"`)嗎?現在你可以利用這些 Id。識別每個元素後你將該元素傳遞給一個名為 `dragElement` 的函數稍後會構建該函數。因此HTML 中的元素現在已啟用拖拽功能,或者即將啟用。
**這段程式碼完成了什麼:**
- **定位** 每個植物元素於 DOM 中,根據其獨特的 ID
- **取得** JavaScript 對應的 HTML 元素參考
- **將** 每個元素傳遞給 `dragElement` 函數(接下來我們會寫)
- **準備** 每棵植物成為可拖放物件
- **橋接** 你的 HTML 結構與 JavaScript 功能
> 🎯 **為什麼用 ID 而不用 Class** ID 是專屬於特定元素的唯一識別碼,而 CSS 的類別class主要是用來對多個元素做樣式設定。當 JavaScript 需要操作單一元素時ID 提供了精確且效能佳的方式。
> 💡 **小貼士**:注意我們對每棵植物都呼叫了 `dragElement()` 一次。這確保每棵植物都有獨立的拖曳行為,這對流暢的使用體驗很重要。
### 🔄 **教學回顧檢查**
**DOM 連結理解**:在進入拖曳功能前,確認你理解:
- ✅ 如何用 `document.getElementById()` 定位 HTML 元素
- ✅ 為什麼我們為每棵植物使用獨特的 ID
- ✅ script 標籤中 `defer` 屬性的目的
- ✅ JavaScript 與 HTML 透過 DOM 如何互相連接
✅ 為什麼我們通過 Id 引用元素?為什麼不通過它們的 CSS 類名?你可以回顧之前的 CSS 課程來回答這個問題。
**自我測試**:如果兩個元素有相同 ID會發生什麼事為什麼 `getElementById()` 只回傳一個元素?
*答案ID 應該唯一;如果重複,只有第一個元素會被找到*
---
## 閉包
## 建立拖曳元素的閉包函數
現在你可以創建 `dragElement` 閉包了,這是一個外部函數,包裹著一個或多個內部函數(在我們的例子中是三個)。
現在我們來打造拖曳功能的核心:一個閉包,用來管理每棵植物的拖曳行為。這閉包包含多個內部函數,協同追蹤滑鼠移動並更新元素位置
當一個或多個函數需要訪問外部函數的作用域時,閉包非常有用。以下是一個例子:
閉包很適合這項任務,因為它能讓我們創造「私有」變數,在函數調用間持續存在,給每棵植物獨立的座標追蹤系統。
### 用簡單範例理解閉包
讓我用一個簡單範例說明閉包的概念:
```javascript
function displayCandy(){
let candy = ['jellybeans'];
function addCandy(candyType) {
candy.push(candyType)
}
addCandy('gumdrops');
function createCounter() {
let count = 0; // 這就像一個私有變量
function increment() {
count++; // 內部函數會記住外部變量
return count;
}
return increment; // 我們將內部函數返回
}
displayCandy();
console.log(candy)
```
在這個例子中,`displayCandy` 函數包裹了一個函數,該函數將新的糖果類型推入已存在於函數中的數組。如果你運行這段代碼,`candy` 數組將是未定義的,因為它是一個局部變數(僅限於閉包的作用域)。
✅ 如何讓 `candy` 數組可訪問?嘗試將其移到閉包之外。這樣,數組將變為全域變數,而不是僅限於閉包的局部作用域。
const myCounter = createCounter();
console.log(myCounter()); // 1
console.log(myCounter()); // 2
```
### 任務
**這個閉包模式做了什麼:**
- **建立** 一個私有的 `count` 變數,只存在於此閉包中
- **內部函數** 可以存取並修改外部變數(這就是閉包機制)
- **我們回傳內部函數時**,它會保留與那份私有資料的連結
- **即使 `createCounter()` 執行完後**`count` 依然存在並記得它的值
### 閉包為什麼特別適合拖曳功能
對我們的植物箱來說,每棵植物需要記住目前的位置座標。閉包提供了完美解決方案:
**專案的主要好處:**
- **獨立維持** 每棵植物私有的位置變數
- **保留** 拖曳事件間的座標數據
- **避免** 不同可拖曳元素間的變數衝突
- **創造** 乾淨且有組織的程式碼結構
> 🎯 **學習目標**:你現在不需要完全精通閉包。專注於看出它們如何幫我們整理程式碼、維持拖曳功能狀態。
```mermaid
stateDiagram-v2
[*] --> Ready: 頁面加載
Ready --> DragStart: 使用者按下 (pointerdown)
DragStart --> Dragging: 鼠標/手指移動 (pointermove)
Dragging --> Dragging: 繼續移動
Dragging --> DragEnd: 使用者放開 (pointerup)
DragEnd --> Ready: 重設以便下一次拖曳
state DragStart {
[*] --> CapturePosition
CapturePosition --> SetupListeners
SetupListeners --> [*]
}
state Dragging {
[*] --> CalculateMovement
CalculateMovement --> UpdatePosition
UpdatePosition --> [*]
}
state DragEnd {
[*] --> RemoveListeners
RemoveListeners --> CleanupState
CleanupState --> [*]
}
```
### 建立 dragElement 函數
`script.js` 中的元素聲明下,創建一個函數:
現在我們寫出控制拖曳邏輯的主要函數。把它加入你聲明植物元素的程式碼下面
```javascript
function dragElement(terrariumElement) {
//set 4 positions for positioning on the screen
let pos1 = 0,
pos2 = 0,
pos3 = 0,
pos4 = 0;
terrariumElement.onpointerdown = pointerDrag;
// 初始化位置追蹤變數
let pos1 = 0, // 之前的滑鼠 X 位置
pos2 = 0, // 之前的滑鼠 Y 位置
pos3 = 0, // 目前的滑鼠 X 位置
pos4 = 0; // 目前的滑鼠 Y 位置
// 設定初始拖曳事件監聽器
terrariumElement.onpointerdown = pointerDrag;
}
```
`dragElement` 從腳本頂部的聲明中獲取其 `terrariumElement` 對象。然後,你為傳遞到函數的對象設置了一些初始位置,這些位置的值為 `0`。這些是局部變數,將在閉包內為每個元素添加拖拽功能時進行操作。玻璃花園將通過這些被拖拽的元素填充,因此應用程序需要跟蹤它們的放置位置。
此外,傳遞給該函數的 `terrariumElement` 被分配了一個 `pointerdown` 事件,這是 [web API](https://developer.mozilla.org/docs/Web/API) 的一部分,旨在幫助管理 DOM。`onpointerdown` 事件在按下按鈕時觸發,或者在我們的例子中,當觸摸一個可拖拽的元素時觸發。該事件處理程序適用於 [網頁和移動瀏覽器](https://caniuse.com/?search=onpointerdown),但有少數例外。
✅ [事件處理程序 `onclick`](https://developer.mozilla.org/docs/Web/API/GlobalEventHandlers/onclick) 在跨瀏覽器支持方面更廣泛;為什麼這裡不使用它?想一想你希望創建的屏幕交互的具體類型。
**位置追蹤系統解說:**
- **`pos1``pos2`**:儲存舊滑鼠位置和新滑鼠位置之間的差距
- **`pos3``pos4`**:追蹤滑鼠目前的座標
- **`terrariumElement`**:我們正在改造成可拖曳的那個植物元素
- **`onpointerdown`**:使用者開始拖曳時觸發的事件
**閉包模式運作方式:**
- **為每個植物元素建立** 私有位置變數
- **在拖曳過程中維持** 這些變數
- **確保** 每棵植物獨立追蹤自己的座標
- **透過 `dragElement` 函數提供** 乾淨的介面
### 為什麼使用 pointer 事件?
你可能會想,為什麼我們用 `onpointerdown` 而不是較常見的 `onclick`。原因如下:
| 事件類型 | 適用情境 | 注意事項 |
|------------|----------|-------------|
| `onclick` | 簡單按鈕點擊 | 只能處理點擊與放開,無法拖曳 |
| `onpointerdown` | 滑鼠和觸控都適用 | 新穎且目前廣泛支援 |
| `onmousedown` | 只適用桌面滑鼠 | 忽略行動裝置使用者 |
**為什麼 pointer 事件很適合我們的需求:**
- **不論是滑鼠、手指或觸控筆** 都能正常運作
- **無論用筆電、平板或手機都一樣流暢**
- **能處理真正的拖曳動作,而非只是點擊放開**
- **帶來使用者期望的順暢體驗**
> 💡 **未來趨勢**Pointer 事件是最新的使用者互動標準。比起寫分開的滑鼠與觸控程式碼,這種方式同時涵蓋兩者。非常酷對吧?
### 🔄 **教學回顧檢查**
**事件處理理解**:暫停確認你掌握:
- ✅ 為何用 pointer 事件而非 mouse 事件?
- ✅ 閉包變數如何在函數調用間持續存在?
- ✅ `preventDefault()` 在流暢拖曳中扮演什麼角色?
- ✅ 為什麼事件監聽器是綁在文件document而不是各個元素上
**與現實連結**:想想你每天使用過的拖放介面:
- **檔案上傳**:將檔案拖到瀏覽器視窗
- **看板任務板**:任務在欄目間移動
- **圖片集**:重新排列照片順序
- **行動介面**:觸控螢幕上的滑動與拖曳
---
## Pointerdrag 函數
## pointerDrag 函數:捕捉拖曳的開始
`terrariumElement` 現在已準備好被拖拽;當觸發 `onpointerdown` 事件時,函數 `pointerDrag` 被調用。在這行代碼下方添加該函數:`terrariumElement.onpointerdown = pointerDrag;`
當使用者按下植物(不論是滑鼠點擊或手指觸控),`pointerDrag` 函數就會啟動。它會存下初始座標並準備拖曳系統。
### 任務
把這個函數加到你的 `dragElement` 閉包裡,緊接在 `terrariumElement.onpointerdown = pointerDrag;` 這行後面:
```javascript
function pointerDrag(e) {
e.preventDefault();
console.log(e);
pos3 = e.clientX;
pos4 = e.clientY;
// 阻止瀏覽器預設行為(例如文字選取)
e.preventDefault();
// 捕捉初始鼠標/觸摸位置
pos3 = e.clientX; // 拖曳開始的 X 座標
pos4 = e.clientY; // 拖曳開始的 Y 座標
// 設置拖曳過程的事件監聽器
document.onpointermove = elementDrag;
document.onpointerup = stopElementDrag;
}
```
這裡發生了幾件事。首先,你使用 `e.preventDefault();` 阻止了 `pointerdown` 事件的默認行為。這樣你可以更好地控制界面的行為。
**逐步解說:**
- **防止** 瀏覽器的預設行為,以免干擾拖曳
- **記錄** 使用者開始拖曳時的確切座標
- **建立** 對持續拖曳移動的事件監聽器
- **準備** 追蹤整個文件中的滑鼠或手指動作
> 當你完全構建腳本文件後,回到這行代碼並嘗試刪除 `e.preventDefault()` - 會發生什麼?
### 理解事件預防
其次,在瀏覽器窗口中打開 `index.html`,並檢查界面。當你點擊一個植物時,你可以看到如何捕獲 'e' 事件。深入查看該事件,看看一次 `pointerdown` 事件收集了多少信息!
`e.preventDefault()` 這行對順暢拖曳至關重要:
接下來,注意如何將局部變數 `pos3``pos4` 設置為 `e.clientX`。你可以在檢查面板中找到 `e` 的值。這些值捕獲了你點擊或觸摸植物時的 x 和 y 坐標。由於你需要對植物的拖拽行為進行精細控制,因此需要跟蹤它們的坐標。
**若不防止,瀏覽器可能會:**
- **選取** 用戶拖曳時劃過的文字
- **觸發** 右鍵拖曳時的選單
- **干擾** 我們自訂的拖曳功能
- **造成** 拖動過程中的視覺異常
✅ 現在是否更清楚為什麼整個應用程序是用一個大的閉包構建的?如果不是閉包,你將如何為 14 個可拖拽的植物維持作用域?
> 🔍 **實驗**:完成本課後,試著移除 `e.preventDefault()`,觀察拖曳體驗如何改變。你會很快知道這行為什麼必要!
完成初始函數,通過在 `pos4 = e.clientY` 下添加另外兩個指針事件操作:
### 座標追蹤系統
```html
`e.clientX``e.clientY` 屬性給出滑鼠/觸控點的精確座標:
| 屬性 | 量測項目 | 使用案例 |
|----------|------------------|----------|
| `clientX` | 相對於視窗的水平位置 | 追蹤左右移動 |
| `clientY` | 相對於視窗的垂直位置 | 追蹤上下移動 |
**了解這些座標:**
- **提供** 像素級精確的定位資訊
- **隨使用者移動指標** 實時更新
- **跨不同螢幕尺寸和縮放等級** 保持一致
- **啟用** 平滑、響應式的拖放互動
### 設置文件級事件監聽器
注意我們如何將移動和停止事件附加在整個 `document` 上,而不僅是植物元素:
```javascript
document.onpointermove = elementDrag;
document.onpointerup = stopElementDrag;
```
現在你正在指示植物隨著指針移動而被拖拽,並在取消選擇植物時停止拖拽。`onpointermove` 和 `onpointerup` 都是與 `onpointerdown` 相同 API 的一部分。由於你尚未定義 `elementDrag``stopElementDrag` 函數,界面現在會拋出錯誤,因此接下來構建這些函數。
## elementDrag 和 stopElementDrag 函數
**為何附加到 document**
- **即使滑鼠離開植物元素也能繼續追蹤**
- **防止使用者快速移動時拖動中斷**
- **在整個螢幕上提供平滑拖動體驗**
- **處理游標移出瀏覽器視窗的邊界情況**
你將通過添加另外兩個內部函數來完成閉包,這些函數將處理拖拽植物和停止拖拽時的行為。你希望的行為是,隨時可以拖拽任何植物,並將其放置在屏幕上的任何位置。這個界面非常靈活(例如,沒有特定的放置區域),讓你可以自由設計自己的玻璃花園,添加、移除和重新定位植物。
> ⚡ **效能提示**:拖動停止時,我們會清理這些文件級監聽器,避免記憶體洩漏和效能問題
### 任務
## 完善拖曳系統:移動與清理
`pointerDrag` 的閉合大括號後添加 `elementDrag` 函數:
現在我們將新增兩個處理實際拖曳移動和拖曳停止時清理的函式。這些函式協同工作,為你的植物造景提供平滑、響應式的移動功能。
### elementDrag 函式:追蹤移動
`pointerDrag` 的結束花括號後面添加 `elementDrag` 函式:
```javascript
function elementDrag(e) {
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
console.log(pos1, pos2, pos3, pos4);
terrariumElement.style.top = terrariumElement.offsetTop - pos2 + 'px';
terrariumElement.style.left = terrariumElement.offsetLeft - pos1 + 'px';
// 計算自上次事件以來移動的距離
pos1 = pos3 - e.clientX; // 水平移動距離
pos2 = pos4 - e.clientY; // 垂直移動距離
// 更新當前位置追蹤
pos3 = e.clientX; // 新的當前 X 位置
pos4 = e.clientY; // 新的當前 Y 位置
// 將移動應用至元素的位置
terrariumElement.style.top = (terrariumElement.offsetTop - pos2) + 'px';
terrariumElement.style.left = (terrariumElement.offsetLeft - pos1) + 'px';
}
```
在這個函數中,你對之前在外部函數中設置的初始位置 1-4 進行了大量操作。這裡發生了什麼?
當你拖拽時,你通過將 `pos3`(之前設置為 `e.clientX`)減去當前的 `e.clientX` 值來重新分配 `pos1`。對 `pos2` 進行了類似的操作。然後,你將 `pos3``pos4` 重置為元素的新 X 和 Y 坐標。你可以在拖拽時在控制台中觀察這些變化。接著,你操作植物的 CSS 樣式,根據這些新位置計算植物的頂部和左側 X 和 Y 坐標,並設置其新位置。
> `offsetTop``offsetLeft` 是 CSS 屬性,用於根據父元素設置元素的位置;其父元素可以是任何非 `static` 定位的元素。
所有這些位置的重新計算使你能夠微調玻璃花園及其植物的行為。
### 任務
**理解座標數學運算:**
- **`pos1``pos2`**:計算滑鼠自上次更新以來移動的距離
- **`pos3``pos4`**:儲存當前滑鼠位置供下次計算使用
- **`offsetTop``offsetLeft`**:獲取元素當前在頁面上的位置
- **減法邏輯**:將元素移動與滑鼠移動距離同步
```mermaid
sequenceDiagram
participant User
participant Mouse
participant JavaScript
participant Plant
User->>Mouse: 在 (100, 50) 開始拖曳
Mouse->>JavaScript: pointerdown 事件
JavaScript->>JavaScript: 儲存初始位置 (pos3=100, pos4=50)
JavaScript->>JavaScript: 設置移動/鬆開偵聽器
User->>Mouse: 移動至 (110, 60)
Mouse->>JavaScript: pointermove 事件
JavaScript->>JavaScript: 計算: pos1=10, pos2=10
JavaScript->>Plant: 更新: left += 10px, top += 10px
Plant->>Plant: 在新位置渲染
User->>Mouse: 在 (120, 65) 放開
Mouse->>JavaScript: pointerup 事件
JavaScript->>JavaScript: 移除偵聽器
JavaScript->>JavaScript: 重設以供下次拖曳
```
**移動計算解析:**
1. **測量** 新舊滑鼠位置差異
2. **計算** 根據滑鼠移動決定元素移動距離
3. **即時更新** 元素的 CSS 位置屬性
4. **儲存** 新位置作為下一次計算基準
### 數學的視覺化表示
```mermaid
sequenceDiagram
participant Mouse
participant JavaScript
participant Plant
Mouse->>JavaScript: 從 (100,50) 移動到 (110,60)
JavaScript->>JavaScript: 計算:向右移動 10px向下移動 10px
JavaScript->>Plant: 位置更新:向右 +10px向下 +10px
Plant->>Plant: 在新位置渲染
```
### stopElementDrag 函式:清理工作
完成界面的最後一步是在 `elementDrag` 的閉合大括號後添加 `stopElementDrag` 函數:
`elementDrag` 的結束花括號後添加此清理函式
```javascript
function stopElementDrag() {
document.onpointerup = null;
document.onpointermove = null;
// 移除文件層級的事件監聽器
document.onpointerup = null;
document.onpointermove = null;
}
```
這個小函數重置了 `onpointerup``onpointermove` 事件,這樣你可以重新開始拖拽植物,或者開始拖拽新的植物。
**清理為何重要:**
- **防止因殘留事件監聽器導致的記憶體洩漏**
- **使用者釋放植物後停止拖曳行為**
- **允許其他元素獨立拖動**
- **重設系統以便下一次拖曳操作**
**未清理會發生什麼:**
- 事件監聽器在拖曳停止後仍持續執行
- 累積未使用的監聽器導致效能下降
- 與其他元素互動時產生意外行為
- 瀏覽器資源浪費於不必要的事件處理
### 理解 CSS 位置屬性
我們的拖動系統操作兩個關鍵 CSS 屬性:
| 屬性 | 控制內容 | 我們如何使用 |
|------|----------|--------------|
| `top` | 距離頂部邊緣的距離 | 拖動過程中的垂直定位 |
| `left` | 距離左側邊緣的距離 | 拖動過程中的水平定位 |
**關於 offset 屬性的重要觀察:**
- **`offsetTop`**:距離已定位祖先元素頂部的當前距離
- **`offsetLeft`**:距離已定位祖先元素左側的當前距離
- **定位上下文**:這些值相對於最近的已定位祖先元素
- **即時更新**:當修改 CSS 屬性時立即變動
> 🎯 **設計理念**:此拖曳系統刻意保持彈性—沒有「放置區域」或其他限制。用戶可自由將植物放置於任意位置,完全掌控自己的造景設計。
## 綜合整合:完整拖曳系統
✅ 如果你不將這些事件設置為 null會發生什麼
恭喜!你剛完成了一個用純 JavaScript 製作的高級拖放系統。你的完整 `dragElement` 函式內含一個功能強大的閉包,管理著:
現在你已經完成了你的項目!
**你的閉包達成的功能:**
- **獨立維護** 每個植物的私有位置變數
- **處理** 從開始拖曳到結束的整個生命週期
- **提供** 跨整個螢幕的平滑、響應式移動
- **妥善清理** 資源以預防記憶體洩漏
- **創造** 直覺且具創造力的造景介面
🥇恭喜!你已經完成了你的美麗玻璃花園!![完成的玻璃花園](../../../../translated_images/terrarium-final.0920f16e87c13a84cd2b553a5af9a3ad1cffbd41fbf8ce715d9e9c43809a5e2c.hk.png)
### 測試你的互動式植物瓶景觀
現在測試你的互動式植物瓶景觀!在瀏覽器中打開你的 `index.html` 並嘗試操作:
1. **點擊並按住** 任一植物開始拖曳
2. **移動滑鼠或手指** 看著植物平滑跟隨
3. **放開** 以將植物放置在新位置
4. **嘗試** 不同的排列方式探索介面
🥇 **成就感**:你已創建一個完全互動的網頁應用,掌握專業開發者每天使用的核心概念。這拖放功能底層原理同時支撐著檔案上傳、看板列表及各種互動介面。
### 🔄 **教學檢視點**
**完整系統理解**:檢驗你對拖曳系統的掌握:
- ✅ 閉包如何維護每個植物的獨立狀態?
- ✅ 為什麼座標計算的數學對平滑移動必要?
- ✅ 忘記清理事件監聽器會導致什麼?
- ✅ 這種模式如何擴展到更複雜的互動?
**程式碼品質反思**:回顧完整方案:
- **模組化設計**:每個植物獲得自己的閉包實例
- **事件效能**:妥善設定與清理監聽器
- **跨裝置支援**:適用桌面與行動裝置
- **效能意識**:無記憶體洩漏或多餘計算
![finished terrarium](../../../../translated_images/terrarium-final.0920f16e87c13a84.hk.png)
---
## 🚀挑戰
## GitHub Copilot Agent 挑戰 🚀
使用 Agent 模式完成以下挑戰:
為你的閉包添加新的事件處理程序,讓植物能夠執行更多操作;例如,雙擊植物將其置於最前方。發揮創意吧!
**描述:** 擴充植物瓶專案,新增重設功能,讓所有植物能夠平滑動畫回復原始位置。
**提示:** 創建一個重設按鈕,點擊後會使用 CSS 過渡動畫將所有植物平滑回復原側欄的原始位置。函式應在頁面載入時儲存原始位置,按下重設按鈕時以 1 秒動畫平滑回復。
了解更多關於 [agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) 的資訊。
## 🚀 額外挑戰:擴展你的技能
準備好將植物瓶昇華到下一層嗎?試試以下擴充功能:
**創意延伸:**
- **雙擊** 植物將其置頂z-index 操作)
- **加入視覺反饋**,如滑鼠懸停時植物微光效果
- **實作邊界限制**,避免將植物拖出植物瓶範圍
- **創建儲存功能**,利用 localStorage 記錄植物位置
- **加入音效**,在拾取與放置植物時播放聲音
> 💡 **學習機會**:這些挑戰將教你更多 DOM 操控、事件處理及用戶體驗設計技巧。
## 課後測驗
[課後測驗](https://ff-quizzes.netlify.app/web/quiz/20)
## 複習與自學
## 複習與自學:深化理解
你已掌握 DOM 操控和閉包的基本,還有更多可學!以下是擴展知識和技能的路徑。
### 替代的拖放實作方法
我們使用了指標事件以獲得最大彈性,但網頁開發還有其他方法:
| 方法 | 適合用例 | 學習價值 |
|------|----------|----------|
| [HTML 拖放 API](https://developer.mozilla.org/docs/Web/API/HTML_Drag_and_Drop_API) | 檔案上傳、正式拖放區域 | 了解瀏覽器原生能力 |
| [Touch 事件](https://developer.mozilla.org/docs/Web/API/Touch_events) | 行動裝置特定互動 | 行動優先開發模式 |
| CSS `transform` 屬性 | 平滑動畫 | 效能最佳化技巧 |
### 進階 DOM 操作主題
**下一步學習重點:**
- **事件委託**:高效處理多個元素事件
- **交叉觀察器 (Intersection Observer)**:偵測元素進出視窗
- **變動觀察器 (Mutation Observer)**:監控 DOM 結構變化
- **網頁元件 (Web Components)**:創建可重用封裝 UI 元素
- **虛擬 DOM 概念**:了解框架如何優化 DOM 更新
### 持續學習的必要資源
**技術文件:**
- [MDN 指標事件指南](https://developer.mozilla.org/docs/Web/API/Pointer_events) - 完整指標事件參考
- [W3C 指標事件規範](https://www.w3.org/TR/pointerevents1/) - 官方標準文件
- [JavaScript 閉包深度解析](https://developer.mozilla.org/docs/Web/JavaScript/Closures) - 進階閉包範例
**瀏覽器相容性:**
- [CanIUse.com](https://caniuse.com/) - 檢查跨瀏覽器功能支援
- [MDN 瀏覽器兼容性資料](https://github.com/mdn/browser-compat-data) - 詳細支援資料
**練習機會:**
- **構建** 拼圖遊戲,使用類似拖曳機制
- **製作** 看板列表,拖放任務管理
- **設計** 可拖動的圖片集
- **嘗試** 移動裝置手勢互動開發
> 🎯 **學習策略**:最好的鞏固方式就是實作。試著建立不同拖曳介面,每個專案都能教你更多關於用戶互動和 DOM 操控。
### ⚡ **接下來 5 分鐘的事**
- [ ] 開啟瀏覽器開發者工具,在控制台輸入 `document.querySelector('body')`
- [ ] 嘗試使用 `innerHTML``textContent` 變更網頁文字
- [ ] 為網頁中任一按鈕或連結添加點擊事件監聽
- [ ] 使用元素面板檢查 DOM 樹結構
### 🎯 **本小時可達成目標**
- [ ] 完成課後測驗並回顧 DOM 操作概念
- [ ] 創建一個回應使用者點擊的互動網頁
- [ ] 練習各類事件處理click、mouseover、keypress
- [ ] 製作簡易待辦清單或計數器,使用 DOM 操控
- [ ] 探索 HTML 元素與 JavaScript 物件的關係
### 📅 **一週的 JavaScript 旅程**
- [ ] 完成帶拖放功能的互動植物瓶專案
- [ ] 掌握事件委託以提升事件處理效率
- [ ] 學習事件循環與非同步 JavaScript
- [ ] 透過建立模組練習閉包私有狀態
- [ ] 探索現代 DOM API如交叉觀察器
- [ ] 製作互動元件,無需框架
### 🌟 **一個月的 JavaScript 精通**
- [ ] 建立複雜單頁應用,使用純 JavaScript
- [ ] 學習並比較現代框架React、Vue、Angular與純 DOM 操控
- [ ] 貢獻開源 JavaScript 專案
- [ ] 精通進階主題,如網頁元件和自定義元素
- [ ] 打造高效能網頁應用,優化 DOM 操作
- [ ] 教授他人 DOM 操控與 JavaScript 基礎
## 🎯 你的 JavaScript DOM 掌握進度
```mermaid
timeline
title DOM 與 JavaScript 學習進度
section 基礎 (15 分鐘)
DOM 理解: 元素選取方法
: 樹狀結構導航
: 屬性存取模式
section 事件處理 (20 分鐘)
使用者互動: 指標事件基礎
: 事件監聽器設定
: 跨裝置相容性
: 事件阻止技術
section 閉包 (25 分鐘)
範圍管理: 私有變數建立
: 函式持續存在
: 狀態管理模式
: 記憶體效率
section 拖曳系統 (30 分鐘)
互動功能: 座標追蹤
: 位置計算
: 移動數學
: 清理程序
section 進階模式 (45 分鐘)
專業技能: 事件代理
: 性能優化
: 錯誤處理
: 無障礙考量
section 框架理解 (1 週)
現代開發: 虛擬 DOM 概念
: 狀態管理函式庫
: 元件架構
: 建置工具整合
section 專家級 (1 個月)
進階 DOM API: 交集觀察器
: 變異觀察器
: 自訂元素
: 網頁元件
```
### 🛠️ 你的 JavaScript 工具包總結
雖然在屏幕上拖拽元素看起來很簡單,但有許多實現方式和潛在的陷阱,具體取決於你想要的效果。事實上,有一個完整的 [拖拽 API](https://developer.mozilla.org/docs/Web/API/HTML_Drag_and_Drop_API) 可以嘗試。我們在本模組中沒有使用該 API因為我們想要的效果有些不同但你可以在自己的項目中嘗試這個 API看看能實現什麼。
完成本課後,你已具備:
- **DOM 精通**:元素選取、屬性操作、樹狀結構導覽
- **事件專業**:跨裝置指標事件互動處理
- **閉包理解**:私有狀態管理與函式持續性
- **互動系統**:從零打造完整拖放實作
- **效能意識**:妥善清理事件避免記憶體洩漏
- **現代模式**:專業開發中的程式碼組織技巧
- **用戶體驗**:打造直覺、響應式界面
在 [W3C 文檔](https://www.w3.org/TR/pointerevents1/) 和 [MDN 網頁文檔](https://developer.mozilla.org/docs/Web/API/Pointer_events) 上找到更多關於指針事件的信息。
**你已習得的專業技能:**
- **Trello/看板列表**:欄位間的卡片拖曳
- **檔案上傳系統**:拖放檔案處理
- **圖片庫**:照片重新排列介面
- **行動應用**:基於觸控的互動模式
隨時使用 [CanIUse.com](https://caniuse.com/) 檢查瀏覽器的兼容性。
**下一階段**:你已準備好探索 React、Vue 或 Angular 等現代框架,這些框架基於你目前 DOM 操控的基本概念建立!
## 作業
[進一步練習 DOM](assignment.md)
[多練習操作 DOM](assignment.md)
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,請注意自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。
本文件乃使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們力求準確,但請注意自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應視為權威資料來源。對於重要資訊,建議採用專業人工翻譯。我們不對因使用本翻譯而產生的任何誤解或誤釋負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,23 +1,139 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "22fb6c3cb570c47f1ac65048393941fa",
"translation_date": "2025-08-23T22:31:51+00:00",
"original_hash": "947ca5ce7c94aee9c7de7034e762bc17",
"translation_date": "2026-01-06T13:04:13+00:00",
"source_file": "3-terrarium/3-intro-to-DOM-and-closures/assignment.md",
"language_code": "hk"
}
-->
# 多了解一些 DOM
# DOM 元素調查作業
## 指引
## 概覽
深入研究 DOM嘗試「採用」一個 DOM 元素。造訪 MDN 的 [DOM 接口列表](https://developer.mozilla.org/docs/Web/API/Document_Object_Model),選擇其中一個。然後在網頁中找到它的使用範例,並撰寫一段解釋說明它是如何被使用的。
現在你已經親身體驗了 DOM 操作的威力,是時候探索更廣泛的 DOM 介面世界。此作業將加深你對不同網頁技術如何與 DOM 互動的理解,而不僅僅是拖曳元素。
## 學習目標
完成此作業後,你將能夠:
- **研究** 並深入了解特定的 DOM 介面
- **分析** 實際應用中的 DOM 操作實現
- **連結** 理論概念與實務應用
- **培養** 技術文件撰寫與分析能力
## 指示
### 第一步:選擇你的 DOM 介面
造訪 MDN 的完整 [DOM 介面列表](https://developer.mozilla.org/docs/Web/API/Document_Object_Model),並選擇一個你感興趣的介面。建議從以下類別中挑選,以豐富學習內容:
**初學者友善選項:**
- `Element.classList` - 動態管理 CSS 類別
- `Document.querySelector()` - 進階元素選取
- `Element.addEventListener()` - 指標事件以外的事件處理
- `Window.localStorage` - 用戶端資料儲存
**中級挑戰:**
- `Intersection Observer API` - 偵測元素可見性
- `MutationObserver` - 監視 DOM 變化
- `Drag and Drop API` - 指標拖曳替代方案
- `Geolocation API` - 取得用戶位置
**進階探索:**
- `Web Components` - 自訂元素與 Shadow DOM
- `Canvas API` - 程式化繪圖
- `Web Workers` - 背景處理
- `Service Workers` - 離線功能
### 第二步:研究並撰寫文件
撰寫一份 300-500 字的完整分析內容,包含:
#### 技術概述
- **定義** 你所選介面是什麼,並以清楚且初學者友善的語言說明
- **說明** 其主要方法、屬性或事件
- **描述** 該介面設計用來解決的問題類型
#### 實際應用
- **尋找** 一個使用該介面的網站(檢查原始碼或搜尋範例)
- **記錄** 具體的實現方式,若可能附上程式碼片段
- **分析** 為什麼開發者會採用這種方案
- **解釋** 它如何提升使用者體驗
#### 實務應用
- **比較** 你選的介面與我們在 terrarium 專案中使用的方法
- **建議** 這個介面如何增強或擴展 terrarium 功能
- **指出** 其他適合使用此介面的項目
### 第三步:程式碼範例
包含一個簡單且可執行的程式碼範例,展現你的介面實際運作。此程式碼應該:
- **具功能性** — 測試後可正確執行
- **有註解** — 解釋各部分功能
- **相關性** — 與實際使用案例相關
- **適合初學者** — 容易被學習網頁開發的人理解
## 繳交格式
請使用清楚的標題結構來安排你的提交內容:
```markdown
# [Interface Name] DOM Investigation
## What It Does
[Technical overview]
## Real-World Example
[Website analysis and implementation details]
## Code Demonstration
[Your working example with comments]
## Reflection
[How this connects to our terrarium project and future applications]
```
## 評分標準
| 評分項目 | 優秀 | 合格 | 需要改進 |
| -------- | ------------------------------------------- | ---------------------------------------------- | ---------------------- |
| | 提供段落式的說明,並附上範例 | 提供段落式的說明,但未附上範例 | 未提供任何說明 |
| 評分準則 | 優異 (A) | 精熟 (B) | 發展中 (C) | 需改進 (D) |
|----------|-----------|----------|------------|------------|
| **技術理解** | 展現深刻理解,說明精準且用詞恰當 | 展示紮實理解,多數說明準確 | 基本理解,有部分誤解 | 理解不足,錯誤明顯 |
| **實際分析** | 定位且徹底分析真實案例並具體說明 | 找到實例且分析一定程度充分 | 找到案例但分析較淺 | 實體連結不清或錯誤 |
| **程式碼範例** | 工作正常且註解清晰,明確演示介面 | 功能正常且有適當註解 | 程式有效,但說明不足 | 程式有錯誤或解釋差 |
| **文筆品質** | 文字清晰且吸引人,結構完善且具技術溝通能力 | 結構良好且技術寫作佳 | 結構與清晰度尚可 | 結構鬆散或表達不清 |
| **批判性思考** | 深刻連結概念,提出創新應用建議 | 有分析思考並相關連結 | 有部分分析但不夠深入 | 缺乏批判性思考跡象 |
## 成功秘訣
**研究策略:**
- **從** MDN 文件開始,取得權威資訊
- **尋找** GitHub 或 CodePen 的程式碼範例
- **利用** 瀏覽器開發工具檢查熱門網站
- **觀看** 教學影片以獲得視覺說明
**寫作指導:**
- **用** 自己的話說明,不要照抄文件
- **包含** 具體範例及程式碼片段
- **用** 教學的語氣解釋技術概念
- **將** 介面與更廣泛的網頁開發概念連結
**程式碼範例點子:**
- **製作** 展示介面主要功能的簡單示範
- **延伸** 我們 terrarium 專案相關的概念
- **重點** 放在功能性而非視覺設計
- **測試** 程式碼,確保可正常執行
## 繳交截止日
[在此插入截止日期]
## 有問題嗎?
如需釐清任何相關問題,歡迎隨時詢問!本次調查將深化你對 DOM 如何支援日常網頁互動體驗的理解。
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為具權威性的來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。
本文件由 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 協助翻譯。雖然我們致力於確保準確性,但請注意自動翻譯可能包含錯誤或不準確之處。原始語言版本的文件應被視為權威來源。對於關鍵資訊,建議採用專業人工翻譯。我們不對因使用本翻譯而產生的任何誤解或誤釋負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,43 +1,30 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "7965cd2bc5dc92ad888dc4c6ab2ab70a",
"translation_date": "2025-08-23T22:23:25+00:00",
"original_hash": "bc5c5550f79d10add90ce419ee34abb3",
"translation_date": "2026-01-06T12:09:40+00:00",
"source_file": "3-terrarium/README.md",
"language_code": "hk"
}
-->
# 我的微型植物園:一個學習 HTML、CSS 和使用 JavaScript 操控 DOM 的項目 🌵🌱
## 部署你的 Terrarium
一個小型的拖放式程式冥想。只需少量的 HTML、JS 和 CSS你就能建立一個網頁介面為其設計樣式甚至添加多種互動功能
你可以使用 **Azure Static Web Apps** 在網絡上部署或發佈你的 Terrarium
![我的微型植物園](../../../3-terrarium/images/screenshot_gray.png)
1. 分叉此倉庫
# 課程
2. 按下此按鈕 👇
1. [HTML 入門](./1-intro-to-html/README.md)
2. [CSS 入門](./2-intro-to-css/README.md)
3. [DOM 和 JS 閉包入門](./3-intro-to-DOM-and-closures/README.md)
[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.StaticApp)
## 致謝
3. 按照設置嚮導創建你的應用。
- 將 **App root** 設定為 `/solution` 或你的代碼庫根目錄。
- 這個應用沒有 API因此你可以跳過 API 配置。
- 系統會自動創建一個 `.github` 文件夾,以幫助 Azure Static Web Apps 構建和發佈你的應用。
由 [Jen Looper](https://www.twitter.com/jenlooper) 用 ♥️ 編寫。
透過 CSS 創建的微型植物園靈感來自 Jakub Mandra 的玻璃罐 [codepen](https://codepen.io/Rotarepmi/pen/rjpNZY)。
藝術作品由 [Jen Looper](http://jenlooper.com) 手繪,並使用 Procreate 協助完成。
## 部署你的微型植物園
你可以使用 Azure 靜態網頁應用程式將你的微型植物園部署或發布到網絡上。
1. Fork 此倉庫
2. 按下這個按鈕
[![部署到 Azure 按鈕](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/?feature.customportal=false&WT.mc_id=academic-77807-sagibbon#create/Microsoft.StaticApp)
3. 按照向導步驟創建你的應用程式。請確保將應用程式根目錄設置為 `/solution` 或你的代碼庫根目錄。此應用程式中沒有 API因此不需要添加相關內容。一個 GitHub 文件夾將會在你 Fork 的倉庫中創建,幫助 Azure 靜態網頁應用程式的構建服務,構建並發布你的應用程式到一個新的 URL。
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋概不負責。
本文件乃使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於確保準確性,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應視為權威來源。對於重要資訊,建議尋求專業人工翻譯。我們不對因使用本翻譯而產生的任何誤解或誤釋負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,42 +1,290 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "957547b822c40042e07d591c4fbfde4f",
"translation_date": "2025-08-24T00:17:09+00:00",
"original_hash": "efa2ab875b8bb5a7883816506da6b6d2",
"translation_date": "2026-01-06T12:08:49+00:00",
"source_file": "4-typing-game/README.md",
"language_code": "hk"
}
-->
# 事件驅動程 - 建立一個打字遊戲
# 事件驅動程式設計 - 建立一個打字遊戲
## 簡介
```mermaid
journey
title 你的打字遊戲開發旅程
section 基礎
計劃遊戲結構: 3: Student
設計用戶界面: 4: Student
設置 HTML 元素: 4: Student
section 功能
處理用戶輸入: 4: Student
跟蹤時間: 5: Student
計算準確度: 5: Student
section 功能特色
增加視覺反饋: 5: Student
實現遊戲邏輯: 5: Student
美化體驗: 5: Student
```
## 介紹
打字是開發者最被低估的技能之一。能夠快速將腦海中的想法轉移到編輯器中,能讓創意自由流動。學習的最佳方式之一就是玩遊戲!
有一件每個開發者都知道但很少談論的事:打字快是超能力!🚀 想想看——你越快把腦中的想法輸入到程式編輯器,創造力就能流動得越順暢。就像你的思緒和螢幕之間有條直接的管道。
> 那麼,讓我們來建立一個打字遊戲吧!
```mermaid
pie title 遊戲功能
"即時反饋" : 25
"效能追蹤" : 20
"互動介面" : 20
"計時系統" : 15
"引言管理" : 10
"結果顯示" : 10
```
想知道提升這項技能的最佳方法之一嗎?你猜對了——我們要做一款遊戲!
你將使用迄今為止學到的 JavaScript、HTML 和 CSS 技能來創建一個打字遊戲。遊戲會向玩家展示一段隨機的名言(我們使用的是 [福爾摩斯](https://en.wikipedia.org/wiki/Sherlock_Holmes) 的名言),並計算玩家準確輸入這段文字所需的時間。你將使用迄今為止學到的 JavaScript、HTML 和 CSS 技能來創建一個打字遊戲。
```mermaid
flowchart LR
A[玩家開始遊戲] --> B[隨機顯示語錄]
B --> C[玩家輸入字元]
C --> D{字元正確嗎?}
D -->|是| E[綠色高亮]
D -->|否| F[紅色高亮]
E --> G[更新準確率]
F --> G
G --> H{語錄完成了嗎?}
H -->|否| C
H -->|是| I[計算每分鐘字數]
I --> J[顯示結果]
J --> K[再玩一次?]
K -->|是| B
K -->|否| L[遊戲結束]
style A fill:#e1f5fe
style D fill:#fff3e0
style E fill:#e8f5e8
style F fill:#ffebee
style I fill:#f3e5f5
```
> 讓我們一起創造一款超棒的打字遊戲吧!
準備好運用你學習的 JavaScript、HTML 和 CSS 技能了嗎?我們要建立一個打字遊戲,遊戲內容會挑戰你打出來自傳奇偵探 [Sherlock Holmes](https://en.wikipedia.org/wiki/Sherlock_Holmes) 的隨機名言。遊戲會追蹤你打字的速度和準確性——相信我,這比你想像中還要令人上癮!
```mermaid
mindmap
root((打字遊戲開發))
User Interface
Input Elements
Visual Feedback
Responsive Design
Accessibility
Game Logic
Quote Selection
Timer Management
Accuracy Tracking
Score Calculation
Event Handling
Keyboard Input
Button Clicks
Real-time Updates
Game State Changes
Performance Metrics
Words Per Minute
Character Accuracy
Error Tracking
Progress Display
User Experience
Immediate Feedback
Clear Instructions
Engaging Content
Achievement System
```
![demo](../../../4-typing-game/images/demo.gif)
## 先決條件
## 你需要知道的
本課假設你已熟悉以下概念:
```mermaid
flowchart TD
A[用戶動作] --> B{事件類型?}
B -->|鍵盤按鍵| C[鍵盤事件]
B -->|按鈕點擊| D[滑鼠事件]
B -->|計時器| E[時間事件]
C --> F[檢查字元]
D --> G[開始/重設遊戲]
E --> H[更新計時器]
F --> I{正確?}
I -->|是| J[標示綠色]
I -->|否| K[標示紅色]
J --> L[更新分數]
K --> L
L --> M[檢查遊戲狀態]
G --> N[生成新名言]
H --> O[顯示時間]
M --> P{遊戲完成?}
P -->|是| Q[顯示結果]
P -->|否| R[繼續遊戲]
style A fill:#e1f5fe
style F fill:#e8f5e8
style I fill:#fff3e0
style Q fill:#f3e5f5
```
在深入之前,請確保你熟悉以下概念(如果需要快速複習也沒關係——我們都經歷過!):
- 創建文字輸入框和按鈕控件
- CSS 和使用類別設置樣式
- 建文字輸入和按鈕控件
- CSS 與使用類別設定樣式
- JavaScript 基礎
- 創建數組
- 生成隨機數
- 獲取當前時間
- 建立陣列
- 產生隨機數字
- 取得目前時間
如果有任何部分感覺有點生疏,也完全沒問題!有時候鞏固知識的最好方式就是開始實作,邊做邊學。
### 🔄 **教學確認**
**基礎評估**:開始開發前,確保你了解:
- ✅ HTML 表單與輸入元素的工作原理
- ✅ CSS 類別與動態樣式設定
- ✅ JavaScript 事件監聽器與處理程序
- ✅ 陣列操作與隨機選擇
- ✅ 時間測量與計算
**快速自我測驗**:你能解釋這些概念如何在互動遊戲中協同運作嗎?
- **事件** 在使用者與元素互動時觸發
- **處理程序** 負責處理事件並更新遊戲狀態
- **CSS** 提供使用者行為的視覺反饋
- **計時** 使得效能測量和遊戲進展成為可能
```mermaid
quadrantChart
title 打字遊戲技能發展
x-axis 初學者 --> 專家
y-axis 靜態 --> 互動式
quadrant-1 進階遊戲
quadrant-2 即時應用程式
quadrant-3 基本頁面
quadrant-4 互動網站
HTML Forms: [0.3, 0.2]
CSS Styling: [0.4, 0.3]
Event Handling: [0.7, 0.8]
Game Logic: [0.8, 0.9]
Performance Tracking: [0.9, 0.7]
```
## 開工吧!
[利用事件驅動程式設計創建打字遊戲](./typing-game/README.md)
### ⚡ **接下來 5 分鐘你可以做什麼**
- [ ] 開啟瀏覽器主控台,試試用 `addEventListener` 監聽鍵盤事件
- [ ] 建立一個簡單 HTML 頁面,有輸入欄並測試打字偵測
- [ ] 練習字串操作,將輸入文字與目標文字比對
- [ ] 嘗試使用 `setTimeout` 理解定時功能
## 課程
### 🎯 **這一小時你可以完成什麼**
- [ ] 完成課後小測,理解事件驅動程式設計
- [ ] 建立打字遊戲的基本版本並驗證字詞
- [ ] 新增正確與錯誤打字的視覺反饋
- [ ] 實作基於速度與準確性的簡單計分系統
- [ ] 用 CSS 美化遊戲介面,使之更吸引人
[使用事件驅動編程創建打字遊戲](./typing-game/README.md)
### 📅 **這一週你的遊戲開發路線**
- [ ] 完成具備所有功能與細節的完整打字遊戲
- [ ] 新增難度等級,包含不同複雜度的字詞
- [ ] 實作用戶統計追蹤WPM、準確率變化
- [ ] 添加音效與動畫,提升使用體驗
- [ ] 讓遊戲響應式適用於觸控裝置
- [ ] 將遊戲分享線上並收集用戶回饋
### 🌟 **這一個月你的互動開發計畫**
- [ ] 開發多款遊戲,探索不同互動模式
- [ ] 學習遊戲循環、狀態管理與效能優化
- [ ] 貢獻開源遊戲開發專案
- [ ] 精通高階計時概念與流暢動畫
- [ ] 建立展示各式互動應用的作品集
- [ ] 指導其他對遊戲開發與用戶互動有興趣的人
## 🎯 你的打字遊戲精通時間表
```mermaid
timeline
title 遊戲開發學習進度
section 設置 (10分鐘)
專案結構: HTML 基礎
: CSS 樣式設置
: JavaScript 檔案建立
section 使用者介面 (20分鐘)
互動元素: 輸入欄位
: 按鈕控制
: 顯示區域
: 響應式佈局
section 事件處理 (25分鐘)
使用者互動: 鍵盤事件
: 滑鼠事件
: 即時反饋
: 狀態管理
section 遊戲邏輯 (30分鐘)
核心功能: 引言產生
: 角色比較
: 精確度計算
: 計時器實作
section 性能追蹤 (35分鐘)
指標與分析: 每分鐘字數計算
: 錯誤追蹤
: 進度可視化
: 結果顯示
section 美化與增強 (45分鐘)
使用者體驗: 視覺反饋
: 音效
: 動畫
: 無障礙功能
section 進階功能 (1週)
擴展功能: 難度等級
: 排行榜
: 自訂引言
: 多人選項
section 專業技能 (1個月)
遊戲開發: 性能優化
: 代碼架構
: 測試策略
: 部署模式
```
### 🛠️ 你的遊戲開發工具包總結
完成這個專案後,你將掌握:
- **事件驅動程式設計**:對輸入反應的互動界面
- **即時反饋**:快速視覺與效能更新
- **效能測量**:精確的計時與計分系統
- **遊戲狀態管理**:控制應用流程與用戶體驗
- **互動設計**:創造引人入勝、令人上癮的使用體驗
- **現代網頁 API**:利用瀏覽器能力實現豐富互動
- **無障礙模式**:為所有使用者打造包容性設計
**實際應用場景**:這些技能適用於:
- **網頁應用程式**:任何互動界面或儀表板
- **教育軟體**:學習平台與技能評估工具
- **生產力工具**:文字編輯器、整合開發環境、協作軟體
- **遊戲產業**:瀏覽器遊戲與互動娛樂
- **行動開發**:觸控介面與手勢處理
**下一階段**:準備探索進階遊戲框架、即時多人系統或複雜的互動應用!
## 致謝
由 [Christopher Harrison](http://www.twitter.com/geektrainer) 用 ♥️ 編寫
由 [Christopher Harrison](http://www.twitter.com/geektrainer) 以 ♥️ 撰寫
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋不承擔責任。
本文件乃使用人工智能翻譯服務【Co-op Translator】(https://github.com/Azure/co-op-translator) 翻譯而成。雖然我們致力於確保準確性,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威資料。對於重要資訊,建議使用專業人工翻譯。對於因使用本翻譯而引致的任何誤解或誤釋,我們概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,88 +1,154 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "1b0aeccb600f83c603cd70cb42df594d",
"translation_date": "2025-08-29T15:10:22+00:00",
"original_hash": "da8bc72041a2bb3826a54654ee1a8844",
"translation_date": "2026-01-06T12:57:28+00:00",
"source_file": "4-typing-game/typing-game/README.md",
"language_code": "hk"
}
-->
# 使用事件創建遊戲
# 使用事件創建一個遊戲
你是否曾經想過網站如何知道你何時點按按鈕或在文字框中輸入?這就是事件驅動程式設計的魔力!有什麼比構建一個有用的東西更好的方式來學習這項基本技能呢——一個會對你每次敲打的按鍵作出反應的打字速度遊戲。
你將親身看到網頁瀏覽器如何「與」你的 JavaScript 代碼「對話」。每次你點按、輸入或移動滑鼠時,瀏覽器都會向你的代碼發送小訊息(我們稱之為事件),而你則決定如何回應!
到本文結束時,你將建立一個真實的打字遊戲,追蹤你的速度和準確度。更重要的是,你將理解驅動每一個你曾使用過的互動網站的基本概念。讓我們開始吧!
## 課前測驗
[課前測驗](https://ff-quizzes.netlify.app/web/quiz/21)
[Pre-lecture quiz](https://ff-quizzes.netlify.app/web/quiz/21)
## 事件驅動程式設計
## 事件驅動編程
想想你最喜歡的應用程式或網站——是什麼令它感覺生動且反應靈敏?全在於它如何回應你的操作!每一次點按、點擊、滑動或鍵入都會創造一個我們稱之為「事件」的東西,而這正是網頁開發中真正的魔法所在。
當我們創建一個基於瀏覽器的應用程式時我們會提供一個圖形用戶介面GUI讓用戶在與我們構建的內容互動時使用。與瀏覽器互動最常見的方式是通過點擊和在各種元素中輸入文字。作為開發者我們面臨的挑戰是我們不知道用戶什麼時候會執行這些操作
這就是為什麼針對網頁編程如此有趣的原因:我們永遠不知道何時有人會點擊按鈕或開始輸入文字。他們可能立即點擊,也可能等待五分鐘,或者可能根本不點擊!這種不可預測性意味著我們需要用不同的方法來撰寫代碼。
[事件驅動編程](https://en.wikipedia.org/wiki/Event-driven_programming) 是我們需要用來創建 GUI 的編程類型。如果我們稍微拆解這個詞語,我們會發現核心詞是 **事件**。根據 Merriam-Webster 的定義,[事件](https://www.merriam-webster.com/dictionary/event) 是指「某件發生的事情」。這完美地描述了我們的情況。我們知道會有某些事情發生,我們希望在這些事情發生時執行一些代碼,但我們不知道它們什麼時候會發生
我們不是寫一段從上到下運行的程式碼(就像食譜一樣),而是寫程式碼耐心地等待事情發生。這就像 1800 年代的電報操作員守候在他們的機器旁,準備在訊息通過電線時立即回應一樣
我們通過創建函數來標記我們希望執行的代碼部分。在 [程序式編程](https://en.wikipedia.org/wiki/Procedural_programming) 中,函數是按照特定順序調用的。在事件驅動編程中也是如此,不同之處在於函數是 **如何** 被調用的
那麼,「事件」究竟是什麼?簡單地說,它是發生的事情!你點擊一個按鈕——這就是一個事件。你輸入一個字母——這是另一個事件。你移動滑鼠——還有另一個事件
為了處理事件(如按鈕點擊、輸入等),我們需要註冊 **事件監聽器**。事件監聽器是一個函數,它會監聽某個事件的發生並在事件發生時執行。事件監聽器可以更新用戶介面、向伺服器發送請求,或者執行其他需要根據用戶操作完成的任務。我們可以使用 [addEventListener](https://developer.mozilla.org/docs/Web/API/EventTarget/addEventListener) 並提供一個函數來添加事件監聽器
事件驅動程式設計讓我們能夠設定代碼來聆聽和回應。我們創建特殊函數稱為**事件監聽器event listeners**,它們耐心等待特定事件發生,然後在事件發生時立即執行
> **NOTE:** 值得一提的是,創建事件監聽器有很多種方式。你可以使用匿名函數,也可以創建命名函數。你還可以使用一些快捷方式,比如設置 `click` 屬性,或者使用 `addEventListener`。在我們的練習中,我們將專注於使用 `addEventListener` 和匿名函數,因為這是網頁開發者最常用的技術之一。它也是最靈活的,因為 `addEventListener` 適用於所有事件,並且事件名稱可以作為參數提供。
把事件監聽器想像成你的代碼的門鈴。你安裝門鈴 (`addEventListener()`)告訴它要聽哪種聲音如「click」或「keypress」然後指定誰按鐘時該做什麼你的自訂函數
**事件監聽器的工作原理:**
- **監聽**特定的用戶操作,如點擊、鍵擊或滑鼠移動
- **執行**指定事件發生時的自訂代碼
- **立即回應**用戶互動,創造無縫體驗
- **可處理**同一元素上的多個不同事件監聽器
> **NOTE:** 值得一提的是,創建事件監聽器的方法有很多。你可以使用匿名函數,或者建立有名稱的函數。你也可以用各種快捷方式,比如設定 `click` 屬性,或者使用 `addEventListener()`。在我們的練習中,我們將專注於 `addEventListener()` 和匿名函數,因為這可能是網頁開發者最常用的技術。它也是最靈活的,因為 `addEventListener()` 適用於所有事件,且事件名稱可作為參數傳入。
### 常見事件
在創建應用程式時,有[數十種事件](https://developer.mozilla.org/docs/Web/Events)可供監聽。基本上,用戶在頁面上執行的任何操作都會觸發事件,這讓你擁有很大的控制力來確保用戶獲得你期望的體驗。幸運的是,你通常只需要使用少數幾種事件。以下是一些常見的事件(包括我們在創建遊戲時會用到的兩個):
雖然網頁瀏覽器提供數十種不同的事件可供監聽,但大多數互動應用程式只依賴幾個基本事件。理解這些核心事件將為你建立複雜的用戶互動奠定基礎。
有[數十種事件](https://developer.mozilla.org/docs/Web/Events)可供你監聽,幾乎用戶在頁面上做的任何事情都可以引發事件,這讓你能夠掌控用戶體驗。幸運的是,你通常只需要少數幾種事件。以下是一些常見的(包括我們創建遊戲時將使用的兩種):
- [click](https://developer.mozilla.org/docs/Web/API/Element/click_event):用戶點擊某個元素,通常是按鈕或超連結
- [contextmenu](https://developer.mozilla.org/docs/Web/API/Element/contextmenu_event):用戶點擊滑鼠右鍵
- [select](https://developer.mozilla.org/docs/Web/API/Element/select_event):用戶選取了一些文字
- [input](https://developer.mozilla.org/docs/Web/API/Element/input_event):用戶輸入了一些文字
| Event | 說明 | 常見用例 |
|-------|-------|---------|
| `click` | 用戶點擊了某些東西 | 按鈕、鏈結、互動元素 |
| `contextmenu` | 用戶點擊了滑鼠右鍵 | 自訂右鍵選單 |
| `select` | 用戶選取了部分文字 | 文字編輯、複製操作 |
| `input` | 用戶輸入了文字 | 表單驗證、即時搜尋 |
**理解這些事件類型:**
- **觸發**用戶與頁面上特定元素互動
- **提供**關於用戶操作的詳細事件對象資訊
- **啟用**你創造反應迅速、互動性強的網頁應用程式
- **跨瀏覽器裝置**皆可一致運作
## 創建遊戲
我們將創建一個遊戲來探索 JavaScript 中的事件是如何工作的。我們的遊戲將測試玩家的打字技能,這是所有開發者應該具備但經常被低估的一項技能。我們都應該練習打字!遊戲的基本流程如下:
既然你了解事件如何運作,讓我們將這些知識付諸實踐,做一些有用的東西。我們將建立一個打字速度遊戲,演示事件處理的同時幫助你培養重要的開發技能。
我們要創建一個遊戲來探索 JavaScript 中事件的運作。遊戲將測試玩家的打字技能,這是每個開發者都應該擁有且被低估的技能。趣聞:我們如今使用的 QWERTY 鍵盤布局其實是在 1870 年代為打字機設計的——而良好的打字技能對程式設計師來說依然十分有價值!遊戲的一般流程如下:
```mermaid
flowchart TD
A[玩家點擊開始] --> B[顯示隨機名言]
B --> C[玩家在文字框輸入]
C --> D{單字完成?}
D -->|是| E[突出顯示下一個單字]
D -->|否| F{目前正確嗎?}
F -->|是| G[保持正常樣式]
F -->|否| H[顯示錯誤樣式]
E --> I{名言完成?}
I -->|否| C
I -->|是| J[顯示成功訊息和時間]
G --> C
H --> C
```
**遊戲流程:**
- **開始**:玩家按下開始按鈕,顯示一段隨機引言
- **追蹤**:即時追蹤玩家的逐字輸入進度
- **標示**:高亮顯示當前字詞,引導玩家專注
- **回饋**:立即以視覺效果提示輸入錯誤
- **計算**:完成全部引言時顯示花費的總時間
- 玩家點擊開始按鈕,並看到一段需要輸入的文字
- 玩家在文本框中盡可能快地輸入這段文字
- 每完成一個單詞,下一個單詞會被高亮顯示
- 如果玩家輸入錯誤,文本框會變成紅色
- 當玩家完成整段文字時,會顯示成功訊息以及所用時間
讓我們一起開發遊戲,並了解事件!
讓我們開始構建遊戲,並學習事件的相關知識吧!
### 檔案結構
### 文件結構
在撰寫代碼前,讓我們做好組織!有一個整潔的檔案結構能避免日後的麻煩,讓你的專案看起來更專業。😊
我們需要三個文件:**index.html**、**script.js** 和 **style.css**。讓我們先設置這些文件,讓後續的工作更輕鬆。
我們將保持簡單,只使用三個檔案:`index.html` 放我們的頁面結構,`script.js` 包含遊戲的邏輯,還有 `style.css` 讓頁面美觀。這是網頁開發最經典的三劍客!
- 打開控制台或終端,輸入以下命令來創建一個新文件夾:
**打開命令列或終端機視窗,輸入以下指令,來為你的工作建立一個新資料夾:**
```bash
# Linux or macOS
# Linux macOS
mkdir typing-game && cd typing-game
# Windows
md typing-game && cd typing-game
```
- 打開 Visual Studio Code
**這些指令做了什麼:**
- **創建**一個名為 `typing-game` 的新目錄,用來存放你的專案檔案
- **自動切換到**新建的目錄中
- **為你的遊戲開發搭建了**一個乾淨的工作環境
**打開 Visual Studio Code**
```bash
code .
```
- 在 Visual Studio Code 中,為該文件夾添加以下三個文件:
- index.html
- script.js
- style.css
**此指令作用:**
- **在目前目錄啟動** Visual Studio Code
- **打開**你的專案資料夾到編輯器中
- **提供**所有你需要的開發工具
**在 Visual Studio Code 中新增三個檔案,命名如下:**
- `index.html` - 包含遊戲的結構和內容
- `script.js` - 處理所有遊戲邏輯和事件監聽器
- `style.css` - 定義視覺外觀和樣式
## 創建使用者介面
現在讓我們搭建實際進行遊戲操作的舞台!把它想成為飛船設計控制面板——我們要確保玩家需要的所有東西都在他們預期的位置。
## 創建用戶介面
先想想遊戲需要些什麼。如果你在玩打字遊戲,你希望畫面上看到什麼?這裡是我們需要的:
根據需求,我們知道我們的 HTML 頁面需要一些元素。這有點像食譜,我們需要一些材料:
| UI 元素 | 用途 | HTML 元素 |
|---------|-------|-----------|
| 引言顯示區 | 顯示要輸入的文字 | `<p>`,具有 `id="quote"` |
| 訊息區域 | 顯示狀態及成功訊息 | `<p>`,具有 `id="message"` |
| 文字輸入框 | 玩家輸入引言 | `<input>`,具有 `id="typed-value"` |
| 開始按鈕 | 開始遊戲 | `<button>`,具有 `id="start"` |
- 一個用來顯示玩家需要輸入的文字的區域
- 一個用來顯示訊息(如成功訊息)的區域
- 一個用來輸入文字的文本框
- 一個開始按鈕
**UI 結構理解:**
- **內容**從上到下有邏輯地組織
- **元素**有唯一 ID方便 JavaScript 操作
- **提供**清晰的視覺階層,提升用戶體驗
- **使用**語意化 HTML增強輔助功能支援
每個元素都需要一個 ID這樣我們才能在 JavaScript 中操作它們。我們還需要添加對 CSS 和 JavaScript 文件的引用
這些元素都需要 ID方便我們在 JavaScript 中操作。我們也會在這裡加入 CSS 和 JavaScript 檔案的連結
創建一個名為 **index.html** 的新文件,並添加以下 HTML
建立新檔案命名為 `index.html`,加入以下 HTML
```html
<!-- inside index.html -->
@ -105,26 +171,52 @@ code .
</html>
```
**這段 HTML 結構做了這些事:**
- **`<head>` 連結** CSS 樣式表,提供樣式
- **建立**明確標題和說明,方便使用者理解
- **設置**具有特定 ID 的佔位段落,用於動態內容
- **包含**具無障礙特性的輸入欄位
- **提供**一個開始按鈕來觸發遊戲
- **在末尾載入** JavaScript 檔案,達到最佳載入效能
### 啟動應用程式
最好以迭代的方式開發隨時查看效果。讓我們啟動應用程式。Visual Studio Code 有一個很棒的擴展工具 [Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer&WT.mc_id=academic-77807-sagibbon),它可以在本地託管你的應用程式,並在每次保存時刷新瀏覽器。
在開發過程中經常測試應用程式能幫助你早期發現問題並且即時看到成果。Live Server 是一個無價的工具,當你儲存檔案時能自動刷新瀏覽器,讓開發更有效率。
最好以迭代方式開發,隨時看看變化。讓我們啟動應用程式。有一款 Visual Studio Code 套件叫做 [Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer&WT.mc_id=academic-77807-sagibbon),它可以本地架設伺服器並在檔案儲存時自動刷新瀏覽器。
**安裝 [Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer&WT.mc_id=academic-77807-sagibbon),點擊安裝:**
**安裝過程中發生的事情:**
- **會提示**你的瀏覽器打開 Visual Studio Code
- **引導你完成**擴充套件的安裝
- **可能需要**重新啟動 Visual Studio Code 以完成設置
**安裝後,在 Visual Studio Code 中按 Ctrl-Shift-P或 Cmd-Shift-P打開命令面板**
**命令面板的功能:**
- **快速存取** VS Code 的所有命令
- **可輸入關鍵字**快速尋找命令
- **提供**鍵盤快捷鍵加快開發速度
**輸入「Live Server: Open with Live Server」**
- 按照鏈接安裝 [Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer&WT.mc_id=academic-77807-sagibbon),點擊 **Install**
- 瀏覽器會提示你打開 Visual Studio Code然後 Visual Studio Code 會提示你進行安裝
- 如果有提示,請重新啟動 Visual Studio Code
- 安裝完成後,在 Visual Studio Code 中按 Ctrl-Shift-P或 Cmd-Shift-P打開命令面板
- 輸入 **Live Server: Open with Live Server**
- Live Server 會開始託管你的應用程式
- 打開瀏覽器,導航到 **https://localhost:5500**
- 你現在應該能看到你創建的頁面!
**Live Server 會做什麼:**
- **啟動**本地開發伺服器服務你的專案
- **你儲存檔案時**自動刷新瀏覽器頁面
- **從本地網址**(通常是 `localhost:5500`)提供服務
接下來,我們添加一些功能。
**打開瀏覽器並造訪 `https://localhost:5500`**
## 添加 CSS
你應該會看到剛才建立的頁面!現在讓我們新增功能。
在創建 HTML 後,我們需要添加 CSS 來進行核心樣式設置。我們需要高亮顯示玩家應該輸入的單詞,並在輸入錯誤時為文本框著色。我們將通過兩個類來實現這些功能。
## 新增 CSS
創建一個名為 **style.css** 的新文件,並添加以下語法。
接下來讓畫面好看一些視覺回饋自計算機早期時代起就是用戶介面的關鍵。在1980年代研究者發現即時的視覺回饋大幅提升使用者效能並且減少錯誤。這正是我們要創建的效果。
我們的遊戲需要清晰明確地呈現目前狀況。玩家應立即知道該輸入哪個字詞,若出錯也應見到即時提示。我們來做一些簡單有效的樣式:
建立新檔案命名為 `style.css`,加入以下語法。
```css
/* inside style.css */
@ -138,40 +230,61 @@ code .
}
```
✅ 關於 CSS你可以根據自己的喜好設計頁面佈局。花點時間讓頁面更吸引人
**這些 CSS 類別的作用:**
- **以黃色背景**標示當前字詞,提供清楚視覺引導
- **用淡珊瑚色背景**提示輸入錯誤
- **提供**即時回饋而不干擾輸入流程
- **使用**對比鮮明的顏色提升無障礙性與可讀性
✅ CSS 部分你可以自由調整頁面佈局。花點時間讓頁面更吸引人:
- 選擇不同的字體
- 為標題添加顏色
- 選用不同字體
- 為標題添顏色
- 調整元素大小
## JavaScript
在創建用戶介面後,現在是時候專注於提供邏輯的 JavaScript 了。我們將把這部分拆分為幾個步驟:
現在開始有趣了!🎉 我們已建立 HTML 結構和 CSS 樣式但遊戲目前如同漂亮的汽車卻沒有引擎。JavaScript 就是這個引擎——它讓一切實際運作並對玩家操作產生反應。
- [創建常量](../../../../4-typing-game/typing-game)
- [開始遊戲的事件監聽器](../../../../4-typing-game/typing-game)
- [輸入的事件監聽器](../../../../4-typing-game/typing-game)
這是你將看到你的創作活起來的地方。我們會按步驟進行,讓一切不感到繁雜:
首先,創建一個名為 **script.js** 的新文件。
| 步驟 | 目的 | 你將學習什麼 |
|------|-------|----------------|
| [新增常數](../../../../4-typing-game/typing-game) | 設定引言與 DOM 參考 | 變數管理與 DOM 選取 |
| [新增開始遊戲的事件監聽器](../../../../4-typing-game/typing-game) | 處理遊戲初始化 | 事件處理與 UI 更新 |
| [新增輸入事件監聽器](../../../../4-typing-game/typing-game) | 實時處理使用者輸入 | 輸入驗證與動態回饋 |
### 創建常量
**這種結構化方法幫助你:**
- **把程式碼**組織成合乎邏輯、可管理的部分
- **逐步構建**功能,便於除錯
- **理解**應用各部分如何協同工作
- **創造**未來專案可重用模式
我們需要一些項目來讓編程更輕鬆。這有點像食譜,以下是我們需要的內容:
但首先,建立新檔案命名為 `script.js`
- 包含所有文字段落的數組
- 用於存儲當前段落所有單詞的空數組
- 用於存儲玩家當前輸入單詞索引的空間
- 玩家點擊開始時的時間
### 新增常數
我們還需要引用用戶介面元素:
在開始動手前,先整理所有資源!就像 NASA 任務控制中心會在發射前設置所有監控系統一樣,事先準備整齊省事很多,也避免後續找資料時東翻西找、打錯字。
- 文本框(**typed-value**
- 顯示段落的區域(**quote**
- 顯示訊息的區域(**message**
以下是我們要先設定的:
| 資料類型 | 用途 | 例子 |
|-----------|-------|-----|
| Array of quotes | 儲存所有遊戲可能的引用語 | `['Quote 1', 'Quote 2', ...]` |
| Word array | 將當前引用分解成單字陣列 | `['When', 'you', 'have', ...]` |
| Word index | 追蹤玩家正在輸入的單字位置 | `0, 1, 2, 3...` |
| Start time | 計算得分的已用時間 | `Date.now()` |
**我們還需要引用 UI 元素:**
| Element | ID | Purpose |
|---------|----|---------|
| Text input | `typed-value` | 玩家輸入的地方 |
| Quote display | `quote` | 顯示要輸入的引用語 |
| Message area | `message` | 顯示狀態更新 |
```javascript
// inside script.js
// all of our quotes
// 在 script.js 裡面
// 我哋所有嘅引述
const quotes = [
'When you have eliminated the impossible, whatever remains, however improbable, must be the truth.',
'There is nothing more deceptive than an obvious fact.',
@ -181,172 +294,320 @@ const quotes = [
'Nothing clears up a case so much as stating it to another person.',
'Education never ends, Watson. It is a series of lessons, with the greatest for the last.',
];
// store the list of words and the index of the word the player is currently typing
// 儲存詞彙列表同玩家而家打緊嘅詞彙索引
let words = [];
let wordIndex = 0;
// the starting time
// 開始時間
let startTime = Date.now();
// page elements
// 頁面元素
const quoteElement = document.getElementById('quote');
const messageElement = document.getElementById('message');
const typedValueElement = document.getElementById('typed-value');
```
✅ 為你的遊戲添加更多段落
**拆解這段設置程式碼的功用:**
- **使用 `const` 儲存** 一組 Sherlock Holmes 的引用語,因為引用語不會改變
- **使用 `let` 初始化** 追蹤變數,因為這些值在遊戲進行時會更新
- **透過 `document.getElementById()` 抓取** DOM 元素的引用以提升存取效率
- **建立遊戲功能基礎**,使用清晰且描述性變數名稱
- **將相關資料與元素組織** 起來便於後續維護程式碼
✅ 馬上為你的遊戲加入更多引用語吧
> **NOTE:** 我們可以在代碼中隨時使用 `document.getElementById` 獲取元素。由於我們會經常引用這些元素,因此使用常量可以避免字符串字面值的拼寫錯誤。像 [Vue.js](https://vuejs.org/) 或 [React](https://reactjs.org/) 這樣的框架可以幫助你更好地集中管理代碼。
> 💡 **專家小提示**:我們可以在程式碼中隨時用 `document.getElementById()` 取得元素。因為會經常用到這些元素,我們會用常數避免字串拼寫錯誤。像是 [Vue.js](https://vuejs.org/) 或 [React](https://reactjs.org/) 這類框架能幫你更好管理及集中你的程式碼。
>
**這種作法為什麼這麼好:**
- **防止** 多次引用元素時拼寫錯誤
- **提升** 程式碼可讀性,使用描述性常數名稱
- **啟用** IDE 的自動完成與錯誤檢查功能
- **未來** 元素 ID 改變時更易重構程式碼
花點時間觀看一段關於使用 `const`、`let` 和 `var` 的影片
一點時間觀看有關使用 `const`、`let` 與 `var` 的影片
[![變數類型](https://img.youtube.com/vi/JNIXfGiDWM8/0.jpg)](https://youtube.com/watch?v=JNIXfGiDWM8 "變數類型")
[![Types of variables](https://img.youtube.com/vi/JNIXfGiDWM8/0.jpg)](https://youtube.com/watch?v=JNIXfGiDWM8 "Types of variables")
> 🎥 點擊上方圖片觀看關於變數的影片。
> 🎥 點擊上方圖片觀看變數相關教學影片。
### 添加開始邏輯
### 新增開始邏輯
為了開始遊戲,玩家需要點擊開始按鈕。當然,我們不知道他們什麼時候會點擊開始。這就是 [事件監聽器](https://developer.mozilla.org/docs/Web/API/EventTarget/addEventListener) 派上用場的地方。事件監聽器允許我們監聽某些事情的發生(事件)並執行相應的代碼。在我們的例子中,我們希望在用戶點擊開始時執行代碼
這是全部運作的關鍵時刻!🚀 你即將寫出第一個真正的事件監聽器,看到你的程式碼回應按鈕點擊會非常有成就感
當用戶點擊 **開始** 時,我們需要選擇一段文字,設置用戶介面,並設置當前單詞和計時的追蹤。以下是你需要添加的 JavaScript我們會在代碼塊後進行討論。
想想看某處某人會按下「Start」按鈕程式必須準備好因應。我們不知道他什麼時候按 — 可能立刻,也可能喝完咖啡才按 — 但當按下去時,你的遊戲就啟動了。
當使用者點擊 `start`,我們需要選擇一段引用,設置界面,並追蹤目前輸入的字與計時。以下是你需要加入的 JavaScript後面會解釋。
```javascript
// at the end of script.js
// 在 script.js 的末尾
document.getElementById('start').addEventListener('click', () => {
// get a quote
// 獲取一個引言
const quoteIndex = Math.floor(Math.random() * quotes.length);
const quote = quotes[quoteIndex];
// Put the quote into an array of words
// 將引言放入字詞陣列中
words = quote.split(' ');
// reset the word index for tracking
// 重置字詞索引以便追蹤
wordIndex = 0;
// UI updates
// Create an array of span elements so we can set a class
// 使用者界面更新
// 建立一個 span 元素陣列,以便設定類別
const spanWords = words.map(function(word) { return `<span>${word} </span>`});
// Convert into string and set as innerHTML on quote display
// 轉換為字串並設為引言顯示的 innerHTML
quoteElement.innerHTML = spanWords.join('');
// Highlight the first word
// 強調第一個字詞
quoteElement.childNodes[0].className = 'highlight';
// Clear any prior messages
// 清除所有先前訊息
messageElement.innerText = '';
// Setup the textbox
// Clear the textbox
// 設置文字框
// 清空文字框
typedValueElement.value = '';
// set focus
// 設置焦點
typedValueElement.focus();
// set the event handler
// 設置事件處理函式
// Start the timer
// 啟動計時器
startTime = new Date().getTime();
});
```
讓我們分解代碼!
**解構這段程式碼成合理的區塊:**
**📊 單字追蹤設定:**
- **隨機選擇** 一段引用,利用 `Math.floor()``Math.random()` 增加變化度
- **利用 `split(' ')`** 將引用切割成單字陣列
- **重置 `wordIndex`** 為 0讓玩家從第一個單字開始
- **準備** 遊戲狀態為全新的一回合
**🎨 使用者介面設定與顯示:**
- **攏成 `<span>` 元素陣列包住每個單字,方便個別樣式設定**
- **將這些 span 合併成單一字串以便快速更新 DOM**
- **透過加上 `highlight` CSS 類,標示第一個單字**
- **清空之前的遊戲訊息,保持畫面清爽**
**⌨️ 文字框準備:**
- **清除輸入欄現有文字**
- **聚焦文字框使玩家能立即開始輸入**
- **為新一場遊戲做好輸入區準備**
- 設置單詞追蹤
- 使用 [Math.floor](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Math/floor) 和 [Math.random](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Math/random) 隨機選擇 `quotes` 數組中的一段文字
- 將 `quote` 轉換為 `words` 數組,以便追蹤玩家當前輸入的單詞
- 將 `wordIndex` 設置為 0因為玩家將從第一個單詞開始
- 設置用戶介面
- 創建一個包含每個單詞的 `spanWords` 數組
- 這樣我們可以在顯示中高亮顯示單詞
- 使用 `join` 將數組轉換為字符串,並更新 `quoteElement``innerHTML`
- 這將顯示段落給玩家
- 將第一個 `span` 元素的 `className` 設置為 `highlight`,以黃色高亮顯示
- 通過將 `messageElement``innerText` 設置為 `''` 來清空訊息
- 設置文本框
- 清除 `typedValueElement` 的當前 `value`
- 將焦點設置到 `typedValueElement`
- 通過調用 `getTime` 開始計時
**⏱️ 計時器初始化:**
- **使用 `new Date().getTime()` 記錄當前時間戳**
- **能精準計算輸入速度及完成時間**
- **開始追蹤這回合的遊戲時間**
### 添加輸入邏輯
### 新增輸入邏輯
當玩家輸入時,會觸發 `input` 事件。這個事件監聽器將檢查玩家是否正確輸入單詞,並處理遊戲的當前狀態。返回 **script.js**,在末尾添加以下代碼。我們會在代碼塊後進行討論。
現在要開始進入遊戲的核心!別擔心,開始看似複雜,我們一步步拆解,最後你會發現邏輯非常合理。
我們建立的邏輯是:每當有人輸入字母,我們的程式都會檢查他輸入了什麼,給予回饋,並判斷下一步要做什麼。這有點像 1970 年代早期的文字處理器 WordStar在輸入時即時提供回饋。
```javascript
// at the end of script.js
// 在 script.js 的結尾
typedValueElement.addEventListener('input', () => {
// Get the current word
// 取得目前的字詞
const currentWord = words[wordIndex];
// get the current value
// 取得目前的數值
const typedValue = typedValueElement.value;
if (typedValue === currentWord && wordIndex === words.length - 1) {
// end of sentence
// Display success
// 句子結束
// 顯示成功
const elapsedTime = new Date().getTime() - startTime;
const message = `CONGRATULATIONS! You finished in ${elapsedTime / 1000} seconds.`;
messageElement.innerText = message;
} else if (typedValue.endsWith(' ') && typedValue.trim() === currentWord) {
// end of word
// clear the typedValueElement for the new word
// 字詞結束
// 為新字詞清除 typedValueElement
typedValueElement.value = '';
// move to the next word
// 移至下一個字詞
wordIndex++;
// reset the class name for all elements in quote
// 重設 quote 中所有元素的類別名稱
for (const wordElement of quoteElement.childNodes) {
wordElement.className = '';
}
// highlight the new word
// 高亮新字詞
quoteElement.childNodes[wordIndex].className = 'highlight';
} else if (currentWord.startsWith(typedValue)) {
// currently correct
// highlight the next word
// 目前正確
// 高亮下一個字詞
typedValueElement.className = '';
} else {
// error state
// 錯誤狀態
typedValueElement.className = 'error';
}
});
```
讓我們分解代碼!我們首先獲取當前單詞和玩家目前輸入的值。然後,我們使用瀑布邏輯來檢查段落是否完成、單詞是否完成、單詞是否正確,或者(最後)是否有錯誤。
- 段落完成:當 `typedValue` 等於 `currentWord`,且 `wordIndex` 等於 `words` 長度減一時
- 通過將當前時間減去 `startTime` 計算 `elapsedTime`
- 將 `elapsedTime` 除以 1,000將毫秒轉換為秒
- 顯示成功訊息
- 單詞完成:當 `typedValue` 以空格結尾(單詞結束)且 `typedValue` 等於 `currentWord`
- 將 `typedElement``value` 設置為 `''`,以便輸入下一個單詞
- 增加 `wordIndex`,移動到下一個單詞
- 遍歷 `quoteElement` 的所有 `childNodes`,將 `className` 設置為 `''`,恢復默認顯示
- 將當前單詞的 `className` 設置為 `highlight`,標記為下一個需要輸入的單詞
- 單詞當前輸入正確(但未完成):當 `currentWord``typedValue` 開頭時
- 通過清除 `className` 確保 `typedValueElement` 顯示為默認狀態
- 如果到這裡,說明有錯誤
- 將 `typedValueElement``className` 設置為 `error`
**理解輸入邏輯流程:**
此函式採用瀑布式檢查,從最特定條件到最普遍的來判斷。逐項說明:
```mermaid
flowchart TD
A[玩家輸入字元] --> B[取得目前字和輸入值]
B --> C{引號完成?}
C -->|是| D[顯示完成訊息及時間]
C -->|否| E{字詞以空格完成?}
E -->|是| F[清除輸入,移至下一字,更新標示]
E -->|否| G{目前輸入正確?}
G -->|是| H[移除錯誤樣式]
G -->|否| I[顯示錯誤樣式]
```
**🏁 完成整句引用(情境一):**
- **檢查** 輸入的字串是否等於目前單字且已是最後一個字
- **計算** 從開始到現在的時間差
- **將** 毫秒轉換成秒,除以 1,000
- **顯示** 恭喜完成消息與所用時間
**✅ 完成單字(情境二):**
- **偵測** 輸入結尾為空白表示完成該字
- **驗證** 去除空白後輸入與當前單字完全相符
- **清除** 輸入框準備下一個單字
- **前進** 單字索引 `wordIndex` 到下一個字
- **更新** 標示樣式,移除所有類別並標亮新單字
**📝 輸入進行中(情境三):**
- **確認** 當前單字以已輸入字串開頭
- **移除** 錯誤樣式以顯示輸入正確
- **允許** 玩家持續輸入不做中斷
**❌ 錯誤狀態(情境四):**
- **觸發** 輸入不符合當前單字前綴
- **加上** 錯誤 CSS 類別,立刻給視覺反饋
- **幫助** 玩家快速察覺並修正錯誤
## 測試你的應用程式
你已經完成了!最後一步是確保應用程式運行正常。試試看吧!如果有錯誤,不用擔心;**所有開發者** 都會遇到錯誤。檢查訊息並根據需要進行調試。
看看你做了什麼!🎉 你從零打造出真正可用的打字遊戲,用事件驅動程式設計。花點時間欣賞你的成果 — 這可不是件小事!
現在是測試時間!它會按預期運作嗎?我們是不是漏了什麼?事情如果沒立刻完美正常也很正常。連經驗豐富的開發者都常常找到 bug這是開發流程的一部分
點擊 `start`,開始打字!應該跟之前看到的動畫類似。
點擊 **開始**,然後開始輸入!它應該看起來像我們之前看到的動畫。
![遊戲運作動畫](../../../../4-typing-game/images/demo.gif)
![遊戲運行動畫](../../../../4-typing-game/images/demo.gif)
**測試內容建議:**
- **確認** 點擊開始時隨機顯示引用語
- **確認** 輸入時正確標示出目前單字
- **檢查** 輸入錯誤時出現錯誤樣式
- **確定** 詞語完成後標示正確前進
- **測試** 完成整句後顯示完成訊息與計時
**常見除錯建議:**
- **查看** 瀏覽器控制台 (F12) 是否有 JavaScript 錯誤
- **確認** 所有檔名大小寫完全正確
- **確保** Live Server 正在正常運作與刷新
- **測試** 多段不同引用,確認隨機選擇有效
---
## GitHub Copilot Agent 挑戰 🎮
使用 Agent 模式完成以下挑戰:
**描述:** 擴充打字遊戲,實作難度系統,根據玩家表現調整遊戲難度。此挑戰將幫助你練習進階事件處理、資料分析與動態 UI 更新。
**提示:** 建立一套難度調整系統,要求:
1. 追蹤玩家打字速度(每分鐘字數)與準確率百分比
2. 自動調整三種難度:簡單(簡單引用)、中等(目前引用)、困難(帶標點符號複雜引用)
3. 在 UI 顯示目前難度等級與玩家統計數據
4. 實作連續三次好表現後提升難度的連勝計數器
5. 加入視覺回饋(顏色、動畫)提示難度變化
新增所需的 HTML 元素、CSS 樣式與 JavaScript 函式實現此功能。包含適當的錯誤處理並確保遊戲無障礙,使用合適的 ARIA 標籤。
更多 agent 模式資訊可見 [agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) 。
## 🚀 挑戰
添加更多功能
準備好讓你的打字遊戲更進階了嗎?嘗試實作以下功能,加深你對事件處理與 DOM 操作的理解:
**添加更多功能:**
| 功能 | 描述 | 練習技能 |
|---------|-------------|------------------------|
| **輸入控管** | 完成後禁用輸入事件監聽器,點擊按鈕時重新啟用 | 事件管理與狀態控制 |
| **UI 狀態管理** | 玩家完成引用後禁用文字框 | DOM 屬性操作 |
| **模態對話框** | 顯示成功訊息的模態對話框 | 進階 UI 模式和輔助功能 |
| **最高分系統** | 使用 `localStorage` 儲存最高分數 | 瀏覽器儲存 API 與資料持久化 |
**實作提示:**
- **研究** `localStorage.setItem()``localStorage.getItem()` 實作持久保存
- **練習** 動態新增和移除事件監聽器
- **探索** HTML 對話框元素或 CSS 模態視窗範例
- **考慮** 在禁用和啟用表單控件時保持無障礙
- 在完成後禁用 `input` 事件監聽器,並在按下按鈕時重新啟用
- 在玩家完成段落後禁用文本框
- 使用模態對話框顯示成功訊息
- 使用 [localStorage](https://developer.mozilla.org/docs/Web/API/Window/localStorage) 存儲最高分
## 課後測驗
[課後測驗](https://ff-quizzes.netlify.app/web/quiz/22)
## 溫習與自學
---
## 🚀 你的打字遊戲精通時間軸
### ⚡ **未來五分鐘能做到的事**
- [ ] 嘗試不同引用語確保遊戲順暢
- [ ] 試著修改 CSS改變標記和錯誤顏色
- [ ] 開啟瀏覽器 DevTools (F12),玩遊戲時觀察 Console
- [ ] 挑戰自己盡快完成一段引用
### ⏰ **一小時內能完成的事**
- [ ] 為陣列新增更多引用(如你喜歡的書本或電影)
- [ ] 實作挑戰區的 localStorage 最高分系統
- [ ] 製作每場遊戲後顯示的每分鐘字數計算器
- [ ] 加入正確輸入、錯誤和完成的聲音效果
### 📅 **你的七天冒險**
- [ ] 打造多人對戰功能,讓朋友並排比賽
- [ ] 建立不同難度等級,引用複雜度分級
- [ ] 新增進度條顯示已完成引用比例
- [ ] 實作使用者帳號與個人統計追蹤
- [ ] 設計自訂主題並讓使用者選擇喜愛的樣式
### 🗓️ **你的三十天蛻變**
- [ ] 建立打字課程,逐步教導正確手指位置
- [ ] 建立錯誤分析,找出最多錯誤的字母或單字
- [ ] 支援不同語言及鍵盤配置
- [ ] 整合教育用 API從文學資料庫取得引用
- [ ] 發佈強化版打字遊戲供其他人使用與享受
### 🎯 **最終反思檢視**
**繼續前,花點時間慶祝:**
- 在製作此遊戲過程中,最令人滿足的時刻是什麼?
- 現在你對事件驅動程式設計的看法如何,和剛開始時相比?
- 有哪個功能讓你期待加入,讓遊戲獨一無二?
- 你會怎麼將事件處理概念應用到其他專案?
```mermaid
journey
title 你的事件程式設計信心之旅
section 今日
了解事件: 3: 你
建立使用者介面: 4: 你
撰寫事件監聽器: 5: 你
section 本週
新增功能: 4: 你
偵錯問題: 5: 你
改善使用者體驗: 4: 你
section 下個月
建立複雜應用程式: 5: 你
教授他人: 5: 你
建立框架: 5: 你
```
> 🌟 **記得**:你剛剛掌握了讓每個互動網站與應用程式運作的核心概念。事件驅動程式設計讓網路維持活躍且貼近使用者。每次看到下拉選單、輸入時即時驗證、或遊戲回應點擊,你現在懂背後的魔法。你不只是學寫程式,你是學著創造直覺且吸引人的體驗!🎉
---
## 複習與自學
閱讀[網頁瀏覽器中可用的所有事件](https://developer.mozilla.org/docs/Web/Events),並思考每個事件適用的情境。
閱讀[瀏覽器提供的所有事件](https://developer.mozilla.org/docs/Web/Events)文件,並思考你會在什麼情況下使用它們
## 作業
[創建一個新的鍵盤遊戲](assignment.md)
[一個新的鍵盤遊戲](assignment.md)
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,請注意自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。
本文件乃使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 所翻譯。儘管我們致力確保翻譯準確,但請注意自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要資訊,建議尋求專業人工翻譯。本公司對因使用本翻譯而導致的任何誤解或誤譯概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,24 +1,57 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "de5384c118e15e4d1d0eaa00fc01b112",
"translation_date": "2025-08-24T00:22:22+00:00",
"original_hash": "3eac59d70e2532a677a2ce6bf765485a",
"translation_date": "2026-01-06T12:59:16+00:00",
"source_file": "4-typing-game/typing-game/assignment.md",
"language_code": "hk"
}
-->
# 建一個新的鍵盤遊戲
# 建一個新的鍵盤遊戲
## 指
## 指
創建一個使用鍵盤事件來完成任務的小型遊戲。它可以是一種不同類型的打字遊戲,或者是一種藝術型遊戲,透過按鍵在螢幕上繪製像素。發揮創意!
既然你已經掌握了以事件驅動程式編寫打字遊戲的基礎是時候發揮你的創意了你將設計並構建一個屬於你自己的基於鍵盤的遊戲展示你對事件處理、DOM 操控和用戶交互模式的理解。
創建一個利用鍵盤事件來完成特定任務的小遊戲。這可以是不同類型的打字遊戲、一個根據按鍵在畫面上繪製像素的藝術應用、一個用方向鍵控制的簡單街機風格遊戲,或你能想像到的任何其他創意概念。發揮創意,思考不同按鍵如何觸發不同的行為!
**你的遊戲應該包含:**
| 需求 | 描述 | 目的 |
|-------------|-------------|---------|
| **事件監聽器** | 回應至少 3 種不同的鍵盤事件 | 展示事件處理的理解 |
| **視覺反饋** | 即時對用戶輸入提供視覺反饋 | 展現 DOM 操控的熟練程度 |
| **遊戲邏輯** | 包含計分、關卡或進展機制 | 練習實作應用狀態管理 |
| **用戶介面** | 清晰指示與直覺控制 | 發展用戶體驗設計技巧 |
**創意專案靈感參考:**
- **節奏遊戲**:玩家根據音樂或視覺提示按鍵
- **像素藝術創作者**:不同按鍵繪製不同顏色或圖案
- **單詞拼湊遊戲**:玩家按特定順序輸入字母組成單字
- **貪食蛇遊戲**:用方向鍵控制蛇身收集物品
- **音樂合成器**:不同按鍵播放不同音符或音效
- **快速打字變體**:特定類別打字(程式術語、外語等)
- **鍵盤鼓手**:透過鍵盤按鍵創作不同鼓聲節拍
**實作指引:**
- **從** 簡單概念開始,逐步增加複雜度
- **著重** 控制流暢、回應靈敏且自然
- **包含** 清楚的視覺指示遊戲狀態及玩家進度
- **測試** 你的遊戲給不同使用者,確保操作直覺
- **用註解** 記錄你的事件處理策略與程式碼
## 評分標準
| 評分標準 | 表現卓越 | 表現尚可 | 需要改進 |
| -------- | ------------------------ | ------------------------ | ----------------- |
| | 提供完整的遊戲 | 遊戲非常簡單 | 遊戲有錯誤 |
| | | | |
| 標準 | 優秀 | 適當 | 需改進 |
| -------- | --------- | -------- | ----------------- |
| **功能性** | 完整且精緻的遊戲,具多種功能且遊戲流程流暢 | 基本功能完善且展示鍵盤事件處理的遊戲 | 實作內容有限或存在嚴重錯誤 |
| **程式碼品質** | 結構良好、註解充分且符合最佳實踐,有效處理事件 | 乾淨易讀,合理使用事件監聽器和 DOM 操控 | 基本程式結構,有組織問題或效率不佳 |
| **使用者體驗** | 控制直覺、反饋清晰,遊戲體驗專業吸引人 | 功能完善,具足夠導引及靈敏控制的介面 | 介面基本,指示不清或回應差 |
| **創意性** | 原創概念,創新運用鍵盤事件及解決問題的創意 | 有趣的遊戲變化,善用事件處理 | 簡單實作基本概念,缺乏創意元素 |
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為具權威性的來源。對於重要資訊,建議使用專業的人類翻譯服務。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。
本文件由人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻譯而成。我們致力於確保翻譯的準確性,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的原文版本應被視為權威來源。對於重要資訊,建議採用專業人工翻譯。我們不對因使用此翻譯而引致的任何誤解或錯誤詮釋承擔責任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,16 +1,32 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "2326d04e194a10aa760b51f5e5a1f61d",
"translation_date": "2025-08-29T14:59:43+00:00",
"original_hash": "00aa85715e1efd4930c17a23e3012e69",
"translation_date": "2026-01-06T12:44:16+00:00",
"source_file": "5-browser-extension/1-about-browsers/README.md",
"language_code": "hk"
}
-->
# 瀏覽器擴展項目第一部分:關於瀏覽器的一切
![瀏覽器手繪筆記](../../../../translated_images/browser.60317c9be8b7f84adce43e30bff8d47a1ae15793beab762317b2bc6b74337c1a.hk.jpg)
> 手繪筆記由 [Wassim Chegham](https://dev.to/wassimchegham/ever-wondered-what-happens-when-you-type-in-a-url-in-an-address-bar-in-a-browser-3dob) 提供
# 瀏覽器擴充功能專案第1部分瀏覽器入門
```mermaid
journey
title 你的瀏覽器擴充功能開發旅程
section 基礎
了解瀏覽器: 3: 學生
學習擴充功能類型: 4: 學生
設置開發環境: 4: 學生
section 開發
建立介面: 4: 學生
新增功能: 5: 學生
處理數據: 5: 學生
section 整合
在瀏覽器測試: 5: 學生
除錯問題: 4: 學生
精煉體驗: 5: 學生
```
![瀏覽器手繪筆記](../../../../translated_images/browser.60317c9be8b7f84a.hk.jpg)
> 手繪筆記作者:[Wassim Chegham](https://dev.to/wassimchegham/ever-wondered-what-happens-when-you-type-in-a-url-in-an-address-bar-in-a-browser-3dob)
## 課前測驗
@ -18,165 +34,522 @@ CO_OP_TRANSLATOR_METADATA:
### 簡介
瀏覽器擴展為瀏覽器添加了額外的功能。但在你開始構建之前,應該先了解一些關於瀏覽器如何運作的知識。
### 關於瀏覽器
瀏覽器擴充功能是能提升瀏覽體驗的小型應用程式。正如提姆·伯納斯-李Tim Berners-Lee最初構想的互動網路擴充功能擴展了瀏覽器超越單純文件瀏覽的能力。從保護您的帳戶安全的密碼管理器到幫助設計師挑選完美色彩的色彩挑選器擴充功能解決了日常瀏覽的種種挑戰。
在打造您的第一個擴充功能之前先了解瀏覽器如何運作。就像亞歷山大·格雷厄姆·貝爾Alexander Graham Bell需要理解聲音傳播原理才能發明電話一樣掌握瀏覽器基礎能幫助您打造能與現有瀏覽器系統無縫整合的擴充功能。
完成本課後,您將理解瀏覽器架構並開始建立第一個擴充功能。
```mermaid
mindmap
root((瀏覽器架構))
Core Components
Rendering Engine
JavaScript Engine
Network Stack
Storage APIs
User Interface
Address Bar
Tab Management
Bookmarks
Extension Icons
Extension System
Manifest Files
Content Scripts
Background Pages
Popup Windows
Security Model
Same-Origin Policy
Permissions API
Content Security
Isolated Worlds
Development Tools
DevTools Integration
Debug Console
Performance Monitor
Extension Inspector
```
## 理解網頁瀏覽器
網頁瀏覽器本質上是一款複雜的文件解釋器。當您在網址列輸入「google.com」時瀏覽器會執行一連串複雜操作──從全球各服務器請求內容然後解析與呈現代碼成為您看到的互動網頁。
這個過程與1990年提姆·伯納斯-李設計的首款網頁瀏覽器WorldWideWeb非常相似目的是讓超連結文件人人可訪問。
**歷史小知識**首款瀏覽器名為「WorldWideWeb」由Sir Timothy Berners-Lee於1990年創造。
![早期瀏覽器](../../../../translated_images/earlybrowsers.d984b711cdf3a42d.hk.jpg)
> 一些早期瀏覽器,來源:[Karen McGrane](https://www.slideshare.net/KMcGrane/week-4-ixd-history-personal-computing)
### 瀏覽器如何處理網頁內容
從輸入 URL 到顯示網頁的過程涉及數個協調執行的步驟,而且在幾秒鐘內完成:
```mermaid
sequenceDiagram
participant User
participant Browser
participant Extension
participant DNS
participant Server
User->>Browser: 輸入網址並按下 Enter
Browser->>Extension: 觸發 beforeRequest 事件
Extension->>Extension: 檢查是否需要修改網址
Browser->>DNS: 查詢伺服器 IP 位址
DNS->>Browser: 回傳 IP 位址
Browser->>Server: 請求網頁內容
Server->>Browser: 傳送 HTML、CSS 及 JavaScript
Browser->>Extension: 觸發 beforeResponse 事件
Extension->>Extension: 如有需要則修改內容
Browser->>User: 呈現完整網頁
Extension->>User: 顯示擴充功能介面更新
```
**這個過程完成了以下工作:**
- **將**人類可讀的 URL 透過 DNS 查詢轉換為伺服器的 IP 位址
- **建立**使用 HTTP 或 HTTPS 協定與網路伺服器的安全連接
- **向**伺服器請求特定的網頁內容
- **接收**伺服器傳回的 HTML 標記、CSS 樣式與 JavaScript 代碼
- **呈現**所有內容為您看到的互動網頁
### 瀏覽器核心功能
現代瀏覽器提供許多擴充功能開發者可利用的功能:
| 功能 | 目的 | 擴充功能機會 |
|---------|---------|------------------------|
| **渲染引擎** | 呈現 HTML、CSS 與 JavaScript | 內容修改、樣式注入 |
| **JavaScript 引擎** | 執行 JavaScript 代碼 | 自訂腳本、API 互動 |
| **本地儲存** | 在本地保存資料 | 使用者偏好、快取資料 |
| **網路堆疊** | 處理網路請求 | 請求監控、資料分析 |
| **安全模型** | 保護用戶免於惡意內容 | 內容過濾、安全強化 |
**理解這些功能幫助您:**
- **辨識**您的擴充功能能在哪些部分增添最大價值
- **選擇**合適的瀏覽器 API 以達成功能需求
- **設計**能有效配合瀏覽器系統運作的擴充功能
- **確保**擴充功能遵守瀏覽器安全規範
### 跨瀏覽器開發注意事項
不同瀏覽器在標準實作上存在細微差異正如不同程式語言可能對相同演算法處理略有不同。Chrome、Firefox 和 Safari 各有特色,開發時必須考量這些差異。
> 💡 **專家提示**:使用 [caniuse.com](https://www.caniuse.com) 查看各瀏覽器支援的網路技術。這對規劃擴充功能特色非常有幫助!
**擴充功能開發主要注意事項:**
- **測試**您的擴充功能於 Chrome、Firefox 及 Edge 瀏覽器
- **調適**適應不同瀏覽器擴充功能 API 與 manifest 格式
- **處理**多樣瀏覽器的效能特性與限制
- **提供**不存在於某些瀏覽器的功能替代方案
**分析洞察**:你可透過在網站專案嵌入分析套件,了解使用者偏好瀏覽器,進而決定優先支援哪些瀏覽器。
## 理解瀏覽器擴充功能
瀏覽器擴充功能透過直接新增功能到瀏覽器介面,解決日常瀏覽挑戰。相比需使用獨立應用程式或複雜流程,擴充功能提供即時工具和功能入口。
此概念呼應早期電腦先驅 Douglas Engelbart 的願景,利用科技增強人類能力——擴充功能則是擴充您瀏覽器的基本功能。
```mermaid
quadrantChart
title 瀏覽器擴充功能類別
x-axis 簡單 --> 複雜
y-axis 個人使用 --> 專業工具
quadrant-1 開發者工具
quadrant-2 企業方案
quadrant-3 個人實用工具
quadrant-4 生產力應用程式
Ad Blockers: [0.3, 0.2]
Password Managers: [0.7, 0.3]
Color Pickers: [0.4, 0.8]
Code Formatters: [0.8, 0.9]
Note Taking: [0.6, 0.5]
Video Downloaders: [0.5, 0.2]
Time Trackers: [0.7, 0.6]
Screenshot Tools: [0.4, 0.4]
```
**熱門擴充功能類別及其效益:**
- **生產力工具**:任務管理器、筆記應用及時間追蹤器,幫助您保持有序
- **安全強化**:密碼管理器、廣告阻擋器與隱私工具,保護您的資料
- **開發者工具**:程式碼格式化器、色彩挑選器及除錯工具,提升開發效率
- **內容增強**:閱讀模式、影片下載器及截圖工具,美化瀏覽體驗
**反思問題**:您最喜歡哪些瀏覽器擴充功能?它們執行哪些具體任務?如何改善您的瀏覽體驗?
### 🔄 **教學檢核**
**瀏覽器架構理解**:在開始開發擴充功能前,確保您能:
- ✅ 解釋瀏覽器如何處理網路請求並呈現內容
- ✅ 識別瀏覽器架構主要組件
- ✅ 理解擴充功能如何與瀏覽器功能整合
- ✅ 了解保護用戶的安全模型
**快速自我測試**:您能描述從輸入 URL 到看到網頁的路徑嗎?
1. **DNS 查詢**將 URL 轉為 IP 位址
2. **HTTP 請求**從伺服器擷取內容
3. **解析**處理 HTML、CSS 與 JavaScript
4. **渲染**呈現最終網頁
5. **擴充功能**可於多步驟修改內容
## 安裝與管理擴充功能
了解擴充功能安裝流程,能幫助您預見用戶安裝擴充功能時的操作體驗。現代瀏覽器的安裝流程標準化,但介面設計或略有差異。
![Edge 瀏覽器截圖顯示開啟 edge://extensions 頁面與設定選單](../../../../translated_images/install-on-edge.d68781acaf0b3d3d.hk.png)
> **重要**:測試自製擴充功能時,務必切換至開發者模式,並允許來自其他商店的擴充功能。
### 開發時擴充功能安裝流程
當您正在開發與測試自己的擴充功能時,遵循此工作流程:
```mermaid
flowchart TD
A[撰寫程式碼] --> B[建立擴充功能]
B --> C{首次安裝?}
C -->|是| D[載入未封裝]
C -->|否| E[重新載入擴充功能]
D --> F[測試功能]
E --> F
F --> G{運作正常?}
G -->|否| H[除錯問題]
G -->|是| I[準備提供用戶使用]
H --> A
I --> J[發佈到商店]
style A fill:#e1f5fe
style F fill:#e8f5e8
style I fill:#f3e5f5
style J fill:#fff3e0
```
```bash
# 第一步:建立你的擴充功能
npm run build
```
在這系列課程中,你將學習如何構建一個可以在 Chrome、Firefox 和 Edge 瀏覽器上運行的瀏覽器擴展。在這部分,你將了解瀏覽器的工作原理,並搭建瀏覽器擴展的基本結構。
**此命令執行的工作:**
- **編譯**您的原始碼成適用於瀏覽器的檔案
- **打包**JavaScript 模組成優化套件
- **產生**最終擴充功能檔案於 `/dist` 資料夾
- **準備**擴充功能以方便安裝與測試
那麼,什麼是瀏覽器?它是一種軟件應用程序,允許終端用戶從伺服器訪問內容並在網頁上顯示。
**步驟 2前往瀏覽器擴充功能頁面**
1. **開啟**瀏覽器的擴充功能管理頁面
2. **點擊**右上角的「設定與更多」按鈕(`...` 圖示)
3. **從下拉選單選取**「擴充功能」
✅ 一點歷史第一個瀏覽器名為「WorldWideWeb」由蒂姆·伯納斯-李爵士於 1990 年創建。
**步驟 3載入您的擴充功能**
- **新安裝**:選擇 `load unpacked` 並指定 `/dist` 資料夾
- **更新**:點選已安裝擴充功能旁的 `reload`
- **測試**:啟用「開發者模式」以存取額外除錯功能
![早期瀏覽器](../../../../translated_images/earlybrowsers.d984b711cdf3a42ddac919d46c4b5ca7232f68ccfbd81395e04e5a64c0015277.hk.jpg)
> 一些早期的瀏覽器,圖片來源 [Karen McGrane](https://www.slideshare.net/KMcGrane/week-4-ixd-history-personal-computing)
### 正式版擴充功能安裝
當用戶使用 URL統一資源定位符地址連接到互聯網時通常通過 `http``https` 地址使用超文本傳輸協議,瀏覽器會與網頁伺服器通信並獲取網頁。
> ✅ **備註**:以上開發指令專供您自行設計擴充功能測試。安裝已發佈擴充功能請至官方瀏覽器擴充功能商店,例如 [Microsoft Edge 加載程式商店](https://microsoftedge.microsoft.com/addons/Microsoft-Edge-Extensions-Home)
此時,瀏覽器的渲染引擎會將網頁顯示在用戶的設備上,可能是手機、桌面電腦或筆記本電腦。
**理解差異:**
- **開發安裝**允許您在開發階段測試未發佈擴充功能
- **商店安裝**提供經審核、持續更新的已發佈擴充功能
- **側載安裝**允許從官方商店以外安裝擴充功能(需開發者模式)
瀏覽器還具有緩存內容的功能這樣就不需要每次都從伺服器檢索內容。它們可以記錄用戶的瀏覽歷史存儲「cookies」小型數據片段用於保存用戶活動的信息等。
## 建立您的碳足跡擴充功能
需要記住的一個重要事實是,瀏覽器並不完全相同!每個瀏覽器都有其優勢和劣勢,專業的網頁開發人員需要了解如何讓網頁在不同瀏覽器中表現良好。這包括處理小型視窗(如手機屏幕)以及離線用戶的情況。
我們將建立一款顯示您所在地區能源使用碳足跡的瀏覽器擴充功能。這個專案展示了擴充功能開發的基本概念,同時為環境意識提供實用工具
一個非常有用的網站是 [caniuse.com](https://www.caniuse.com),你應該將其加入你喜歡使用的瀏覽器的書籤中。在構建網頁時,使用 caniuse 提供的支持技術列表可以幫助你更好地支持用戶。
此做法遵循約翰·杜威John Dewey教育理論中行之有效的「做中學」原則——結合技術技能與實際應用
✅ 如何知道哪些瀏覽器在你的網站用戶中最受歡迎?檢查你的分析數據——你可以在網頁開發過程中安裝各種分析工具包,它們會告訴你不同流行瀏覽器的使用情況。
### 專案需求
## 瀏覽器擴展
開始開發前,先準備必要資源與依賴:
為什麼要構建瀏覽器擴展?當你需要快速訪問經常重複的任務時,這是一個非常方便的工具。例如,如果你經常需要檢查網頁上的顏色,你可以安裝一個顏色選取器瀏覽器擴展。如果你記不住密碼,你可以使用密碼管理瀏覽器擴展。
**必須的 API 存取:**
- **[CO2 Signal API key](https://www.co2signal.com/)**:輸入電子郵件地址以取得免費 API 金鑰
- **[區域代碼](http://api.electricitymap.org/v3/zones)**:使用 [Electricity Map](https://www.electricitymap.org/map) 查詢您的區域代碼例如波士頓為「US-NEISO」
開發瀏覽器擴展也很有趣。它們通常只需處理有限的任務,並能很好地完成這些任務。
**開發工具:**
- **[Node.js 與 NPM](https://www.npmjs.com)**:用於安裝專案依賴的套件管理工具
- **[起始程式碼](../../../../5-browser-extension/start)**:下載 `start` 資料夾開始開發
✅ 你最喜歡的瀏覽器擴展是什麼?它們執行哪些任務?
**深入學習**:透過此 [完整 Learn 模組](https://docs.microsoft.com/learn/modules/create-nodejs-project-dependencies/?WT.mc_id=academic-77807-sagibbon) 增強您的套件管理技能
### 安裝擴展
### 理解專案架構
在開始構建之前,先了解一下構建和部署瀏覽器擴展的過程。雖然每個瀏覽器在管理這個過程時略有不同,但在 Chrome 和 Firefox 上的過程與 Edge 上的示例類似:
了解專案架構有助於有效規劃開發工作。就像亞歷山大圖書館設計易於檢索的知識庫,一個良好結構的程式碼庫讓開發更有效率
![Edge 瀏覽器截圖,顯示打開的 edge://extensions 頁面和設置菜單](../../../../translated_images/install-on-edge.d68781acaf0b3d3dada8b7507cde7a64bf74b7040d9818baaa9070668e819f90.hk.png)
```
project-root/
├── dist/ # Built extension files
│ ├── manifest.json # Extension configuration
│ ├── index.html # User interface markup
│ ├── background.js # Background script functionality
│ └── main.js # Compiled JavaScript bundle
├── src/ # Source development files
│ └── index.js # Your main JavaScript code
├── package.json # Project dependencies and scripts
└── webpack.config.js # Build configuration
```
> 注意:確保切換到開發者模式並允許來自其他商店的擴展。
**各檔案功能解析:**
- **`manifest.json`****定義**擴充功能元資料、權限與入口點
- **`index.html`****建立**使用者界面,使用者點擊擴充功能時顯示
- **`background.js`****處理**背景工作與瀏覽器事件監聽
- **`main.js`****包含**建置流程完成後的最終打包 JavaScript
- **`src/index.js`****編寫**主要開發代碼,最終編譯入 `main.js`
基本過程如下:
> 💡 **組織建議**:將您的 API 金鑰與區域代碼記錄在安全筆記,方便開發過程中參考。您將需要這些數值來測試擴充功能功能。
- 使用 `npm run build` 構建你的擴展
- 在瀏覽器中通過右上角的「設置及更多」按鈕(`...` 圖標)導航到擴展面板
- 如果是新安裝,選擇「加載未打包的擴展」來從構建文件夾(在我們的例子中是 `/dist`)上傳新的擴展
- 或者,如果是重新加載已安裝的擴展,點擊「重新加載」
**安全提醒**:切勿將 API 金鑰或敏感憑證提交至代碼版本庫。我們將在後續步驟示範如何安全管理。
✅ 這些指令適用於你自己構建的擴展;如果要安裝已發布到各瀏覽器擴展商店的擴展,應導航到這些 [商店](https://microsoftedge.microsoft.com/addons/Microsoft-Edge-Extensions-Home) 並安裝你選擇的擴展。
## 建立擴充功能介面
### 開始構建
接著我們打造使用者介面元件。擴充功能採用兩個畫面:設定畫面用於初次配置,結果畫面用於顯示資料。
你將構建一個顯示你所在地區碳足跡的瀏覽器擴展,展示你所在地區的能源使用情況及能源來源。該擴展將包含一個表單,用於收集 API 密鑰,以訪問 CO2 Signal 的 API。
這遵循自早期運算介面設計以來推行的漸進披露原則——以邏輯序列揭露資訊與選項,避免用戶被過多訊息淹沒
**你需要:**
### 擴充功能視圖概述
- [一個 API 密鑰](https://www.co2signal.com/);在此頁面輸入你的電子郵件,密鑰將發送給你
- 與 [Electricity Map](https://www.electricitymap.org/map) 對應的 [地區代碼](http://api.electricitymap.org/v3/zones)例如在波士頓我使用「US-NEISO」
- [起始代碼](../../../../5-browser-extension/start)。下載 `start` 文件夾;你將在此文件夾中完成代碼
- [NPM](https://www.npmjs.com) - NPM 是一個包管理工具;本地安裝它,並安裝 `package.json` 文件中列出的包以供網頁資產使用
**設定畫面** - 用戶首次配置:
![完成的擴充功能在瀏覽器中顯示含地區名稱及 API 金鑰輸入欄位的表單截圖。](../../../../translated_images/1.b6da8c1394b07491.hk.png)
✅ 在這個 [優秀的學習模組](https://docs.microsoft.com/learn/modules/create-nodejs-project-dependencies/?WT.mc_id=academic-77807-sagibbon) 中了解更多關於包管理的知識。
**結果畫面** - 顯示碳足跡資料:
![完成的擴充功能顯示 US-NEISO 地區碳使用量及化石燃料百分比數值的截圖。](../../../../translated_images/2.1dae52ff08042246.hk.png)
花點時間瀏覽代碼庫:
### 建構設定表單
dist
-|manifest.json默認設置在這裡
-|index.html前端 HTML 標記在這裡)
-|background.js背景 JS 在這裡)
-|main.js構建後的 JS
src
-|index.js你的 JS 代碼在這裡)
設定表單於初次使用時收集用戶配置資料。配置後資料將存於瀏覽器儲存供日後使用。
✅ 一旦你準備好 API 密鑰和地區代碼,將它們存儲在一個筆記中以備後用。
`/dist/index.html` 檔案中加入此表單結構:
### 為擴展構建 HTML
```html
<form class="form-data" autocomplete="on">
<div>
<h2>New? Add your Information</h2>
</div>
<div>
<label for="region">Region Name</label>
<input type="text" id="region" required class="region-name" />
</div>
<div>
<label for="api">Your API Key from tmrow</label>
<input type="text" id="api" required class="api-key" />
</div>
<button class="search-btn">Submit</button>
</form>
```
這個擴展有兩個視圖。一個用於收集 API 密鑰和地區代碼:
**此表單實現了以下功能:**
- **建立**語義正確的表單結構,包含適切標籤與輸入關聯
- **啟用**瀏覽器自動完成以提升用戶體驗
- **須填寫**兩欄位方可提交,透過 `required` 屬性實現
- **以描述性類名**組織輸入欄位,方便樣式設計與 JavaScript 操控
- **提供**清楚指引給首次設定擴充功能的用戶
![瀏覽器中打開的已完成擴展截圖,顯示帶有地區名稱和 API 密鑰輸入框的表單。](../../../../translated_images/1.b6da8c1394b07491afeb6b2a8e5aca73ebd3cf478e27bcc9aeabb187e722648e.hk.png)
### 建構結果展示區
另一個用於顯示地區的碳使用情況:
接下來,建立用於顯示碳足跡資料的結果區。將以下 HTML 加在表單下方
![已完成擴展的截圖,顯示 US-NEISO 地區的碳使用值和化石燃料百分比。](../../../../translated_images/2.1dae52ff0804224692cd648afbf2342955d7afe3b0101b617268130dfb427f55.hk.png)
```html
<div class="result">
<div class="loading">loading...</div>
<div class="errors"></div>
<div class="data"></div>
<div class="result-container">
<p><strong>Region: </strong><span class="my-region"></span></p>
<p><strong>Carbon Usage: </strong><span class="carbon-usage"></span></p>
<p><strong>Fossil Fuel Percentage: </strong><span class="fossil-fuel"></span></p>
</div>
<button class="clear-btn">Change region</button>
</div>
```
我們先從構建表單的 HTML 開始,並用 CSS 進行樣式設計。
**此結構提供了以下功能:**
- **`loading`**:在獲取 API 資料時顯示讀取訊息
- **`errors`**API 呼叫失敗或資料無效時顯示錯誤訊息
- **`data`**:用來持有原始資料,方便開發階段調試
- **`result-container`**:友善地呈現碳足跡資訊給使用者
- **`clear-btn`**:允許用戶變更地區,重新配置擴充功能
`/dist` 文件夾中,你將構建一個表單和一個結果區域。在 `index.html` 文件中,填充表單區域:
### 設定建置流程
```HTML
<form class="form-data" autocomplete="on">
<div>
<h2>New? Add your Information</h2>
</div>
<div>
<label for="region">Region Name</label>
<input type="text" id="region" required class="region-name" />
</div>
<div>
<label for="api">Your API Key from tmrow</label>
<input type="text" id="api" required class="api-key" />
</div>
<button class="search-btn">Submit</button>
</form>
```
這是保存信息並將其輸入本地存儲的表單。
接下來,創建結果區域;在最後的表單標籤下添加一些 div
```HTML
<div class="result">
<div class="loading">loading...</div>
<div class="errors"></div>
<div class="data"></div>
<div class="result-container">
<p><strong>Region: </strong><span class="my-region"></span></p>
<p><strong>Carbon Usage: </strong><span class="carbon-usage"></span></p>
<p><strong>Fossil Fuel Percentage: </strong><span class="fossil-fuel"></span></p>
</div>
<button class="clear-btn">Change region</button>
</div>
```
此時,你可以嘗試構建。確保安裝此擴展的包依賴項:
接著安裝專案依賴,並測試建置流程:
```
```bash
npm install
```
```
此命令將使用 npmNode 包管理器)為你的擴展構建過程安裝 webpack。Webpack 是一個處理代碼編譯的打包工具。你可以通過查看 `/dist/main.js` 來查看此過程的輸出——你會看到代碼已被打包。
**此安裝流程達成的工作:**
- **下載** Webpack 及 `package.json` 指定的其他開發依賴
- **設定**建置工具鏈以編譯現代 JavaScript
- **準備**開發環境用於擴充功能建置與測試
- **啟用**代碼打包、優化及跨瀏覽器相容功能
目前,該擴展應該可以構建,並且如果你將其部署到 Edge 作為擴展,你會看到一個整齊顯示的表單。
> 💡 **建置流程洞察**Webpack 將您的原始碼從 `/src/index.js` 打包進 `/dist/main.js`,優化代碼兼顧生產環境與瀏覽器相容性。
恭喜你,已經邁出了構建瀏覽器擴展的第一步。在後續課程中,你將使其更具功能性和實用性。
### 測試您的進度
---
此時,您可以開始測試您的擴充功能:
1. **執行** 建置指令以編譯你的程式碼
2. **載入** 開發者模式到你的瀏覽器中
3. **驗證** 表單是否正確顯示並專業美觀
4. **檢查** 所有表單元素是否適當對齊並可正常運作
**你已完成的事項:**
- **建立** 擴充功能的基礎 HTML 結構
- **創建** 配置與結果介面,皆使用正確的語義標記
- **設定** 使用業界標準工具的現代開發流程
- **準備** 加入互動式 JavaScript 功能的基礎
### 🔄 **教學檢視**
**擴充功能開發進度**:在繼續前確認你的理解:
- ✅ 你可以說明專案結構中各檔案的用途嗎?
- ✅ 你理解建置流程是如何轉換你的原始碼的嗎?
- ✅ 為什麼我們把設定和結果分成不同的 UI 區塊?
- ✅ 表單結構如何同時支援可用性與無障礙性?
**開發流程理解**:你現在應該能夠:
1. **修改** 擴充功能介面的 HTML 和 CSS
2. **執行** 建置指令編譯你的變更
3. **重新載入** 瀏覽器中的擴充功能以測試更新
4. **使用** 瀏覽器開發者工具除錯問題
你已完成瀏覽器擴充功能開發的第一階段。就像萊特兄弟在實現飛行前必須理解空氣動力學一樣,掌握這些基礎概念將幫助你在下一課中開發更複雜的互動功能。
## GitHub Copilot Agent 挑戰 🚀
使用 Agent 模式完成以下挑戰:
**說明:** 透過加入表單驗證與使用者回饋功能,增強瀏覽器擴充功能,改善輸入 API 金鑰與區域代碼時的用戶體驗。
**提示:** 撰寫 JavaScript 驗證函式,檢查 API 金鑰欄位是否至少包含 20 個字元,以及區域代碼是否遵循正確格式(例如 'US-NEISO')。利用邊框顏色變換(綠色表示有效,紅色表示無效)做出視覺回饋。並新增切換功能,用於顯示或隱藏 API 金鑰以提升安全性。
更多有關 [agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) 的資訊。
## 🚀 挑戰
瀏覽一個瀏覽器擴展商店並安裝一個擴展到你的瀏覽器。你可以用有趣的方式檢查它的文件。你發現了什麼?
瀏覽瀏覽器擴充功能商店並安裝一個擴充功能至你的瀏覽器。你可以用有趣的方式檢視其檔案。你發現了什麼?
## 課後測驗
## 課後測驗
[課後測驗](https://ff-quizzes.netlify.app/web/quiz/24)
[課後測驗](https://ff-quizzes.netlify.app/web/quiz/24)
## 回顧與自學
## 複習與自學
在本課中,你學到了一些關於網頁瀏覽器歷史的知識;利用這個機會,通過閱讀更多歷史資料來了解萬維網的發明者如何設想它的用途。一些有用的網站包括:
本課程中你略為了解了網頁瀏覽器的歷史;請利用這個機會,透過閱讀更多歷史資料,了解全球資訊網發明者如何設想它的用途。以下是一些有用的網站
[網頁瀏覽器的歷史](https://www.mozilla.org/firefox/browsers/browser-history/)
[網絡的歷史](https://webfoundation.org/about/vision/history-of-the-web/)
[蒂姆·伯納斯-李的訪談](https://www.theguardian.com/technology/2019/mar/12/tim-berners-lee-on-30-years-of-the-web-if-we-dream-a-little-we-can-get-the-web-we-want)
[網路的歷史](https://webfoundation.org/about/vision/history-of-the-web/)
[TIm Berners-Lee 專訪](https://www.theguardian.com/technology/2019/mar/12/tim-berners-lee-on-30-years-of-the-web-if-we-dream-a-little-we-can-get-the-web-we-want)
### ⚡ **你在接下來 5 分鐘內可以做的事**
- [ ] 開啟 Chrome/Edge 擴充功能頁面 (chrome://extensions),探索你已安裝的擴充功能
- [ ] 載入網頁時查看瀏覽器 DevTools 的 Network 分頁
- [ ] 嘗試查看網頁原始碼 (Ctrl+U) 以檢視 HTML 結構
- [ ] 檢查並修改任意網頁元素的 CSS
### 🎯 **你能在這一小時內完成的目標**
- [ ] 完成課後小測驗並理解瀏覽器基本概念
- [ ] 建立基本的 manifest.json 檔案用於瀏覽器擴充功能
- [ ] 製作一個簡單的「Hello World」擴充功能並顯示 popup
- [ ] 測試在開發者模式中載入你的擴充功能
- [ ] 探索目標瀏覽器的擴充功能文件
### 📅 **你的週長擴充功能旅程**
- [ ] 完成一個具有實用功能的瀏覽器擴充功能
- [ ] 學習內容腳本、背景腳本及 popup 互動
- [ ] 精通瀏覽器 API 如儲存、分頁與訊息傳遞
- [ ] 設計用戶友善的擴充功能介面
- [ ] 在不同網站和情境中測試擴充功能
- [ ] 將你的擴充功能發佈至擴充功能商店
### 🌟 **你的月長瀏覽器開發計畫**
- [ ] 開發多個解決不同用戶問題的擴充功能
- [ ] 學習進階瀏覽器 API 與安全最佳實踐
- [ ] 為開源瀏覽器擴充功能專案做出貢獻
- [ ] 精通跨瀏覽器相容性與漸進式增強技術
- [ ] 為他人打造擴充功能開發工具與範本
- [ ] 成為協助其他開發者的瀏覽器擴充功能專家
## 🎯 你的瀏覽器擴充功能專精時間表
```mermaid
timeline
title 瀏覽器擴充功能開發進展
section 基礎 (15分鐘)
瀏覽器理解: 核心架構
: 渲染流程
: 擴充功能整合點
section 設置 (20分鐘)
開發環境: 專案結構
: 建置工具配置
: 瀏覽器開發者模式
: 擴充功能載入流程
section 介面設計 (25分鐘)
使用者體驗: HTML 結構
: CSS 樣式
: 表單驗證
: 響應式設計
section 核心功能 (35分鐘)
JavaScript 整合: 事件處理
: API 互動
: 資料儲存
: 錯誤處理
section 瀏覽器 API (45分鐘)
平台整合: 權限系統
: 儲存 API
: 分頁管理
: 快捷選單
section 高級功能 (1週)
專業擴充: 背景腳本
: 內容腳本
: 跨瀏覽器相容性
: 性能優化
section 發佈 (2週)
發行: 商店提交
: 審核流程
: 使用者反饋
: 更新管理
section 專家級 (1個月)
擴充生態系統: 進階 API
: 安全最佳實踐
: 企業功能
: 框架整合
```
### 🛠️ 你的擴充功能開發工具組摘要
完成本課後,你已具備:
- **瀏覽器架構知識**:了解渲染引擎、安全模型與擴充功能整合
- **開發環境**:擁有包含 Webpack、NPM 及偵錯功能的現代工具鏈
- **UI/UX 基礎**:具有語義 HTML 結構及漸進揭露模式
- **安全意識**:掌握瀏覽器權限與安全開發實務
- **跨瀏覽器概念**:理解相容性考量與測試方法
- **API 整合**:具備處理外部資料來源的基礎
- **專業工作流程**:擁有業界標準的開發與測試流程
**實際應用範圍**:這些技能可直接應用於:
- **網頁開發**:單頁應用程式與漸進式網頁應用
- **桌面應用程式**Electron 和基於網頁的桌面軟體
- **行動開發**:混合式應用與基於網頁的行動解決方案
- **企業工具**:企業內部生產力應用與工作流程自動化
- **開源貢獻**:瀏覽器擴充功能專案與網路標準
**下一階段**:你已準備好加入互動功能、使用瀏覽器 API並打造能解決實際用戶問題的擴充功能
## 作業
[重新設計你的擴展](assignment.md)
[重新設計你的擴充功能](assignment.md)
---
**免責聲明**
此文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原始語言的文件作為權威來源。對於關鍵資訊,建議使用專業的人類翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋不承擔責任。
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件是使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯的。雖然我們力求準確,但請注意自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們不對因使用本翻譯而引起的任何誤解或誤譯承擔責任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,23 +1,146 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "e3c6f2a03c2336e60412612d870af547",
"translation_date": "2025-08-23T23:44:52+00:00",
"original_hash": "b6897c02603d0045dd6d8256e8714baa",
"translation_date": "2026-01-06T12:46:05+00:00",
"source_file": "5-browser-extension/1-about-browsers/assignment.md",
"language_code": "hk"
}
-->
# 重新設計你的擴展
# Assignment: 重新設計你的瀏覽器擴充功能風格
## 指引
## 概覽
這個擴展的代碼庫已經包含了樣式,但你不一定要使用它們;通過編輯其 CSS 文件來重新設計,讓你的擴展更具個人特色。
現在你已經建立了碳足跡瀏覽器擴充功能的 HTML 結構,是時候讓它變得更加美觀和易用。出色的設計能提升使用者體驗,使你的擴充功能顯得更專業、更吸引人。
你的擴充功能已附帶基本的 CSS 樣式,但此作業挑戰你創造獨特的視覺識別,反映你的個人風格,同時維持優良的使用性。
## 指示
### 第一部分:分析現有設計
在進行更改前,檢視現有的 CSS 結構:
1. **定位** 擴充功能專案中的 CSS 檔案
2. **檢閱** 目前的樣式方法與配色方案
3. **辨識** 版面、排版和視覺層次結構可改進之處
4. **考量** 設計如何支持使用者目標(輕鬆完成表單與清晰資料顯示)
### 第二部分:設計你的自訂風格
建立一個統一的視覺設計,其中包含:
**配色方案:**
- 選擇反映環保主題的主色調
- 確保足夠的對比度以符合可及性(可使用 WebAIM 的對比度檢查工具)
- 考慮不同瀏覽器主題下顏色的呈現效果
**排版:**
- 選擇在擴充功能小尺寸視窗下仍易讀的字型
- 建立明確的層次,設定合適的字體大小與字重
- 確保文字在明暗兩種瀏覽器主題中皆清晰可讀
**版面與間距:**
- 改善表單元素與資料呈現的視覺組織
- 加入適當內距與外距增進可讀性
- 考慮針對不同螢幕尺寸的響應式設計原則
### 第三部分:實作你的設計
修改 CSS 檔以實現你的設計:
```css
/* Example starting points for customization */
.form-data {
/* Style the configuration form */
background: /* your choice */;
padding: /* your spacing */;
border-radius: /* your preference */;
}
.result-container {
/* Style the data display area */
background: /* complementary color */;
border: /* your border style */;
margin: /* your spacing */;
}
/* Add your custom styles here */
```
**主要樣式區域:**
- **表單元素**:輸入欄位、標籤與送出按鈕
- **結果顯示**:資料容器、文字樣式與載入狀態
- **互動元素**:滑鼠懸停效果、按鈕狀態與過渡動畫
- **整體版面**:容器間距、背景色彩與視覺層次
### 第四部分:測試與調整
1. 使用 `npm run build` **建置**你的擴充功能
2. **載入**更新後的擴充功能進入瀏覽器
3. **測試**所有視覺狀態(表單輸入、載入中、結果顯示、錯誤)
4. 利用瀏覽器開發工具 **驗證**可及性
5. 根據實際使用狀況 **調整**樣式
## 創意挑戰
### 基礎等級
- 更新色彩與字體以打造統一主題
- 改善整體介面的間距與對齊
- 為互動元素加入細膩的懸停效果
### 中階等級
- 設計自訂圖示或圖像
- 實作不同狀態間的平滑過渡動畫
- 創建專屬 API 呼叫的載入動畫
### 高階等級
- 設計多種主題選項(亮色/暗色/高對比)
- 實作適應不同瀏覽器視窗尺寸的響應式設計
- 新增提升使用體驗的微互動效果
## 提交指南
請包含以下項目:
- **修改後的 CSS 檔案**,呈現你的自訂風格
- **截圖**,展示擴充功能在不同狀態(表單、載入、結果)的樣貌
- **簡要說明**2-3 句),解釋你的設計選擇及其如何提升使用體驗
## 評分標準
| 標準 | 優秀 | 合格 | 需要改進 |
| -------- | -------------------------------------------- | --------------------- | ----------------- |
| | 提交的代碼包含功能完善的新樣式 | 樣式不完整 | 樣式有問題 |
| 評分項目 | 優異 (4) | 尚可 (3) | 發展中 (2) | 初學 (1) |
|----------|----------|----------|------------|----------|
| **視覺設計** | 創意且統一的設計提升可用性,展現強烈設計原則 | 良好的設計選擇,風格一致且視覺層次明確 | 基本設計改進,但存在一致性問題 | 樣式變動極少或設計不一致 |
| **功能性** | 全部樣式在不同狀態與瀏覽器環境完美運作 | 樣式良好,偶有邊緣情況小問題 | 大部分樣式運作正常,但有部分顯示問題 | 樣式問題嚴重,影響可用性 |
| **程式碼品質** | 乾淨有序的 CSS類別命名意義清楚選擇器高效 | 良好 CSS 結構,恰當使用選擇器與屬性 | 可接受的 CSS但組織稍嫌混亂 | CSS 結構差或樣式過於複雜 |
| **可及性** | 優秀的色彩對比度、易讀字體且考慮到有障礙的使用者 | 良好的可及性實踐,少數需改進處 | 基本考量可及性,但存在問題 | 對可及性要求關注不足 |
## 成功秘訣
> 💡 **設計提示**:從細微變化開始,逐步累積更明顯的樣式。排版與間距的小調整往往能大幅提升質感。
**建議遵循的最佳做法:**
- **測試**你的擴充功能於明色與暗色瀏覽器主題中
- **使用**相對單位em、rem以提升可擴展性
- **維持**利用 CSS 自定義屬性保持間距一致
- **考慮**不同視覺需求使用者的觀感
- **驗證**你的 CSS 以確保符合語法規範
> ⚠️ **常見錯誤**:不要為了美觀犧牲可用性。你的擴充功能應兼具美觀與實用。
**切記:**
- **保持**重要資訊可輕鬆閱讀
- **確保**按鈕和互動元素易於點擊
- **維持**操作時明確的視覺回饋
- **用真實資料測試**設計,而非僅用佔位文字
祝你打造出既實用又賞心悅目的瀏覽器擴充功能,成功!
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。
本文件乃使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 所翻譯。雖然我們致力於確保準確性,但請注意,機器翻譯可能存在錯誤或不準確之處。原始文件以其母語版本為準。對於重要資訊,建議採用專業人工翻譯。本公司對因使用本翻譯而引致的任何誤解或誤譯概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,37 +1,114 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "a7587943d38d095de8613e1b508609f5",
"translation_date": "2025-08-29T15:01:41+00:00",
"original_hash": "2b6203a48c48d8234e0948353b47d84e",
"translation_date": "2026-01-06T12:39:22+00:00",
"source_file": "5-browser-extension/2-forms-browsers-local-storage/README.md",
"language_code": "hk"
}
-->
# 瀏覽器擴展項目第二部分:調用 API使用本地存儲
## 課前測驗
[課前測驗](https://ff-quizzes.netlify.app/web/quiz/25)
### 簡介
在這節課中,你將通過提交瀏覽器擴展的表單來調用 API並在瀏覽器擴展中顯示結果。此外你還將學習如何將數據存儲在瀏覽器的本地存儲中以供未來參考和使用。
✅ 按照適當文件中的編號段落,了解應該在哪裡放置你的代碼。
### 設置擴展中需要操作的元素:
到目前為止,你已經為瀏覽器擴展構建了表單和結果 `<div>` 的 HTML。接下來你需要在 `/src/index.js` 文件中逐步構建你的擴展。參考[上一課](../1-about-browsers/README.md)了解如何設置項目和構建過程。
在你的 `index.js` 文件中,首先創建一些 `const` 變量來保存與各個字段相關的值:
```JavaScript
// form fields
# 瀏覽器擴充功能專案第二部分:呼叫 API使用本地儲存
```mermaid
journey
title 你的 API 集成與儲存旅程
section 基礎
設定 DOM 參考: 3: Student
新增事件監聽器: 4: Student
處理表單提交: 4: Student
section 資料管理
實作本地儲存: 4: Student
建立 API 呼叫: 5: Student
處理非同步操作: 5: Student
section 使用者體驗
新增錯誤處理: 5: Student
建立載入狀態: 4: Student
精修互動: 5: Student
```
## 課前小測驗
[課前小測驗](https://ff-quizzes.netlify.app/web/quiz/25)
## 介紹
還記得你開始製作的瀏覽器擴充功能嗎?現在你有一個看起來不錯的表單,但它本質上是靜態的。今天我們將讓它活起來,連接真實資料並賦予它記憶。
想想阿波羅任務控制電腦 —— 它們不只是顯示固定資訊。它們不斷和太空船通訊,更新遙測資料,並記錄重要的任務參數。這正是我們今天要打造的動態行為。你的擴充功能將連接網路,取得真實的環境資料,並記錄你下次使用的設定。
API 整合乍聽很複雜,但實際上就是教你的程式怎麼跟其他服務溝通。無論你是抓取天氣資料、社群媒體資訊,還是像今天一樣取得碳足跡相關資訊,都是建立這些數位連結。我們也會探討瀏覽器如何持久保存資訊 —— 就像圖書館用卡片目錄記錄書本位置一樣。
上完本課後,你會有一個可抓取真實資料、儲存使用者偏好,並提供流暢操作體驗的瀏覽器擴充功能。開始吧!
```mermaid
mindmap
root((動態擴充))
DOM Manipulation
Element Selection
Event Handling
State Management
UI Updates
Local Storage
Data Persistence
Key-Value Pairs
Session Management
User Preferences
API Integration
HTTP Requests
Authentication
Data Parsing
Error Handling
Async Programming
Promises
Async/Await
Error Catching
Non-blocking Code
User Experience
Loading States
Error Messages
Smooth Transitions
Data Validation
```
✅ 遵循對應檔案中的編號段落,知道在哪裡放程式碼
## 建立要操作的擴充功能元素
在你的 JavaScript 操作介面前,需要先取得特定 HTML 元素的參考。這就像望遠鏡需要對準特定星體——伽利略在觀察木星衛星前,得先找到並對準木星。
在你的 `index.js` 檔案中,我們會建立 `const` 變數,捕捉每個重要表單元素的參考。這很像科學家標記他們實驗設備——不用每次翻找整個實驗室,就能直接存取需要的東西。
```mermaid
flowchart LR
A[JavaScript 程式碼] --> B[document.querySelector]
B --> C[CSS 選擇器]
C --> D[HTML 元素]
D --> E[".form-data"]
D --> F[".region-name"]
D --> G[".api-key"]
D --> H[".loading"]
D --> I[".errors"]
D --> J[".result-container"]
E --> K[表單元素]
F --> L[輸入欄位]
G --> M[輸入欄位]
H --> N[介面元素]
I --> O[介面元素]
J --> P[介面元素]
style A fill:#e1f5fe
style D fill:#e8f5e8
style K fill:#fff3e0
style L fill:#fff3e0
style M fill:#fff3e0
```
```javascript
// 表單欄位
const form = document.querySelector('.form-data');
const region = document.querySelector('.region-name');
const apiKey = document.querySelector('.api-key');
// results
// 結果
const errors = document.querySelector('.errors');
const loading = document.querySelector('.loading');
const results = document.querySelector('.result-container');
@ -41,200 +118,546 @@ const myregion = document.querySelector('.my-region');
const clearBtn = document.querySelector('.clear-btn');
```
所有這些字段都是通過它們的 CSS class 引用的,這些 class 是你在上一課的 HTML 中設置的。
### 添加事件監聽器
接下來,為表單和重置按鈕添加事件監聽器,當用戶提交表單或點擊重置按鈕時,會觸發某些操作,並在文件底部添加初始化應用的調用:
```JavaScript
**這段程式碼做了什麼:**
- **捕捉** 用 `document.querySelector()` 搭配 CSS 類別選擇器取得表單元素
- **建立** 區域名稱和 API 金鑰輸入欄位的參考
- **連接** 顯示碳排放數據結果的元素
- **設定** 加載指示器和錯誤訊息等 UI 元素的存取
- **把** 每個元素參考存入 `const` 變數,方便在程式中重複使用
## 新增事件監聽器
現在我們要讓擴充功能響應使用者操作。事件監聽器是程式監控使用者互動的方式。就像早期電話交換手——監聽來電並在有人想連線時接通正確電路。
```mermaid
sequenceDiagram
participant User
participant Form
participant JavaScript
participant API
participant Storage
User->>Form: 填寫地區/API 金鑰
User->>Form: 點擊提交
Form->>JavaScript: 觸發提交事件
JavaScript->>JavaScript: handleSubmit(e)
JavaScript->>Storage: 保存用戶偏好
JavaScript->>API: 獲取碳數據
API->>JavaScript: 返回數據
JavaScript->>Form: 以結果更新介面
User->>Form: 點擊清除按鈕
Form->>JavaScript: 觸發點擊事件
JavaScript->>Storage: 清除已保存的數據
JavaScript->>Form: 重置至初始狀態
```
```javascript
form.addEventListener('submit', (e) => handleSubmit(e));
clearBtn.addEventListener('click', (e) => reset(e));
init();
```
✅ 注意這裡使用的簡寫方式來監聽提交或點擊事件,以及事件如何被傳遞到 handleSubmit 或 reset 函數。你能否用更長的格式寫出這個簡寫的等效代碼?你更喜歡哪種方式?
**理解以下概念:**
- **附加** submit 監聽器到表單,當使用者按 Enter 或點擊送出時觸發
- **連接** 點擊監聽器給清除按鈕,用於重置表單
- **把** 事件物件 `(e)` 傳給處理函式以做額外控制
- **立即** 呼叫 `init()` 函式,設定擴充功能的初始狀態
✅ 注意這裡使用的箭頭函式語法shorthand arrow function。這是現代 JavaScript 的寫法,比傳統函式表達式更簡潔,但兩者都運作正常!
### 🔄 **教學檢核點**
**事件處理理解**:開始初始化前,請確認你能:
- ✅ 解釋 `addEventListener` 如何將使用者行為與 JavaScript 函式連結
- ✅ 理解為何將事件物件 `(e)` 傳給處理函式
- ✅ 辨識 `submit``click` 事件的差異
- ✅ 描述 `init()` 函式何時執行以及原因
### 構建 init() 函數和 reset() 函數:
**小自測**:如果忘記 `e.preventDefault()` 在表單送出時會發生什麼?
*答案:頁面會重新載入,導致所有 JavaScript 狀態消失並中斷使用者體驗*
現在你需要構建初始化擴展的函數,這個函數叫做 init()
## 編寫初始化和重置功能
```JavaScript
讓我們建立擴充功能的初始化邏輯。`init()` 函式就像船隻的導航系統檢查儀器一樣——判斷當前狀態並相應調整介面。它會檢查是否首次使用你的擴充功能,並載入之前的設定。
`reset()` 函式則提供使用者重新開始的機會——就像科學家在不同實驗間重置儀器,確保資料乾淨。
```javascript
function init() {
//if anything is in localStorage, pick it up
// 檢查用戶是否之前已儲存 API 認證資料
const storedApiKey = localStorage.getItem('apiKey');
const storedRegion = localStorage.getItem('regionName');
//set icon to be generic green
//todo
// 將擴充功能圖示設為通用綠色(未來教學的佔位符)
// 待辦事項:在下一課中實作圖示更新
if (storedApiKey === null || storedRegion === null) {
//if we don't have the keys, show the form
// 首次使用者:顯示設定表單
form.style.display = 'block';
results.style.display = 'none';
loading.style.display = 'none';
clearBtn.style.display = 'none';
errors.textContent = '';
} else {
//if we have saved keys/regions in localStorage, show results when they load
displayCarbonUsage(storedApiKey, storedRegion);
// 回歸使用者:自動載入其已儲存的資料
displayCarbonUsage(storedApiKey, storedRegion);
results.style.display = 'none';
form.style.display = 'none';
clearBtn.style.display = 'block';
}
};
}
function reset(e) {
e.preventDefault();
//clear local storage for region only
// 清除已儲存的地區設定以允許用戶選擇新位置
localStorage.removeItem('regionName');
// 重新啟動初始化過程
init();
}
```
在這個函數中,有一些有趣的邏輯。閱讀代碼後,你能看出發生了什麼嗎?
- 設置兩個 `const` 來檢查用戶是否在本地存儲中保存了 APIKey 和地區代碼。
- 如果其中任何一個為 null通過將表單的樣式設置為 'block' 來顯示表單。
- 隱藏結果、加載和 clearBtn並將任何錯誤文本設置為空字符串。
- 如果存在 key 和地區,啟動一個例程來:
- 調用 API 獲取碳使用數據。
- 隱藏結果區域。
- 隱藏表單。
- 顯示重置按鈕。
在繼續之前,了解瀏覽器中一個非常重要的概念:[LocalStorage](https://developer.mozilla.org/docs/Web/API/Window/localStorage)。LocalStorage 是一種在瀏覽器中以 `key-value` 對形式存儲字符串的有用方式。這種網頁存儲可以通過 JavaScript 操作來管理瀏覽器中的數據。LocalStorage 不會過期,而另一種網頁存儲 SessionStorage 則會在瀏覽器關閉時清除。這些存儲類型在使用上各有利弊。
> 注意 - 你的瀏覽器擴展有自己的本地存儲;主瀏覽器窗口是不同的實例,行為獨立。
你可以將 APIKey 設置為一個字符串值,例如,你可以通過 "檢查" 網頁(你可以右鍵點擊瀏覽器進行檢查)並進入 Applications 標籤查看存儲來看到它在 Edge 上的設置。
![本地存儲面板](../../../../translated_images/localstorage.472f8147b6a3f8d141d9551c95a2da610ac9a3c6a73d4a1c224081c98bae09d9.hk.png)
**解析這裡發生了什麼:**
- **從** 瀏覽器本地儲存取回儲存的 API 金鑰與區域
- **判斷** 這是初次使用者(無儲存資料)還是回訪使用者
- **顯示** 新用戶的設定表單並隱藏其他元素
- **自動** 載入回訪用戶的資料並顯示重置選項
- **根據** 可用資料管理使用者介面狀態
**關於本地儲存的核心概念:**
- **資料持久**保存於瀏覽器會話之間(不同於 session storage
- **以** 鍵值對方式利用 `getItem()``setItem()` 儲存資料
- **當** 指定鍵無資料時回傳 `null`
- **提供** 簡單方式記住使用者偏好和設定
> 💡 **理解瀏覽器儲存**[LocalStorage](https://developer.mozilla.org/docs/Web/API/Window/localStorage) 就像給你的擴充功能持久記憶體。想像古代亞歷山大圖書館如何保存卷軸──資料即使學者離開再回來仍可取得。
>
> **主要特點:**
> - **資料持續存在**,即使關閉瀏覽器
> - **經得起** 電腦重新啟動和瀏覽器崩潰
> - **提供** 相當大的存儲空間以保存使用者偏好
> - **即時存取**,無需等網路響應
> **重要提醒**:你的瀏覽器擴充功能有獨立且隔離的本地儲存空間,不同於一般網站。這樣可提高安全性並防止網站間衝突。
你可以開啟瀏覽器開發者工具F12切換到 **Application** 分頁,展開 **Local Storage** 區域來查看儲存資料。
```mermaid
stateDiagram-v2
[*] --> CheckStorage: 擴充功能啟動
CheckStorage --> FirstTime: 無儲存資料
CheckStorage --> Returning: 發現資料
FirstTime --> ShowForm: 顯示設定表單
ShowForm --> UserInput: 使用者輸入資料
UserInput --> SaveData: 儲存至 localStorage
SaveData --> FetchAPI: 取得碳排放資料
Returning --> LoadData: 從 localStorage 讀取
LoadData --> FetchAPI: 取得碳排放資料
FetchAPI --> ShowResults: 顯示資料
ShowResults --> UserAction: 使用者互動
UserAction --> Reset: 點擊清除按鈕
UserAction --> ShowResults: 查看資料
Reset --> ClearStorage: 移除已儲存資料
ClearStorage --> FirstTime: 返回設定
```
![Local storage pane](../../../../translated_images/localstorage.472f8147b6a3f8d1.hk.png)
✅ 思考哪些情況下你不應該將某些數據存儲在 LocalStorage 中。一般來說,將 API Keys 放在 LocalStorage 中是個壞主意!你能看出原因嗎?在我們的案例中,由於我們的應用僅用於學習且不會部署到應用商店,我們將使用這種方法。
> ⚠️ **安全注意**:在正式應用中,將 API 金鑰儲存在 LocalStorage 有安全風險,因為任何 JavaScript 都能讀取。學習用途可以暫時使用,但實際應用應採用安全的伺服器端儲存敏感憑證
注意,你使用 Web API 操作 LocalStorage可以通過 `getItem()`、`setItem()` 或 `removeItem()`。它在各瀏覽器中廣泛支持。
## 處理表單提交事件
在構建 `displayCarbonUsage()` 函數(在 `init()` 中調用)之前,讓我們先構建處理初始表單提交的功能。
現在我們來處理使用者提交表單時的行為。瀏覽器預設表單送出會重整頁面,我們將攔截這個行為,創造更順暢的體驗
### 處理表單提交
這類似任務控制中心管理太空船通訊——不會為每次通訊重置整個系統,而是維持持續運作並同步處理新資訊。
創建一個名為 `handleSubmit` 的函數,接受一個事件參數 `(e)`。阻止事件傳播(在這種情況下,我們希望阻止瀏覽器刷新),並調用一個新函數 `setUpUser`,傳入參數 `apiKey.value``region.value`。通過這種方式,你使用了通過初始表單填充的適當字段中獲取的兩個值。
建立一個函式來捕捉表單提交事件,並提取使用者輸入:
```JavaScript
```javascript
function handleSubmit(e) {
e.preventDefault();
setUpUser(apiKey.value, region.value);
}
```
✅ 回憶一下 - 你在上一課設置的 HTML 有兩個輸入字段,其 `values` 是通過你在文件頂部設置的 `const` 捕獲的,並且它們都是 `required`,因此瀏覽器會阻止用戶輸入空值。
**上述程式碼中,我們:**
- **阻止** 預設的表單送出重整行為
- **擷取** API 金鑰與區域兩個輸入欄位的使用者值
- **傳遞** 表單資料給 `setUpUser()` 函式處理
- **保持** 單頁應用行為,不重新載入頁面
✅ 請記得你的 HTML 表單欄位包含 `required` 屬性,所以瀏覽器會自動驗證使用者必填 API 金鑰和區域,確保此函式執行時輸入值有效。
### 設置用戶
## 設定使用者偏好
接下來是 `setUpUser` 函數,這裡你設置了本地存儲的 apiKey 和 regionName 值。添加一個新函數:
`setUpUser` 函式負責儲存使用者憑證並啟動第一次 API 呼叫。這為從設定跳轉到顯示結果提供流暢體驗。
```JavaScript
```javascript
function setUpUser(apiKey, regionName) {
// 儲存用戶憑證以供未來使用
localStorage.setItem('apiKey', apiKey);
localStorage.setItem('regionName', regionName);
// 更新用戶介面以顯示加載狀態
loading.style.display = 'block';
errors.textContent = '';
clearBtn.style.display = 'block';
//make initial call
// 使用戶憑證獲取碳排放使用數據
displayCarbonUsage(apiKey, regionName);
}
```
此函數設置了一條加載消息,以在調用 API 時顯示。到目前為止,你已經到達了創建這個瀏覽器擴展最重要的函數!
### 顯示碳使用數據
最後,是時候查詢 API 了!
在進一步之前,我們應該討論 API。API即[應用程序編程接口](https://www.webopedia.com/TERM/A/API.html),是網頁開發者工具箱中的關鍵元素。它們為程序之間的交互和接口提供了標準方式。例如,如果你正在構建一個需要查詢數據庫的網站,可能有人已經為你創建了一個 API 可以使用。雖然 API 有很多類型,其中一種最流行的是 [REST API](https://www.smashingmagazine.com/2018/01/understanding-using-rest-api/)。
✅ "REST" 代表 "表現性狀態轉移",其特點是使用不同配置的 URL 來獲取數據。研究一下開發者可用的各種 API 類型。哪種格式最吸引你?
關於這個函數有一些重要的注意事項。首先,注意 [`async` 關鍵字](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/async_function)。將函數設計為異步運行意味著它會等待某個操作(例如數據返回)完成後再繼續。
這裡有一段關於 `async` 的快速視頻:
[![管理 Promise 的 Async 和 Await](https://img.youtube.com/vi/YwmlRkrxvkk/0.jpg)](https://youtube.com/watch?v=YwmlRkrxvkk "管理 Promise 的 Async 和 Await")
> 🎥 點擊上方圖片觀看關於 async/await 的視頻。
創建一個新函數來查詢 C02Signal API
```JavaScript
import axios from '../node_modules/axios';
**步驟解析:**
- **把** API 金鑰與區域名稱保存到本地儲存,未來使用
- **顯示** 加載指示器,告知使用者資料正在抓取中
- **清空** 先前可能出現的錯誤訊息
- **顯示** 清除按鈕,讓使用者後續可以重置設定
- **啟動** API 呼叫取得真實碳排放數據
這函式一次管理資料持久和介面更新,營造無縫使用者體驗。
## 顯示碳排放使用資料
現在我們將擴充功能連接到外部資料來源,透過 API 取得網路上即時資訊。
**認識 API**
[API](https://www.webopedia.com/TERM/A/API.html) 是不同應用程式間溝通的橋樑。可以把它想像成 19 世紀連接都市的電報系統——操作員發送請求到遠方站點,並回應所需資訊。每次你查看社群媒體、問語音助理問題、或用外送 App背後都是 API 在交換資料。
```mermaid
flowchart TD
A[你的擴充功能] --> B[HTTP 請求]
B --> C[CO2 信號 API]
C --> D{有效的請求?}
D -->|是| E[查詢資料庫]
D -->|否| F[返回錯誤]
E --> G[碳排放資料]
G --> H[JSON 回應]
H --> I[你的擴充功能]
F --> I
I --> J[更新使用者介面]
subgraph "API 請求"
K[標頭auth-token]
L[參數countryCode]
M[方法GET]
end
subgraph "API 回應"
N[碳密度]
O[化石燃料百分比]
P[時間戳記]
end
style C fill:#e8f5e8
style G fill:#fff3e0
style I fill:#e1f5fe
```
**關於 REST API 的重要概念:**
- **REST** 代表「表述性狀態轉移」
- **使用** 標準 HTTP 方法GET、POST、PUT、DELETE操作資料
- **回傳** 通常是可預測的 JSON 格式
- **提供** 基於 URL 的一致端點,處理不同請求
✅ 我們使用的 [CO2 Signal API](https://www.co2signal.com/) 提供全球電網實時碳強度數據。幫助使用者了解用電的環境影響!
> 💡 **理解非同步 JavaScript**[`async` 關鍵字](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/async_function) 讓你的程式同時處理多個操作。當你向伺服器請求資料時,不會讓整個擴充功能凍結——這就像航管不會為等待某飛機而停止所有作業。
>
> **優點:**
> - **保持** 擴充功能在資料讀取期間仍反應敏捷
> - **允許** 其他程式碼在網路請求時繼續執行
> - **提升** 程式可讀性,比傳統回呼更直觀
> - **能夠** 優雅處理網路錯誤
這裡有段關於 `async` 的短影片:
[![Async and Await for managing promises](https://img.youtube.com/vi/YwmlRkrxvkk/0.jpg)](https://youtube.com/watch?v=YwmlRkrxvkk "Async and Await for managing promises")
> 🎥 點擊上圖觀看 async/await 教學影片。
### 🔄 **教學檢核點**
**非同步程式設計理解**:進入 API 函式前,請確認你理解:
- ✅ 為什麼用 `async/await` 而非阻塞整個擴充
- ✅ 如何用 `try/catch` 優雅處理網路錯誤
- ✅ 同步與非同步作業的差異
- ✅ 為何 API 呼叫可能失敗及錯誤處理方法
**現實生活類比**:想想日常非同步情境:
- **訂餐**:你不會在廚房等餐,拿到收據去做其他事
- **寄郵件**:郵件發送時 app 不會凍結,可以繼續寫新郵件
- **載入網頁**:圖片漸進式載入,你已可閱讀文字
**API 認證流程**
```mermaid
sequenceDiagram
participant Ext as Extension
participant API as CO2 Signal API
participant DB as 資料庫
Ext->>API: 使用認證令牌請求
API->>API: 驗證令牌
API->>DB: 查詢碳排放數據
DB->>API: 回傳數據
API->>Ext: JSON 回應
Ext->>Ext: 更新介面
```
建立抓取並顯示碳排放數據的函式:
```javascript
// 現代 fetch API 方法(無需外部依賴)
async function displayCarbonUsage(apiKey, region) {
try {
await axios
.get('https://api.co2signal.com/v1/latest', {
params: {
countryCode: region,
},
headers: {
'auth-token': apiKey,
},
})
.then((response) => {
let CO2 = Math.floor(response.data.data.carbonIntensity);
//calculateColor(CO2);
loading.style.display = 'none';
form.style.display = 'none';
myregion.textContent = region;
usage.textContent =
Math.round(response.data.data.carbonIntensity) + ' grams (grams C02 emitted per kilowatt hour)';
fossilfuel.textContent =
response.data.data.fossilFuelPercentage.toFixed(2) +
'% (percentage of fossil fuels used to generate electricity)';
results.style.display = 'block';
});
// 從 CO2 Signal API 獲取碳強度數據
const response = await fetch('https://api.co2signal.com/v1/latest', {
method: 'GET',
headers: {
'auth-token': apiKey,
'Content-Type': 'application/json'
},
// 為特定地區添加查詢參數
...new URLSearchParams({ countryCode: region }) && {
url: `https://api.co2signal.com/v1/latest?countryCode=${region}`
}
});
// 檢查 API 請求是否成功
if (!response.ok) {
throw new Error(`API request failed: ${response.status}`);
}
const data = await response.json();
const carbonData = data.data;
// 計算四捨五入的碳強度值
const carbonIntensity = Math.round(carbonData.carbonIntensity);
// 用獲取的數據更新使用者界面
loading.style.display = 'none';
form.style.display = 'none';
myregion.textContent = region.toUpperCase();
usage.textContent = `${carbonIntensity} grams (grams CO₂ emitted per kilowatt hour)`;
fossilfuel.textContent = `${carbonData.fossilFuelPercentage.toFixed(2)}% (percentage of fossil fuels used to generate electricity)`;
results.style.display = 'block';
// TODO: calculateColor(carbonIntensity) - 於下一課實作
} catch (error) {
console.log(error);
console.error('Error fetching carbon data:', error);
// 顯示易明的錯誤訊息
loading.style.display = 'none';
results.style.display = 'none';
errors.textContent = 'Sorry, we have no data for the region you have requested.';
errors.textContent = 'Sorry, we couldn\'t fetch data for that region. Please check your API key and region code.';
}
}
```
這是一個大的函數。這裡發生了什麼?
**解析程式做了什麼:**
- **使用** 現代 `fetch()` API摒棄外部函式庫如 Axios程式簡潔無依賴
- **實作** 判斷 `response.ok` 進行 API 呼叫錯誤偵測
- **用** `async/await` 處理非同步,提高程式流程易讀性
- **用** `auth-token` 標頭認證 CO2 Signal API
- **解析** JSON 資料並擷取碳強度資訊
- **更新** 多個介面元素,展示格式化後的環境資訊
- **提供** 當 API 呼叫失敗時的友善錯誤訊息
**示範重要現代 JavaScript 技術:**
- 使用 `${}` 字串模板字面量清晰格式化字串
- 用 try/catch 錯誤處理確保應用穩健
- 採用 async/await 模式優雅管理網路請求
- 利用物件解構賦值摘取 API 回傳資料
- 透過函式鏈式呼叫更新 DOM 多重元素
✅ 這支函式涵蓋多項關鍵網頁開發技能:與外部伺服器通訊、身份驗證、資料處理、介面更新及錯誤管理。專業開發者的基本功。
```mermaid
flowchart TD
A[開始 API 呼叫] --> B[取得請求]
B --> C{網絡成功?}
C -->|否| D[網絡錯誤]
C -->|是| E{回應正常?}
E -->|否| F[API 錯誤]
E -->|是| G[解析 JSON]
G --> H{資料有效?}
H -->|否| I[資料錯誤]
H -->|是| J[更新介面]
D --> K[顯示錯誤訊息]
F --> K
I --> K
J --> L[隱藏載入中]
K --> L
style A fill:#e1f5fe
style J fill:#e8f5e8
style K fill:#ffebee
style L fill:#f3e5f5
```
### 🔄 **教學檢核點**
**完整流程理解**:確認你對整體流程掌握:
- ✅ 為何 DOM 參考能讓 JavaScript 控制介面
- ✅ 為何本地儲存讓瀏覽器會話間資料持續存在
- ✅ async/await 如何讓 API 呼叫不中斷擴充功能
- ✅ API 呼叫失敗時會發生什麼及錯誤如何處理
- ✅ 使用者體驗中為何包含載入狀態和錯誤提示
🎉 **你已達成**:你建立了一支瀏覽器擴充功能,能:
- **連結** 網路,擷取真實環境數據
- **持久** 儲存使用者設定,跨會話保存
- **優雅** 處理錯誤而非崩潰
- **提供** 流暢且專業的用戶體驗
執行 `npm run build` 然後在瀏覽器重新載入擴充功能以測試。你現在擁有一個功能完整的碳足跡追蹤器。下一課會加上動態圖示功能讓擴充功能更完整。
- 遵循最佳實踐,你使用 `async` 關鍵字使這個函數異步運行。該函數包含一個 `try/catch` 塊,因為當 API 返回數據時,它會返回一個 Promise。由於你無法控制 API 的響應速度(它可能根本不響應!),你需要通過異步調用來處理這種不確定性。
- 你正在查詢 co2signal API 以獲取地區數據,使用你的 API Key。要使用該 Key你需要在 header 參數中使用一種身份驗證方式。
- 一旦 API 響應,你將其響應數據的各個元素分配給你設置的屏幕部分以顯示這些數據。
- 如果出現錯誤或沒有結果,你會顯示一條錯誤消息。
---
✅ 使用異步編程模式是工具箱中的另一個非常有用的工具。閱讀[各種配置此類代碼的方法](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/async_function)。
## GitHub Copilot Agent 挑戰 🚀
恭喜!如果你構建了擴展(`npm run build`)並在擴展面板中刷新它,你就擁有了一個可運行的擴展!唯一未完成的是圖標,你將在下一課中修復它。
使用 Agent 模式完成以下挑戰:
**描述:** 加強瀏覽器擴充功能,新增錯誤處理改進及用戶體驗功能。這個挑戰將幫助你練習使用現代 JavaScript 模式操作 API、本地儲存及 DOM 操作。
---
**提示:** 建立一個增強版的 displayCarbonUsage 函數包括1失敗的 API 呼叫重試機制搭配指數退避2呼叫 API 前對地區代碼進行輸入驗證3帶有進度指示的載入動畫4將 API 回應快取到 localStorage 並設定過期時間(快取 30 分鐘),及 5顯示先前 API 呼叫的歷史資料功能。並加上適當的 TypeScript 風格 JSDoc 註解來記錄所有函數參數和回傳類型。
了解更多關於[agent 模式](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode)。
## 🚀 挑戰
我們在這些課程中討論了幾種 API 類型。選擇一個網頁 API深入研究它提供的功能。例如查看瀏覽器中可用的 API例如 [HTML 拖放 API](https://developer.mozilla.org/docs/Web/API/HTML_Drag_and_Drop_API)。在你看來,什麼樣的 API 是優秀的?
擴展你對 API 的認識,探索豐富的瀏覽器 API 來進行網頁開發。從以下瀏覽器 API 選擇一個,並建立一個小型示範:
## 課後測驗
- [Geolocation API](https://developer.mozilla.org/docs/Web/API/Geolocation_API) - 取得使用者當前位置
- [Notification API](https://developer.mozilla.org/docs/Web/API/Notifications_API) - 發送桌面通知
- [HTML Drag and Drop API](https://developer.mozilla.org/docs/Web/API/HTML_Drag_and_Drop_API) - 建立互動式拖曳介面
- [Web Storage API](https://developer.mozilla.org/docs/Web/API/Web_Storage_API) - 進階本地儲存技巧
- [Fetch API](https://developer.mozilla.org/docs/Web/API/Fetch_API) - XMLHttpRequest 的現代替代方案
[課後測驗](https://ff-quizzes.netlify.app/web/quiz/26)
**研究時可思考的問題:**
- 這個 API 解決了哪些現實世界問題?
- API 如何處理錯誤及極端狀況?
- 使用此 API 有哪些安全考量?
- 這個 API 在不同瀏覽器中的支援度如何?
完成研究後,找出哪些特性讓 API 對開發者友善且可靠。
## 回顧與自學
## 講課後測驗
在這節課中,你學到了 LocalStorage 和 API這兩個對專業網頁開發者非常有用。你能想到這兩者如何協同工作嗎思考一下如何設計一個網站將項目存儲起來以供 API 使用。
[課後測驗](https://ff-quizzes.netlify.app/web/quiz/26)
## 複習與自學
你在本課學到了 LocalStorage 和 API對專業網頁開發者非常有用。思考這兩者如何協同運作試想如何架構一個網頁讓 API 使用從中儲存的項目。
### ⚡ **接下來 5 分鐘你可以做到**
- [ ] 開啟 DevTools 的 Application 頁籤,探索任一網站的 localStorage
- [ ] 建立簡單的 HTML 表單,測試瀏覽器的表單驗證
- [ ] 嘗試在瀏覽器主控台使用 localStorage 存取資料
- [ ] 利用 Network 頁籤檢視送出的表單資料
### 🎯 **這一小時內你可完成**
- [ ] 完成課後測驗並理解表單處理概念
- [ ] 製作一個瀏覽器擴充功能表單,保存用戶偏好設定
- [ ] 實作用戶端表單驗證及友善的錯誤訊息
- [ ] 練習使用 chrome.storage API 來持久化擴充功能資料
- [ ] 建立一個介面,依據儲存的用戶設定做出反應
### 📅 **你的週長擴充功能開發**
- [ ] 完成具備表單功能的完整瀏覽器擴充功能
- [ ] 精通不同的儲存選項local、sync 及 session storage
- [ ] 實作進階表單功能,如自動完成與驗證
- [ ] 新增匯入/匯出用戶資料功能
- [ ] 在多瀏覽器間徹底測試你的擴充功能
- [ ] 打磨擴充功能的用戶體驗與錯誤處理
### 🌟 **你的月長網頁 API 大師訓練**
- [ ] 使用各種瀏覽器儲存 API 建置複雜應用程式
- [ ] 學習離線優先開發模式
- [ ] 參與涉及資料持久化的開源專案
- [ ] 精通隱私導向開發與 GDPR 合規
- [ ] 建立可重用的表單處理與資料管理函式庫
- [ ] 分享網頁 API 與擴充功能開發相關知識
## 🎯 你的擴充功能開發大師時程表
```mermaid
timeline
title API 整合與儲存學習進度
section DOM 基礎 (15 分鐘)
元素參考: querySelector 精通
: 事件監聽器設定
: 狀態管理基礎
section 本地儲存 (20 分鐘)
資料持久化: 鍵值儲存
: 會話管理
: 使用者偏好處理
: 儲存檢視工具
section 表單處理 (25 分鐘)
使用者輸入: 表單驗證
: 事件預防
: 資料擷取
: 使用者介面狀態轉換
section API 整合 (35 分鐘)
外部通訊: HTTP 請求
: 認證模式
: JSON 資料解析
: 回應處理
section 非同步程式設計 (40 分鐘)
現代 JavaScript: Promise 處理
: Async/await 模式
: 錯誤管理
: 非阻塞操作
section 錯誤處理 (30 分鐘)
穩健應用程式: Try/catch 區塊
: 使用者友善訊息
: 優雅降級
: 除錯技術
section 進階模式 (1 週)
專業發展: 快取策略
: 流量限制
: 重試機制
: 效能優化
section 生產技能 (1 個月)
企業功能: 安全最佳實踐
: API 版本控制
: 監控與記錄
: 可擴展架構
```
### 🛠️ 你的全端開發工具總結
完成本課後,你已經擁有:
- **DOM 精通**:精準定位與操作元素
- **儲存專家**:使用 localStorage 管理持久資料
- **API 整合**:即時資料擷取與驗證
- **非同步程式設計**:使用現代 JavaScript 進行非阻塞作業
- **錯誤處理**:建構能優雅處理失敗的穩健應用
- **用戶體驗**:載入狀態、驗證與流暢互動
- **現代模式**fetch API、async/await 與 ES6+ 功能
**專業技能提升**:你已實作以下模式於:
- **網頁應用程式**:使用外部資料源的單頁應用
- **行動開發**:具備離線能力的 API 驅動應用
- **桌面軟體**Electron 應用與持久儲存
- **企業系統**:認證、快取與錯誤處理
- **現代框架**React/Vue/Angular 的資料管理模式
**下一階段**:你已準備好探究更進階主題,如快取策略、即時 WebSocket 連接或複雜狀態管理!
## 作業
[採用一個 API](assignment.md)
[採用 API](assignment.md)
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
此文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原始語言的文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解讀概不負責。
本文件乃透過 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於確保準確性,但請注意自動翻譯可能包含錯誤或不準確之處。原始語言文件應視為權威資料來源。對於重要資訊,建議採用專業人工翻譯。我們對於因使用本翻譯而導致的任何誤解或誤譯概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,23 +1,109 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "a0c78d1dd9d1acdbf7f52e7cc3ebe1a7",
"translation_date": "2025-08-23T23:40:48+00:00",
"original_hash": "25b8d28b8531352d4eb67291fd7824c4",
"translation_date": "2026-01-06T12:41:26+00:00",
"source_file": "5-browser-extension/2-forms-browsers-local-storage/assignment.md",
"language_code": "hk"
}
-->
# 採用 API
## 指引
## 概覽
使用 API 可以非常有趣。這裡有一個[免費 API 的列表](https://github.com/public-apis/public-apis)。選擇一個 API並建立一個解決問題的瀏覽器擴展。這個問題可以很小例如缺乏足夠的寵物圖片可以試試 [dog CEO API](https://dog.ceo/dog-api/)),或者是更大的問題——盡情享受吧!
API 為創意網站開發帶來無限可能!在此作業中,你將選擇一個外部 API然後建立一個解決實際問題或為用戶提供有價值功能的瀏覽器擴充功能。
## 指示
### 第 1 步:選擇你的 API
從此精選的[免費公開 API 清單](https://github.com/public-apis/public-apis)中選擇一個 API。考慮以下類別
**初學者熱門選擇:**
- **娛樂**[Dog CEO API](https://dog.ceo/dog-api/) ,隨機狗狗圖片
- **天氣**[OpenWeatherMap](https://openweathermap.org/api) ,即時天氣數據
- **名言**[Quotable API](https://quotable.io/) ,勵志名言
- **新聞**[NewsAPI](https://newsapi.org/) ,最新頭條
- **趣味知識**[Numbers API](http://numbersapi.com/) ,有趣的數字知識
### 第 2 步:規劃你的擴充功能
在撰寫程式碼前,回答以下規劃問題:
- 你的擴充功能解決了什麼問題?
- 目標用戶是誰?
- 會在本地儲存什麼資料?
- 如何處理 API 故障或速率限制?
### 第 3 步:建立你的擴充功能
你的擴充功能應包含:
**必要功能:**
- 表單輸入,用於任何必要的 API 參數
- API 整合及完善的錯誤處理
- 本地儲存使用者偏好或 API 金鑰
- 清晰且響應式的使用者介面
- 載入狀態與使用者回饋
**程式碼要求:**
- 使用現代 JavaScriptES6+)功能
- 使用 async/await 來呼叫 API
- 以 try/catch 區塊實作適當的錯誤處理
- 添加有意義的註解說明程式碼
- 遵守一致的程式碼格式
### 第 4 步:測試與完善
- 使用各種輸入測試你的擴充功能
- 處理邊緣狀況無網路、API 回應無效)
- 確保瀏覽器重啟後擴充功能依然正常運作
- 添加使用者友善的錯誤訊息
## 加分挑戰
讓你的擴充功能更上一層樓:
- 添加多個 API 端點以豐富功能
- 實作資料快取以減少 API 呼叫
- 建立常用動作的鍵盤快捷鍵
- 添加資料匯出/匯入功能
- 提供使用者自訂選項
## 提交需求
1. **能正常運作的瀏覽器擴充功能**,成功整合所選 API
2. **README 檔案** 說明:
- 選擇的 API 及原因
- 安裝和使用方式
- 任何 API 金鑰或設定需求
- 擴充功能操作的螢幕截圖
3. **乾淨且有註解的程式碼**,遵守現代 JavaScript 實務
## 評分標準
| 評分標準 | 卓越表現 | 合格表現 | 需要改進 |
| -------- | -------------------------------------------------------------------------- | ---------------------------------------- | ----------------------- |
| | 使用上述列表中的 API 提交了一個完整的瀏覽器擴展 | 提交了一個部分完成的瀏覽器擴展 | 提交的作品有錯誤 |
| 標準 | 優秀 (90-100%) | 精通 (80-89%) | 發展中 (70-79%) | 入門 (60-69%) |
|----------|---------------------|---------------------|---------------------|--------------------|
| **API 整合** | 完美的 API 整合,並包含全面錯誤處理與邊緣案例管理 | 成功整合 API 且有基本錯誤處理 | API 運作但錯誤處理有限 | API 整合存在重大問題 |
| **程式碼品質** | 乾淨且有良好註解的現代 JavaScript遵守最佳實務 | 良好的程式碼架構與充分註解 | 程式碼可用但結構需改善 | 程式碼品質差,註解甚少 |
| **使用者體驗** | 精緻介面,具優秀的載入狀態與使用者回饋 | 良好的介面及基本使用者回饋 | 基本介面且功能可行 | 使用者體驗差,介面混亂 |
| **本地儲存** | 複雜的本地儲存運用,包含資料驗證與管理 | 適當實作本地儲存關鍵功能 | 基本本地儲存實作 | 本地儲存使用不足或錯誤 |
| **文件說明** | 完整 README 包含設定說明和截圖 | 良好文件涵蓋多數需求 | 基本文檔但缺少部分細節 | 文件差或缺失 |
## 入門提示
1. **從簡單開始**:選擇不需要複雜認證的 API
2. **閱讀文件**:徹底了解選定 API 的端點與回應格式
3. **規劃 UI**:先手繪擴充功能介面再寫程式碼
4. **頻繁測試**:逐步構建並測試每個功能
5. **處理錯誤**:總假設 API 呼叫可能失敗並做出應對
## 資源
- [瀏覽器擴充功能文件](https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions)
- [Fetch API 指南](https://developer.mozilla.org/docs/Web/API/Fetch_API/Using_Fetch)
- [本地儲存教學](https://developer.mozilla.org/docs/Web/API/Window/localStorage)
- [JSON 解析與處理](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON)
祝你打造出有趣又實用的作品!🚀
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋不承擔責任。
本文件由 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻譯而成。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原文文件的母語版本應被視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用本翻譯而引致的任何誤解或誤釋概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,175 +1,597 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "b91cbf14240ee59411b96448b994ace1",
"translation_date": "2025-10-03T12:17:03+00:00",
"original_hash": "b275fed2c6fc90d2b9b6661a3225faa2",
"translation_date": "2026-01-06T12:41:45+00:00",
"source_file": "5-browser-extension/3-background-tasks-and-performance/README.md",
"language_code": "hk"
}
-->
# 瀏覽器擴展項目第三部分:了解背景任務和性能
## 課前測驗
[課前測驗](https://ff-quizzes.netlify.app/web/quiz/27)
### 簡介
在本模組的前兩節課中,你學習了如何建立一個表單以及顯示從 API 獲取的數據區域。這是一種非常標準的方式來在網絡上建立網絡存在。你甚至學會了如何異步獲取數據。你的瀏覽器擴展幾乎已經完成。
現在需要管理一些背景任務,包括刷新擴展圖標的顏色,因此這是一個很好的時機來討論瀏覽器如何管理這類任務。在構建網絡資產時,讓我們從性能的角度來思考這些瀏覽器任務。
## 網絡性能基礎
> 「網站性能主要涉及兩件事:頁面加載速度,以及頁面上的代碼運行速度。」 -- [Zack Grossbart](https://www.smashingmagazine.com/2012/06/javascript-profiling-chrome-developer-tools/)
如何讓你的網站在各種設備、各種用戶和各種情況下都能快速運行是一個非常廣泛的話題。以下是一些在構建標準網絡項目或瀏覽器擴展時需要注意的要點。
確保網站高效運行的第一步是收集其性能數據。第一個地方可以查看的是網絡瀏覽器的開發者工具。在 Edge 中,你可以選擇「設置及更多」按鈕(瀏覽器右上角的三點圖標),然後導航到「更多工具 > 開發者工具」,並打開「性能」選項卡。你也可以使用快捷鍵 `Ctrl` + `Shift` + `I`Windows`Option` + `Command` + `I`Mac來打開開發者工具。
「性能」選項卡包含一個分析工具。打開一個網站(例如,[https://www.microsoft.com](https://www.microsoft.com/?WT.mc_id=academic-77807-sagibbon)),然後點擊「記錄」按鈕,刷新網站。隨時停止記錄,你將能看到生成的例程,包括「腳本」、「渲染」和「繪製」網站:
![Edge 分析工具](../../../../translated_images/profiler.5a4a62479c5df01cfec9aab74173dba13f91d2c968e1a1ae434c26165792df15.hk.png)
✅ 查看 [Microsoft 文檔](https://docs.microsoft.com/microsoft-edge/devtools-guide/performance/?WT.mc_id=academic-77807-sagibbon) 中有關 Edge 性能面板的內容
> 提示:為了獲得網站啟動時間的準確讀數,請清除瀏覽器的緩存
選擇性能時間線的元素,放大頁面加載時發生的事件。
通過選擇性能時間線的一部分並查看摘要窗格,獲取頁面性能的快照:
![Edge 分析工具快照](../../../../translated_images/snapshot.97750180ebcad73794a3594b36925eb5c8dbaac9e03fec7f9b974188c9ac63c7.hk.png)
檢查事件日誌窗格,查看是否有任何事件超過了 15 毫秒:
![Edge 事件日誌](../../../../translated_images/log.804026979f3707e00eebcfa028b2b5a88cec6292f858767bb6703afba65a7d9c.hk.png)
✅ 熟悉你的分析工具!打開本網站的開發者工具,看看是否有任何瓶頸。哪個資產加載最慢?哪個最快?
## 分析檢查
通常,為了避免在部署到生產環境時出現意外問題,每個網絡開發者都應該注意一些「問題區域」。
**資產大小**:過去幾年,網絡變得「更重」,因此也更慢。其中一些重量與使用圖片有關。
✅ 查看 [Internet Archive](https://httparchive.org/reports/page-weight) 以了解頁面重量的歷史視圖及更多信息。
一個好的做法是確保你的圖片已經過優化,並以適合用戶的大小和分辨率交付。
**DOM 遍歷**:瀏覽器需要根據你編寫的代碼構建其文檔對象模型,因此為了良好的頁面性能,應保持標籤的最小化,只使用和樣式化頁面所需的內容。基於此,與頁面相關的多餘 CSS 可以進行優化;僅需在某一頁面使用的樣式不需要包含在主樣式表中。
# 瀏覽器擴充套件專案 第三部分:了解背景任務與效能
```mermaid
journey
title 你的效能優化旅程
section 基礎
學習瀏覽器工具: 3: Student
理解剖析工具: 4: Student
識別瓶頸: 4: Student
section 擴展功能
建立色彩系統: 4: Student
建立背景任務: 5: Student
動態更新圖標: 5: Student
section 優化
監控效能: 5: Student
調試問題: 4: Student
打磨體驗: 5: Student
```
是否曾想過,為何有些瀏覽器擴充套件感覺反應靈敏,而有些則顯得遲緩?秘密就在背後正在發生的事情。當使用者在你的擴充套件介面中點擊時,背後有一整個世界的背景程序正悄悄管理資料擷取、圖示更新和系統資源。
**JavaScript**:每個 JavaScript 開發者都應注意「渲染阻塞」腳本,這些腳本必須在 DOM 遍歷和繪製到瀏覽器之前加載。考慮在內聯腳本中使用 `defer`(如在 Terrarium 模組中所做)
這是我們瀏覽器擴充套件系列的最後一課,我們將讓你的碳足跡追蹤器順暢運作。你將新增動態圖示更新,並學會如何在效能問題成為麻煩前就發現它們。這就像調校賽車—小小的優化可以讓一切運行更順暢。
✅ 在 [網站速度測試網站](https://www.webpagetest.org/) 上嘗試一些網站,了解用於確定網站性能的常見檢查
完成後,你會有一個磨練好的擴充套件,並了解區分好與優秀 Web 應用的效能原則。讓我們潛入瀏覽器優化的世界吧。
現在你已經了解瀏覽器如何渲染你發送的資產,讓我們來看看完成擴展所需的最後幾件事:
## 課前小測驗
### 創建計算顏色的函數
[課前小測驗](https://ff-quizzes.netlify.app/web/quiz/27)
`/src/index.js` 中,添加一個名為 `calculateColor()` 的函數,放在你設置的用於訪問 DOM 的一系列 `const` 變量之後:
### 簡介
```JavaScript
在我們之前的課程中,你建立了表單,連接到 API並處理非同步資料擷取。你的擴充套件已經初具雛形。
現在我們需要添上最後的潤飾—像是讓擴充套件圖示根據碳數據變色。這讓我想起 NASA 必須優化阿波羅太空船上的每個系統。他們無法浪費任何處理週期或記憶體,因為性命攸關。雖然我們的瀏覽器擴充套件沒有那麼重要,但相同的原則適用—有效率的程式碼帶來更好的使用體驗。
```mermaid
mindmap
root((效能與背景任務))
Browser Performance
Rendering Pipeline
Asset Optimization
DOM Manipulation
JavaScript Execution
Profiling Tools
開發者工具
性能標籤
時間軸分析
瓶頸偵測
Extension Architecture
背景腳本
內容腳本
訊息傳遞
圖示管理
Optimization Strategies
程式碼分割
慢載入
快取
資源壓縮
Visual Feedback
動態圖示
顏色編碼
即時更新
使用者體驗
```
## 網路效能基礎
當你的程式碼有效率地執行,人們真的能 *感覺到* 差異。你一定知道那種頁面瞬間載入或動畫流暢播放的時刻?那就是良好效能在運作。
效能不僅僅是速度—它關乎創造出自然、不笨重且不令人沮喪的網頁體驗。回到計算早期Grace Hopper 著名地在桌上放了一根奈秒長度的導線(約一英尺長),來展示光在十億分之一秒內能走多遠。這是她說明在計算中每個微秒都重要的方式。讓我們一起探索能幫你找出拖慢速度原因的偵測工具。
>「網站效能是兩件事:頁面載入速度,以及程式碼執行速度。」-- [Zack Grossbart](https://www.smashingmagazine.com/2012/06/javascript-profiling-chrome-developer-tools/)
如何讓你網站在各種設備、使用者及狀況下都爆速,這主題毫不意外地龐大。以下是建構標準網頁專案或瀏覽器擴充套件時要記住的幾點。
優化網站的第一步是了解實際底層發生了什麼。幸運的是,你的瀏覽器已內建強大的偵測工具。
```mermaid
flowchart LR
A[HTML] --> B[解析]
B --> C[DOM 樹]
D[CSS] --> E[解析]
E --> F[CSSOM]
G[JavaScript] --> H[執行]
C --> I[渲染樹]
F --> I
H --> I
I --> J[排版]
J --> K[繪製]
K --> L[合成]
L --> M[顯示]
subgraph "關鍵渲染路徑"
N["1. 解析 HTML"]
O["2. 解析 CSS"]
P["3. 執行 JS"]
Q["4. 建立渲染樹"]
R["5. 元素排版"]
S["6. 像素繪製"]
T["7. 圖層合成"]
end
style M fill:#e8f5e8
style I fill:#fff3e0
style H fill:#ffebee
```
在 Edge 中打開開發者工具,點擊右上角的那三個點,然後去「更多工具」>「開發者工具」。或者使用快捷鍵Windows 上是 `Ctrl` + `Shift` + `I`Mac 是 `Option` + `Command` + `I`。進入後,點擊「效能」標籤頁—這裡就是你的調查現場。
**這是你的效能偵查工具包:**
- **打開** 開發者工具(作為開發者你會一直用到它!)
- **進入** 效能標籤頁—把它想像成你的網頁應用健身追蹤器
- **按下** 錄製按鈕,觀看你的頁面運作
- **分析** 結果以找出拖慢速度的原因
我們試試看。打開一個網站Microsoft.com 很適合),點按「錄製」按鈕。現在刷新頁面,並觀看分析器捕捉整個過程。停止錄製後,你會看到詳細的瀏覽器如何「腳本執行」、「渲染」和「繪製」網站的分析報告。這讓我想到任務控制中心在火箭發射時監控每個系統—你會取得實時資料,知道到底發生了什麼、什麼時候發生。
![Edge profiler](../../../../translated_images/profiler.5a4a62479c5df01c.hk.png)
✅ 若想深入學習,參考 [Microsoft 文件](https://docs.microsoft.com/microsoft-edge/devtools-guide/performance/?WT.mc_id=academic-77807-sagibbon) 裡有豐富細節
> 專家小撇步:測試前先清除瀏覽器快取,看看第一次造訪者的效能通常和重複造訪有何不同!
選取剖面時間軸的元素,可放大查看載入過程中發生的事件。
選取剖面時間軸的一部分,查看摘要面板,以獲取頁面效能快照:
![Edge profiler snapshot](../../../../translated_images/snapshot.97750180ebcad737.hk.png)
查看事件紀錄面板,看看是否有事件耗時超過 15 毫秒:
![Edge event log](../../../../translated_images/log.804026979f3707e0.hk.png)
✅ 熟悉你的剖析器!打開本網站的開發者工具,看看是否有哪些瓶頸。最慢載入的資源是什麼?最快的是什麼?
```mermaid
flowchart TD
A[打開開發者工具] --> B[導航至效能分頁]
B --> C[點擊錄製按鈕]
C --> D[執行操作]
D --> E[停止錄製]
E --> F{分析結果}
F --> G[檢查時間軸]
F --> H[檢視網絡]
F --> I[審查腳本]
F --> J[識別繪製事件]
G --> K{長任務?}
H --> L{大型資源?}
I --> M{阻塞渲染?}
J --> N{昂貴的繪製?}
K -->|是| O[優化 JavaScript]
L -->|是| P[壓縮資源]
M -->|是| Q[新增 async/defer]
N -->|是| R[簡化樣式]
O --> S[再次測試]
P --> S
Q --> S
R --> S
style A fill:#e1f5fe
style F fill:#fff3e0
style S fill:#e8f5e8
```
## 剖析時要注意什麼
執行剖析只是一開始—真正的技巧是了解那些彩色圖表實際告訴你什麼。別擔心,你會漸漸看懂。經驗豐富的開發者能在問題成真之前察覺警訊。
讓我們來談談常見嫌疑犯—那些會暗中拖慢網站專案效能的壞份子。就像 Marie Curie 必須仔細監控實驗室的輻射量,我們也要注意某些模式顯示問題正在醞釀。早期抓出它們能幫你(和使用者)避免很多挫折。
**資源大小**:網站近年愈來愈「重」,很多額外負擔來自圖片。就好像我們不斷往數位行李箱塞東西。
✅ 瀏覽 [Internet Archive](https://httparchive.org/reports/page-weight) 看看頁面大小隨時間的成長—非常發人深省。
**保持資源優化的做法:**
- **壓縮** 圖片!現代格式如 WebP 能大幅減少檔案大小
- **根據設備提供** 合適尺寸圖片—不須將巨大的桌機圖傳給手機
- **縮小** CSS 和 JavaScript 程式碼—每個位元都重要
- **使用** 懶載入,讓圖片只在使用者實際滾動看到時才下載
**DOM 遍歷**瀏覽器建構文件物件模型DOM基於你的程式碼因此為了良好效能保持標籤最小且僅使用及樣式化頁面所需的東西相當重要。舉例來說過多頁面不必要的 CSS 可以優化;只在一頁用到的樣式根本不該納入主要樣式表。
**DOM 優化策略要點:**
- **減少** HTML 元素數量及巢狀層級
- **刪除** 未使用的 CSS 規則並有效整合樣式表
- **組織** CSS 只載入每頁需要的部分
- **結構化** HTML讓瀏覽器解析更高效
**JavaScript**:每個 JavaScript 開發者都要注意「阻塞渲染」的腳本,這些腳本必須先載入,瀏覽器才能繼續解析 DOM 並繪製網頁。考慮對內嵌腳本使用 `defer` 屬性(就像我們在 Terrarium 模組做的一樣)。
**現代 JavaScript 優化技巧:**
- **使用** `defer` 屬性在 DOM 解析後載入腳本
- **實作** 程式碼分割,僅載入必要 JS
- **應用** 懶載入以延遲非必要功能
- **盡量減少** 使用重量級函式庫和框架
✅ 在 [網站速度測試網站](https://www.webpagetest.org/) 上試試幾個網站,了解常見檢查項目如何衡量網站效能。
### 🔄 **教學檢核**
**效能理解**:在建立擴充套件功能前,確保你能:
- ✅ 解釋從 HTML 轉成像素的關鍵渲染路徑
- ✅ 辨識網頁應用中常見的效能瓶頸
- ✅ 使用瀏覽器開發者工具剖析頁面效能
- ✅ 理解資源大小與 DOM 複雜度如何影響速度
**快速自我測試**:當你有阻塞渲染的 JavaScript 時會發生什麼?
*回答:瀏覽器必須下載並執行該腳本,才能繼續解析 HTML 並渲染頁面*
**實際效能影響**
- **100 毫秒延遲**:使用者感受到速度變慢
- **1 秒延遲**:使用者開始失去專注
- **3 秒以上**40% 使用者放棄該頁面
- **行動網路**:效能更為重要
既然你對瀏覽器如何呈現你傳給它的資源已有概念,讓我們看完成擴充套件所需的最後幾步:
### 建立函數計算顏色
現在我們來寫一個函數,將數值資料轉成有意義的顏色。想像成交通號誌系統—綠色代表乾淨能源,紅色代表碳強度高。
此函數會根據我們 API 的 CO2 資料判斷最適合代表環境影響的顏色。這如同科學家利用熱度圖的色彩編碼來視覺化複雜數據模式—從海洋溫度到星體生成。把它加入 `/src/index.js`,就放在之前設定的那些 `const` 變數之後:
```mermaid
flowchart LR
A[二氧化碳值] --> B[尋找最近刻度點]
B --> C[獲取刻度索引]
C --> D[對應顏色]
D --> E[發送到背景程式]
subgraph "顏色刻度"
F["0-150綠色潔淨"]
G["150-600黃色中等"]
H["600-750橙色"]
I["750以上褐色非常高"]
end
subgraph "訊息傳遞"
J[內容腳本]
K[chrome.runtime.sendMessage]
L[背景腳本]
M[圖示更新]
end
style A fill:#e1f5fe
style D fill:#e8f5e8
style E fill:#fff3e0
```
```javascript
function calculateColor(value) {
let co2Scale = [0, 150, 600, 750, 800];
let colors = ['#2AA364', '#F5EB4D', '#9E4229', '#381D02', '#381D02'];
// 定義二氧化碳強度範圍(每千瓦時克數)
const co2Scale = [0, 150, 600, 750, 800];
// 對應顏色從綠色(清潔)到深棕色(高碳)
const colors = ['#2AA364', '#F5EB4D', '#9E4229', '#381D02', '#381D02'];
let closestNum = co2Scale.sort((a, b) => {
// 尋找最接近輸入的強度數值
const closestNum = co2Scale.sort((a, b) => {
return Math.abs(a - value) - Math.abs(b - value);
})[0];
console.log(value + ' is closest to ' + closestNum);
let num = (element) => element > closestNum;
let scaleIndex = co2Scale.findIndex(num);
let closestColor = colors[scaleIndex];
console.log(`${value} is closest to ${closestNum}`);
// 找出顏色對應的索引
const num = (element) => element > closestNum;
const scaleIndex = co2Scale.findIndex(num);
const closestColor = colors[scaleIndex];
console.log(scaleIndex, closestColor);
// 傳送顏色更新訊息到背景程式
chrome.runtime.sendMessage({ action: 'updateIcon', value: { color: closestColor } });
}
```
這裡發生了什麼?你傳入一個值(來自上一課完成的 API 調用中的碳強度),然後計算其值與顏色數組中索引的接近程度。接著,你將最接近的顏色值發送到 chrome runtime。
chrome.runtime 有一個 [API](https://developer.chrome.com/extensions/runtime),可以處理各種背景任務,而你的擴展正在利用它:
> 「使用 chrome.runtime API 獲取背景頁面,返回有關清單的詳細信息,並監聽和響應應用或擴展生命周期中的事件。你還可以使用此 API 將 URL 的相對路徑轉換為完全限定的 URL。」
✅ 如果你正在為 Edge 開發此瀏覽器擴展,可能會驚訝於你正在使用 chrome API。新版 Edge 瀏覽器基於 Chromium 瀏覽器引擎,因此你可以利用這些工具。
**來拆解這個巧妙的小函數:**
- **建立** 兩個陣列—一個存放 CO2 等級,另一個存放顏色(綠代表乾淨,棕代表骯髒!)
- **用** 陣列排序找到最接近實際 CO2 數值的匹配
- **利用** findIndex() 方法抓出對應顏色
- **將** 選好的顏色訊息傳給 Chrome 背景腳本
- **使用** 樣板字串(就是反引號那種)讓字串格式更清晰
- **統一** 使用 const 聲明保持整潔有序
`chrome.runtime` [API](https://developer.chrome.com/extensions/runtime) 就像是你擴充套件的神經系統—負責所有幕後通訊和任務:
>「使用 chrome.runtime API 取用背景頁面、取得 manifest 詳細資訊,並監聽及回應應用或擴充套件生命週期事件。你也可以用此 API 將相對路徑 URL 轉換成完整 URL。」
**Chrome Runtime API 方便之處:**
- **讓** 擴充套件不同部分能互相對話
- **處理** 背景工作不會凍結使用者介面
- **管理** 擴充套件生命週期事件
- **簡化** 腳本間的訊息傳遞
✅ 如果你在為 Edge 開發這瀏覽器擴充套件,可能會驚訝你在使用 chrome API。新版 Edge 採用 Chromium 引擎,你因此可用這些工具。
```mermaid
architecture-beta
group browser(logos:chrome)[瀏覽器]
service popup(logos:html5)[彈出介面] in browser
service content(logos:javascript)[內容腳本] in browser
service background(database)[後台腳本] in browser
service api(logos:api)[外部 API] in browser
popup:R -- L:content
content:R -- L:background
background:T -- B:api
content:T -- B:api
junction junctionCenter in browser
popup:R -- L:junctionCenter
junctionCenter:R -- L:background
```
> **專家小撇步**:若你要剖析瀏覽器擴充套件,請直接從該擴充套件中啟動開發者工具,因為它是獨立的瀏覽器實例。如此你能取得針對擴充套件的特定效能指標。
> 注意,如果你想分析瀏覽器擴展,請從擴展本身內部啟動開發者工具,因為它是獨立的瀏覽器實例。
### 設定預設圖示顏色
### 設置默認圖標顏色
在開始取用真實資料前,先給擴充套件一個起點。沒有人喜歡看著一個空白或壞掉的圖示。先設為綠色,讓用戶從安裝那刻起就知道擴充套件正常運作。
現在,在 `init()` 函數中,通過再次調用 chrome 的 `updateIcon` 操作,將圖標設置為默認的綠色:
在你的 `init()` 函數裡設置預設的綠色圖示
```JavaScript
```javascript
chrome.runtime.sendMessage({
action: 'updateIcon',
value: {
color: 'green',
},
value: {
color: 'green',
},
});
```
### 調用函數並執行調用
接下來,通過將其添加到 C02Signal API 返回的 promise 中,調用你剛剛創建的函數:
**這次初始化達到的效果:**
- **設定** 中性的綠色作為預設狀態
- **提供** 擴充套件啟動時立即的視覺回饋
- **建立** 與背景腳本的通訊模式
- **確保** 在資料載入前用戶能看到可用的擴充套件
### 呼叫函數,執行更新
現在將所有東西連結起來,當新 CO2 資料到達時,你的圖示會自動用正確顏色更新。就像電子裝置接通最後一條電路—各部分突然如一個系統般協同運作。
```JavaScript
//let CO2...
在取得 API 的 CO2 資料後,加入這行:
```javascript
// 從 API 取得二氧化碳數據後
// 讓 CO2 = data.data[0].intensity.actual;
calculateColor(CO2);
```
最後,在 `/dist/background.js` 中,為這些背景操作調用添加監聽器:
**這種整合具備:**
- **連結** API 資料流程與視覺指標系統
- **當新資料到達**,自動觸發圖示更新
- **確保** 依現行碳強度即時視覺回饋
- **保持** 資料擷取與顯示邏輯的職責分離
最後,在 `/dist/background.js` 加入監聽這些背景操作請求的事件監聽器:
```JavaScript
```javascript
// 監聽來自內容腳本的訊息
chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
if (msg.action === 'updateIcon') {
chrome.action.setIcon({ imageData: drawIcon(msg.value) });
}
});
//borrowed from energy lollipop extension, nice feature!
// 使用 Canvas API 繪製動態圖示
// 借用自 energy lollipop 擴充功能 - 很好的功能!
function drawIcon(value) {
let canvas = new OffscreenCanvas(200, 200);
let context = canvas.getContext('2d');
// 建立一個離屏畫布以提升效能
const canvas = new OffscreenCanvas(200, 200);
const context = canvas.getContext('2d');
// 繪製代表碳密度的彩色圓圈
context.beginPath();
context.fillStyle = value.color;
context.arc(100, 100, 50, 0, 2 * Math.PI);
context.fill();
// 回傳瀏覽器圖示的影像資料
return context.getImageData(50, 50, 100, 100);
}
```
在此代碼中,你為任何發送到後端任務管理器的消息添加了一個監聽器。如果消息名為 'updateIcon',則接下來的代碼將使用 Canvas API 繪製適當顏色的圖標。
✅ 你將在 [太空遊戲課程](../../6-space-game/2-drawing-to-canvas/README.md) 中學到更多有關 Canvas API 的知識。
**這段背景腳本負責:**
- **監聽** 主腳本訊息(如同接待員接電話)
- **處理** 那些「updateIcon」請求以更換工具列圖示
- **利用** Canvas API 動態創建新圖示
- **繪製** 表示當前碳強度的簡單彩色圓圈
- **更新** 你的瀏覽器工具列上的圖示
- **使用** OffscreenCanvas 以提供流暢效能(避免 UI 阻塞)
✅ 你會在 [太空遊戲課程](../../6-space-game/2-drawing-to-canvas/README.md) 中學到更多 Canvas API 的應用。
```mermaid
sequenceDiagram
participant CS as 內容腳本
participant BG as 背景腳本
participant Canvas as 離屏畫布
participant Browser as 瀏覽器圖示
CS->>BG: sendMessage({action: 'updateIcon', color})
BG->>Canvas: new OffscreenCanvas(200, 200)
Canvas->>Canvas: getContext('2d')
Canvas->>Canvas: beginPath() + fillStyle + arc()
Canvas->>Canvas: fill() + getImageData()
Canvas->>BG: 返回影像資料
BG->>Browser: chrome.action.setIcon(imageData)
Browser->>Browser: 更新工具列圖示
```
### 🔄 **教學檢核**
**完整擴充套件理解**:檢核你對整個系統的掌握度:
- ✅ 擴充套件不同腳本間訊息傳遞是怎麼運作的?
- ✅ 為何我們用 OffscreenCanvas 而非一般 Canvas 來提高效能?
- ✅ Chrome Runtime API 在擴充功能架構中扮演什麼角色?
- ✅ 顏色計算算法如何將資料映射到視覺反饋?
現在,重新構建你的擴展(`npm run build`),刷新並啟動你的擴展,觀察顏色變化。是時候去跑腿或洗碗了嗎?現在你知道了!
**效能考量**:你的擴充功能現在展現了:
- **高效訊息傳遞**:腳本語境間的乾淨通訊
- **優化渲染**OffscreenCanvas 防止 UI 阻塞
- **即時更新**:基於即時資料動態改變圖示
- **記憶體管理**:適當的清理與資源處理
恭喜你,你已經構建了一個有用的瀏覽器擴展,並且更深入地了解了瀏覽器的工作原理以及如何分析其性能。
**現在是測試你的擴充功能的時候了:**
- **用** `npm run build` **編譯**所有內容
- **在瀏覽器重新載入**你的擴充功能(別忘了這一步)
- **打開**你的擴充功能,觀察圖示顏色變化
- **檢查**它如何對全球的實時碳排放資料反應
---
現在你一眼就能知道洗衣服是否適合時機,還是應該等更乾淨的能源。你剛完成了真正有用的東西,並且在過程中學習了瀏覽器效能。
## 🚀 挑戰
## GitHub Copilot 代理人挑戰 🚀
調查一些已存在很久的開源網站,根據其 GitHub 歷史,看看它們是否在多年來進行了性能優化。如果有,最常見的痛點是什麼?
使用代理人模式完成以下挑戰:
## 課後測驗
**描述:** 增強瀏覽器擴充功能的效能監控能力,新增追蹤並顯示擴充功能各組件載入時間的功能。
[課後測驗](https://ff-quizzes.netlify.app/web/quiz/28)
**提示:** 建立一個性能監控系統,用於測量和記錄從 API 抓取 CO2 資料、計算顏色及更新圖示所需的時間。新增名為 `performanceTracker` 的函式,利用 Performance API 來測量這些操作,並在瀏覽器控制台中顯示帶有時間戳和持續時間指標的結果。
## 回顧與自學
在此了解更多 [agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode)。
考慮訂閱一份 [性能新聞簡報](https://perf.email/)
## 🚀 挑戰
調查瀏覽器通過其網絡工具中的性能選項卡來衡量網絡性能的一些方式。你是否發現了任何主要差異?
來個有趣的偵探任務:挑幾個存在多年的開源網站(想想 Wikipedia、GitHub、或 Stack Overflow深入研究它們的提交歷史。能發現它們在哪些地方做了效能提升嗎哪些問題屢次出現
**你的調查方法:**
- **搜尋**提交訊息中「optimize」、「performance」或「faster」等字眼
- **尋找**模式—它們是否持續修正同類問題?
- **辨別**使網站變慢的常見問題根源
- **分享**你的發現—讓其他開發者從真實案例中學習
## 課後小測驗
[課後小測驗](https://ff-quizzes.netlify.app/web/quiz/28)
## 複習與自學
考慮訂閱 [效能電子報](https://perf.email/)
透過瀏覽器開發工具的效能分頁,探索瀏覽器如何衡量網頁效能。你發現哪些重大差異?
### ⚡ **你接下來 5 分鐘能做的事**
- [ ] 開啟瀏覽器任務管理員Chrome 中使用 Shift+Esc查看擴充功能的資源使用情況
- [ ] 使用 DevTools 的 Performance 分頁錄製並分析網頁效能
- [ ] 檢視瀏覽器的擴充功能頁面,看看哪些擴充會影響啟動時間
- [ ] 嘗試暫時停用擴充功能,看效能差異
### 🎯 **你這小時能達成的目標**
- [ ] 完成課後小測驗並理解效能概念
- [ ] 為你的瀏覽器擴充功能實作背景腳本
- [ ] 學習使用 browser.alarms 進行高效率背景任務
- [ ] 練習內容腳本與背景腳本之間的訊息傳遞
- [ ] 測量並優化擴充功能的資源使用
### 📅 **你一週的效能之旅**
- [ ] 完成具備背景功能的高效能瀏覽器擴充功能
- [ ] 精通 Service Workers 和現代擴充功能架構
- [ ] 實作高效資料同步和快取策略
- [ ] 學習進階的擴充功能效能除錯技巧
- [ ] 優化擴充功能的功能性與資源效率
- [ ] 建立擴充功能效能場景的全面測試
### 🌟 **你一個月內的優化大師之路**
- [ ] 建置企業級瀏覽器擴充功能,達到最佳效能
- [ ] 學習 Web Workers、Service Workers 與現代網路效能
- [ ] 貢獻專注於效能優化的開源專案
- [ ] 精通瀏覽器底層與進階除錯技巧
- [ ] 建立效能監控工具與最佳實務指南
- [ ] 成為幫助優化網路應用的效能專家
## 🎯 你的瀏覽器擴充功能精通時程
```mermaid
timeline
title 完整擴充套件開發進度
section 效能基礎 (20分鐘)
瀏覽器性能剖析:開發工具精通
: 時間軸分析
: 瓶頸識別
: 關鍵渲染路徑
section 背景任務 (25分鐘)
擴充套件架構:訊息傳遞
: 背景腳本
: 執行時API使用
: 跨上下文通訊
section 視覺回饋 (30分鐘)
動態介面:色彩計算算法
: Canvas API整合
: 圖示生成
: 即時更新
section 效能優化 (35分鐘)
高效程式碼:非同步操作
: 記憶體管理
: 資源清理
: 效能監控
section 生產準備 (45分鐘)
精煉與測試:跨瀏覽器相容性
: 錯誤處理
: 使用者體驗
: 效能驗證
section 進階功能 (1週)
擴充套件生態系統Chrome 網上應用程式商店
: 使用者反饋
: 分析整合
: 更新管理
section 專業發展 (2週)
企業擴充套件:團隊協作
: 程式碼審查
: CI/CD 管線
: 安全審計
section 專家精通 (1個月)
平台專業知識:進階 Chrome API
: 效能優化
: 架構模式
: 開源貢獻
```
### 🛠️ 你的完整擴充功能開發工具箱
完成這系列後,你已精通:
- **瀏覽器架構**:深入瞭解擴充功能如何與瀏覽器系統整合
- **效能剖析**:能利用開發者工具辨識及修正瓶頸
- **非同步程式設計**:現代 JavaScript 模式,實現響應式非阻塞操作
- **API 整合**:外部資料擷取與驗證錯誤處理
- **視覺設計**:動態 UI 更新與以 Canvas 為基礎的圖形生成
- **訊息傳遞**:擴充架構中腳本間通訊
- **使用者體驗**:載入狀態、錯誤處理與直覺互動
- **生產技能**:測試、除錯與真實世界優化
**實務應用**:你的擴充功能開發技能可直接套用於:
- **漸進式網頁應用**:相似架構與效能模式
- **Electron 桌面應用**:使用網路技術的跨平台應用
- **行動混合應用**Cordova / PhoneGap 以網頁 API 開發
- **企業網頁應用**:複雜儀表板與生產力工具
- **Chrome DevTools 擴充**:進階開發者工具與除錯
- **網頁 API 整合**:任何與外部服務通訊的應用
**職業影響力**:你現在能:
- **打造**從概念到部署的生產級瀏覽器擴充功能
- **優化**使用業界標準剖析工具的網頁應用效能
- **架構**具適切責任分離的可擴展系統
- **除錯**複雜的非同步操作與跨語境通訊
- **貢獻**開源擴充功能專案與瀏覽器標準
**下一階段機會:**
- **Chrome Web Store 開發者**:為數百萬用戶發布擴充功能
- **網頁效能工程師**:專注優化與使用者體驗
- **瀏覽器平台開發者**:參與瀏覽器引擎開發
- **擴充功能架構創作者**:打造幫助其他開發者的工具
- **開發者關係**:透過教學與內容創作分享知識
🌟 **成就解鎖**:你已建立一套完整、具備專業開發慣例及現代網頁標準的瀏覽器擴充功能!
## 作業
[分析網站性能](assignment.md)
[分析網站能](assignment.md)
---
**免責聲明**
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件是使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻譯而成。雖然我們盡力確保準確性,但請注意,自動翻譯可能包含錯誤或不準確之處。文件的原始語言版本應被視為權威來源。對於重要資訊,建議採用專業人工翻譯。本公司不會對使用本翻譯所引起的任何誤解或曲解負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,21 +1,109 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "fc09b0fb314a5ab0507ba99216e6a843",
"translation_date": "2025-08-23T23:47:50+00:00",
"original_hash": "a203e560e58ccc6ba68bffc40c7c8676",
"translation_date": "2026-01-06T12:43:55+00:00",
"source_file": "5-browser-extension/3-background-tasks-and-performance/assignment.md",
"language_code": "hk"
}
-->
# 分析網站效能
提供一份詳細的網站報告,指出效能存在問題的地方。分析網站速度緩慢的原因,以及可以採取哪些措施來加快速度。不僅僅依賴瀏覽器工具,還需要研究其他可以幫助撰寫報告的工具。
## 作業概述
效能分析是現代網頁開發者的一項關鍵技能。在此作業中,您將針對一個真實網站進行全面的效能審核,使用瀏覽器內建工具與第三方服務來識別瓶頸並提出優化策略。
您的任務是提供一份詳盡的效能報告,展示您對網頁效能原則的理解以及有效使用專業分析工具的能力。
## 作業指示
**選擇一個網站** 進行分析 — 可從以下選項中挑選:
- 您經常使用的熱門網站(新聞網站、社群媒體、電商平台)
- 開源專案網站GitHub Pages、文件網站
- 本地商家網站或作品集網站
- 您自己的專案或先前課程作品
**進行多工具分析**,至少使用三種不同的方法:
- **瀏覽器開發工具** — 使用 Chrome/Edge 的效能標籤進行詳細分析
- **線上稽核工具** — 嘗試 Lighthouse、GTmetrix 或 WebPageTest
- **網路分析** — 檢視資源載入、檔案大小與請求模式
**將您的發現紀錄在一份完整報告中,包含:**
### 效能指標分析
- **多工具與多角度的載入時間測量**
- **核心網頁指標** 分數LCP、FID、CLS及意義
- **資源細目** 展示哪些資產對載入時間貢獻最大
- **網路瀑布圖分析** 識別阻塞資源
### 問題辨識
- **具體效能瓶頸** 並附支持資料
- **根本原因分析** 說明每個問題發生的原因
- **用戶影響評估** 描述問題如何影響實際使用者
- **問題優先順序排序** 根據嚴重性與修復難度
### 優化建議
- **具體且可執行的改進措施** 及預期成效
- **每項建議的實作策略**
- **可應用的現代最佳實踐**(例如懶加載、壓縮等)
- **持續效能監控的工具與手法**
## 研究要求
**不要只依賴瀏覽器工具** — 擴大分析範圍使用:
**第三方稽核服務:**
- [Google Lighthouse](https://developers.google.com/web/tools/lighthouse) — 全面稽核
- [GTmetrix](https://gtmetrix.com/) — 效能與優化見解
- [WebPageTest](https://www.webpagetest.org/) — 真實測試條件
- [Pingdom](https://tools.pingdom.com/) — 全球效能監控
**專業分析工具:**
- [Bundle Analyzer](https://bundlephobia.com/) — JavaScript 包大小分析
- [Image optimization tools](https://squoosh.app/) — 資產優化機會
- [Security headers analysis](https://securityheaders.com/) — 安全效能影響
## 成果物格式
製作一份專業報告2-3 頁),內容包括:
1. **執行摘要** — 主要發現與建議概述
2. **方法論** — 使用工具與測試方法
3. **當前效能評估** — 基線指標與測量數據
4. **識別問題** — 詳細問題分析與數據支持
5. **建議方案** — 依優先順序排列的改進策略
6. **實施計畫** — 逐步優化方案
**包含視覺證據:**
- 效能工具與指標截圖
- 效能數據的圖表或圖形
- 盡可能的前後比較
- 網路瀑布圖與資源細目
## 評分標準
| 評分標準 | 卓越 | 合格 | 有待改善 |
| -------- | ---------------------------------------------------------------------------------------------------------- | --------------------------- | ----------------------------- |
| | 報告詳細,內容不僅來自瀏覽器工具,還包括第三方工具(如有) | 提供基本報告 | 僅提供簡略報告 |
| 評分標準 | 優異90-100% | 充足70-89% | 需改進50-69% |
| -------- | ---------------- | -------------- | ----------------- |
| **分析深度** | 使用 4 種以上工具進行全面分析,包含詳細指標、根本原因分析與用戶影響評估 | 使用 3 種工具,具清楚指標與基礎問題辨識 | 使用 2 種工具,分析深度有限且問題辨識不足 |
| **工具多樣性** | 使用瀏覽器工具加 3 種以上第三方服務,並具比較分析與見解 | 使用瀏覽器工具加 2 種第三方服務,包含部分比較分析 | 使用瀏覽器工具加 1 種第三方服務,比較有限 |
| **問題識別** | 具體識別 5 個以上效能問題,結合根本原因與量化影響分析 | 識別 3-4 個效能問題,分析良好並有部分影響評估 | 識別 1-2 個效能問題,分析較基礎 |
| **建議方案** | 具體且可執行的建議,含實施細節、預期成效與現代最佳實踐 | 提供良好建議,含部分實施指導與預期結果 | 提供基本建議,實施細節有限 |
| **專業呈現** | 報告組織良好,結構清晰,有視覺證據、執行摘要與專業格式 | 組織良好,含部分視覺證據與清晰結構 | 組織基本,視覺證據不足 |
## 學習成果
完成此作業後,您將展現出:
- **應用** 專業效能分析工具與方法
- **識別** 效能瓶頸並以資料驅動分析
- **分析** 程式碼質量與用戶體驗間關係
- **建議** 具體且可行的優化策略
- **溝通** 以專業格式傳達技術發現
此作業強化課程中學習的效能概念,同時培養您在網頁開發職涯中會持續用到的實作技能。
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件已使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋概不負責。
本文件由 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於確保翻譯準確,但請注意自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要資訊,建議聘請專業人工翻譯。我們不承擔因使用本翻譯而產生的任何誤解或誤釋之責任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,50 +1,142 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "979cfcce2413a87d9e4c67eb79234bc3",
"translation_date": "2025-08-29T15:04:03+00:00",
"original_hash": "a6332a7bb4d0be3bfd24199c83993777",
"translation_date": "2026-01-06T12:35:19+00:00",
"source_file": "6-space-game/1-introduction/README.md",
"language_code": "hk"
}
-->
# 建立太空遊戲第一部分:簡介
![video](../../../../6-space-game/images/pewpew.gif)
# 建立太空遊戲第一部分:介紹
```mermaid
journey
title 你的遊戲開發旅程
section 基礎
學習遊戲架構: 3: Student
理解繼承: 4: Student
探索組合: 4: Student
section 溝通
建立發布/訂閱系統: 4: Student
設計事件流程: 5: Student
連接元件: 5: Student
section 應用
創建遊戲物件: 5: Student
實作設計模式: 5: Student
規劃遊戲結構: 5: Student
```
![太空遊戲動畫展示遊戲玩法](../../../../6-space-game/images/pewpew.gif)
就如同 NASA 的任務控制在太空發射時協調多個系統一樣,我們將建立一個太空遊戲,展示程式不同部分如何無縫協作。在創建一個你能實際玩到的作品的同時,你會學習到適用於任何軟件項目中的基本程式設計概念。
我們將探討組織程式碼的兩種基本方法:繼承與組合。這不僅是學術概念,這些模式正是從電子遊戲到銀行系統所採用的基礎。我們還將實現一個稱為 pub/sub 的通訊系統,類似於航天器中使用的通訊網絡,讓不同組件能分享信息而不產生依賴。
完成此系列後,你將理解如何建立可擴展且可演進的應用程式——無論是開發遊戲、網絡應用還是其他軟件系統。
```mermaid
mindmap
root((遊戲架構))
Object Organization
繼承
組合
類別層級結構
行為混合
Communication Patterns
發佈/訂閱系統
事件發射器
訊息傳遞
鬆散耦合
Game Objects
屬性 (x, y)
行為 (移動, 碰撞)
生命週期管理
狀態管理
Design Patterns
工廠函數
觀察者模式
組件系統
事件驅動架構
Scalability
模組化設計
可維護的程式碼
測試策略
性能優化
```
## 課前測驗
[課前測驗](https://ff-quizzes.netlify.app/web/quiz/29)
### 繼承與組合在遊戲開發中的應用
## 遊戲開發中的繼承與組合
在之前的課程中,由於項目範圍較小,並不需要過多考慮應用程式的設計架構。然而,當你的應用程式規模和範圍逐漸擴大時,架構設計的決策就變得更加重要。在 JavaScript 中,有兩種主要方法可以用來建立大型應用程式:*組合* 或 *繼承*。這兩者各有優缺點,但我們可以從遊戲的角度來解釋它們。
隨著專案複雜度增加,程式碼組織變得至關重要。從簡單的腳本開始,如果沒有適當結構,很快會變得難以維護——就像阿波羅任務需要在數千個組件之間進行細緻協調一樣
✅ 最著名的程式設計書籍之一是關於 [設計模式](https://en.wikipedia.org/wiki/Design_Patterns)。
我們將探討兩種基本的程式碼組織方法:繼承和組合。兩者各有明顯優勢,理解兩者能幫你在不同情境下選擇最合適的方法。我們將透過太空遊戲展現這些概念,裡面有英雄、敵人、強化道具和其他物件需要有效互動
在遊戲中,你會有 `遊戲物件`,這些物件存在於螢幕上。這意味著它們在笛卡爾座標系統中有一個位置,通常由 `x``y` 座標來表示。當你開發遊戲時,你會注意到所有的遊戲物件都有一些標準屬性,這些屬性在每個遊戲中都很常見,例如:
✅ 最著名的程式設計書之一是關於[設計模式](https://en.wikipedia.org/wiki/Design_Patterns)的。
- **基於位置** 大多數(如果不是全部)遊戲元素都是基於位置的。這意味著它們有一個位置,即 `x``y`
- **可移動** 這些物件可以移動到新的位置。通常是英雄、怪物或 NPC非玩家角色但例如像樹這樣的靜態物件則不屬於此類。
- **自我銷毀** 這些物件只存在於一定的時間內,然後會設置為刪除。通常這是通過一個 `dead``destroyed` 的布林值來表示,告訴遊戲引擎該物件不再需要渲染。
- **冷卻時間** "冷卻時間" 是短暫存在的物件中常見的屬性。一個典型的例子是文字或圖形效果(例如爆炸),它們只需要顯示幾毫秒。
在任何遊戲中,你會有「遊戲物件」——充滿互動元素的遊戲世界。英雄、敵人、強化道具和視覺效果都是遊戲物件。它們存在於特定的螢幕座標,用 `x``y` 值表示,類似在坐標平面繪點。
✅ 想想像吃豆人Pac-Man這樣的遊戲。你能在這個遊戲中識別出上述四種物件類型嗎
儘管視覺不同,這些物件常具備基本行為:
### 表達行為
- **它們存在某處** —— 每個物件擁有 x, y 座標,遊戲藉此知道在哪裡繪製它
- **許多會移動** —— 英雄奔跑、敵人追逐、子彈飛過螢幕
- **它們有生命週期** —— 有些持續存在,有些(如爆炸)短暫出現然後消失
- **它們會對事件作出反應** —— 碰撞發生、強化道具被收集、血條更新
上述所有描述的都是遊戲物件可能具有的行為。那麼我們如何編碼這些行為呢?我們可以通過與類別或物件相關聯的方法來表達這些行為。
✅ 想想看看 Pac-Man 遊戲。你能識別出上述四種物件類型嗎?
**類別**
```mermaid
classDiagram
class GameObject {
+x: number
+y: number
+type: string
+exists_somewhere()
}
class MovableObject {
+moveTo(x, y)
+can_move_around()
}
class TemporaryObject {
+lifespan: number
+has_lifespan()
}
class InteractiveObject {
+onCollision()
+reacts_to_stuff()
}
GameObject <|-- MovableObject
GameObject <|-- TemporaryObject
GameObject <|-- InteractiveObject
MovableObject <|-- Hero
MovableObject <|-- Enemy
MovableObject <|-- Bullet
TemporaryObject <|-- PowerUp
TemporaryObject <|-- Explosion
InteractiveObject <|-- Collectible
InteractiveObject <|-- Obstacle
```
### 透過程式碼表達行為
使用 `類別` 結合 `繼承` 的概念,可以為類別添加特定的行為。
既然你了解遊戲物件共通的行為,接下來探討如何用 JavaScript 實現這些行為。你可以透過附加於類別或物件本身的方法來表達物件行為,且有多種方法可選
✅ 繼承是一個重要的概念。可以在 [MDN 的文章關於繼承](https://developer.mozilla.org/docs/Web/JavaScript/Inheritance_and_the_prototype_chain) 中了解更多。
**基於類別的方法**
用程式碼表示,一個遊戲物件通常看起來像這樣:
類別與繼承提供一個結構化方式來組織遊戲物件。就像卡爾林奈Carl Linnaeus所發展的分類系統一樣你會從包含共同屬性的基底類開始再建立專門的子類繼承這些基礎並添加專屬功能。
```javascript
✅ 繼承是很重要的概念。可參考 [MDN 的繼承及原型鏈文章](https://developer.mozilla.org/docs/Web/JavaScript/Inheritance_and_the_prototype_chain) 了解更多。
//set up the class GameObject
這是使用類別與繼承實作遊戲物件的方式:
```javascript
// 第一步:建立基礎的 GameObject 類別
class GameObject {
constructor(x, y, type) {
this.x = x;
@ -52,187 +144,519 @@ class GameObject {
this.type = type;
}
}
```
//this class will extend the GameObject's inherent class properties
**我們一步步拆解:**
- 我們建立一個基本範本,讓每個遊戲物件都可使用
- 建構函式儲存物件位置 (`x`, `y`) 和物件種類
- 這成為所有遊戲物件建立的基礎
```javascript
// 第2步透過繼承新增移動能力
class Movable extends GameObject {
constructor(x,y, type) {
super(x,y, type)
constructor(x, y, type) {
super(x, y, type); // 調用父類構造函數
}
//this movable object can be moved on the screen
// 新增移動到新位置的能力
moveTo(x, y) {
this.x = x;
this.y = y;
}
}
```
**在上述程式碼中,我們:**
- **擴充** GameObject 類來加入移動功能
- **使用** `super()` 呼叫父類建構子以初始化繼承的屬性
- **新增** `moveTo()` 方法更新物件位置
//this is a specific class that extends the Movable class, so it can take advantage of all the properties that it inherits
```javascript
// 第3步創建特定遊戲物件類型
class Hero extends Movable {
constructor(x,y) {
super(x,y, 'Hero')
constructor(x, y) {
super(x, y, 'Hero'); // 自動設定類型
}
}
//this class, on the other hand, only inherits the GameObject properties
class Tree extends GameObject {
constructor(x,y) {
super(x,y, 'Tree')
constructor(x, y) {
super(x, y, 'Tree'); // 樹木不需要移動
}
}
//a hero can move...
const hero = new Hero();
hero.moveTo(5,5);
// 第4步使用你的遊戲物件
const hero = new Hero(0, 0);
hero.moveTo(5, 5); // 英雄可以移動!
//but a tree cannot
const tree = new Tree();
const tree = new Tree(10, 15);
// tree.moveTo() 會導致錯誤 - 樹木無法移動
```
✅ 花幾分鐘重新構想一下吃豆人的英雄(例如 Inky、Pinky 或 Blinky並思考它如何用 JavaScript 編寫。
**理解這些概念:**
- **建立** 專門類型物件並繼承適合的行為
- **展示** 繼承允許選擇性包含特性
- **表示** 英雄可以移動但樹保持靜止
- **說明** 類別階層如何防止不當操作
**組合**
✅ 花幾分鐘重新想像如何用 JavaScript 撰寫如 Pac-Man 中的英雄(例如 Inky、Pinky 或 Blinky
另一種處理物件繼承的方法是使用 *組合*。然後,物件可以像這樣表達它們的行為:
**組合的方法**
組合遵循模組化設計哲學,類似工程師設計航天器可替換組件。不是繼承父類,而是組合具體行為來創造具所需功能的物件。這種方法提供彈性,避免僵硬的階層限制。
```javascript
//create a constant gameObject
// 第一步:建立基本行為物件
const gameObject = {
x: 0,
y: 0,
type: ''
};
//...and a constant movable
const movable = {
moveTo(x, y) {
this.x = x;
this.y = y;
}
}
//then the constant movableObject is composed of the gameObject and movable constants
const movableObject = {...gameObject, ...movable};
};
```
**這段程式碼做了什麼:**
- **定義** 一個有位置與類型屬性的基本 `gameObject`
- **建立** 分離的 `movable` 行為物件,具備移動功能
- **分離** 位置資料與移動邏輯,保持關注點分離
```javascript
// 第2步透過組合行為來組成物件
const movableObject = { ...gameObject, ...movable };
//then create a function to create a new Hero who inherits the movableObject properties
// 第3步為不同物件類型建立工廠函數
function createHero(x, y) {
return {
...movableObject,
x,
y,
type: 'Hero'
}
};
}
//...and a static object that inherits only the gameObject properties
function createStatic(x, y, type) {
return {
...gameObject
...gameObject,
x,
y,
type
}
};
}
//create the hero and move it
const hero = createHero(10,10);
hero.moveTo(5,5);
//and create a static tree which only stands around
const tree = createStatic(0,0, 'Tree');
```
**我應該使用哪種模式?**
**在上述中,我們:**
- **結合** 基本物件屬性與移動行為,使用擴散語法
- **創建** 工廠函式回傳定製化物件
- **使** 物件建立具靈活性,無須嚴格階層結構
- **允許** 物件擁有正好需要的行為
選擇哪種模式完全取決於你。JavaScript 支援這兩種範式。
```javascript
// 第4步創建並使用你的組合物件
const hero = createHero(10, 10);
hero.moveTo(5, 5); // 運作完美!
--
const tree = createStatic(0, 0, 'Tree');
// tree.moveTo() 未定義 - 沒有組合任何移動行為
```
在遊戲開發中,另一種常見的模式是解決遊戲的用戶體驗和性能問題。
**要點回顧:**
- **透過組合** 混合行為而非繼承來構建物件
- **提供** 比嚴格繼承階層更大彈性
- **使** 物件有精確功能
- **運用** 現代 JavaScript 擴散運算符簡潔合併物件
```
## Pub/sub 模式
**Which Pattern Should You Choose?**
**Which Pattern Should You Choose?**
```mermaid
quadrantChart
title Code Organization Patterns
x-axis Simple --> Complex
y-axis Rigid --> Flexible
quadrant-1 Advanced Composition
quadrant-2 Hybrid Approaches
quadrant-3 Basic Inheritance
quadrant-4 Modern Composition
Class Inheritance: [0.3, 0.2]
Interface Implementation: [0.6, 0.4]
Mixin Patterns: [0.7, 0.7]
Pure Composition: [0.8, 0.9]
Factory Functions: [0.5, 0.8]
Prototype Chain: [0.4, 0.3]
```
> 💡 **專業提示**:兩種模式在現代 Javascript 開發中各有用途。類別適用明確定義的階層,組合則在需要極致彈性時表現優異。
>
**使用時機:**
- **選擇** 繼承當存在明確「是」關係(例如 Hero *是一個* 可移動物件)
- **挑選** 組合當需要「擁有」關係(例如 Hero *擁有* 移動能力)
- **考慮** 團隊偏好與專案需求
- **記得** 可以在同一專案中混用這兩種方法
### 🔄 **教學小檢核**
**物件組織理解**:在進入通訊模式前,請確保你能:
- ✅ 解釋繼承與組合的差別
- ✅ 分辨何時用類別或工廠函式
- ✅ 理解繼承中 `super()` 關鍵字的用途
- ✅ 認識每種方法對遊戲開發的好處
**快速自我測驗**:如何建立一個會移動且能飛行的 Flying Enemy
- **繼承方法**: `class FlyingEnemy extends Movable`
- **組合方法**: `{ ...movable, ...flyable, ...gameObject }`
**真實應用連結**:這些模式無處不在:
- **React 元件**: Props組合 vs 類別繼承
- **遊戲引擎**: 實體-組件系統採用組合
- **行動應用**: UI 框架常用繼承階層
## 通訊模式Pub/Sub 系統
當應用程式變複雜,管理組件間通訊變困難。發佈-訂閱pub/sub模式解決此問題原理類似無線電廣播——一個發射器可觸及多個接收器且不需知道誰在聽。
想像英雄受到傷害會發生什麼血條更新、音效播放、視覺反饋出現。Pub/sub 允許英雄廣播「受到傷害」訊息。任何需回應該事件的系統都可訂閱此訊息並對應處理,而非英雄直接連結到這些系統。
**Pub/Sub** 是「發布-訂閱」的縮寫
```mermaid
flowchart TD
A[英雄受傷] --> B[發布HERO_DAMAGED]
B --> C[事件系統]
C --> D[血量條訂閱者]
C --> E[聲音系統訂閱者]
C --> F[視覺效果訂閱者]
C --> G[成就系統訂閱者]
D --> H[更新血量顯示]
E --> I[播放受傷音效]
F --> J[顯示紅色閃爍]
G --> K[檢查生存成就]
style A fill:#ffebee
style B fill:#e1f5fe
style C fill:#e8f5e8
style H fill:#fff3e0
style I fill:#fff3e0
style J fill:#fff3e0
style K fill:#fff3e0
```
### 理解 Pub/Sub 架構
✅ Pub/Sub 代表 "發布-訂閱"
Pub/sub 模式讓應用內各部分鬆散耦合,意即它們能合作而不直接相依。這種分離使程式碼更可維護、易測試且彈性高。
這種模式的核心思想是應用程式的不同部分不應該彼此了解。為什麼呢?如果各部分是分離的,整體上會更容易理解發生了什麼。同時,如果需要突然改變行為,也會更容易實現。那麼我們如何做到這一點呢?我們可以通過建立以下概念來實現:
**pub/sub 主要角色:**
- **訊息** — 簡單的文字標籤,如 `'PLAYER_SCORED'`,描述事件(可附加額外資訊)
- **發佈者** — 發出「有事情發生!」訊息的物件
- **訂閱者** — 宣告「我關心這事件」並在事件發生時反應的物件
- **事件系統** — 中間人,確保訊息送達相應聽眾
- **訊息**:訊息通常是一個文字字串,並伴隨一個可選的有效負載(用於說明訊息內容的數據)。在遊戲中,典型的訊息可以是 `KEY_PRESSED_ENTER`
- **發布者**:這個元素 *發布* 一個訊息並將其發送給所有訂閱者。
- **訂閱者**:這個元素 *監聽* 特定的訊息,並根據接收到的訊息執行某些任務,例如發射雷射。
### 建立事件系統
這種模式的實現非常簡單,但它非常強大。以下是它的實現方式:
讓我們建立一個簡單卻強大的事件系統,展示這些概念:
```javascript
//set up an EventEmitter class that contains listeners
// 第一步:建立 EventEmitter 類別
class EventEmitter {
constructor() {
this.listeners = {};
this.listeners = {}; // 儲存所有事件監聽器
}
//when a message is received, let the listener to handle its payload
// 為特定訊息類型註冊監聽器
on(message, listener) {
if (!this.listeners[message]) {
this.listeners[message] = [];
}
this.listeners[message].push(listener);
}
//when a message is sent, send it to a listener with some payload
// 向所有已註冊的監聽器發送訊息
emit(message, payload = null) {
if (this.listeners[message]) {
this.listeners[message].forEach(l => l(message, payload))
this.listeners[message].forEach(listener => {
listener(message, payload);
});
}
}
}
```
要使用上述程式碼,我們可以建立一個非常小的實現:
**這裡發生的事情分解:**
- **建立** 使用簡單類別的中央事件管理系統
- **以訊息類型存儲** 聽眾於物件中
- **使用 `on()` 註冊** 新的監聽器
- **`emit()` 廣播** 訊息給所有關心的監聽者
- **支援** 選擇性資料載荷傳遞相關資訊
### 綜合應用示例
現在看到實作吧!我們建立一個簡單移動系統,展示 pub/sub 如何清晰且靈活:
```javascript
//set up a message structure
// 步驟 1定義你的消息類型
const Messages = {
HERO_MOVE_LEFT: 'HERO_MOVE_LEFT'
HERO_MOVE_LEFT: 'HERO_MOVE_LEFT',
HERO_MOVE_RIGHT: 'HERO_MOVE_RIGHT',
ENEMY_SPOTTED: 'ENEMY_SPOTTED'
};
//invoke the eventEmitter you set up above
// 步驟 2建立你的事件系統和遊戲物件
const eventEmitter = new EventEmitter();
//set up a hero
const hero = createHero(0,0);
//let the eventEmitter know to watch for messages pertaining to the hero moving left, and act on it
const hero = createHero(0, 0);
```
**程式碼說明:**
- **定義** 常數物件避免訊息名稱錯誤
- **建立** 事件發射器實例處理所有通訊
- **初始化** 英雄物件於起始位置
```javascript
// 第3步設置事件監聽器訂閱者
eventEmitter.on(Messages.HERO_MOVE_LEFT, () => {
hero.move(5,0);
hero.moveTo(hero.x - 5, hero.y);
console.log(`Hero moved to position: ${hero.x}, ${hero.y}`);
});
//set up the window to listen for the keyup event, specifically if the left arrow is hit, emit a message to move the hero left
window.addEventListener('keyup', (evt) => {
if (evt.key === 'ArrowLeft') {
eventEmitter.emit(Messages.HERO_MOVE_LEFT)
}
eventEmitter.on(Messages.HERO_MOVE_RIGHT, () => {
hero.moveTo(hero.x + 5, hero.y);
console.log(`Hero moved to position: ${hero.x}, ${hero.y}`);
});
```
在上面的程式碼中,我們連接了一個鍵盤事件 `ArrowLeft`,並發送 `HERO_MOVE_LEFT` 訊息。我們監聽該訊息並移動 `hero` 作為結果。這種模式的優勢在於事件監聽器和英雄彼此並不相互了解。你可以將 `ArrowLeft` 重新映射到 `A` 鍵。此外,通過對 eventEmitter 的 `on` 函數進行一些編輯,也可以在 `ArrowLeft` 上執行完全不同的操作:
**上述程式碼中,我們:**
- **註冊** 事件監聽器回應移動訊息
- **根據移動方向** 更新英雄位置
- **加入** 主控台日誌追蹤英雄位置變化
- **將** 移動邏輯與輸入處理分離
```javascript
eventEmitter.on(Messages.HERO_MOVE_LEFT, () => {
hero.move(5,0);
// 第四步:將鍵盤輸入連接到事件(發布者)
window.addEventListener('keydown', (event) => {
switch(event.key) {
case 'ArrowLeft':
eventEmitter.emit(Messages.HERO_MOVE_LEFT);
break;
case 'ArrowRight':
eventEmitter.emit(Messages.HERO_MOVE_RIGHT);
break;
}
});
```
隨著遊戲規模的增大,事情變得更加複雜,但這種模式的複雜性保持不變,並且你的程式碼保持乾淨。非常推薦採用這種模式。
**理解這些概念:**
- **連結** 鍵盤輸入與遊戲事件,避免緊耦合
- **使** 輸入系統間接與遊戲物件通訊
- **允許** 多個系統同時回應鍵盤事件
- **方便** 更改按鍵綁定或新增輸入方式
```mermaid
sequenceDiagram
participant User
participant Keyboard
participant EventEmitter
participant Hero
participant SoundSystem
participant Camera
User->>Keyboard: 按下左方向鍵
Keyboard->>EventEmitter: emit('HERO_MOVE_LEFT')
EventEmitter->>Hero: 向左移動 5 像素
EventEmitter->>SoundSystem: 播放腳步聲
EventEmitter->>Camera: 跟隨英雄
Hero->>Hero: 更新位置
SoundSystem->>SoundSystem: 播放音效
Camera->>Camera: 調整視窗
```
> 💡 **專業提示**:這模式的美妙之處在於彈性!你可以輕鬆新增音效、螢幕震動或粒子效果,只需添加事件監聽器──不必更改現有鍵盤或移動程式碼。
>
**為何你會喜愛此模式:**
- 新增功能變得超簡單──只要收聽你想關注的事件
- 多個系統可同時反應同一事件,互不干擾
- 測試更直觀,因各部分獨立運作
- 發生錯誤時知道確切檢查位置
### Pub/Sub 為何能有效擴展
Pub/sub 模式保持架構簡潔,即使應用變得龐大。無論管理數十個敵人、動態 UI 更新或音效系統,其模式均能應付規模提升而無需架構變更。新功能可整合進現有事件系統,不影響既有功能。
> ⚠️ **常見錯誤**:不要一開始就創建太多特定訊息類型。先從廣泛類別開始,隨遊戲需求漸明逐漸細化。
>
**最佳實踐:**
- **將** 相關訊息分為合理類群
- **使用** 明確描述事件的名稱
- **保持** 訊息載荷簡潔且聚焦
- **為團隊** 製作訊息類型文件與說明
### 🔄 **教學小檢核**
**事件驅動架構理解**:確認你掌握整套系統:
- ✅ pub/sub 如何避免組件間緊耦合?
- ✅ 為何事件驅動架構更容易新增功能?
- ✅ EventEmitter 在通訊流程中扮演什麼角色?
- ✅ 訊息常數如何防止錯誤並提升可維護性?
**設計挑戰**:這些遊戲場景你會怎麼用 pub/sub 處理?
1. **敵人死亡**:更新分數、播放音效、產生強化道具、從螢幕移除
2. **關卡完成**:停止音樂、顯示 UI、保存進度、載入下一關
3. **強化道具收集**:增強能力、更新 UI、播放特效、啟動計時器
**專業連結**:此模式出現在:
- **前端框架**React/Vue 事件系統
- **後端服務**:微服務通訊
- **遊戲引擎**Unity 事件系統
- **行動開發**iOS/Android 通知系統
---
## 🚀 挑戰
## GitHub Copilot Agent 挑戰 🚀
思考 Pub/Sub 模式如何增強遊戲。哪些部分應該發出事件,遊戲應該如何對它們作出反應?現在是你發揮創意的時候,想像一個新遊戲及其各部分可能的行為。
使用 Agent 模式完成以下挑戰:
## 課後測驗
**描述:** 使用繼承和 pub/sub 模式創建一個簡單遊戲物件系統。你將實現一個基本遊戲,讓不同物件透過事件通訊,而不需彼此直接認識。
[課後測驗](https://ff-quizzes.netlify.app/web/quiz/30)
**提示:** 建立 JavaScript 遊戲系統需求如下1) 創建基底 GameObject 類,帶有 x, y 座標與類型屬性。2) 創建 Hero 類繼承自 GameObject並能移動。3) 創建 Enemy 類繼承 GameObject能追蹤英雄。4) 實作 EventEmitter 類作為 pub/sub 模式。5) 設置事件監聽器,當英雄移動時,附近敵人會收到 'HERO_MOVED' 事件,並更新位置朝英雄移動。加入 console.log 陳述顯示物件通訊。
## 回顧與自學
想了解更多 [agent 模式](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) 請點此。
通過 [閱讀相關內容](https://docs.microsoft.com/azure/architecture/patterns/publisher-subscriber/?WT.mc_id=academic-77807-sagibbon) 了解更多關於 Pub/Sub 的知識。
## 🚀 挑戰
考慮 pub-sub 模式如何加強遊戲架構。識別哪些元件應該發出事件,以及系統應如何回應。設計一個遊戲概念並繪製其元件間的通訊模式。
## 課後小測驗
[課後小測驗](https://ff-quizzes.netlify.app/web/quiz/30)
## 複習與自學
透過[閱讀相關內容](https://docs.microsoft.com/azure/architecture/patterns/publisher-subscriber/?WT.mc_id=academic-77807-sagibbon)深入了解 Pub/Sub。
### ⚡ **你可以在接下來 5 分鐘做的事**
- [ ] 打開任何線上 HTML5 遊戲並用 DevTools 檢查其程式碼
- [ ] 創建一個簡單的 HTML5 Canvas 元素並繪製基本圖形
- [ ] 嘗試使用 `setInterval` 來製作一個簡單的動畫循環
- [ ] 探索 Canvas API 文件並嘗試繪製方法
### 🎯 **你這一小時可以完成的目標**
- [ ] 完成課後測驗並理解遊戲開發概念
- [ ] 建立遊戲專案架構HTML、CSS、JavaScript 檔案)
- [ ] 製作基本遊戲迴圈,持續更新與渲染
- [ ] 在畫布上繪製你的第一批遊戲精靈
- [ ] 實作圖片與音效的基本資源加載
### 📅 **你的週度遊戲製作計劃**
- [ ] 完成具備所有計劃功能的完整太空遊戲
- [ ] 增加精緻的圖形、音效和流暢動畫
- [ ] 實作遊戲狀態(開始畫面、遊戲進行、遊戲結束)
- [ ] 製作計分系統與玩家進度追蹤
- [ ] 使你的遊戲適應各種裝置並具備可及性
- [ ] 在線分享遊戲並從玩家取得回饋
### 🌟 **你的月度遊戲開發規劃**
- [ ] 製作多款不同類型及機制的遊戲
- [ ] 學習 Phaser 或 Three.js 等遊戲開發框架
- [ ] 參與開源遊戲開發專案
- [ ] 精通進階遊戲編程模式與優化技巧
- [ ] 建立展示你的遊戲開發作品集
- [ ] 指導有興趣的遊戲開發者與互動媒體創作者
## 🎯 你的遊戲開發精通時間表
```mermaid
timeline
title 遊戲架構學習進程
section 物件模式20分鐘
代碼組織:類別繼承
:組合模式
:工廠函數
:行為混入
section 通訊系統25分鐘
事件架構:發布/訂閱實作
:訊息設計
:事件發射器
:鬆耦合
section 遊戲物件設計30分鐘
實體系統:屬性管理
:行為組合
:狀態處理
:生命週期管理
section 架構模式35分鐘
系統設計:元件系統
:觀察者模式
:命令模式
:狀態機
section 進階概念45分鐘
可擴展架構:效能優化
:記憶體管理
:模組化設計
:測試策略
section 遊戲引擎概念(一週)
專業發展:場景圖
:資產管理
:渲染管線
:物理整合
section 框架精通(兩週)
現代遊戲開發React遊戲模式
Canvas優化
WebGL基礎
PWA遊戲
section 業界實務(一個月)
專業技能:團隊協作
:程式碼審查
:遊戲設計模式
:效能分析
```
### 🛠️ 你的遊戲架構工具組總結
完成本課程後,你已具備:
- **設計模式掌握**:理解繼承與組合的取捨
- **事件驅動架構**Pub/sub 實作以支持可擴充通訊
- **物件導向設計**:類別階層與行為組合
- **現代 JavaScript**:工廠函式、展開語法與 ES6+ 模式
- **可擴充架構**:鬆耦合與模組化設計原則
- **遊戲開發基礎**:實體系統與元件模式
- **專業模式**:業界標準的程式碼組織方法
**實際應用**:這些模式直接適用於:
- **前端框架**React/Vue 元件架構與狀態管理
- **後端服務**:微服務通訊與事件驅動系統
- **行動開發**iOS/Android 應用架構與通知系統
- **遊戲引擎**Unity、Unreal 與網頁遊戲開發
- **企業軟體**:事件源與分散式系統設計
- **API 設計**RESTful 服務與即時通訊
**專業技能提升**:你現在能:
- **設計** 使用成熟模式的可擴充軟體架構
- **實作** 處理複雜互動的事件驅動系統
- **選擇** 適合不同情境的程式碼組織策略
- **除錯** 與維護鬆耦合系統
- **溝通** 使用業界標準術語說明技術決策
**下一階段**:你已準備好將這些模式套用於真實遊戲、探索進階遊戲開發議題,或將架構概念應用於網頁應用!
🌟 **成就解鎖**:你已精通從簡單遊戲到複雜企業系統核心架構模式!
## 作業
[設計一個遊戲](assignment.md)
[設計一遊戲](assignment.md)
---
**免責聲明**
此文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議使用專業的人手翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋不承擔責任。
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件由 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 所翻譯。雖然我們致力於達到準確性,但請注意自動翻譯可能包含錯誤或不準確之處。原始語言版本的文件應被視為權威來源。對於重要資訊,建議採用專業人工翻譯。我們對因使用本翻譯而引起的任何誤解或誤釋概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,23 +1,102 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "009bdedee9cc82988264be8cb31f9bf4",
"translation_date": "2025-08-23T23:06:20+00:00",
"original_hash": "c8fc39a014d08247c082878122e2ba73",
"translation_date": "2026-01-06T12:37:04+00:00",
"source_file": "6-space-game/1-introduction/assignment.md",
"language_code": "hk"
}
-->
# 模擬一個遊戲
# 模擬設計一款遊戲:應用設計模式
## 指引
## 作業概述
利用課堂中的代碼範例,寫一個你喜歡的遊戲的簡單表示形式。這需要是一個簡單的遊戲,但目標是使用類別或組合模式以及發布/訂閱模式來展示遊戲如何啟動。發揮創意吧!
利用你新學到的設計模式知識,創造一個簡單的遊戲原型!這個作業將幫助你練習架構模式(繼承或組合)以及課程中學到的 pub/sub 通訊系統。
## 指示
創造一個簡單的遊戲表示,展示本課程的設計模式。你的遊戲應該具備功能性,但不需要複雜的圖形 — 重點在於底層架構和通訊模式。
### 要求
**選擇你的架構模式:**
- **選項 A**:使用基於類別的繼承(如 `GameObject``Movable``Hero` 範例)
- **選項 B**:使用組合(如工廠函式方法搭配混合行為)
**實作通訊:**
- **包含** 一個 `EventEmitter` 類別用於 pub/sub 訊息傳遞
- **設定** 至少 2-3 種不同訊息類型(如 `PLAYER_MOVE`、`ENEMY_SPAWN`、`SCORE_UPDATE`
- **連結** 使用者輸入(鍵盤/滑鼠)透過事件系統驅動遊戲事件
**必須包含的遊戲元素:**
- 至少一個玩家控制角色
- 至少一個其他遊戲物件(敵人、可收集物或環境元素)
- 基本的物件互動(碰撞、收集或通訊)
### 建議遊戲構想
**可以考慮的簡單遊戲:**
- **貪食蛇遊戲** — 蛇身會跟隨蛇頭,食物隨機產生
- **乒乓球變化版** — 球拍響應輸入,球會彈碰牆壁
- **收集者遊戲** — 玩家移動收集物品同時躲避障礙
- **塔防基礎** — 防禦塔偵測並射擊移動敵人
### 程式結構指引
```javascript
// 範例啟始結構
const Messages = {
// 在此定義你的遊戲訊息
};
class EventEmitter {
// 你的事件系統實作
}
// 選擇類別基礎或組合方式
// 類別基礎範例:
class GameObject { /* base properties */ }
class Player extends GameObject { /* player-specific behavior */ }
// 或組合範例:
const gameObject = { /* base properties */ };
const movable = { /* movement behavior */ };
function createPlayer() { /* combine behaviors */ }
```
### 測試你的實作
**驗證程式運作的方法:**
- **測試** 事件觸發時物件是否移動或改變
- **確認** 多個物件是否能對同一事件作出反應
- **檢查** 是否能新增行為而不修改現有程式碼
- **確保** 鍵盤/滑鼠輸入正確觸發遊戲事件
## 繳交指引
**你的繳交應包含:**
1. **JavaScript 檔案** 實作你的遊戲
2. **HTML 檔案** 用於執行和測試你的遊戲(可簡單)
3. **註解** 說明選用的模式及原因
4. **簡短文檔** 說明你的訊息類型及其功能
## 評分標準
| 評分標準 | 優異 | 合格 | 需要改進 |
| -------- | ------------------------------------------------------- | ----------------------------------------------------- | --------------------------------------------------- |
| | 在螢幕上放置並操作三個元素 | 在螢幕上放置並操作兩個元素 | 在螢幕上放置並操作一個元素 |
| 評分項目 | 優秀3 分) | 足夠2 分) | 需改進1 分) |
|----------|--------------|--------------|----------------|
| **架構模式** | 正確實作繼承或組合,有清楚的類別/物件階層 | 使用選擇的模式但有小問題或不一致 | 嘗試使用模式但實作有重大問題 |
| **Pub/Sub 實作** | EventEmitter 正確運作,支援多種訊息類型及事件流程 | 基本 pub/sub 系統運作且有事件處理 | 有事件系統但不穩定或無法正常運作 |
| **遊戲功能** | 三個以上互動元素能透過事件溝通 | 兩個互動元素且有基本事件通信 | 一個元素回應事件或有簡單互動 |
| **程式碼品質** | 程式碼乾淨、有良好註解、組織合理且採用現代 JavaScript | 一般組織與註解尚可 | 程式碼可用但缺乏組織或清楚註解 |
**加分項目:**
- **具創意的遊戲機制** 展現設計模式有趣的應用
- **多種輸入方式**(鍵盤及滑鼠事件皆有)
- **可擴展架構** 方便新增功能擴充
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋不承擔責任。
本文件由 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們力求準確,但請注意自動翻譯可能包含錯誤或不準確之處。原始文件的原文版本應被視為權威來源。對於重要資訊,建議尋求專業人工翻譯。本公司對於因使用本翻譯而引起的任何誤解或誤釋概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,230 +1,624 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "056641280211e52fd0adb81b6058ec55",
"translation_date": "2025-08-29T15:03:22+00:00",
"original_hash": "7994743c5b21fdcceb36307916ef249a",
"translation_date": "2026-01-06T12:29:28+00:00",
"source_file": "6-space-game/2-drawing-to-canvas/README.md",
"language_code": "hk"
}
-->
# 建立太空遊戲第二部分:將英雄和怪物繪製到 Canvas
# 建立一個太空遊戲 第二部分:在畫布上繪製主角與怪物
```mermaid
journey
title 你的 Canvas 繪圖旅程
section 基礎
了解 Canvas API: 3: Student
學習座標系統: 4: Student
繪製基本圖形: 4: Student
section 圖像處理
載入遊戲資源: 4: Student
處理非同步載入: 5: Student
定位精靈: 5: Student
section 遊戲渲染
建立遊戲畫面: 5: Student
建造陣型: 5: Student
優化效能: 4: Student
```
Canvas API 是網頁開發中最強大的功能之一,能讓你在瀏覽器中直接創建動態且互動的圖形。在本課程中,我們將把空白的 HTML `<canvas>` 元素變成一個充滿英雄與怪物的遊戲世界。可以把畫布想像成你的數碼繪畫板,程式碼變成了視覺呈現。
我們將延續上一堂課所學的內容,現在深入視覺層面。你將學會如何載入並顯示遊戲精靈 (sprite)、精確定位元素,並為你的太空遊戲建立視覺基礎。這將連結靜態網頁與動態互動體驗之間的差距。
課程結束時,你將擁有一個完整的遊戲場景,主角飛船正確定位,敵軍陣型準備出擊。你會理解現代遊戲如何在瀏覽器中渲染圖像,並獲得創建互動視覺體驗的技能。一起探索畫布圖形,讓你的太空遊戲活起來吧!
```mermaid
mindmap
root((畫布圖形))
Canvas Element
HTML5 功能
2D 上下文
坐標系統
像素控制
Drawing Operations
基本形狀
文字渲染
圖像顯示
路徑繪製
Asset Management
圖像載入
非同步操作
錯誤處理
性能
Game Rendering
精靈位置
陣型佈局
場景組合
幀更新
Visual Effects
顏色與樣式
變換
動畫
圖層
```
## 課前小測驗
## 課前測驗
[課前小測驗](https://ff-quizzes.netlify.app/web/quiz/31)
[課前測驗](https://ff-quizzes.netlify.app/web/quiz/31)
## 什麼是 Canvas
## Canvas
那這個 `<canvas>` 元素到底是什麼?它是 HTML5 用來在瀏覽器中建立動態圖形和動畫的解決方案。不同於靜態的圖片或影片canvas 能讓你對螢幕上的每一個像素進行控制。這使它非常適合遊戲、數據視覺化以及互動藝術。它就像是一個可編程的繪畫表面,而 JavaScript 是你的畫筆。
Canvas 是一個 HTML 元素,預設是空白的;它是一塊空白的畫布,你需要透過繪圖來填充內容。
預設情況下canvas 元素看起來像是頁面上一個空白且透明的矩形。但潛力就在這裡!當你用 JavaScript 畫出形狀、載入圖片、創造動畫並讓畫面回應使用者互動時,它的力量就會顯現。這種方式類似於 1960 年代貝爾實驗室初期電腦圖形先驅,必須逐像素編寫程式來創造第一批數字動畫
✅ 在 MDN 上閱讀 [更多關於 Canvas API 的資訊](https://developer.mozilla.org/docs/Web/API/Canvas_API)。
✅ 在 MDN 上閱讀[更多關於 Canvas API 的介紹](https://developer.mozilla.org/docs/Web/API/Canvas_API)。
以下是它通常在頁面中宣告的方式:
它通常會這樣被宣告,在頁面主體中:
```html
<canvas id="myCanvas" width="200" height="100"></canvas>
```
上面我們設定了 `id`、`width` 和 `height`
- `id`:設定這個值以便在需要與它互動時取得引用。
- `width`:這是元素的寬度。
- `height`:這是元素的高度。
**這段程式碼的作用是:**
- **設定** `id` 屬性,好讓你在 JavaScript 中引用這個特定的 canvas 元素
- **定義** 寬度,單位為像素,用來控制 canvas 的水平尺寸
- **建立** 高度,單位為像素,用來決定 canvas 的垂直大小
## 繪製簡單幾何圖形
Canvas 使用笛卡爾座標系統來繪製物件,因此它使用 x 軸和 y 軸來表示物件的位置。位置 `0,0` 是左上角,而右下角則是你設定的 Canvas 的寬度和高度。
![Canvas 的網格](../../../../translated_images/canvas_grid.5f209da785ded492a01ece440e3032afe51efa500cc2308e5ea4252487ceaf0b.hk.png)
> 圖片來源:[MDN](https://developer.mozilla.org/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes)
要在 Canvas 元素上繪圖,你需要完成以下步驟:
1. **取得引用** Canvas 元素。
2. **取得引用** Canvas 元素上的 Context 元素。
3. **使用 Context 元素執行繪圖操作**。
現在你知道 canvas 元素是什麼了讓我們來探索如何真正地在它上面繪製canvas 使用的座標系你可能在數學課有聽過,唯獨有一個關於電腦圖形的特別地方。
canvas 使用的是笛卡爾座標系,有一條 x 軸(水平)和 y 軸(垂直)來定位你畫的所有東西。但關鍵差異在於:不同於數學課上的座標系,原點 `(0,0)` 是從左上角開始x 值向右增長y 值則向下增長。這個方法源自早期電腦顯示器,電子束從上往下掃描,因此左上角是自然的起點。
```mermaid
quadrantChart
title 畫布座標系統
x-axis 左 --> 右
y-axis 上 --> 下
quadrant-1 第一象限
quadrant-2 第二象限
quadrant-3 第三象限
quadrant-4 第四象限
Origin Point: [0.1, 0.1]
Hero Center: [0.5, 0.8]
Enemy Formation: [0.3, 0.2]
Power-up: [0.7, 0.6]
UI Elements: [0.9, 0.1]
```
![canvas 的格線](../../../../translated_images/canvas_grid.5f209da785ded492.hk.png)
> 圖片來自 [MDN](https://developer.mozilla.org/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes)
在 canvas 元素上繪圖,你會按照以下三個步驟,這是所有 canvas 圖形的基礎。練習幾次之後,你就能輕鬆習慣:
```mermaid
flowchart LR
A[HTML 畫布元素] --> B[取得畫布參考]
B --> C[取得 2D 上下文]
C --> D[繪圖操作]
D --> E[繪製圖形]
D --> F[繪製文字]
D --> G[繪製圖像]
D --> H[套用樣式]
E --> I[渲染到螢幕]
F --> I
G --> I
H --> I
style A fill:#e1f5fe
style C fill:#e8f5e8
style I fill:#fff3e0
```
1. **從 DOM 取得** canvas 元素參考(就像取得其他 HTML 元素一樣)
2. **取得 2D 繪圖上下文** — 這提供了所有繪圖方法
3. **開始繪製!** 利用上下文的內建方法來創建你的圖形
上述步驟的程式碼通常如下所示:
程式碼看起來像這樣
```javascript
// draws a red rectangle
//1. get the canvas reference
canvas = document.getElementById("myCanvas");
// 第一步:獲取畫布元素
const canvas = document.getElementById("myCanvas");
//2. set the context to 2D to draw basic shapes
ctx = canvas.getContext("2d");
// 第二步獲取2D繪圖上下文
const ctx = canvas.getContext("2d");
//3. fill it with the color red
// 第三步:設定填充顏色並繪製矩形
ctx.fillStyle = 'red';
//4. and draw a rectangle with these parameters, setting location and size
ctx.fillRect(0,0, 200, 200) // x,y,width, height
ctx.fillRect(0, 0, 200, 200); // x, y, 寬度, 高度
```
✅ Canvas API 主要專注於 2D 圖形,但你也可以在網站上繪製 3D 元素;為此,你可以使用 [WebGL API](https://developer.mozilla.org/docs/Web/API/WebGL_API)。
**分解步驟:**
- 我們用 ID 取得 canvas 元素,並存入變數
- 取得 2D 繪圖上下文 — 這是裝滿繪圖方法的工具箱
- 告訴 canvas 使用 `fillStyle` 屬性設成紅色
- 繪製一個從左上角 (0,0) 開始、寬高皆為 200 像素的矩形
使用 Canvas API你可以繪製各種內容例如
✅ Canvas API 主要聚焦於 2D 形狀,但你也可以在網站上繪製 3D 元素;這時可能會用到 [WebGL API](https://developer.mozilla.org/docs/Web/API/WebGL_API)。
- **幾何圖形**,我們已展示如何繪製矩形,但還有更多可以繪製的內容。
- **文字**,你可以使用任何字型和顏色繪製文字。
- **圖片**,你可以根據圖片資源(例如 .jpg 或 .png繪製圖片。
你可以用 Canvas API 繪製各種東西:
✅ 試試看!你已經知道如何繪製矩形,能否嘗試在頁面上繪製一個圓形?看看 CodePen 上一些有趣的 Canvas 繪圖。這裡有一個 [特別令人印象深刻的例子](https://codepen.io/dissimulate/pen/KrAwx)。
- **幾何圖形**,我們已經示範過如何繪製矩形,但其實可以畫更多。
- **文字**,你可以使用任何字體和顏色繪製文字。
- **圖片**,你可以根據圖片資源,如 .jpg 或 .png繪製圖像。
## 加載並繪製圖片資源
✅ 試試看!你知道如何畫矩形,能問問自己怎麼畫一個圓嗎?可以看看 CodePen 上一些有趣的 Canvas 畫作。這裡有個[特別令人印象深刻的範例](https://codepen.io/dissimulate/pen/KrAwx)。
你可以透過建立一個 `Image` 物件並設定其 `src` 屬性來加載圖片資源。然後監聽 `load` 事件,以確保圖片已準備好使用。程式碼如下:
### 🔄 **教學檢核點**
**Canvas 基礎理解**:在進入圖片載入前,請確保你會:
- ✅ 解釋 canvas 座標系如何與數學座標系不同
- ✅ 理解繪製 canvas 的三步驟流程
- ✅ 知道 2D 繪圖上下文提供什麼功能
- ✅ 描述 fillStyle 與 fillRect 是如何搭配使用
### 加載資源
**快速自我測試**:如何在 (100, 50) 位置畫一個半徑為 25 的藍色圓形?
```javascript
ctx.fillStyle = 'blue';
ctx.beginPath();
ctx.arc(100, 50, 25, 0, 2 * Math.PI);
ctx.fill();
```
**你已經知道的 Canvas 繪圖方法:**
- **fillRect()**:繪製實心矩形
- **fillStyle**:設定顏色和圖案
- **beginPath()**:開始新的繪圖路徑
- **arc()**:繪製圓和曲線
## 載入與繪製圖片資源
繪製基本形狀很好入門,但大多數遊戲需要真正的圖片!精靈圖、背景和材質是遊戲視覺吸引力的關鍵。載入並顯示圖片與繪製幾何形狀的方法不同,但理解過程後相當簡單。
我們需要建立一個 `Image` 物件,載入圖片檔案(這是非同步過程,也就是「在背景進行」),然後一旦圖片準備好,就繪製到 canvas 上。這種方式可確保圖片正確顯示,且不會阻塞應用程式的運行。
```mermaid
sequenceDiagram
participant JS as JavaScript
participant Img as 圖像物件
participant Server as 檔案伺服器
participant Canvas as 畫布上下文
JS->>Img: new Image()
JS->>Img: 設定 src 屬性
Img->>Server: 請求圖像檔案
Server->>Img: 回傳圖像資料
Img->>JS: 觸發 onload 事件
JS->>Canvas: drawImage(img, x, y)
Canvas->>Canvas: 渲染到螢幕
Note over JS,Canvas: 非同步載入防止介面阻塞
```
### 基本的圖片載入
```javascript
const img = new Image();
img.src = 'path/to/my/image.png';
img.onload = () => {
// image loaded and ready to be used
}
// 圖像已加載並準備好使用
console.log('Image loaded successfully!');
};
```
### 加載資源模式
**以下這段程式碼的運作原理:**
- 建立一個全新的 Image 物件來放置精靈或材質
- 指定要載入的圖片檔案路徑
- 監聽載入事件,確保圖片準備好後才使用
建議將上述程式碼包裝在如下結構中,這樣更容易使用,並且只有在資源完全加載後才嘗試操作它:
### 一個更好的圖片載入方法
這邊示範一個更健壯的圖片載入方式,專業開發者常用,將圖片載入包裝成 Promise 函式 — 這種方式在 ES6 讓 JavaScript Promises 標準化後廣受歡迎,程式碼更清晰也能優雅地處理錯誤:
```javascript
function loadAsset(path) {
return new Promise((resolve) => {
return new Promise((resolve, reject) => {
const img = new Image();
img.src = path;
img.onload = () => {
// image loaded and ready to be used
resolve(img);
}
})
};
img.onerror = () => {
reject(new Error(`Failed to load image: ${path}`));
};
});
}
// use like so
async function run() {
const heroImg = await loadAsset('hero.png')
const monsterImg = await loadAsset('monster.png')
// 現代用法,配合 async/await
async function initializeGame() {
try {
const heroImg = await loadAsset('hero.png');
const monsterImg = await loadAsset('monster.png');
// 圖像現已準備好使用
} catch (error) {
console.error('Failed to load game assets:', error);
}
}
```
要將遊戲資源繪製到螢幕上,程式碼如下:
**我們做了什麼:**
- 把整個圖片載入邏輯包裝進 Promise方便更好的管理
- 加入錯誤處理,確保出錯時會通知
- 使用現代 async/await 語法,讓程式碼更易讀
- 用 try/catch 區塊優雅地處理載入問題
載入完成後,繪製到 canvas 實際上非常簡單:
```javascript
async function run() {
const heroImg = await loadAsset('hero.png')
const monsterImg = await loadAsset('monster.png')
canvas = document.getElementById("myCanvas");
ctx = canvas.getContext("2d");
ctx.drawImage(heroImg, canvas.width/2,canvas.height/2);
ctx.drawImage(monsterImg, 0,0);
async function renderGameScreen() {
try {
// 載入遊戲資產
const heroImg = await loadAsset('hero.png');
const monsterImg = await loadAsset('monster.png');
// 獲取畫布和上下文
const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d");
// 將圖像繪製到指定位置
ctx.drawImage(heroImg, canvas.width / 2, canvas.height / 2);
ctx.drawImage(monsterImg, 0, 0);
} catch (error) {
console.error('Failed to render game screen:', error);
}
}
```
**逐步說明:**
- 使用 await 同步載入英雄與怪物圖片
- 取得 canvas 元素並抓取 2D 渲染上下文
- 利用簡單的座標數學把英雄圖片定位到正中間
- 把怪物圖片放在左上角,開始排列敵人陣型
- 捕捉載入或繪製過程中可能發生的錯誤
```mermaid
flowchart TD
A[加載資源] --> B{所有圖片已加載?}
B -->|否| C[顯示加載中]
B -->|是| D[獲取畫布上下文]
C --> B
D --> E[清除畫面]
E --> F[繪製背景]
F --> G[繪製敵人陣型]
G --> H[繪製英雄飛船]
H --> I[應用視覺效果]
I --> J[渲染畫面]
subgraph "渲染流程"
K[資源管理]
L[場景組合]
M[繪製操作]
N[畫面輸出]
end
style A fill:#e1f5fe
style J fill:#e8f5e8
style I fill:#fff3e0
```
## 現在是開始建立你的遊戲的時候了
### 建立什麼
現在我們把所有東西整合,建立你的太空遊戲的視覺基礎。你已經對 canvas 基礎和圖像載入技術有所掌握,這個實作單元會引導你創建完整的遊戲畫面,並正確地擺放精靈。
### 要建立什麼
你將建立一個包含 Canvas 元素的網頁。它應該渲染一個黑色螢幕,大小為 `1024*768`。我們已提供兩張圖片給你:
你將建立一個含有 Canvas 元素的網頁。它要呈現一個寬 1024px、高 768px、背景為黑色的畫面。我們已為你準備了兩張圖片
- 英雄飛船
![英雄飛船](../../../../translated_images/player.dd24c1afa8c71e9b82b2958946d4bad13308681392d4b5ddcc61a0e818ef8088.hk.png)
![英雄飛船](../../../../translated_images/player.dd24c1afa8c71e9b.hk.png)
- 5*5 怪物
- 5×5 隻怪物
![怪物飛船](../../../../translated_images/enemyShip.5df2a822c16650c2fb3c06652e8ec8120cdb9122a6de46b9a1a56d54db22657f.hk.png)
![怪物飛船](../../../../translated_images/enemyShip.5df2a822c16650c2.hk.png)
### 建議的開發步驟
### 推薦開發步驟
找到在 `your-work` 子資料夾中為你建立的檔案。它應包含以下內容:
`your-work` 子資料夾中你可以找到為你建立的入門檔案。你的專案結構應該包含
```bash
-| assets
-| enemyShip.png
-| player.png
-| index.html
-| app.js
-| package.json
your-work/
├── assets/
│ ├── enemyShip.png
│ └── player.png
├── index.html
├── app.js
└── package.json
```
在 Visual Studio Code 中打開這個資料夾的副本。你需要設置本地開發環境,最好使用 Visual Studio Code 並安裝 NPM 和 Node。如果你的電腦尚未設置 `npm`[這裡是設置方法](https://www.npmjs.com/get-npm)。
**你手上的資源有:**
- **遊戲精靈** 存放於 `assets/` 資料夾,方便管理
- **主 HTML 檔案** 用於設定 canvas 元素並準備環境
- **JavaScript 檔案** 你將在這裡撰寫所有遊戲渲染程式碼
- **package.json** 用於設置開發伺服器,讓你能在本機測試
打開該資料夾於 Visual Studio Code 開始開發。你需要一個本機開發環境,包含 Visual Studio Code、NPM 以及 Node.js。如果你尚未安裝 npm[可以點這裡了解如何安裝](https://www.npmjs.com/get-npm)。
通過導航到 `your_work` 資料夾開始你的專案:
透過命令行導航到 `your-work` 資料夾,啟動你的開發伺服器
```bash
cd your-work
npm start
```
上述指令會在地址 `http://localhost:5000` 上啟動一個 HTTP 伺服器。打開瀏覽器並輸入該地址。現在是一個空白頁面,但很快就會改變。
**這條命令能做什麼:**
- **啟動** 在 `http://localhost:5000` 的本機伺服器,方便你測試遊戲
- **正確服務** 你的所有檔案,確保瀏覽器可以順利載入
- **監控檔案** 變動,讓你開發時更順暢
- **提供** 專業的開發環境體驗
> 注意:要在螢幕上看到變更,請刷新瀏覽器。
> 💡 **注意**:你的瀏覽器一開始會呈現空白頁面 — 這是正常的!隨著你添加程式碼,請刷新瀏覽器查看變動。這種反覆迭代開發的方式很像 NASA 構建阿波羅導航電腦時,先測試單一組件再整合到整體系統
### 添加程式碼
### 新增程式碼
`your-work/app.js`添加所需程式碼以完成以下任務:
`your-work/app.js`加入必要程式碼以完成下列任務:
1. **繪製**一個黑色背景的 Canvas
> 提示:在 `/app.js` 中適當的 TODO 下添加兩行程式碼,將 `ctx` 元素設置為黑色,並將左上角座標設置為 0,0高度和寬度設置為 Canvas 的大小。
2. **加載**材質
> 提示:使用 `await loadTexture` 並傳入圖片路徑來加載玩家和敵人的圖片。你暫時還看不到它們出現在螢幕上!
3. **繪製**英雄到螢幕下半部分的中心
> 提示:使用 `drawImage` API 將 heroImg 繪製到螢幕上,座標設置為 `canvas.width / 2 - 45``canvas.height - canvas.height / 4)`
4. **繪製** 5*5 怪物
> 提示:現在你可以取消註解程式碼以將敵人繪製到螢幕上。接下來,進入 `createEnemies` 函數並完成它。
1. **繪製黑色背景的 canvas**
> 💡 **做法**:找到 `/app.js` 裡的 TODO 標記,增加兩行程式碼。設定 `ctx.fillStyle` 為黑色,然後用 `ctx.fillRect()` 繪製整個 canvas 大小。非常簡單!
首先,設置一些常數:
2. **載入遊戲材質**
> 💡 **做法**:用 `await loadAsset()` 載入玩家與敵人圖片,用變數保存。記得沒有繪製前是看不到的!
```javascript
const MONSTER_TOTAL = 5;
const MONSTER_WIDTH = MONSTER_TOTAL * 98;
const START_X = (canvas.width - MONSTER_WIDTH) / 2;
const STOP_X = START_X + MONSTER_WIDTH;
```
3. **將英雄飛船繪製於底部中間位置**
> 💡 **做法**:利用 `ctx.drawImage()` 來定位英雄。x 座標用 `canvas.width / 2 - 45` 置中y 座標用 `canvas.height - canvas.height / 4` 放在下方區域。
然後,建立一個迴圈將怪物陣列繪製到螢幕上:
4. **繪製 5×5 的敵人隊形**
> 💡 **做法**:找到 `createEnemies` 函式,設置巢狀迴圈。你需要計算間距和位置,但別擔心 — 我會帶你一步步完成!
```javascript
for (let x = START_X; x < STOP_X; x += 98) {
for (let y = 0; y < 50 * 5; y += 50) {
ctx.drawImage(enemyImg, x, y);
}
}
```
首先,設定常數以安排敵人陣型位置:
```javascript
const ENEMY_TOTAL = 5;
const ENEMY_SPACING = 98;
const FORMATION_WIDTH = ENEMY_TOTAL * ENEMY_SPACING;
const START_X = (canvas.width - FORMATION_WIDTH) / 2;
const STOP_X = START_X + FORMATION_WIDTH;
```
**這些常數的功能分解:**
- 設定每列和每行均為 5 個敵人(漂亮的 5×5 格子)
- 定義敵人間距,避免擁擠
- 計算整個陣型的寬度
- 確定起始與結束點,讓陣型看起來置中
```mermaid
flowchart LR
A["畫布寬度1024px"] --> B["陣型寬度490px"]
B --> C["起始 X267px"]
C --> D["敵人間距98px"]
subgraph "5x5 敵人陣型"
E["第 1 行Y=0"]
F["第 2 行Y=50"]
G["第 3 行Y=100"]
H["第 4 行Y=150"]
I["第 5 行Y=200"]
end
subgraph "欄距"
J["第 1 欄X=267"]
K["第 2 欄X=365"]
L["第 3 欄X=463"]
M["第 4 欄X=561"]
N["第 5 欄X=659"]
end
style A fill:#e1f5fe
style B fill:#e8f5e8
style C fill:#fff3e0
```
接著,創建巢狀迴圈繪製敵人陣型:
```javascript
for (let x = START_X; x < STOP_X; x += ENEMY_SPACING) {
for (let y = 0; y < 50 * 5; y += 50) {
ctx.drawImage(enemyImg, x, y);
}
}
```
**這段巢狀迴圈的作用:**
- 外層迴圈從左到右移動陣型
- 內層迴圈從上到下建立列
- 在精準計算出的 x,y 座標繪製每隻敵人精靈
- 保持均勻間距,讓畫面專業且整齊
### 🔄 **教學檢核點**
**遊戲渲染精通**:請確認你理解整體渲染系統:
- ✅ 非同步圖片載入如何避免遊戲啟動時阻塞使用者介面?
- ✅ 為什麼用常數計算敵人陣型位置,比硬編碼更佳?
- ✅ 2D 繪圖上下文在繪圖過程中扮演什麼角色?
- ✅ 巢狀迴圈如何建立有組織的精靈陣型?
**效能考量**:你的遊戲現已展示:
- **有效的資源載入**:採用 Promise 管理圖片
- **有條理的渲染**:組織良好的繪製操作
- **精準的數學定位**:計算精靈擺放位置
- **錯誤管理**:優雅地處理載入失敗
**視覺程式設計概念**:你已學到:
- **座標系統**:將數學轉換為螢幕位置
- **精靈管理**:載入及顯示遊戲圖形
- **陣型演算法**:用於組織佈局的數學模式
- **非同步操作**:現代 JavaScript 以提供順暢的用戶體驗
## 結果
完成的結果應如下所示:
完成的結果應該看起來像這樣
![黑色螢幕,包含英雄和 5*5 怪物](../../../../translated_images/partI-solution.36c53b48c9ffae2a5e15496b23b604ba5393433e4bf91608a7a0a020eb7a2691.hk.png)
![Black screen with a hero and 5*5 monsters](../../../../translated_images/partI-solution.36c53b48c9ffae2a.hk.png)
## 解決方案
請先嘗試自己解決,但如果遇到困難,可以查看 [解決方案](../../../../6-space-game/2-drawing-to-canvas/solution/app.js)。
請先試著自己解決,但如果卡住了,可以看看這個 [solution](../../../../6-space-game/2-drawing-to-canvas/solution/app.js)
---
## 🚀 挑戰
## GitHub Copilot Agent 挑戰 🚀
使用 Agent 模式完成以下挑戰:
你已經學習了使用專注於 2D 的 Canvas API看看 [WebGL API](https://developer.mozilla.org/docs/Web/API/WebGL_API),並嘗試繪製一個 3D 物件。
**描述:** 利用已學的 Canvas API 技術,通過加入視覺效果和互動元素來強化您的太空遊戲畫布
## 課後測驗
**提示:** 建立一個名為 `enhanced-canvas.html` 的新檔案,裡面有一個畫布,背景顯示動畫星星,主角飛船有跳動的生命值條,敵機緩慢向下移動。加入 JavaScript 程式碼,使用隨機位置與透明度繪製閃爍星星,實作根據生命值改變顏色(綠 > 黃 > 紅)的生命值條,並讓敵機以不同速度向下動畫移動。
[課後測驗](https://ff-quizzes.netlify.app/web/quiz/32)
在這裡了解更多關於 [agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode)。
## 回顧與自學
## 🚀 挑戰
透過 [閱讀相關內容](https://developer.mozilla.org/docs/Web/API/Canvas_API) 了解更多關於 Canvas API 的資訊。
你已學會使用 2D 專注的 Canvas API 畫圖,現在來看看 [WebGL API](https://developer.mozilla.org/docs/Web/API/WebGL_API),試著繪製一個 3D 物件。
## 課後小測
[課後小測](https://ff-quizzes.netlify.app/web/quiz/32)
## 複習與自學
想了解更多 Canvas API請參考 [Canvas API 文件](https://developer.mozilla.org/docs/Web/API/Canvas_API)。
### ⚡ **你可在接下來 5 分鐘完成的事**
- [ ] 開啟瀏覽器主控台並以 `document.createElement('canvas')` 建立一個畫布元素
- [ ] 嘗試用畫布上下文的 `fillRect()` 畫一個矩形
- [ ] 使用 `fillStyle` 屬性試驗不同顏色
- [ ] 用 `arc()` 方法畫一個簡單的圓形
### 🎯 **你可在這一小時完成的事**
- [ ] 完成課後測驗並理解 Canvas 基礎
- [ ] 實作一個多種形狀和顏色的畫布繪圖應用
- [ ] 實現圖片載入與精靈渲染功能
- [ ] 建立簡單動畫,讓物件在畫布上移動
- [ ] 練習畫布轉換如縮放、旋轉和平移
### 📅 **你一週的 Canvas 旅程**
- [ ] 完成具備細緻圖形與精靈動畫的太空遊戲
- [ ] 精通進階畫布技術,如漸層、圖案及合成
- [ ] 使用畫布實作互動數據視覺化
- [ ] 學習優化畫布效能技巧以提升流暢度
- [ ] 製作多功能繪圖或繪畫應用
- [ ] 探索創意編碼模式及利用畫布製作生成藝術
### 🌟 **你一個月的圖形大師路線**
- [ ] 利用 Canvas 2D 與 WebGL 建置複雜視覺應用
- [ ] 學習圖形編程概念與著色器基礎
- [ ] 貢獻開源圖形庫與視覺化工具
- [ ] 精通圖形高效能優化
- [ ] 製作教學內容分享 Canvas 編程與電腦圖學
- [ ] 成為協助他人創建視覺體驗的圖形編程專家
## 🎯 你的 Canvas 圖形大師時間表
```mermaid
timeline
title Canvas API 學習進度
section Canvas Fundamentals (15 minutes)
Basic Operations: 元素參考
: 2D 上下文存取
: 坐標系統
: 簡單圖形繪製
section Drawing Techniques (20 minutes)
Graphics Primitives: 矩形與圓形
: 顏色與樣式
: 文字渲染
: 路徑操作
section Image Handling (25 minutes)
Asset Management: 圖像物件建立
: 非同步加載模式
: 錯誤處理
: 效能優化
section Game Graphics (30 minutes)
Sprite Rendering: 位置演算法
: 陣形計算
: 場景組合
: 幀渲染
section Advanced Techniques (40 minutes)
Visual Effects: 變換
: 動畫
: 分層
: 狀態管理
section Performance (35 minutes)
Optimization: 高效繪製
: 記憶體管理
: 幀率控制
: 資產快取
section Professional Skills (1 week)
Production Graphics: WebGL 整合
: Canvas 函式庫
: 遊戲引擎
: 跨平台考量
section Advanced Graphics (1 month)
Specialized Applications: 資料視覺化
: 互動藝術
: 即時特效
: 3D 圖形
```
### 🛠️ 你的 Canvas 圖形工具箱總結
完成本課後,你已具備:
- **Canvas API 精通**:完全理解 2D 圖形編程
- **座標數學**:精確定位與佈局演算法
- **資產管理**:專業圖片載入與錯誤處理
- **渲染流程**:有條理的場景組合方法
- **遊戲圖形**:精靈定位與陣型計算
- **非同步程式設計**:現代 JavaScript 模式確保流暢效能
- **視覺程式設計**:將數學概念轉換為螢幕圖形
**實務應用範圍廣泛**:你的 Canvas 技能可直接運用於:
- **資料視覺化**:製作圖表、圖形與互動儀表板
- **遊戲開發**2D 遊戲、模擬與互動體驗
- **數位藝術**:創意編碼與生成藝術專案
- **介面設計**:自訂圖形與互動元素
- **教育軟體**:視覺學習工具與模擬器
- **網頁應用**:動態圖形與即時視覺化
**職場技能提升**:你現在能:
- **打造** 無需依賴外部函式庫的自訂圖形解決方案
- **優化** 渲染效能,打造順暢的用戶體驗
- **除錯** 使用瀏覽器開發工具解決複雜視覺問題
- **設計** 使用數學原理的可擴展圖形系統
- **整合** Canvas 圖形至現代網頁應用架構
**你已精通的 Canvas API 方法**
- **元素管理**getElementById、getContext
- **繪圖操作**fillRect、drawImage、fillStyle
- **資產載入**Image 物件、Promise 模式
- **數學定位**:座標計算、陣型演算法
**下一階段**:你已準備好加入動畫、用戶互動、碰撞偵測,或探索 WebGL 進行 3D 圖形!
🌟 **成就解鎖**:你已使用基礎 Canvas API 技術製作完整的遊戲渲染系統!
## 作業
[玩轉 Canvas API](assignment.md)
[練習 Canvas API](assignment.md)
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
此文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原始語言的文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋不承擔責任。
本文件由 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們力求準確,但請注意自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於關鍵資訊,建議採用專業人工翻譯。我們不對因使用此翻譯而引起的任何誤解或誤釋承擔責任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,23 +1,83 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "ca1cf78a4c60df77ab32a154ec024d7f",
"translation_date": "2025-08-23T23:00:02+00:00",
"original_hash": "87cd43afe5b69dbbffb5c4b209ea6791",
"translation_date": "2026-01-06T12:31:24+00:00",
"source_file": "6-space-game/2-drawing-to-canvas/assignment.md",
"language_code": "hk"
}
-->
# 使用 Canvas API 玩創意
# 作業:探索 Canvas API
## 指引
## 學習目標
選擇 Canvas API 的其中一個元素,並圍繞它創作一些有趣的內容。你能否創造一個由重複星星組成的小型星系?或者創造一個由彩色線條組成的有趣紋理?你可以參考 CodePen 的作品尋找靈感(但不要直接複製)。
完成此作業後,您將展示對 Canvas API 基礎的理解,並運用創意思維來使用 JavaScript 和 HTML5 canvas 建立視覺元素。
## 指示
選擇您感興趣的 Canvas API 其中一個面向,並圍繞它創作一個具吸引力的視覺專案。本作業鼓勵您在實驗學過的繪圖功能時,創造獨一無二的作品。
### 專案靈感建議
**幾何圖案:**
- **創建** 一個使用隨機定位的閃爍動態星空
- **設計** 使用重複幾何形狀的有趣紋理
- **製作** 旋轉且多彩的萬花筒效果
**互動元素:**
- **開發** 回應滑鼠移動的繪圖工具
- **實作** 點擊時會變色的圖形
- **設計** 帶移動元素的簡單動畫循環
**遊戲相關圖形:**
- **打造** 太空遊戲的捲動背景
- **製作** 爆炸或魔法咒語等粒子特效
- **創造** 多幀動畫精靈
### 開發指南
**研究和靈感:**
- **瀏覽** CodePen 以獲取創意 canvas 範例(用於靈感,不是複製)
- **研讀** [Canvas API 文件](https://developer.mozilla.org/docs/Web/API/Canvas_API) 以了解更多方法
- **嘗試** 不同繪圖函數、顏色及動畫效果
**技術需求:**
- **使用** 正確的 canvas 設置,如 `getContext('2d')`
- **包含** 有意義的註解說明您的方法
- **徹底測試** 代碼以確保無錯誤執行
- **應用** 現代 JavaScript 語法const/let箭頭函數
**創意表達:**
- **專注** 深入探索一個 Canvas API 功能
- **加入** 您的創意巧思,使專案獨特
- **思考** 您的作品如何成為較大型應用程式的一部分
### 提交指南
請提交包含嵌入式 CSS 及 JavaScript 的單一 HTML 檔案,或放在一個資料夾中的多個檔案。附上一段簡短註解說明您的創作選擇及所探索的 Canvas API 功能。
## 評分標準
| 評分準則 | 卓越表現 | 合格表現 | 有待改善 |
| -------- | --------------------------------------------------- | ------------------------------- | --------------------- |
| | 提交的程式碼展示了有趣的紋理或形狀 | 提交的程式碼,但無法正常運行 | 未提交程式碼 |
| 項目 | 優秀 | 充足 | 需要改進 |
|----------|-----------|----------|-------------------|
| **技術實作** | 創意運用多項 Canvas API 功能,程式碼運行無誤,採用現代 JavaScript 語法 | 正確使用 Canvas API程式碼運行有少許問題基本實作 | 嘗試使用 Canvas API但代碼錯誤或無法執行 |
| **創意與設計** | 概念高度原創,視覺效果精緻,顯示深入探索所選 Canvas 功能 | 良好運用 Canvas 功能,具部分創意元素,視覺表現扎實 | 基本實作,創意及視覺吸引力有限 |
| **程式碼品質** | 代碼組織良好,有註解,遵從最佳實踐,演算法有效率 | 程式碼整潔,稍有註解,遵循基本編碼標準 | 代碼缺乏組織,註解很少,實作效率低 |
## 反思問題
完成專案後,請思考以下問題:
1. **您選擇了哪個 Canvas API 功能?為什麼?**
2. **製作過程中遇到了哪些挑戰?**
3. **您如何將此專案擴展成更大型的應用程式或遊戲?**
4. **您接下來想探索哪些其他 Canvas API 功能?**
> 💡 **專家秘訣**:從簡單開始,逐漸增加複雜度。一個執行良好的簡單專案,比一個過於龐大卻無法正常運作的專案更好!
---
**免責聲明**
本文件使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋不承擔責任。
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件由 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於確保準確性,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要資料,建議採用專業人工翻譯。本公司對因使用本翻譯而引起的任何誤解或誤釋概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,23 +1,38 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "ccfcd8c2932761359fbaff3d6b01ace4",
"translation_date": "2025-08-23T22:57:32+00:00",
"original_hash": "c162b3b3a1cafc1483c8015e9b266f0d",
"translation_date": "2026-01-06T12:39:12+00:00",
"source_file": "6-space-game/3-moving-elements-around/assignment.md",
"language_code": "hk"
}
-->
# 為你的程式碼添加註解
# 註解你的程式碼
## 指
## 指
檢查你遊戲文件夾中的 /app.js 文件,找出可以添加註解和整理的地方。程式碼很容易變得混亂,現在是一個好機會添加註解,確保你的程式碼易於閱讀,方便日後使用。
乾淨且文件完善的程式碼對於維護和分享你的專案非常重要。在這次作業中,你將練習專業開發者最重要的習慣之一:撰寫清晰、有幫助的註解說明程式碼的目的與功能。
瀏覽你遊戲資料夾中的現有 `app.js` 文件,並尋找可加入註解及整理的方式。程式碼很容易失控,現在是個好機會加入註解,確保程式碼易於閱讀以便日後使用。
**你的任務包括:**
- **加入註解** 解釋每個主要程式區塊的功能
- **文件化函式** 清楚描述其用途與參數
- **將程式碼組織** 成合乎邏輯的區塊並加上區段標題
- **移除** 未使用或冗餘的程式碼
- **使用一致的** 變數與函式命名慣例
## 評分標準
| 評分標準 | 優秀 | 合格 | 需要改進 |
| -------- | ------------------------------------------------------------- | --------------------------------- | ---------------------------------------------------- |
| | `app.js` 程式碼完全有註解並且整理成邏輯區塊 | `app.js` 程式碼有適當的註解 | `app.js` 程式碼有些混亂,缺乏良好的註解 |
| 標準 | 優異 | 適當 | 需改進 |
| -------- | --------- | -------- | ----------------- |
| **程式碼文件化** | `app.js` 具有完整註解並對所有主要區塊和函式提供清晰、有幫助的說明 | `app.js` 有適度註解,對大部分區塊提供基本說明 | `app.js` 註解很少且缺少清楚說明 |
| **程式碼組織** | 程式碼有合邏輯的區塊,清楚的區段標題和一致結構 | 程式碼有一定組織,基本分組相關功能 | 程式碼組織不良,難以閱讀 |
| **程式碼品質** | 變數與函式名稱具描述性,無未使用程式碼,遵守一致慣例 | 大多數程式碼遵守良好命名規範,極少未使用程式碼 | 變數名稱不清楚,包含未使用程式碼,風格不一致 |
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋不承擔責任。
本文件經由 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻譯而成。儘管我們致力於確保準確性,請注意自動翻譯可能包含錯誤或不準確之處。原始語言文件應被視為權威來源。對於重要資訊,建議採用專業人工翻譯。本公司對因使用本翻譯而引起的任何誤解或曲解概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,131 +1,325 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "a6ce295ff03bb49df7a3e17e6e7100a0",
"translation_date": "2025-08-29T15:02:51+00:00",
"original_hash": "039b4d8ce65f5edd82cf48d9c3e6728c",
"translation_date": "2026-01-06T12:27:34+00:00",
"source_file": "6-space-game/4-collision-detection/README.md",
"language_code": "hk"
}
-->
# 建立太空遊戲第4部分加入雷射和偵測碰撞
## 課前測驗
# 建立太空遊戲第4部分加入雷射與碰撞偵測
```mermaid
journey
title 你的碰撞檢測旅程
section 物理基礎
了解矩形: 3: 學生
學習交集數學: 4: 學生
掌握座標系統: 4: 學生
section 遊戲機制
實作雷射發射: 4: 學生
新增物件生命週期: 5: 學生
建立碰撞規則: 5: 學生
section 系統整合
建立碰撞檢測: 5: 學生
優化效能: 5: 學生
測試互動系統: 5: 學生
```
## 講義前測驗
[講義前測驗](https://ff-quizzes.netlify.app/web/quiz/35)
想像一下星球大戰中路克的質子魚雷擊中死星排氣孔的那一刻。那精準的碰撞偵測改變了整個銀河的命運!在遊戲中,碰撞偵測也是如此運作——判斷物件何時互動以及接下來會發生什麼事。
這堂課中你將會加入雷射武器到太空遊戲中並實作碰撞偵測。就像NASA任務規劃者計算太空船軌跡以避開碎片你將學習如何偵測遊戲物件的交錯狀況。我們會拆解成易於管理、層層堆疊的步驟。
完成後,你會有一套運作正常的戰鬥系統,雷射會消滅敵人,碰撞會觸發遊戲事件。這些相同的碰撞原理被應用在從物理模擬到互動網頁介面的一切中。
```mermaid
mindmap
root((碰撞偵測))
Physics Concepts
Rectangle Boundaries
Intersection Testing
Coordinate Systems
Separation Logic
Game Objects
Laser Projectiles
Enemy Ships
Hero Character
Collision Zones
Lifecycle Management
Object Creation
Movement Updates
Destruction Marking
Memory Cleanup
Event Systems
Keyboard Input
Collision Events
Game State Changes
Audio/Visual Effects
Performance
Efficient Algorithms
Frame Rate Optimization
Memory Management
Spatial Partitioning
```
✅ 做些小研究,了解最早的電腦遊戲是什麼。它的功能是什麼?
[課前測驗](https://ff-quizzes.netlify.app/web/quiz/35)
## 碰撞偵測
在這一課中,你將學習如何用 JavaScript 發射雷射!我們會為遊戲加入以下兩個功能:
碰撞偵測的運作就像阿波羅月球登陸器上的接近感測器——不斷檢查距離,當物體靠得太近時就觸發警報。在遊戲裡,這系統決定物件何時互動以及接下來要發生什麼。
- **雷射**:從英雄的飛船發射,垂直向上移動
- **碰撞偵測**:作為實現「射擊」功能的一部分,我們還會加入一些有趣的遊戲規則:
- **雷射擊中敵人**:敵人被雷射擊中後會死亡
- **雷射擊中螢幕頂部**:雷射擊中螢幕頂部後會被銷毀
- **敵人與英雄碰撞**:敵人和英雄相撞後雙方都會被銷毀
- **敵人到達螢幕底部**:敵人到達螢幕底部後,敵人和英雄都會被銷毀
我們使用的做法是將每個遊戲物件視為長方形,類似航空管制系統用簡化的幾何圖形來追蹤飛機。這種長方形方法可能看起來很基本,但運算效率高,且適用於大多數遊戲情境。
簡而言之,你——*英雄*——需要在敵人到達螢幕底部之前,用雷射擊敗所有敵人。
### 長方形表示法
✅ 做一些研究,了解第一個電腦遊戲是什麼?它的功能是什麼?
每個遊戲物件都需要座標邊界,就像火星探路者車輛繪製在火星表面的位置一樣。以下定義這些邊界座標的方法:
讓我們一起成為英雄吧!
```mermaid
flowchart TD
A["🎯 遊戲物件"] --> B["📍 位置 (x, y)"]
A --> C["📏 尺寸 (寬度, 高度)"]
B --> D["頂部: y"]
B --> E["左側: x"]
C --> F["底部: y + 高度"]
C --> G["右側: x + 寬度"]
D --> H["🔲 矩形邊界"]
E --> H
F --> H
G --> H
H --> I["碰撞檢測準備"]
style A fill:#e3f2fd
style H fill:#e8f5e8
style I fill:#fff3e0
```
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width
}
}
```
## 碰撞偵測
**讓我們拆解說明:**
- **上邊緣**物件垂直起始位置y座標
- **左邊緣**物件水平起始位置x座標
- **下邊緣**將高度加到y座標就知道物件的底部位置
- **右邊緣**將寬度加到x座標即得完整邊界
我們該如何進行碰撞偵測?我們需要將遊戲中的物件視為移動的矩形。你可能會問,為什麼是矩形?因為用來繪製遊戲物件的圖像本身就是一個矩形:它有 `x`、`y`、`width` 和 `height`
### 交集演算法
如果兩個矩形(例如英雄和敵人)*相交*,那麼就發生了碰撞。碰撞後應該發生什麼,則取決於遊戲的規則。要實現碰撞偵測,你需要以下幾點:
偵測長方形交集的邏輯與哈伯太空望遠鏡判斷天體是否重疊視野類似。演算法會檢查分離情況
1. 一種方法來獲取遊戲物件的矩形表示,例如:
```mermaid
flowchart LR
A["矩形 1"] --> B{"分離測試"}
C["矩形 2"] --> B
B --> D["R2 左邊 > R1 右邊?"]
B --> E["R2 右邊 < R1 "]
B --> F["R2 頂部 > R1 底部?"]
B --> G["R2 底部 < R1 "]
D --> H{"有任何為真?"}
E --> H
F --> H
G --> H
H -->|是| I["❌ 無碰撞"]
H -->|否| J["✅ 偵測到碰撞"]
style B fill:#e3f2fd
style I fill:#ffebee
style J fill:#e8f5e8
```
```javascript
function intersectRect(r1, r2) {
return !(r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top);
}
```
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width
}
}
```
**分離檢驗就像雷達系統:**
- 長方形2是否完全在長方形1的右邊
- 長方形2是否完全在長方形1的左邊
- 長方形2是否完全在長方形1的下面
- 長方形2是否完全在長方形1的上面
2. 一個比較函數,該函數可以像這樣:
若上述條件都不成立,兩長方形必定重疊。這種做法與雷達操作員判斷兩飛機是否安全距離的方法相似。
```javascript
function intersectRect(r1, r2) {
return !(r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top);
}
```
## 管理物件生命週期
## 如何銷毀物件
當雷射擊中敵人,兩者物件皆須從遊戲中移除。然而,中途刪除物件可能造成程式崩潰——這是阿波羅導航電腦早期系統中慘痛的教訓。取而代之,我們使用「標記刪除」的方式,安全地在畫面更新間隔移除物件。
要在遊戲中銷毀物件,你需要讓遊戲知道在某個時間間隔觸發的遊戲循環中不再繪製該物件。一種方法是當某些事情發生時,將遊戲物件標記為*死亡*,如下所示:
```mermaid
stateDiagram-v2
[*] --> Active: 物件已建立
Active --> Collided: 偵測到碰撞
Collided --> MarkedDead: 設定 dead = true
MarkedDead --> Filtered: 下一幀
Filtered --> [*]: 物件已移除
Active --> OutOfBounds: 離開畫面範圍
OutOfBounds --> MarkedDead
note right of MarkedDead
當前幀
可安全繼續
end note
note right of Filtered
幀與幀之間
已移除物件
end note
```
以下示範如何標記要移除的物件:
```javascript
// collision happened
enemy.dead = true
// 標記物件以便移除
enemy.dead = true;
```
然後你可以在重新繪製螢幕之前,篩選出*死亡*的物件,如下所示:
**這種做法的好處:**
- 將物件標示為「已死」但不立即刪除
- 等待目前遊戲幀數安全結束
- 避免嘗試使用已消失的物件導致崩潰!
接著,在下一次渲染循環之前過濾標記物件:
```javascript
gameObjects = gameObject.filter(go => !go.dead);
gameObjects = gameObjects.filter(go => !go.dead);
```
## 如何發射雷射
**這個過濾動作:**
- 產生只包含「活著」物件的新清單
- 丟棄被標記為死的物件
- 保持遊戲流暢執行
- 避免累積已摧毀物件造成記憶體膨脹
## 實作雷射機制
發射雷射的過程是響應按鍵事件並創建一個向某個方向移動的物件。因此,我們需要執行以下步驟:
遊戲中的雷射投射物原理與星艦迷航記中的光子魚雷相同——它們是獨立的物件,沿直線移動直到擊中目標。每次按空白鍵都會產生一個新的雷射物件,並在螢幕上移動。
1. **創建雷射物件**:從英雄飛船的頂部發射,創建後開始向螢幕頂部移動。
2. **綁定按鍵事件**:我們需要選擇鍵盤上的某個按鍵來代表玩家發射雷射。
3. **創建一個看起來像雷射的遊戲物件**:當按下按鍵時。
為了實現,我們需要協調幾個部分:
## 雷射的冷卻時間
**主要元件包括:**
- **建立** 從英雄位置生成的雷射物件
- **處理** 鍵盤輸入以觸發雷射生成
- **管理** 雷射的移動與生命週期
- **實作** 雷射投射物的視覺呈現
雷射需要在每次按下按鍵(例如空格鍵)時發射。為了防止遊戲在短時間內生成過多雷射,我們需要解決這個問題。解決方法是實現所謂的*冷卻時間*,即一個計時器,確保雷射只能在一定時間間隔內發射一次。你可以這樣實現:
## 實作射擊速率控制
無限制射擊會讓遊戲引擎不堪負荷也讓遊戲過於簡單。真實武器系統也有類似限制——即使是USS Enterprise的光束炮也需要時間充能才能連發。
我們將實作冷卻系統來防止連點同時保持操作反應靈敏:
```mermaid
sequenceDiagram
participant Player
participant Weapon
participant Cooldown
participant Game
Player->>Weapon: 按空白鍵
Weapon->>Cooldown: 檢查是否冷卻
alt 武器已準備好
Cooldown->>Weapon: cool = true
Weapon->>Game: 創建激光
Weapon->>Cooldown: 開始新冷卻
Cooldown->>Cooldown: cool = false
Note over Cooldown: 等待 500 毫秒
Cooldown->>Cooldown: cool = true
else 武器正在冷卻中
Cooldown->>Weapon: cool = false
Weapon->>Player: 無動作
end
```
```javascript
class Cooldown {
constructor(time) {
this.cool = false;
setTimeout(() => {
this.cool = true;
}, time)
}, time);
}
}
class Weapon {
constructor {
constructor() {
this.cooldown = null;
}
fire() {
if (!this.cooldown || this.cooldown.cool) {
// produce a laser
// 建立激光射彈
this.cooldown = new Cooldown(500);
} else {
// do nothing - it hasn't cooled down yet.
// 武器仍在冷卻中
}
}
}
```
✅ 回顧太空遊戲系列的第一課,提醒自己什麼是*冷卻時間*。
**冷卻原理說明:**
- 生成時武器處於「過熱」(還不能射擊)
- 經過等待時間變成「冷卻完畢」(可射擊)
- 射擊前先檢查:「武器是否冷卻完畢?」
- 防止無節制連射,且保持控制反應迅速
✅ 參考太空遊戲系列的第一課,提醒自己冷卻機制細節。
## 建立碰撞系統
你將擴充現有太空遊戲程式碼,創建碰撞偵測系統。這就像國際太空站的自動避撞系統,你的遊戲會持續監測物件位置並回應交錯狀況。
從之前課程的程式碼出發,加入碰撞偵測和特定規則,來決定物件互動。
## 要建構的內容
> 💡 **專家小建議**:你的資產資料夾中已包含雷射精靈圖片,程式碼中也已引用,準備實作。
你將使用上一課的程式碼(應該已經清理並重構過),並進一步擴展它。可以從第二部分的程式碼開始,或者使用 [第三部分的起始程式碼](../../../../../../../../../your-work)。
### 要實作的碰撞規則
> 提示:你將使用的雷射已經在資產資料夾中,並且已在程式碼中引用。
**遊戲機制的新增項目:**
1. **雷射擊中敵人**:被雷射擊中時,敵人物件會被摧毀
2. **雷射撞到螢幕邊界**:雷射碰到螢幕上方邊緣會被移除
3. **敵人與英雄碰撞**:雙方相撞時都被摧毀
4. **敵人到底部**:敵人抵達螢幕底部時,遊戲結束
- **加入碰撞偵測**,當雷射與某物件碰撞時,應遵循以下規則:
1. **雷射擊中敵人**:敵人被雷射擊中後會死亡
2. **雷射擊中螢幕頂部**:雷射擊中螢幕頂部後會被銷毀
3. **敵人與英雄碰撞**:敵人和英雄相撞後雙方都會被銷毀
4. **敵人到達螢幕底部**:敵人到達螢幕底部後,敵人和英雄都會被銷毀
### 🔄 **教學檢核**
**碰撞偵測基礎**:實作前請確保你懂得:
- ✅ 長方形邊界如何定義碰撞區域
- ✅ 為何分離檢測比直接交集計算更有效率
- ✅ 遊戲循環中物件生命週期管理的重要性
- ✅ 事件驅動系統如何協調碰撞回應
## 推薦步驟
**簡易自我測驗**:如果你立刻刪除物件而非先標記,會發生什麼?
*答案:迴圈中刪除物件可能導致程式崩潰或遺漏物件處理*
找到在 `your-work` 子資料夾中為你創建的檔案。它應該包含以下內容:
**物理理解**:你現在已掌握:
- **座標系統**:位置與尺寸如何構成邊界
- **交集邏輯**:碰撞偵測背後的數學原理
- **效能優化**:為何即時系統需要高效算法
- **記憶體管理**:安全的物件生命週期控管方式
## 設定你的開發環境
好消息——我們已經幫你準備好大部分基礎!所有遊戲素材和基本架構皆在 `your-work` 子資料夾中,等待你加入酷炫的碰撞功能。
### 專案架構
```bash
-| assets
@ -137,169 +331,438 @@ class Weapon {
-| package.json
```
你可以通過輸入以下指令來啟動專案:
**理解檔案結構:**
- **包含** 遊戲物件所需的所有精靈圖
- **含有** 主要HTML文件和JavaScript應用程式檔
- **提供** 用於本地開發伺服器的套件設定
### 啟動開發伺服器
切換到你的專案資料夾並啟動本地開發伺服器:
```bash
cd your-work
npm start
```
上述指令會在地址 `http://localhost:5000` 啟動一個 HTTP 伺服器。打開瀏覽器並輸入該地址,目前應該只會顯示英雄和所有敵人,暫時還沒有任何移動。
**這組指令的作用:**
- **切換** 目錄至工作專案資料夾
- **啟動** 本地HTTP伺服器於 `http://localhost:5000`
- **提供** 測試與開發遊戲檔案
- **支援** 自動重新載入以便即時開發
用瀏覽器打開 `http://localhost:5000`,你就能看到當前遊戲狀態,包括英雄和敵人已經呈現。
### 一步步實作
就像NASA系統性的設計旅行者太空船程式我們會系統化一步步構建碰撞偵測元件。
```mermaid
flowchart TD
A["1. 矩形邊界"] --> B["2. 相交檢測"]
B --> C["3. 雷射系統"]
C --> D["4. 事件處理"]
D --> E["5. 碰撞規則"]
E --> F["6. 冷卻系統"]
G["物件邊界"] --> A
H["物理演算法"] --> B
I["投射物生成"] --> C
J["鍵盤輸入"] --> D
K["遊戲邏輯"] --> E
L["速率限制"] --> F
F --> M["🎮 完整遊戲"]
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
style E fill:#e0f2f1
style F fill:#fce4ec
style M fill:#e1f5fe
```
#### 1. 加入長方形碰撞邊界
首先,教會遊戲物件如何描述自己的邊界。在你的 `GameObject` 類別中加入這個方法:
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width,
};
}
```
**這個方法能完成:**
- **建立** 一個帶有精確邊界座標的長方形物件
- **計算** 下邊緣和右邊緣(位置加上尺寸)
- **回傳** 適用於碰撞演算法的物件
- **提供** 統一介面給所有遊戲物件使用
#### 2. 實作交集偵測
接著建立碰撞警探功能——用來判別兩個長方形是否重疊的函式:
```javascript
function intersectRect(r1, r2) {
return !(
r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top
);
}
```
**此演算法原理:**
- **測試** 四種長方形分離條件
- **返回** `false` 若任一分離條件成立
- **表示** 交集存在時回傳true
- **使用** 否定邏輯提高交集測試效率
#### 3. 實作雷射射擊系統
接下來有趣的部分!我們要設置雷射射擊系統。
### 加入程式碼
##### 訊息常數
1. **為遊戲物件設置矩形表示以處理碰撞** 以下程式碼允許你獲取 `GameObject` 的矩形表示。編輯你的 GameObject 類別以擴展它:
先定義一些訊息型態,方便遊戲系統之間溝通
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width,
};
```javascript
KEY_EVENT_SPACE: "KEY_EVENT_SPACE",
COLLISION_ENEMY_LASER: "COLLISION_ENEMY_LASER",
COLLISION_ENEMY_HERO: "COLLISION_ENEMY_HERO",
```
**這些常數帶來的效用:**
- **標準化** 全程使用的事件名稱
- **讓** 系統間溝通更一致
- **避免** 註冊事件處理器時犯錯誤
##### 鍵盤輸入處理
在鍵盤事件監聽中加入空白鍵偵測:
```javascript
} else if(evt.keyCode === 32) {
eventEmitter.emit(Messages.KEY_EVENT_SPACE);
}
```
**這個輸入處理器:**
- **偵測** 空白鍵keyCode 32按下
- **發出** 標準化事件訊息
- **促成** 射擊邏輯與輸入的解耦
##### 事件監聽註冊
在你的 `initGame()` 函數中註冊射擊行為:
```javascript
eventEmitter.on(Messages.KEY_EVENT_SPACE, () => {
if (hero.canFire()) {
hero.fire();
}
});
```
**這個監聽器:**
- **回應** 空白鍵事件
- **檢查** 射擊冷卻狀態
- **允許** 符合條件時創建雷射
加入雷射與敵人碰撞處理:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
});
```
**這個碰撞處理:**
- **接收** 包含兩個物件的碰撞事件數據
- **標記** 兩物件為待刪除
- **確保** 碰撞後適當清理
#### 4. 創建雷射類別
實作一個往上移動並管理自身生命週期的雷射投射物:
```javascript
class Laser extends GameObject {
constructor(x, y) {
super(x, y);
this.width = 9;
this.height = 33;
this.type = 'Laser';
this.img = laserImg;
let id = setInterval(() => {
if (this.y > 0) {
this.y -= 15;
} else {
this.dead = true;
clearInterval(id);
}
```
2. **加入檢查碰撞的程式碼** 這將是一個新函數,用於測試兩個矩形是否相交:
```javascript
function intersectRect(r1, r2) {
return !(
r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top
);
}
```
3. **加入雷射發射功能**
1. **加入按鍵事件訊息**。按下*空格鍵*應該在英雄飛船的正上方創建一個雷射。在 Messages 物件中加入三個常數:
```javascript
KEY_EVENT_SPACE: "KEY_EVENT_SPACE",
COLLISION_ENEMY_LASER: "COLLISION_ENEMY_LASER",
COLLISION_ENEMY_HERO: "COLLISION_ENEMY_HERO",
```
1. **處理空格鍵**。編輯 `window.addEventListener` 的 keyup 函數以處理空格鍵:
```javascript
} else if(evt.keyCode === 32) {
eventEmitter.emit(Messages.KEY_EVENT_SPACE);
}
```
1. **加入監聽器**。編輯 `initGame()` 函數,確保當按下空格鍵時英雄可以發射雷射:
```javascript
eventEmitter.on(Messages.KEY_EVENT_SPACE, () => {
if (hero.canFire()) {
hero.fire();
}
```
並加入一個新的 `eventEmitter.on()` 函數,確保當敵人與雷射碰撞時觸發行為:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
})
```
1. **移動物件**,確保雷射逐漸移動到螢幕頂部。你將創建一個新的 Laser 類別,繼承自 `GameObject`,如之前所做:
```javascript
class Laser extends GameObject {
constructor(x, y) {
super(x,y);
(this.width = 9), (this.height = 33);
this.type = 'Laser';
this.img = laserImg;
let id = setInterval(() => {
if (this.y > 0) {
this.y -= 15;
} else {
this.dead = true;
clearInterval(id);
}
}, 100)
}
}, 100);
}
}
```
**這個類別實作:**
- **繼承** 自GameObject取得基本功能
- **設定** 雷射精靈合適尺寸
- **利用** `setInterval()` 自動向上移動
- **自己** 在觸頂時處理銷毀
- **管理** 自身動畫時序及清理
#### 5. 實作碰撞偵測系統
建立完整的碰撞偵測函式:
```javascript
function updateGameObjects() {
const enemies = gameObjects.filter(go => go.type === 'Enemy');
const lasers = gameObjects.filter(go => go.type === "Laser");
// 測試激光與敵人碰撞
lasers.forEach((laser) => {
enemies.forEach((enemy) => {
if (intersectRect(laser.rectFromGameObject(), enemy.rectFromGameObject())) {
eventEmitter.emit(Messages.COLLISION_ENEMY_LASER, {
first: laser,
second: enemy,
});
}
```
1. **處理碰撞**,實現雷射的碰撞規則。加入一個 `updateGameObjects()` 函數,用於測試碰撞物件是否命中:
```javascript
function updateGameObjects() {
const enemies = gameObjects.filter(go => go.type === 'Enemy');
const lasers = gameObjects.filter((go) => go.type === "Laser");
// laser hit something
lasers.forEach((l) => {
enemies.forEach((m) => {
if (intersectRect(l.rectFromGameObject(), m.rectFromGameObject())) {
eventEmitter.emit(Messages.COLLISION_ENEMY_LASER, {
first: l,
second: m,
});
}
});
});
gameObjects = gameObjects.filter(go => !go.dead);
}
```
確保將 `updateGameObjects()` 加入到 `window.onload` 的遊戲循環中。
4. **實現雷射的冷卻時間**,確保雷射只能在一定時間間隔內發射。
最後,編輯 Hero 類別以實現冷卻功能:
```javascript
class Hero extends GameObject {
constructor(x, y) {
super(x, y);
(this.width = 99), (this.height = 75);
this.type = "Hero";
this.speed = { x: 0, y: 0 };
this.cooldown = 0;
}
fire() {
gameObjects.push(new Laser(this.x + 45, this.y - 10));
this.cooldown = 500;
let id = setInterval(() => {
if (this.cooldown > 0) {
this.cooldown -= 100;
} else {
clearInterval(id);
}
}, 200);
}
canFire() {
return this.cooldown === 0;
}
});
});
// 移除被摧毀的物件
gameObjects = gameObjects.filter(go => !go.dead);
}
```
**碰撞系統做了什麼:**
- **依類別** 過濾遊戲物件,提升測試效率
- **逐一** 測試所有雷射與敵人是否交疊
- **發出** 碰撞事件於交錯時
- **移除** 碰撞後被摧毀的物件
> ⚠️ **重要**:請於 `window.onload` 主遊戲迴圈中加入 `updateGameObjects()` ,啟用碰撞偵測。
#### 6. 在Hero類別中加入冷卻系統
為Hero類別增強射擊機制與射擊速率限制
```javascript
class Hero extends GameObject {
constructor(x, y) {
super(x, y);
this.width = 99;
this.height = 75;
this.type = "Hero";
this.speed = { x: 0, y: 0 };
this.cooldown = 0;
}
fire() {
gameObjects.push(new Laser(this.x + 45, this.y - 10));
this.cooldown = 500;
let id = setInterval(() => {
if (this.cooldown > 0) {
this.cooldown -= 100;
} else {
clearInterval(id);
}
```
}, 200);
}
canFire() {
return this.cooldown === 0;
}
}
```
**這強化版Hero類包括**
- **初始化** 冷卻計時器為零(準備射擊)
- **建立** 生成在英雄飛船上方的雷射物件
- **設定** 冷卻時間防止快速連射
- **用** 基於時間間隔的更新遞減冷卻計時器
- **提供** `canFire()` 方法回報能否射擊
### 🔄 **教學檢核**
**系統理解完成**:檢視自己對碰撞系統是否掌握良好:
- ✅ 長方形邊界如何促成高效碰撞偵測?
- ✅ 為何物件生命週期管理是遊戲穩定性的關鍵?
- ✅ 冷卻機制如何避免效能問題?
- ✅ 事件驅動架構在碰撞處理上扮演什麼角色?
**系統整合性**:你的碰撞偵測展現:
- **數學精確**:長方形交集演算法
- **效能優化**:高效碰撞測試模式
- **記憶體管理**:安全的物件建立與銷毀
- **事件協調**:鬆耦合系統溝通
- **即時處理**frame-based更新流程
**專業模式**:你已實作:
- **關注點分離**:物理、渲染、輸入分工清楚
- **物件導向設計**:繼承與多型
- **狀態管理**:物件生命週期和遊戲狀態追蹤
- **效能優化**:即時運算的高效演算法
### 測試你的實作
你的太空遊戲現在具備完整碰撞偵測和戰鬥機制。🚀 測試新功能:
- **用箭頭鍵** 移動看操作是否順暢
- **用空白鍵射擊** 注意冷卻阻止連點濫射
- **觀察碰撞** 雷射擊中敵人觸發移除
- **驗證清理** 被消滅的物件消失不再畫面
你已成功實作一套依靠導引太空船和機器人導航相同數學原理的碰撞偵測系統。
### ⚡ **接下來5分鐘你可以做什麼**
- [ ] 開啟瀏覽器開發者工具,在碰撞偵測函式設置斷點
- [ ] 嘗試修改雷射速度或敵人移動速度,觀察碰撞行為
- [ ] 調整冷卻數值,測試不同射擊速率的效果
- [ ] 加入 `console.log` 陳述式以實時追蹤碰撞事件
### 🎯 **這小時你可以完成的目標**
- [ ] 完成課後測驗並瞭解碰撞偵測算法
- [ ] 碰撞發生時加入爆炸等視覺效果
- [ ] 實作不同特性的投射物類型
- [ ] 建立暫時提升玩家能力的增益道具
- [ ] 新增碰撞時更具滿足感的音效效果
### 📅 **你的一週物理程式設計計劃**
- [ ] 完成具完善碰撞系統的完整太空遊戲
- [ ] 實作除了矩形以外的進階碰撞形狀(圓形、多邊形)
- [ ] 新增粒子系統以呈現真實爆炸效果
- [ ] 創造具碰撞迴避行為的複雜敵人AI
- [ ] 優化碰撞偵測以支援大量物件的效能
- [ ] 加入物理模擬,例如動量與真實移動
### 🌟 **你的一個月遊戲物理精通計劃**
- [ ] 使用先進物理引擎與真實模擬建構遊戲
- [ ] 學習 3D 碰撞偵測和空間分割算法
- [ ] 貢獻開源物理函式庫與遊戲引擎
- [ ] 掌握圖形密集應用的效能優化
- [ ] 創作關於遊戲物理與碰撞偵測的教學內容
- [ ] 建構展示進階物理程式技能的作品集
## 🎯 你的碰撞偵測精通時間表
```mermaid
timeline
title 碰撞檢測與遊戲物理學習進度
section 基礎 (10 分鐘)
Rectangle Math: 坐標系統
: 邊界計算
: 位置追蹤
: 尺寸管理
section 演算法設計 (20 分鐘)
Intersection Logic: 分離測試
: 重疊偵測
: 性能優化
: 邊界情況處理
section 遊戲實作 (30 分鐘)
Object Systems: 生命週期管理
: 事件協調
: 狀態追蹤
: 記憶體清理
section 互動功能 (40 分鐘)
Combat Mechanics: 投射物系統
: 武器冷卻
: 傷害計算
: 視覺反饋
section 進階物理 (50 分鐘)
Real-time Systems: 幀率優化
: 空間分割
: 碰撞反應
: 物理模擬
section 專業技巧 (1 週)
Game Engine Concepts: 元件系統
: 物理管線
: 性能剖析
: 跨平台優化
section 業界應用 (1 個月)
Production Skills: 大規模優化
: 團隊協作
: 引擎開發
: 平台部署
```
### 🛠️ 你的遊戲物理工具箱總結
完成本課程後,你已掌握:
- **碰撞數學**:矩形交集算法與座標系統
- **效能優化**:即時應用的高效碰撞偵測
- **物件生命週期管理**:安全的物件建立、更新與銷毀模式
- **事件驅動架構**:解耦系統以響應碰撞事件
- **遊戲迴圈整合**:基於幀的物理更新與渲染協調
- **輸入系統**:具有速率限制與反饋的響應式控制
- **記憶體管理**:高效的物件池與清理策略
**實際應用**:你的碰撞偵測技能直接適用於:
- **互動模擬**:科學模型與教育工具
- **使用者介面設計**:拖放互動與觸控偵測
- **資料視覺化**:互動圖表與可點擊元件
- **行動開發**:觸控手勢辨識與碰撞處理
- **機器人程式設計**:路徑規劃與障礙迴避
- **電腦圖形學**:光線追蹤與空間演算法
**專業技能獲得**:你現在能夠:
- **設計** 用於即時碰撞偵測的高效算法
- **實作** 適應物件複雜度擴展的物理系統
- **除錯** 使用數學原理診斷複雜交互系統
- **優化** 適用於不同硬體與瀏覽器的效能
- **架構** 使用成熟設計模式建立可維護的遊戲系統
到這裡,你的遊戲已經具備了一些功能!你可以用方向鍵移動,用空格鍵發射雷射,並且當雷射擊中敵人時,敵人會消失。做得好!
**遊戲開發概念掌握**
- **物理模擬**:即時碰撞偵測與回應
- **效能工程**:互動應用的優化算法
- **事件系統**:遊戲元件間解耦通訊
- **物件管理**:動態內容的高效生命週期模式
- **輸入處理**:適時反饋的響應式控制
**下一階段**:你已準備好探索 Matter.js 等進階物理引擎、實作 3D 碰撞偵測,或建立複雜粒子系統!
🌟 **成就解鎖**:你已建構出具專業級碰撞偵測的完整物理互動系統!
## GitHub Copilot Agent 挑戰 🚀
使用 Agent 模式完成以下挑戰:
**說明:** 加強碰撞偵測系統,實作能隨機生成並被英雄飛船收集後提供暫時能力的增益道具。
**提示:** 建立一個繼承 GameObject 的 PowerUp 類別,並實現英雄與增益道具間的碰撞偵測。新增至少兩種增益道具:一種增加射速(減少冷卻時間)、另一種產生暫時護盾。包含隨機時間與位置產生增益道具的邏輯。
---
## 🚀 挑戰
加入爆炸效果!查看 [Space Art 資源庫](../../../../6-space-game/solution/spaceArt/readme.txt) 中的遊戲資產,嘗試在雷射擊中外星人時加入爆炸效果。
新增爆炸效果!查看 [Space Art repo](../../../../6-space-game/solution/spaceArt/readme.txt) 的遊戲素材,嘗試在雷射射中外星人時加入爆炸
## 課後測驗
[課後測驗](https://ff-quizzes.netlify.app/web/quiz/36)
## 回顧與自學
## 複習與自我學習
試著調整遊戲中的時間間隔。當你改變它們時會發生什麼?閱讀更多關於 [JavaScript 計時事件](https://www.freecodecamp.org/news/javascript-timing-events-settimeout-and-setinterval/) 的內容。
試調整目前遊戲中的間隔時間。改變它們會發生什麼?閱讀更多 [JavaScript 計時事件](https://www.freecodecamp.org/news/javascript-timing-events-settimeout-and-setinterval/)。
## 作業
@ -307,5 +770,7 @@ npm start
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
此文件已使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原始語言的文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤詮釋概不負責。
本文件使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們努力確保準確性,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於關鍵信息,建議採用專業人工翻譯。我們對因使用此翻譯所導致的任何誤解或誤釋概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,23 +1,64 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "8a0a097b45e7c75a611e2795e4013f16",
"translation_date": "2025-08-23T23:03:00+00:00",
"original_hash": "124efddbb65166cddb38075ad6dae324",
"translation_date": "2026-01-06T12:29:14+00:00",
"source_file": "6-space-game/4-collision-detection/assignment.md",
"language_code": "hk"
}
-->
# 探索碰撞
## 指
## 指
為了更好地了解碰撞的運作方式,嘗試建立一個非常小型的遊戲,包含一些會發生碰撞的物件。讓它們透過按鍵或滑鼠點擊移動,並在其中一個物件被撞擊時觸發某些事件。這可以是像隕石撞擊地球,或者碰碰車之類的情景。發揮你的創意!
運用你的碰撞檢測知識,創作一個展示不同物件互動類型的自訂迷你遊戲。這個作業將透過創意實作與實驗,幫助你理解碰撞機制。
### 專案需求
**創建一個小型互動遊戲,特色包含:**
- **多個可移動物件**,可透過鍵盤或滑鼠控制
- **碰撞檢測系統**,使用課程中的矩形相交原理
- **碰撞發生時的視覺反饋**(物件銷毀、顏色變化、特效)
- **讓碰撞具有意義且有趣的遊戲規則**
### 創意建議
**可考慮實作下列其中一種場景:**
- **小行星帶**:操控太空船穿越危險碎片
- **碰碰車**:創造基於物理碰撞的競技場
- **隕石防禦**:保衛地球免受太空隕石攻擊
- **收集遊戲**:收集物品同時避開障礙
- **領土爭奪**:競爭物件嘗試佔領區域
### 技術實作
**你的方案應展現:**
- 正確使用基於矩形的碰撞檢測
- 事件驅動程式設計來處理用戶輸入
- 物件生命週期管理(創建與銷毀)
- 清晰的程式碼結構及適當的類別組織
### 額外挑戰
**為遊戲增添以下功能:**
- **碰撞時的粒子特效**
- **不同碰撞類型的音效**
- **根據碰撞結果的計分系統**
- **多種碰撞類型,帶有各異行為**
- **隨時間增加的漸進式難度**
## 評分標準
| 評分標準 | 卓越 | 合格 | 需要改進 |
| -------- | ------------------------------------------------------------------------------------------------------------------- | -------------------------- | ---------------- |
| | 提供完整可運行的程式碼範例,物件成功繪製到畫布上,基本碰撞發生,並有相應的反應產生 | 程式碼在某些方面不完整 | 程式碼無法正常運作 |
| 標準 | 優異 | 合格 | 需改進 |
|----------|-----------|----------|-------------------|
| **碰撞檢測** | 實作精確的基於矩形碰撞檢測,涵蓋多種物件類型及複雜互動規則 | 基本碰撞檢測正確,配合簡單物件互動 | 碰撞檢測有問題或不穩定 |
| **程式碼品質** | 程式碼乾淨且結構良好,類別架構合理,變數名稱具意義,註解適當 | 程式碼能運作,但結構或文件可改進 | 程式碼難以理解或結構不良 |
| **用戶互動** | 控制反應靈敏,遊戲流暢,視覺回饋清楚,機制有吸引力 | 基本控制可用且有適當回饋 | 控制不靈敏或操作混亂 |
| **創意** | 原創概念帶有獨特特色、視覺打磨及創新碰撞行為 | 標準實作,具部分創意元素 | 只有基本功能,缺乏創意提升 |
---
**免責聲明**
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們力求準確,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要資訊,建議採用專業人工翻譯。我們不對因使用本翻譯而引致的任何誤解或誤釋承擔責任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,23 +1,89 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "adda95e02afa3fbee67b6e385b1109e1",
"translation_date": "2025-08-29T15:02:34+00:00",
"original_hash": "2ed9145a16cf576faa2a973dff84d099",
"translation_date": "2026-01-06T12:34:17+00:00",
"source_file": "6-space-game/5-keeping-score/README.md",
"language_code": "hk"
}
-->
# 建立太空遊戲第五部分:分數與生命
# 建立太空遊戲第5部分得分與生命值
```mermaid
journey
title 你的遊戲設計旅程
section 玩家回饋
了解評分心理學: 3: Student
學習視覺傳達: 4: Student
設計獎勵系統: 4: Student
section 技術實作
Canvas 文字渲染: 4: Student
狀態管理: 5: Student
事件驅動更新: 5: Student
section 遊戲潤飾
使用者體驗設計: 5: Student
平衡挑戰與獎勵: 5: Student
創造引人入勝的遊戲玩法: 5: Student
```
## 課前測驗
[課前測驗](https://ff-quizzes.netlify.app/web/quiz/37)
在這節課中,你將學習如何在遊戲中添加分數以及計算生命。
準備讓你的太空遊戲感覺像真正的遊戲了嗎?讓我們加入得分和生命管理 —— 這些是讓像太空侵略者這類早期街機遊戲,從簡單展示轉變成令人上癮娛樂的核心機制。這是你的遊戲真正變得可玩之處。
```mermaid
mindmap
root((遊戲反饋系統))
Visual Communication
Text Rendering
Icon Display
Color Psychology
Layout Design
Scoring Mechanics
Point Values
Reward Timing
Progress Tracking
Achievement Systems
Life Management
Risk vs Reward
Player Agency
Difficulty Balance
Recovery Mechanics
User Experience
Immediate Feedback
Clear Information
Emotional Response
Engagement Loops
Implementation
Canvas API
State Management
Event Systems
Performance
```
## 在畫面上繪製文字 — 你的遊戲之聲
## 在螢幕上繪製文字
要顯示你的分數,我們需要學會如何在畫布上渲染文字。`fillText()` 方法是你的主要工具 —— 這和經典街機遊戲用來顯示分數和狀態資訊的技術相同。
為了能夠在螢幕上顯示遊戲分數,你需要知道如何在螢幕上放置文字。答案是使用 canvas 物件上的 `fillText()` 方法。你還可以控制其他方面,例如使用什麼字體、文字的顏色,甚至文字的對齊方式(左、右、居中)。以下是一些在螢幕上繪製文字的程式碼。
```mermaid
flowchart LR
A["📝 文字內容"] --> B["🎨 樣式"]
B --> C["📍 定位"]
C --> D["🖼️ 畫布渲染"]
E["字體"] --> B
F["字體大小"] --> B
G["顏色"] --> B
H["對齊"] --> B
I["X 座標"] --> C
J["Y 座標"] --> C
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
```
你可以完全控制文字的外觀:
```javascript
ctx.font = "30px Arial";
@ -26,22 +92,74 @@ ctx.textAlign = "right";
ctx.fillText("show this on the screen", 0, 0);
```
閱讀更多關於[如何在 canvas 上添加文字](https://developer.mozilla.org/docs/Web/API/Canvas_API/Tutorial/Drawing_text)的內容,並隨意讓你的文字看起來更有趣
深入了解[在畫布上添加文本](https://developer.mozilla.org/docs/Web/API/Canvas_API/Tutorial/Drawing_text) —— 你可能會驚訝於你能用字體與樣式創造出多麼有創意的效果
## 生命作為遊戲概念
## 生命值 — 不只是個數字
在遊戲中,生命的概念只是一個數字。在太空遊戲的背景下,通常會分配一組生命,當你的飛船受到傷害時,生命會逐一扣減。如果能用圖形表示生命,例如小型飛船或心形,而不是僅僅用數字表示,會更好
在遊戲設計中,「生命」代表玩家的容錯空間。這種概念可追溯到彈珠台機器,你會得到多顆彈珠來玩。像小行星這類早期遊戲中,生命讓玩家有機會承擔風險並從錯誤中學習
## 要建立的內容
```mermaid
flowchart TD
A["🎮 玩家行動"] --> B{"風險評估"}
B --> C["高風險,高回報"]
B --> D["安全策略"]
C --> E{"結果"}
D --> F["穩定進展"]
E -->|成功| G["🏆 大分數"]
E -->|失敗| H["💔 失去生命"]
H --> I{"剩餘生命?"}
I -->|是| J["🔄 再試一次"]
I -->|否| K["💀 遊戲結束"]
J --> B
G --> B
F --> B
style C fill:#ffebee
style D fill:#e8f5e8
style G fill:#e3f2fd
style H fill:#fff3e0
```
視覺呈現非常重要 —— 用飛船圖示來展示生命而不是僅僅顯示「Lives: 3」會創造即刻的視覺識別就像早期街機機台用圖標跨越語言障礙般。
讓我們在你的遊戲中添加以下內容:
## 建立你的遊戲獎勵系統
- **遊戲分數**:每摧毀一艘敵方飛船,英雄應獲得一些分數,我們建議每艘飛船獲得 100 分。遊戲分數應顯示在左下角。
- **生命**:你的飛船有三條生命。每當敵方飛船與你碰撞時,你會失去一條生命。生命分數應顯示在右下角,並由以下圖形表示 ![生命圖像](../../../../translated_images/life.6fb9f50d53ee0413cd91aa411f7c296e10a1a6de5c4a4197c718b49bf7d63ebf.hk.png)。
現在我們會實作保持玩家投入的核心回饋系統:
```mermaid
sequenceDiagram
participant Player
participant GameEngine
participant ScoreSystem
participant LifeSystem
participant Display
Player->>GameEngine: 射擊敵人
GameEngine->>ScoreSystem: 獎勵分數
ScoreSystem->>ScoreSystem: +100 分
ScoreSystem->>Display: 更新分數
Player->>GameEngine: 與敵人碰撞
GameEngine->>LifeSystem: 減少生命
LifeSystem->>LifeSystem: -1 生命
LifeSystem->>Display: 更新生命值
alt 生命值 > 0
LifeSystem->>Player: 繼續遊戲
else 生命值 = 0
LifeSystem->>GameEngine: 遊戲結束
end
```
- **得分系統**每摧毀一艘敵機獲得100分整數更方便玩家心算。分數顯示在左下角。
- **生命計數器**:你的英雄起始有三條命 —— 這是早期街機遊戲定下的標準,平衡挑戰與可玩性。每次與敵機碰撞損失一條命。剩餘生命用飛船圖示顯示在右下角 ![life image](../../../../translated_images/life.6fb9f50d53ee0413.hk.png)。
## 建議步驟
## 開始動手吧!
找到在 `your-work` 子資料夾中為你建立的檔案。它應包含以下內容:
先設置工作環境。瀏覽到你的 `your-work` 子資料夾。你應該會看到這些檔案
```bash
-| assets
@ -53,24 +171,49 @@ ctx.fillText("show this on the screen", 0, 0);
-| package.json
```
你可以通過輸入以下指令啟動你的專案
要測試你的遊戲,從 `your_work` 資料夾啟動開發伺服器
```bash
cd your-work
npm start
```
上述指令會在地址 `http://localhost:5000` 上啟動一個 HTTP 伺服器。打開瀏覽器並輸入該地址,目前應顯示英雄和所有敵人,當你按下左右箭頭時,英雄會移動並能擊落敵人
這會啟動一個本地伺服器於 `http://localhost:5000`。在瀏覽器打開這個地址看你的遊戲。用方向鍵試試控制,並嘗試射擊敵人確認功能正常
### 添加程式碼
```mermaid
flowchart TD
A["1. 資產載入"] --> B["2. 遊戲變數"]
B --> C["3. 碰撞檢測"]
C --> D["4. 英雄強化"]
D --> E["5. 顯示功能"]
E --> F["6. 事件處理器"]
G["生命圖示影像"] --> A
H["分數與生命追蹤"] --> B
I["英雄與敵人相交"] --> C
J["點數與生命方法"] --> D
K["文字與圖示渲染"] --> E
L["獎勵與懲罰邏輯"] --> F
F --> M["🎮 完整遊戲"]
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
style E fill:#e0f2f1
style F fill:#fce4ec
style M fill:#e1f5fe
```
### 開始編碼!
1. **複製所需資產** 從 `solution/assets/` 資料夾複製到 `your-work` 資料夾;你需要添加一個 `life.png` 資產。將 lifeImg 添加到 window.onload 函數中:
1. **準備你需要的視覺資源**。將 `solution/assets/` 資料夾中的 `life.png` 複製到你的 `your-work` 資料夾。然後在 `window.onload` 函數中加入 lifeImg
```javascript
lifeImg = await loadTexture("assets/life.png");
```
1. 將 `lifeImg` 添加到資產列表中:
1. 別忘了把 `lifeImg` 加入你的資源列表
```javascript
let heroImg,
@ -80,9 +223,9 @@ npm start
eventEmitter = new EventEmitter();
```
2. **添加變數**。添加代表你的總分數0和剩餘生命3的程式碼並在螢幕上顯示這些分數
2. **設定遊戲變數**。加入程式碼以追蹤你的總分初始為0與剩餘生命初始為3。我們會將它們顯示在畫面上好讓玩家隨時知道狀況
3. **擴展 `updateGameObjects()` 函數**。擴展 `updateGameObjects()` 函數以處理敵人碰撞
3. **實作碰撞偵測**。擴充你的 `updateGameObjects()` 函數,偵測敵人與英雄碰撞時的狀態
```javascript
enemies.forEach(enemy => {
@ -93,19 +236,19 @@ npm start
})
```
4. **添加生命和分數**。
1. **初始化變數**。在 `Hero` 類別中的 `this.cooldown = 0` 下,設置生命和分數:
4. **將生命與得分追蹤加到 Hero 中**。
1. **初始化計數器**。在 Hero 類別中 `this.cooldown = 0` 下方設定生命與分數:
```javascript
this.life = 3;
this.points = 0;
```
1. **在螢幕上繪製變數**。將這些值繪製到螢幕上
1. **顯示這些數值給玩家**。建立函數在畫面繪製這些值
```javascript
function drawLife() {
// TODO, 35, 27
// 待辦3527
const START_POS = canvas.width - 180;
for(let i=0; i < hero.life; i++ ) {
ctx.drawImage(
@ -128,18 +271,34 @@ npm start
```
1. **方法添加到遊戲循環中**。確保在 `updateGameObjects()` 下將這些函數添加到你的 window.onload 函數中
1. **所有功能掛勾到遊戲迴圈**。在 `window.onload``updateGameObjects()` 後加入這些函數
```javascript
drawPoints();
drawLife();
```
1. **實現遊戲規則**。實現以下遊戲規則:
### 🔄 **教學檢核**
**遊戲設計理解**:在實作後果之前,先確保你理解:
- ✅ 視覺反饋如何將遊戲狀態傳達給玩家
- ✅ 一致擺放 UI 元素提升使用便利性
- ✅ 分數與生命管理的心理學意義
- ✅ Canvas 文字渲染與 HTML 文字的不同
**快速自測**:為什麼街機遊戲通常使用整數作為分數?
*答:整數更方便玩家心算,也能創造令人滿足的心理獎勵*
**用戶體驗原則**:你現在正在應用:
- **視覺層級**:重要資訊顯著呈現
- **即時回饋**:玩家動作即時更新
- **認知負荷**:資訊簡單明確呈現
- **情感設計**:用圖示與顏色建立玩家連結
1. **實作遊戲後果與獎勵**。現在加入讓玩家行為有意義的回饋系統:
1. **每次英雄與敵人碰撞**,扣減一條生命。
1. **碰撞會損失生命**。每次英雄撞上敵人就會損失一條命。
擴展 `Hero` 類別以執行此扣減:
在你的 `Hero` 類別加入此方法
```javascript
decrementLife() {
@ -150,17 +309,17 @@ npm start
}
```
2. **每次雷射擊中敵人**,增加遊戲分數 100 分。
2. **射擊敵人得分**。每次成功命中敵人可獲得100分立刻給予精準射擊的正向回饋。
擴充 Hero 類別加入此分數增加方法:
擴展 Hero 類別以執行此增量:
```javascript
incrementPoints() {
this.points += 100;
}
```
將這些函數添加到你的碰撞事件發射器中:
現在將這些方法連結到碰撞事件中:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
@ -175,29 +334,175 @@ npm start
});
```
✅ 做一些研究,探索其他使用 JavaScript/Canvas 建立的遊戲。它們有哪些共同特徵?
完成這些工作後,你應該能在右下角看到小型的“生命”飛船,在左下角看到分數,並且當你與敵人碰撞時生命計數會減少,當你擊中敵人時分數會增加。做得好!你的遊戲幾乎完成了。
✅ 對用 JavaScript 和 Canvas 製作的其他遊戲感到好奇嗎?去探索看看 — 你會驚訝於還能做到什麼!
完成這些功能後,測試你的遊戲,看看完整的回饋系統如何運作。你應該會在右下角看到生命圖示,左下角看到分數,碰撞時生命減少,成功射擊時分數增加。
你的遊戲現在已經具備早期街機遊戲讓人著迷的基本機制 —— 明確目標、即時回饋及有意義的玩家行為後果。
### 🔄 **教學檢核**
**完整遊戲設計系統**:確認你掌握了玩家回饋系統:
- ✅ 得分機制如何激發玩家動機與投入?
- ✅ 為什麼視覺一致性對用戶介面設計重要?
- ✅ 生命系統如何平衡挑戰與玩家留存?
- ✅ 即時回饋在製造滿足感遊戲體驗中扮演什麼角色?
**系統整合**:你的回饋系統展現了:
- **用戶體驗設計**:清晰視覺傳達與資訊層級
- **事件驅動架構**:對玩家動作做出迅速回應
- **狀態管理**:追蹤並呈現動態遊戲資料
- **Canvas 精通**:文字渲染與精靈定位
- **遊戲心理學**:理解玩家動機與投入
**專業模式**:你已實作:
- **MVC 架構**:遊戲邏輯、資料與呈現分離
- **觀察者模式**:事件驅動遊戲狀態更新
- **元件設計**:可重用的繪圖與邏輯函數
- **效能優化**:遊戲迴圈中高效渲染
### ⚡ **5分鐘內可以做的事**
- [ ] 嘗試不同字型大小和顏色來顯示分數
- [ ] 嘗試改變分數值,看看遊戲節奏如何改變
- [ ] 添加 console.log 訊息來追蹤分數與生命變化
- [ ] 測試極端狀況,例如生命用盡或達成高分
### 🎯 **這一小時內可以完成的目標**
- [ ] 完成課後測驗並理解遊戲設計心理學
- [ ] 新增得分與損失生命的音效
- [ ] 使用 localStorage 實作高分系統
- [ ] 為不同敵人設定不同點數
- [ ] 新增損失生命時的畫面震動特效
### 📅 **你的一週遊戲設計旅程**
- [ ] 完成整個太空遊戲並優化回饋系統
- [ ] 實作連擊倍率等進階得分機制
- [ ] 新增成就與可解鎖內容
- [ ] 創建難度進程與平衡系統
- [ ] 設計選單與遊戲結束畫面介面
- [ ] 研究其他遊戲理解玩家黏著機制
### 🌟 **你的一個月遊戲開發精通**
- [ ] 製作帶有完整進階系統的遊戲
- [ ] 學習遊戲分析與玩家行為度量
- [ ] 參與開源遊戲開發專案
- [ ] 精通進階遊戲設計模式與營利策略
- [ ] 製作關於遊戲設計與用戶體驗的教學內容
- [ ] 建立遊戲設計與開發技能的作品集
## 🎯 你的遊戲設計精通時程
```mermaid
timeline
title 遊戲設計與玩家反饋學習進程
section 基礎 (10 分鐘)
視覺傳達:文字呈現
:圖示設計
:版面原則
:色彩心理學
section 玩家心理學 (20 分鐘)
動機系統:積分值
:風險與獎勵
:進度反饋
:成就設計
section 技術實作 (30 分鐘)
Canvas 精通:文字定位
:精靈呈現
:狀態管理
:效能優化
section 遊戲平衡 (40 分鐘)
難度設計:生命管理
:得分曲線
:玩家留存
:可及性
section 使用者體驗 (50 分鐘)
介面設計:資訊層級
:回饋響應
:情感設計
:可用性測試
section 進階系統 (1 週)
遊戲機制:進度系統
:分析整合
:變現設計
:社群功能
section 業界技能 (1 個月)
專業發展:團隊協作
:設計文件
:玩家研究
:平台優化
```
### 🛠️ 你的遊戲設計工具箱總結
完成本課程後,你已掌握:
- **玩家心理學**:理解動機、風險/報酬與參與循環
- **視覺傳達**:使用文字、圖示與排版設計有效介面
- **回饋系統**:即時回應玩家行為與遊戲事件
- **狀態管理**:有效追蹤與呈現動態遊戲資料
- **Canvas 文字渲染**:專業文字顯示與樣式位置調整
- **事件整合**:連結使用者行為到有意義遊戲後果
- **遊戲平衡**:設計難度曲線與玩家進度系統
**真實世界應用**:你的遊戲設計技能直接適用於:
- **用戶介面設計**:打造有吸引力且直覺的介面
- **產品開發**:理解用戶動機與回饋循環
- **教育科技**:遊戲化與學習參與系統
- **資料視覺化**:使複雜資訊易於理解與吸引人
- **行動應用開發**:用戶留存與體驗設計
- **行銷科技**:理解用戶行為與轉換優化
**所獲專業技能**:你現在能:
- **設計**激發與吸引用戶的體驗
- **實作**引導用戶行為的回饋系統
- **平衡**互動系統中的挑戰與易用性
- **創造**跨群組皆有效的視覺傳達
- **分析**用戶行為並迭代設計優化
**掌握的遊戲開發概念**
- **玩家動機**:理解推動投入與留存的因素
- **視覺設計**:打造清晰、美觀且實用的介面
- **系統整合**:連結多種遊戲系統創造整合體驗
- **效能優化**:高效渲染與狀態管理
- **可及性**:為不同技能等級與玩家需求設計
**下一步**:你已準備好探索進階遊戲設計模式、實作分析系統,或研究遊戲營利與玩家留存策略!
🌟 **成就解鎖**:你已建立完整的玩家回饋系統、具備專業遊戲設計原則!
---
## GitHub Copilot 代理挑戰 🚀
使用代理模式完成以下挑戰:
**描述:** 強化太空遊戲得分系統,實作可持久儲存的高分功能與額外連擊加分機制。
**提示:** 創建高分系統,將玩家最佳分數保存到 localStorage。為連續擊殺敵人新增連擊獎勵分數並為不同敵人類型設置不同點數。當玩家達成新高分時顯示視覺提示並在遊戲畫面顯示當前最高分。
## 🚀 挑戰
你的程式碼幾乎完成了。你能想像下一步該做什麼嗎?
現在有一個功能完整的有得分與生命的遊戲。思考還能加入哪些額外功能提升玩家體驗。
## 課後測驗
[課後測驗](https://ff-quizzes.netlify.app/web/quiz/38)
## 回顧與自學
## 複習與自學
研究一些可以增加和減少遊戲分數和生命的方法。有一些有趣的遊戲引擎,例如 [PlayFab](https://playfab.com)。使用其中一個引擎如何能提升你的遊戲?
想探究更多?研究不同的遊戲得分與生命系統方案。市面上有像 [PlayFab](https://playfab.com) 這樣管理得分、排行榜和玩家進度的強大遊戲引擎。整合類似系統如何提升你的遊戲層次
## 作業
[建立一個計分遊戲](assignment.md)
[造得分遊戲](assignment.md)
---
**免責聲明**
此文件已使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原始語言的文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤詮釋概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於確保準確性,但請注意自動翻譯可能存在錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要資訊,建議採用專業人工翻譯。我們不對因使用此翻譯而引起的任何誤解或誤譯承擔責任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,41 +1,163 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "05be6c37791668e3719c4fba94566367",
"translation_date": "2025-08-29T15:03:41+00:00",
"original_hash": "a4b78043f4d64bf3ee24e0689b8b391d",
"translation_date": "2026-01-06T12:31:42+00:00",
"source_file": "6-space-game/6-end-condition/README.md",
"language_code": "hk"
}
-->
# 建立太空遊戲第六部分:結束與重新開始
## 課前測驗
[課前測驗](https://ff-quizzes.netlify.app/web/quiz/39)
在遊戲中,有不同的方法來表達*結束條件*。作為遊戲的創作者,由你來決定遊戲為什麼會結束。如果我們假設你正在建立的太空遊戲,以下是一些可能的原因:
- **摧毀了 `N` 艘敵方飛船**:如果你將遊戲分成不同的關卡,通常需要摧毀 `N` 艘敵方飛船才能完成一個關卡。
- **你的飛船被摧毀**:有些遊戲中,如果你的飛船被摧毀,你就會輸掉遊戲。另一種常見的方法是引入生命值的概念。每次你的飛船被摧毀,就會扣除一條生命值。一旦所有生命值都用完,遊戲就結束。
- **收集了 `N` 分**:另一個常見的結束條件是收集分數。如何獲得分數由你決定,但通常會將分數分配給各種活動,例如摧毀敵方飛船,或者收集敵方飛船被摧毀後掉落的物品。
- **完成一個關卡**:這可能涉及多個條件,例如摧毀 `X` 艘敵方飛船、收集 `Y` 分,或者收集特定的物品。
# 建立太空遊戲第6部分結束與重新開始
```mermaid
journey
title 你的遊戲完成旅程
section 結束條件
定義勝負狀態: 3: Student
實作條件檢查: 4: Student
處理狀態轉換: 4: Student
section 玩家體驗
設計反饋系統: 4: Student
建立重新開始機制: 5: Student
美化用戶介面: 5: Student
section 系統整合
管理遊戲生命週期: 5: Student
處理記憶體清理: 5: Student
建立完整體驗: 5: Student
```
每一款偉大的遊戲都需要明確的結束條件和流暢的重新開始機制。你已經建立了一款令人印象深刻的太空遊戲,有移動、戰鬥和計分——現在是時候加入最後的元素,讓它感覺完整了。
你的遊戲目前無限運行就像1977年NASA發射的旅行者探測器——數十年後仍在太空中旅行。雖然這對太空探索來說沒問題但遊戲需要明確的終點來創造令人滿意的體驗。
今天,我們將實現正確的勝利/失敗條件和重新開始系統。課程結束時,你會擁有一款經過精緻打磨,玩家可以完成並重玩,就像定義了媒介的經典街機遊戲一樣的遊戲。
```mermaid
mindmap
root((遊戲完成))
End Conditions
Victory States
Defeat Conditions
Progress Tracking
State Validation
Player Feedback
Visual Messages
Color Psychology
Clear Communication
Emotional Response
State Management
Game Loop Control
Memory Cleanup
Object Lifecycle
Event Handling
Restart Systems
Input Handling
State Reset
Fresh Initialization
User Experience
Polish Elements
Message Display
Smooth Transitions
Error Prevention
Accessibility
```
## 課前小測
[課前小測](https://ff-quizzes.netlify.app/web/quiz/39)
## 理解遊戲結束條件
你的遊戲應該何時結束?這個基本問題自早期街機時代以來一直塑造著遊戲設計。吃豆人當你被鬼抓住或吃完所有豆子時結束,而太空侵略者當外星人到達底部或你消滅所有外星人時結束。
作為遊戲創作者,你定義勝利和失敗條件。對於我們的太空遊戲,以下是經證明能創造吸引人玩法的做法:
```mermaid
flowchart TD
A["🎮 遊戲開始"] --> B{"檢查條件"}
B --> C["敵人數量"]
B --> D["英雄生命"]
B --> E["分數門檻"]
B --> F["關卡進度"]
C --> C1{"敵人 = 0?"}
D --> D1{"生命 = 0?"}
E --> E1{"分數 ≥ 目標?"}
F --> F1{"目標完成?"}
C1 -->|是| G["🏆 勝利"]
D1 -->|是| H["💀 失敗"]
E1 -->|是| G
F1 -->|是| G
C1 -->|否| B
D1 -->|否| B
E1 -->|否| B
F1 -->|否| B
G --> I["🔄 重新開始選項"]
H --> I
style G fill:#e8f5e8
style H fill:#ffebee
style I fill:#e3f2fd
```
- **已摧毀 `N` 艘敵方飛船**:如果你將遊戲分成不同關卡,需要摧毀 `N` 艘敵方飛船才能完成該關是相當常見的
- **你的飛船被摧毀**:確實有遊戲是飛船被摧毀就輸。另一個常見方式是有生命值概念。每次飛船被摧毀會扣一條命,當所有命都用完後你就輸了
- **你已收集 `N` 分數**:另一個常見結束條件是你收集分數。你怎麼獲得分數取決於你,但通常會對摧毀敵人或收集被摧毀敵人掉落的物品給予分數
- **完成一個關卡**:這可能包含多個條件,例如摧毀 `X` 艘敵人、收集 `Y` 分數或收集特定物品
## 實現遊戲重新開始功能
好的遊戲通過流暢的重新開始機制鼓勵重玩。當玩家結束遊戲(或失敗)時,他們通常想立即再試一次——無論是為了打破自己的紀錄還是提升表現。
```mermaid
stateDiagram-v2
[*] --> Playing: 遊戲開始
Playing --> Victory: 全部敵人被消滅
Playing --> Defeat: 生命值 = 0
Victory --> MessageDisplay: 顯示勝利訊息
Defeat --> MessageDisplay: 顯示失敗訊息
MessageDisplay --> WaitingRestart: 按下 Enter 提示
WaitingRestart --> Resetting: 按下 Enter 鍵
Resetting --> CleanupMemory: 清除計時器
CleanupMemory --> ClearEvents: 移除監聽器
ClearEvents --> InitializeGame: 全新開始
InitializeGame --> Playing: 新遊戲開始
note right of MessageDisplay
顏色標示回饋:
綠色 = 勝利
紅色 = 失敗
end note
note right of Resetting
完成狀態重設
防止記憶體洩漏
end note
```
俄羅斯方塊完美詮釋了這一點:當方塊堆到頂端,你可以立即開始新遊戲,無需瀏覽複雜的選單。我們將建立類似的重新開始系統,乾淨地重置遊戲狀態,讓玩家迅速回到遊戲中。
## 重新開始
**思考點**:想想你玩過的遊戲。它們在什麼條件下結束,並且如何提示你重新開始?什麼讓重新開始的體驗感覺流暢而不是令人沮喪?
如果玩家喜歡你的遊戲,他們可能會想要重玩一次。無論遊戲因為什麼原因結束,你都應該提供一個重新開始的選項。
## 你將建立什麼
✅ 想一想,遊戲在什麼條件下結束,然後玩家是如何被提示重新開始的。
你將實作最終功能,讓專案轉變為完整的遊戲體驗。這些元素讓精緻遊戲有別於基本原型
## 要建立的內容
**我們今天要加的東西:**
你將為遊戲新增以下規則:
1. **勝利條件**:消滅所有敵人並有適當的慶祝(你應得的!)
2. **失敗條件**:命盡時出現失敗畫面接受結果
3. **重新開始機制**:按 Enter 立即重新開始—因為玩一次永遠不夠
4. **狀態管理**:每次都是乾淨的起點——沒有殘留敵人或上局的怪異錯誤
1. **贏得遊戲**。當所有敵方飛船被摧毀時,你贏得遊戲。此外,顯示某種勝利訊息。
2. **重新開始**。當所有生命值耗盡或遊戲勝利時,你應該提供一個重新開始遊戲的方法。記住!你需要重新初始化遊戲,並清除之前的遊戲狀態。
## 開始吧
## 建議步驟
讓我們準備你的開發環境。你應該已擁有前幾課的太空遊戲所有檔案。
找到在 `your-work` 子資料夾中為你建立的檔案。它應該包含以下內容:
**你的專案應該長這樣:**
```bash
-| assets
@ -47,190 +169,517 @@ CO_OP_TRANSLATOR_METADATA:
-| app.js
-| package.json
```
`your_work` 資料夾中啟動你的專案,輸入:
**啟動開發伺服器:**
```bash
cd your-work
npm start
```
**這個指令會:**
- 啟動本地伺服器於 `http://localhost:5000`
- 正確提供你的檔案
- 讓你更改時自動重新整理
打開瀏覽器進入 `http://localhost:5000`,確認遊戲在運行。你應該能移動、射擊及與敵人互動。確認後,我們就能進行實作了。
> 💡 **專家提示**:為避免 Visual Studio Code 警告,建議在檔案頂部宣告 `let gameLoopId;`,而非在 `window.onload` 函數內宣告。這符合現代JavaScript變數宣告最佳實踐。
```mermaid
flowchart TD
A["1. 狀態追蹤"] --> B["2. 事件處理器"]
B --> C["3. 訊息常數"]
C --> D["4. 重新開始控制"]
D --> E["5. 訊息顯示"]
E --> F["6. 系統重設"]
G["isHeroDead()\nisEnemiesDead()"] --> A
H["碰撞事件\n結束遊戲事件"] --> B
I["遊戲結束_勝利\n遊戲結束_失敗"] --> C
J["Enter 鍵\n重新開始觸發"] --> D
K["勝利/失敗\n顏色標示文字"] --> E
L["狀態清理\n全新初始化"] --> F
F --> M["🎮 完整遊戲"]
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
style E fill:#e0f2f1
style F fill:#fce4ec
style M fill:#e1f5fe
```
## 實作步驟
### 步驟1建立結束條件追蹤函數
我們需要函數監控遊戲何時結束。就像國際太空站經常監控關鍵系統一樣,這些函數會不斷檢查遊戲狀態。
```javascript
function isHeroDead() {
return hero.life <= 0;
}
function isEnemiesDead() {
const enemies = gameObjects.filter((go) => go.type === "Enemy" && !go.dead);
return enemies.length === 0;
}
```
**幕後發生的事:**
- **檢查**我們的主角是否已無命(好痛!)
- **統計**還有多少敵人活著努力抵抗
- **回傳**當戰場清空敵人時返回 `true`
- **使用**簡單的真/假邏輯讓流程簡潔明瞭
- **篩選**過所有遊戲物件尋找存活者
### 步驟2更新事件處理程序連結結束條件
現在將這些條件檢查連結到遊戲事件系統。每次發生碰撞時,遊戲會評估是否觸發結束條件。這會讓重要事件有即時反饋。
```mermaid
sequenceDiagram
participant Collision
participant GameLogic
participant Conditions
participant EventSystem
participant Display
Collision->>GameLogic: 激光擊中敵人
GameLogic->>GameLogic: 毀壞物件
GameLogic->>Conditions: 檢查 isEnemiesDead()
alt 所有敵人被擊敗
Conditions->>EventSystem: 發出 GAME_END_WIN
EventSystem->>Display: 顯示勝利訊息
else 敵人仍存在
Conditions->>GameLogic: 繼續遊戲
end
Collision->>GameLogic: 敵人擊中英雄
GameLogic->>GameLogic: 生命減少
GameLogic->>Conditions: 檢查 isHeroDead()
alt 生命 = 0
Conditions->>EventSystem: 發出 GAME_END_LOSS
EventSystem->>Display: 顯示失敗訊息
else 生命剩餘
GameLogic->>Conditions: 檢查 isEnemiesDead()
alt 所有敵人被擊敗
Conditions->>EventSystem: 發出 GAME_END_WIN
end
end
```
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
hero.incrementPoints();
if (isEnemiesDead()) {
eventEmitter.emit(Messages.GAME_END_WIN);
}
});
eventEmitter.on(Messages.COLLISION_ENEMY_HERO, (_, { enemy }) => {
enemy.dead = true;
hero.decrementLife();
if (isHeroDead()) {
eventEmitter.emit(Messages.GAME_END_LOSS);
return; // 凱旋之前的損失
}
if (isEnemiesDead()) {
eventEmitter.emit(Messages.GAME_END_WIN);
}
});
eventEmitter.on(Messages.GAME_END_WIN, () => {
endGame(true);
});
eventEmitter.on(Messages.GAME_END_LOSS, () => {
endGame(false);
});
```
**發生了什麼事:**
- **雷射射中敵人**:雙方消失,你獲得分數並檢查是否勝利
- **敵人碰撞你**:你損失一條命,然後檢查是否還活著
- **聰明排序**:先檢查失敗(沒有人想又贏又輸!)
- **即時反應**:重要事件一發生,遊戲立刻知道
上述指令會在地址 `http://localhost:5000` 啟動一個 HTTP 伺服器。打開瀏覽器並輸入該地址。你的遊戲應該處於可玩狀態。
### 步驟3新增訊息常數
> 提示:為了避免在 Visual Studio Code 中出現警告,編輯 `window.onload` 函數,直接調用 `gameLoopId`(不加 `let`),並在檔案頂部獨立宣告:`let gameLoopId;`
你需要為`Messages`常數物件新增訊息類型。這些常數有助於保持一致性並防止事件系統中打錯字。
### 新增程式碼
```javascript
GAME_END_LOSS: "GAME_END_LOSS",
GAME_END_WIN: "GAME_END_WIN",
```
**於上文中我們:**
- **新增**遊戲結束事件的常數以維持一致
- **使用**清晰說明事件用途的名稱
- **遵循**既有訊息類型命名慣例
1. **追蹤結束條件**。新增程式碼來追蹤敵方數量,或英雄飛船是否被摧毀,透過新增以下兩個函數:
### 步驟4實作重新開始控制
```javascript
function isHeroDead() {
return hero.life <= 0;
}
現在你將新增鍵盤控制允許玩家重新開始遊戲。Enter 鍵是自然選擇,因為它通常與確認操作和開始新遊戲相關。
**將 Enter 鍵偵測加入現有 keydown 事件監聽器:**
```javascript
else if(evt.key === "Enter") {
eventEmitter.emit(Messages.KEY_EVENT_ENTER);
}
```
**新增訊息常數:**
```javascript
KEY_EVENT_ENTER: "KEY_EVENT_ENTER",
```
**你需要知道的:**
- **擴充**現有鍵盤事件處理系統
- **使用**Enter 鍵作為重新開始觸發器,提供直覺使用體驗
- **發出**自訂事件供遊戲其他部分監聽
- **維持**與其他鍵盤控制相同的模式
### 步驟5建立訊息顯示系統
function isEnemiesDead() {
const enemies = gameObjects.filter((go) => go.type === "Enemy" && !go.dead);
return enemies.length === 0;
你的遊戲需要清晰地傳達結果給玩家。我們將建立訊息系統,用顏色區分勝敗狀態,類似早期電腦系統終端,綠色表示成功,紅色代表錯誤。
**建立 `displayMessage()` 函式:**
```javascript
function displayMessage(message, color = "red") {
ctx.font = "30px Arial";
ctx.fillStyle = color;
ctx.textAlign = "center";
ctx.fillText(message, canvas.width / 2, canvas.height / 2);
}
```
**步驟解析:**
- **設定**字型大小及字體以利清楚閱讀
- **應用**顏色參數,預設為紅色警告色
- **將**文本在畫布中水平與垂直置中
- **採用**現代JavaScript預設參數提供彈性調色
- **利用**畫布2D上下文直接渲染文字
**建立 `endGame()` 函式:**
```javascript
function endGame(win) {
clearInterval(gameLoopId);
// 設定延遲以確保任何待處理的渲染完成
setTimeout(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
if (win) {
displayMessage(
"Victory!!! Pew Pew... - Press [Enter] to start a new game Captain Pew Pew",
"green"
);
} else {
displayMessage(
"You died !!! Press [Enter] to start a new game Captain Pew Pew"
);
}
```
2. **新增邏輯到訊息處理器**。編輯 `eventEmitter` 以處理這些條件:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
hero.incrementPoints();
if (isEnemiesDead()) {
eventEmitter.emit(Messages.GAME_END_WIN);
}
});
eventEmitter.on(Messages.COLLISION_ENEMY_HERO, (_, { enemy }) => {
enemy.dead = true;
hero.decrementLife();
if (isHeroDead()) {
eventEmitter.emit(Messages.GAME_END_LOSS);
return; // loss before victory
}
if (isEnemiesDead()) {
eventEmitter.emit(Messages.GAME_END_WIN);
}
});
eventEmitter.on(Messages.GAME_END_WIN, () => {
endGame(true);
});
eventEmitter.on(Messages.GAME_END_LOSS, () => {
endGame(false);
});
```
3. **新增新的訊息類型**。將這些訊息新增到常數物件中:
```javascript
GAME_END_LOSS: "GAME_END_LOSS",
GAME_END_WIN: "GAME_END_WIN",
```
4. **新增重新開始的程式碼**,在按下選定按鈕時重新開始遊戲。
1. **監聽按鍵 `Enter`**。編輯你的視窗事件監聽器以監聽此按鍵:
```javascript
else if(evt.key === "Enter") {
eventEmitter.emit(Messages.KEY_EVENT_ENTER);
}
```
2. **新增重新開始訊息**。將此訊息新增到你的 Messages 常數中:
```javascript
KEY_EVENT_ENTER: "KEY_EVENT_ENTER",
```
5. **實現遊戲規則**。實現以下遊戲規則:
1. **玩家勝利條件**。當所有敵方飛船被摧毀時,顯示勝利訊息。
1. 首先,建立一個 `displayMessage()` 函數:
```javascript
function displayMessage(message, color = "red") {
ctx.font = "30px Arial";
ctx.fillStyle = color;
ctx.textAlign = "center";
ctx.fillText(message, canvas.width / 2, canvas.height / 2);
}
```
2. 建立一個 `endGame()` 函數:
```javascript
function endGame(win) {
clearInterval(gameLoopId);
}, 200)
}
```
**此函式的功能:**
- **凍結**所有動作——不再有移動的飛船或雷射
- **暫停**200毫秒讓最後一幀完成繪製
- **清空**畫面並用黑色繪底,營造戲劇效果
- **依勝負顯示**不同訊息
- **用顏色區分**好消息為綠色,壞消息為紅色
- **指示**玩家如何重新開始
### 🔄 **教學檢核點**
**遊戲狀態管理**:在實作重置功能前,請確保你了解:
- ✅ 結束條件如何創造明確的遊戲目標
- ✅ 視覺回饋為何對玩家理解至關重要
- ✅ 適當清理防止記憶體洩漏的重要性
- ✅ 事件驅動架構如何實現乾淨的狀態轉換
**快速自測**:如果重置時不清除事件監聽器會發生什麼?
*答:記憶體洩漏及事件處理器重複導致不可預期行為*
**遊戲設計原則**:你現在實作的包括:
- **清晰目標**:玩家知道成功與失敗的定義
- **即時反饋**:遊戲狀態變化即時傳遞
- **使用者控制**:玩家準備好時能重新開始
- **系統可靠性**:適當清理避免錯誤和效能問題
### 步驟6實作遊戲重置功能
重置系統需要完整清理當前遊戲狀態並初始化新遊戲。這確保玩家有乾淨起點,不會帶有上回遊戲的殘留資料。
**建立 `resetGame()` 函式:**
```javascript
function resetGame() {
if (gameLoopId) {
clearInterval(gameLoopId);
eventEmitter.clear();
initGame();
gameLoopId = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawPoints();
drawLife();
updateGameObjects();
drawGameObjects(ctx);
}, 100);
}
}
```
**逐項解析:**
- **確認**是否已有遊戲循環在執行,才進行重置
- **清除**現有遊戲循環停止所有遊戲活動
- **移除**所有事件監聽器以防記憶體洩漏
- **重新初始化**遊戲狀態及相關物件變數
- **啟動**新遊戲循環包含全部必要功能
- **維持**每100毫秒一次的固定間隔保證一致效能
**將 Enter 鍵事件處理器加入你的 `initGame()` 函式:**
```javascript
eventEmitter.on(Messages.KEY_EVENT_ENTER, () => {
resetGame();
});
```
**新增 `clear()` 方法到你的 EventEmitter 類別:**
```javascript
clear() {
this.listeners = {};
}
```
**重點提醒:**
- **連結**Enter鍵按下與遊戲重置功能
- **在遊戲初始化時**註冊此事件監聽器
- **提供**清理所有事件監聽器的乾淨方法
- **防止**記憶體洩漏,重新遊戲間清空事件處理器
- **將**監聽器物件重置成空狀態,利於後續初始化
## 恭喜你!🎉
👽 💥 🚀 你成功從零建構一款完整遊戲。就像1970年代第一批電玩開發者一樣你將一行行程式碼轉化為擁有正確遊戲機制與用戶回饋的互動體驗。🚀 💥 👽
**你已達成:**
- **實作**完整的勝利與失敗條件並有使用者反饋
- **建立**無縫的重新開始系統,支持持續遊玩
- **設計**清晰視覺溝通呈現遊戲狀態
- **管理**複雜的遊戲狀態轉換與清理
- **組裝**所有組件成為一款可玩且流暢的遊戲
### 🔄 **教學檢核點**
**完整遊戲開發系統**:慶祝你掌握整個遊戲開發流程:
- ✅ 結束條件如何創造令人滿意的玩家體驗?
- ✅ 為何適當的狀態管理對遊戲穩定性至關重要?
- ✅ 視覺回饋如何提升玩家理解?
- ✅ 重新開始系統對玩家留存起什麼作用?
**系統熟練**:你的完整遊戲展現了:
- **全端遊戲開發**:從圖像到輸入到狀態管理
- **專業架構**:事件驅動系統並有妥善清理
- **使用者體驗設計**:清晰回饋和直覺控制
- **效能優化**:高效率渲染與記憶體管理
- **精緻與完整**:所有讓遊戲感覺完成的細節
**業界技能**:你已實作:
- **遊戲循環架構**:即時系統具持續穩定性
- **事件驅動程式設計**:解耦能有效擴展系統
- **狀態管理**:複雜資料處理與生命週期管理
- **使用者介面設計**:清晰溝通與流暢操作
- **測試與除錯**:反覆開發與問題解決
### ⚡ **你接下來5分鐘能做的事**
- [ ] 玩完整遊戲並測試所有勝利與失敗條件
- [ ] 嘗試不同的結束條件參數
- [ ] 加入 console.log 語句追蹤遊戲狀態變化
- [ ] 與朋友分享遊戲,蒐集回饋
### 🎯 **你這小時能完成的目標**
- [ ] 完成課後測驗並反思你的遊戲開發歷程
- [ ] 加入勝利與失敗狀態的音效效果
- [ ] 實作額外結束條件,例如限時或獎勵目標
- [ ] 創建不同難度關卡與不同敵人數量
- [ ] 美化視覺呈現,改善字體與色彩
### 📅 **你一週內的遊戲開發精修計畫**
- [ ] 完成擴充版太空遊戲,有多重關卡與進度追蹤
- [ ] 新增強化道具、不同敵人類型及特殊武器
- [ ] 建立高分系統與持久化存儲
- [ ] 設計使用者介面包含選單、設定及遊戲選項
- [ ] 優化不同裝置和瀏覽器的效能
- [ ] 將遊戲部署至網路並與社群分享
### 🌟 **你的一個月遊戲開發職業路線**
- [ ] 製作多款完整遊戲,探索不同類型與機制
- [ ] 學習進階遊戲開發框架,如 Phaser 或 Three.js
- [ ] 參與開源遊戲開發專案
- [ ] 研讀遊戲設計原理與玩家心理學
- [ ] 建立展示你遊戲開發技能的作品集
- [ ] 與遊戲開發社群連結,持續學習
## 🎯 你的完整遊戲開發精通時間表
```mermaid
timeline
title 完整遊戲開發學習進度
section 基礎 (課程 1-2)
遊戲架構:專案結構
:資產管理
:畫布基礎
:事件系統
section 互動系統 (課程 3-4)
玩家操作:輸入處理
:移動機制
:碰撞偵測
:物理模擬
section 遊戲機制 (課程 5)
反饋系統:計分機制
:生命管理
:視覺傳達
:玩家動機
// set a delay so we are sure any paints have finished
setTimeout(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
if (win) {
displayMessage(
"Victory!!! Pew Pew... - Press [Enter] to start a new game Captain Pew Pew",
"green"
);
} else {
displayMessage(
"You died !!! Press [Enter] to start a new game Captain Pew Pew"
);
}
}, 200)
}
```
2. **重新開始邏輯**。當所有生命值耗盡或玩家贏得遊戲時,顯示遊戲可以重新開始。此外,當按下*重新開始*按鍵時重新開始遊戲(你可以決定哪個按鍵對應於重新開始)。
1. 建立 `resetGame()` 函數:
```javascript
function resetGame() {
if (gameLoopId) {
clearInterval(gameLoopId);
eventEmitter.clear();
initGame();
gameLoopId = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawPoints();
drawLife();
updateGameObjects();
drawGameObjects(ctx);
}, 100);
}
}
```
2. 在 `initGame()` 中新增一個呼叫 `eventEmitter` 的程式碼以重置遊戲:
```javascript
eventEmitter.on(Messages.KEY_EVENT_ENTER, () => {
resetGame();
});
```
3. 為 EventEmitter 新增一個 `clear()` 函數:
```javascript
clear() {
this.listeners = {};
}
```
👽 💥 🚀 恭喜你,船長!你的遊戲完成了!幹得好!🚀 💥 👽
section 遊戲完成 (課程 6)
打磨與流程:結束條件
:狀態管理
:重啟系統
:使用者體驗
section 進階功能 (1 週)
強化技能:音效整合
:視覺效果
:關卡進展
:效能優化
section 專業發展 (1 個月)
行業準備:框架掌握
:團隊合作
:作品集開發
:社群參與
section 職涯提升 (3 個月)
專門化:高級遊戲引擎
:平台部署
:貨幣化策略
:產業人脈
```
### 🛠️ 你的完整遊戲開發工具總結
---
完成這整個太空遊戲系列後,你已掌握:
- **遊戲架構**:事件驅動系統、遊戲循環與狀態管理
- **圖形程式設計**Canvas API、精靈渲染與視覺特效
- **輸入系統**:鍵盤處理、碰撞偵測與回應式控制
- **遊戲設計**:玩家反饋、進程系統與吸引力機制
- **效能優化**:高效渲染、記憶體管理與幀率控制
- **使用者體驗**:清晰溝通、直覺控制與細節打磨
- **專業模式**:乾淨程式碼、除錯技巧與專案組織
**實務運用**:你的遊戲開發技能直接適用於:
- **互動網頁應用程式**:動態介面與即時系統
- **資料視覺化**:動畫圖表與互動圖形
- **教育科技**:遊戲化與引人入勝的學習體驗
- **行動開發**:觸控互動與效能優化
- **模擬軟體**:物理引擎與即時建模
- **創意產業**:互動藝術、娛樂與數位體驗
**獲得的專業技能**:你現在可以:
- **架構** 複雜互動系統從零開始
- **除錯** 使用系統性方法處理即時應用程式
- **優化** 效能,提供順暢用戶體驗
- **設計** 吸引人的使用者介面與互動模式
- **協作** 透過良好程式碼組織,有效參與技術專案
**掌握的遊戲開發概念**
- **即時系統**:遊戲循環、幀率管理與效能
- **事件驅動架構**:解耦系統與訊息傳遞
- **狀態管理**:複雜資料處理和生命週期管理
- **使用者介面程式設計**Canvas 圖形與響應式設計
- **遊戲設計理論**:玩家心理與吸引力機制
**下一階段**你已準備好探索進階遊戲框架、3D 圖像、多玩家系統,或轉向專業遊戲開發職涯!
🌟 **成就解鎖**:你完成了一整趟遊戲開發旅程,並從零打造出專業品質的互動體驗!
**歡迎加入遊戲開發社群!** 🎮✨
## GitHub Copilot Agent 挑戰 🚀
使用 Agent 模式完成以下挑戰:
**說明:** 強化太空遊戲,實作關卡進程系統,難度逐級提升並有獎勵功能。
**提示:** 創建一個多關卡太空遊戲系統,每關有更多敵機且速度與血量提升。增加隨關數增加的分數倍率,並實現當敵機被摧毀時隨機出現的能力提升(如連發射擊或護盾)。加入關卡完成獎勵,並在螢幕上同時顯示當前關卡、現有分數與生命數。
在此了解更多關於 [agent 模式](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode)。
## 🚀 選擇性強化挑戰
**為你的遊戲加入音效**:透過音效提升遊戲體驗!可考慮新增:
- **玩家射擊時**的雷射聲
- **敵機被摧毀時**的爆炸聲
- **主角受傷時**的受擊聲
- **勝利時**的勝利音樂
- **失敗時**的失敗音效
**音效實作範例:**
```javascript
// 建立音訊物件
const laserSound = new Audio('assets/laser.wav');
const explosionSound = new Audio('assets/explosion.wav');
// 在遊戲事件期間播放聲音
function playLaserSound() {
laserSound.currentTime = 0; // 重設到開始位置
laserSound.play();
}
```
## 🚀 挑戰
**你需要知道:**
- **建立** 不同音效的 Audio 物件
- **重置** `currentTime` 以允許快速連續播放聲音
- **處理** 瀏覽器自動播放政策,靠用戶互動觸發音效
- **管理** 音量與時間安排,提升遊戲體驗
新增音效!你能為遊戲新增音效來增強遊戲體驗嗎?例如當激光擊中、英雄死亡或勝利時播放音效?看看這個 [sandbox](https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_audio_play) 來學習如何使用 JavaScript 播放音效。
> 💡 **學習資源**:探索此 [音效沙盒](https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_audio_play),了解更多如何在 JavaScript 遊戲中實作音效。
## 課後測驗
## 課測驗
[課後測驗](https://ff-quizzes.netlify.app/web/quiz/40)
[測驗](https://ff-quizzes.netlify.app/web/quiz/40)
## 回顧與自學
## 複習與自學
你的作業是建立一個全新的樣本遊戲,因此探索一些有趣的遊戲,看看你可能會建立什麼類型的遊戲。
你的任務是創作一款新的樣本遊戲,可以瀏覽一些有趣的遊戲,看看你想製作什麼類型的遊戲。
## 作業
[建立一個樣本遊戲](assignment.md)
[製作一款樣本遊戲](assignment.md)
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
此文件已使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原始語言的文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤詮釋概不負責。
本文件乃使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於確保準確性,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件之母語版本應被視為權威來源。對於重要資訊,建議採用專業人工翻譯。本公司不對因使用此翻譯而引起之任何誤解或誤釋承擔責任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,31 +1,173 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "24201cf428c7edba1ccec2a78a0dd8f8",
"translation_date": "2025-08-23T23:08:45+00:00",
"original_hash": "232d592791465c1678cab3a2bb6cd3e8",
"translation_date": "2026-01-06T12:33:53+00:00",
"source_file": "6-space-game/6-end-condition/assignment.md",
"language_code": "hk"
}
-->
# 建立一個簡單遊戲
# 建立範例遊戲
## 指引
## 作業概覽
嘗試建立一個小型遊戲,練習不同的結局條件。可以設計不同的目標,例如獲得一定的分數、英雄失去所有生命值或擊敗所有怪物。建立一些簡單的遊戲,例如基於控制台的冒險遊戲。以下的遊戲流程可作為靈感:
既然你已經掌握了太空遊戲中的遊戲結束條件和重啟功能,現在是時候將這些概念應用於全新的遊戲體驗。你將設計並製作自己的遊戲,展示不同的結束條件模式和重啟機制。
本作業挑戰你對遊戲設計的創意思考,同時練習你學到的技術技能。你將探索不同的勝利與失敗場景、實作玩家成長,並創造吸引人的重啟體驗。
## 專案需求
### 核心遊戲功能
你的遊戲必須包含以下關鍵元素:
**結束條件多樣性**:實作至少兩種不同的遊戲結束方式:
- **基於點數的勝利**:玩家達到目標分數或收集特定物品
- **基於生命的失敗**:玩家失去所有可用生命或生命值
- **目標完成**:所有敵人被擊敗、特定物品被收集或目標達成
- **基於時間**:遊戲在設定時間結束或倒數計時歸零時結束
**重啟功能**
- **清除遊戲狀態**:移除所有先前遊戲物件並重設變數
- **重新初始化系統**:以新的玩家狀態、敵人和目標重新開始
- **使用者友善的操作**:提供明確的重啟遊戲指示
**玩家回饋**
- **勝利訊息**:透過正面回饋慶祝玩家成就
- **失敗訊息**:提供鼓勵話語激勵玩家重玩
- **進度指示**:顯示當前分數、生命或目標狀態
### 遊戲點子與靈感
選擇以下遊戲概念其一或自行創作:
#### 1. 主控台冒險遊戲
製作具有戰鬥機制的文字冒險:
```
Hero> Strikes with broadsword - orc takes 3p damage
Orc> Hits with club - hero takes 2p damage
Orc> Hits with club - hero takes 2p damage
Hero> Kicks - orc takes 1p damage
Game> Orc is defeated - Hero collects 2 coins
Game> ****No more monsters, you have conquered the evil fortress****
```
**要實作的關鍵功能:**
- **回合制戰鬥**,具備不同攻擊選項
- 玩家與敵人皆有**生命值**
- 蒐集金幣或物品的**物品欄系統**
- 多種敵人類型,難度各異
- 當所有敵人被擊敗即為**勝利條件**
#### 2. 收藏遊戲
- **目標**:收集特定物品並避開障礙
- **結束條件**:達到目標收集數或失去所有生命
- **進展**:隨遊戲進行物品變得更難收集
#### 3. 解謎遊戲
- **目標**:解開日益困難的謎題
- **結束條件**:完成所有關卡或用完移動次數/時間
- **重啟**:重設至第一關並清除進度
#### 4. 防禦遊戲
- **目標**:保護基地抵禦敵人波次
- **結束條件**:生存所有波次(勝利)或基地被摧毀(失敗)
- **進展**:敵人波次增加難度與數量
## 實作指引
### 開始步驟
1. **規劃你的遊戲設計**
- 繪製基本遊戲循環草圖
- 清楚定義你的結束條件
- 確認重啟時需要重設哪些資料
2. **建立專案結構**
```
my-game/
├── index.html
├── style.css
├── game.js
└── README.md
```
3. **建立核心遊戲循環**
- 初始化遊戲狀態
- 處理使用者輸入
- 更新遊戲邏輯
- 檢查結束條件
- 呈現目前狀態
### 技術需求
**使用現代 JavaScript**
- 使用 `const``let` 來宣告變數
- 適當使用箭頭函數
- 實作 ES6+ 的功能,例如模板字串與解構賦值
**事件驅動架構**
- 建立使用者互動事件處理器
- 透過事件實作遊戲狀態變化
- 使用事件監聽器支援重啟功能
**良好程式碼慣例**
- 編寫單一職責的函式
- 使用具描述性的變數與函數名稱
- 添加說明遊戲邏輯與規則的註解
- 程式碼依邏輯區段組織
## 繳交需求
### 繳交資料
1. **完整遊戲檔案**:包含所有執行遊戲所需的 HTML、CSS 及 JavaScript 檔案
2. **README.md**:文件說明:
- 如何遊玩你的遊戲
- 你實作了哪些結束條件
- 重啟指令說明
- 任何特殊功能或機制
3. **程式碼註解**:清楚解釋你的遊戲邏輯與演算法
### 測試檢查表
提交前請確認你的遊戲:
- [ ] **在瀏覽器控制台沒有錯誤**
- [ ] **實作多種結束條件**
- [ ] **能夠正確重啟並重設狀態**
- [ ] **提供清楚的遊戲狀態回饋**
- [ ] **使用現代 JavaScript 語法與最佳實作**
- [ ] **README.md 包含完整文件說明**
## 評分標準
| 評分標準 | 優秀表現 | 合格表現 | 需要改進 |
| -------- | ---------------------- | --------------------------- | -------------------------- |
| | 完整遊戲已呈現 | 部分遊戲已呈現 | 部分遊戲存在錯誤 |
| 評分標準 | 優異 (4) | 熟練 (3) | 進展中 (2) | 初學 (1) |
|----------|----------|----------|------------|----------|
| **遊戲功能** | 完整遊戲,多種結束條件,順暢重啟,遊玩體驗優良 | 全遊戲含基本結束條件及功能性重啟機制 | 部分遊戲,有部分結束條件,重啟有些微問題 | 遊戲不完整,功能有限且有明顯錯誤 |
| **程式碼品質** | 乾淨、組織良好程式碼,用現代 JavaScript詳盡註解結構優良 | 良好程式碼組織,使用現代語法,足夠註解,結構清楚 | 基本程式碼組織,部分現代語法,註解少 | 程式碼組織差,使用過時語法,缺少註解與結構 |
| **使用者體驗** | 直覺式遊玩,清楚指示,卓越回饋,吸引人結束與重啟體驗 | 良好遊玩,足夠指示與回饋,結束與重啟功能可用 | 基本玩法,指示少,遊戲狀態回饋有限 | 遊玩混亂,指示不清,回饋差 |
| **技術實作** | 展現遊戲開發概念、事件處理與狀態管理之專業 | 對遊戲概念理解良好並有良好實作 | 理解基礎並有可接受實作 | 理解有限,實作不佳 |
| **文件說明** | 全面 README指示清楚程式碼說明豐富測試證據完整 | 良好文件,指示清楚,程式碼註解足夠 | 基本文件,指示簡單 | 文件不足或缺失 |
### 評分區間
- **優異 (16-20 分)**:超出期望,具創意功能與完善實作
- **熟練 (12-15 分)**:符合所有需求且執行良好
- **進展中 (8-11 分)**:大部分需求滿足,少許問題
- **初學 (4-7 分)**:部分需求滿足,需大幅改進
## 其他學習資源
- [MDN 遊戲開發指南](https://developer.mozilla.org/en-US/docs/Games)
- [JavaScript 遊戲開發教學](https://developer.mozilla.org/en-US/docs/Games/Tutorials)
- [Canvas API 文件](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API)
- [遊戲設計原則](https://www.gamasutra.com/blogs/)
> 💡 **專業提示**:從簡單開始,逐步加入功能。一款精心打磨的簡單遊戲比有漏洞的複雜遊戲更好!
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件已使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。
本文件係使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能存在錯誤或不準確之處。原始語言版本的文件應被視為權威來源。對於重要資訊,建議採用專業人工翻譯。我們對因使用此翻譯而產生的任何誤解或錯誤解讀概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,31 +1,96 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "8a07db14e75ac62f013b7de5df05981d",
"translation_date": "2025-08-29T14:59:01+00:00",
"original_hash": "351678bece18f07d9daa987a881fb062",
"translation_date": "2026-01-06T12:48:54+00:00",
"source_file": "7-bank-project/1-template-route/README.md",
"language_code": "hk"
}
-->
# 建立銀行應用程式 第1部分網頁應用中的HTML模板與路由
# 建構銀行應用程式第一部分Web 應用程式中的 HTML 模板與路由
```mermaid
journey
title 你的銀行應用程式開發旅程
section SPA 基礎知識
了解單頁應用程式: 3: Student
學習模板概念: 4: Student
精通 DOM 操作: 4: Student
section 路由系統
實現客戶端路由: 4: Student
處理瀏覽器歷史紀錄: 5: Student
建立導航系統: 5: Student
section 專業模式
建立模組化架構: 5: Student
應用最佳實踐: 5: Student
創造使用者體驗: 5: Student
```
當阿波羅 11 號的導航電腦在 1969 年登月時,它必須在不同的程式間切換,而不重新啟動整個系統。現代的 Web 應用程式也是如此——在不重新載入整個頁面的情況下改變你所看到的內容。這便造就了使用者今天所期望的順暢且反應靈敏的體驗。
與傳統網站為每次互動重新載入整個頁面不同,現代 Web 應用程式只更新需要變更的部分。這種方法,就像任務控制中心在維持恆定通訊的同時切換不同的顯示內容一樣,創造出我們所期待的流暢體驗。
以下對比展示了差異的戲劇性:
| 傳統多頁應用程式 | 現代單頁應用程式 |
|----------------------------|-------------------------|
| **導覽** | 每個畫面均重新載入整頁 | 內容即時切換 |
| **效能** | 需完整下載 HTML較慢 | 部分更新,較快 |
| **使用者體驗** | 頁面閃爍突兀 | 流暢、類似應用程式過渡效果 |
| **資料共享** | 不同頁面間困難 | 狀態管理容易 |
| **開發** | 需維護多個 HTML 檔案 | 單一 HTML 搭配動態模板 |
**了解演進:**
- **傳統應用程式** 每次切換頁面都需向伺服器發送請求
- **現代單頁應用(SPA)** 載入一次後用 JavaScript 動態更新內容
- **使用者期望** 是即時且無縫的互動體驗
- **效能優勢** 包括帶寬節省和更快速回應
在本課程中,我們將建構一個多畫面且流暢銜接的銀行應用程式。就像科學家使用可模組化的儀器,根據不同實驗重新配置一樣,我們將以 HTML 模板作為可重複使用的元件按需顯示。
你將使用 HTML 模板不同畫面的可重複藍圖、JavaScript 路由系統(切換畫面的機制),以及瀏覽器的歷史 API保持返回按鍵正常運作。這些是 React、Vue 和 Angular 等框架所用的基礎技術。
課程結束時,你將擁有一個示範專業單頁應用原則的工作銀行應用。
```mermaid
mindmap
root((單頁應用程式))
Architecture
Template System
Client-side Routing
State Management
Event Handling
Templates
可重用元件
動態內容
DOM 操作
內容切換
Routing
URL 管理
歷史記錄 API
導航邏輯
浏覽器整合
User Experience
快速導航
平滑過渡
狀態一致性
現代互動
Performance
減少伺服器請求
更快頁面過渡
高效資源使用
更佳回應速度
```
## 課前測驗
[課前測驗](https://ff-quizzes.netlify.app/web/quiz/41)
### 簡介
自從JavaScript在瀏覽器中出現以來網站變得比以往更加互動且複雜。網頁技術現在常被用來創建直接在瀏覽器中運行的完整應用程式我們稱之為[網頁應用程式](https://en.wikipedia.org/wiki/Web_application)。由於網頁應用程式高度互動化使用者不希望每次執行操作時都需要重新載入整個頁面。因此JavaScript被用來直接使用DOM更新HTML以提供更流暢的使用者體驗。
在這節課中我們將奠定基礎使用HTML模板來創建銀行網頁應用實現多個畫面顯示與更新而無需重新載入整個HTML頁面。
### 先決條件
### 你需要準備的東西
你需要一個本地網頁伺服器來測試我們在這節課中構建的網頁應用。如果你還沒有,可以安裝[Node.js](https://nodejs.org),並在你的專案資料夾中使用指令 `npx lite-server`。這將創建一個本地網頁伺服器並在瀏覽器中打開你的應用。
我們需要一個本地 Web 伺服器來測試銀行應用程式——別擔心,這比聽起來簡單!如果你尚未設置,只要安裝 [Node.js](https://nodejs.org) 並在專案資料夾中執行 `npx lite-server` 指令。這條方便的指令會啟動一個本地伺服器並自動在瀏覽器打開你的應用程式。
### 準備工作
### 預備工作
在你的電腦上,建立一個名為`bank`的資料夾,並在其中建立一個名為`index.html`的檔案。我們將從這個HTML[樣板代碼](https://en.wikipedia.org/wiki/Boilerplate_code)開始:
在你的電腦上,建立一個名為 `bank` 的資料夾,並在此資料夾內放入一個 `index.html` 檔案。我們會從這個 HTML [樣板](https://en.wikipedia.org/wiki/Boilerplate_code)開始:
```html
<!DOCTYPE html>
@ -41,30 +106,77 @@ CO_OP_TRANSLATOR_METADATA:
</html>
```
**這個樣板包含的內容說明:**
- **建立** HTML5 文件結構,並正確宣告 DOCTYPE
- **設定** 字元編碼為 UTF-8以支援國際文字
- **啟用** 透過 viewport meta 標籤實現響應式設計以兼容行動裝置
- **設定** 一個描述性標題,會顯示在瀏覽器頁籤上
- **建立** 一個乾淨的 <body> 區域作為我們應用程式的基礎
> 📁 **專案結構預覽**
>
> **完成本課程後,你的專案會包含:**
> ```
> bank/
> ├── index.html <!-- Main HTML with templates -->
> ├── app.js <!-- Routing and navigation logic -->
> └── style.css <!-- (Optional for future lessons) -->
> ```
>
> **檔案功能說明:**
> - **index.html**:包含所有模板並建構應用架構
> - **app.js**:處理路由、導覽與模板管理
> - **Templates**:定義登入、儀表板及其他畫面的 UI
---
## HTML模板
## HTML 模板
模板解決了 Web 開發中的一個基本問題。當 Gutenberg 在 1440 年代發明可動式活字印刷時他發現不用刻整頁而是可以製作可重用的字母字塊並根據需要排列。HTML 模板也是同樣的原理——不用為每個畫面建立獨立的 HTML 檔,而是定義可重用結構,再在需要時顯示。
```mermaid
flowchart TD
A["📋 模板定義"] --> B["💬 在 DOM 中隱藏"]
B --> C["🔍 JavaScript 尋找模板"]
C --> D["📋 複製模板內容"]
D --> E["🔗 附加到可見 DOM"]
E --> F["👁️ 用戶看到內容"]
G["登入模板"] --> A
H["儀表板模板"] --> A
I["未來的模板"] --> A
style A fill:#e3f2fd
style D fill:#e8f5e8
style F fill:#fff3e0
style B fill:#f3e5f5
```
將模板想像成應用程式不同部分的藍圖。就像建築師繪製一個藍圖並多次使用,而不是重畫相同房間一樣,我們建立模板一次,需用時實例化。瀏覽器會將這些模板隱藏,直到 JavaScript 啟動它們。
如果你想為一個網頁創建多個畫面一種解決方案是為每個要顯示的畫面創建一個HTML檔案。然而這種解決方案有一些不便之處
如果你想為網頁建立多個畫面,一種解決方案是為每個畫面建立一個 HTML 檔。然而,這種做法有些不便
- 切換畫面時需要重新載入整個HTML這可能會很慢。
- 在不同畫面之間共享數據會變得困難。
- 切換畫面時必須重新載入整個 HTML速度較慢。
- 不同畫面間共享資料困難。
另一種方法是只使用一個HTML檔案並使用`<template>`元素定義多個[HTML模板](https://developer.mozilla.org/docs/Web/HTML/Element/template)。模板是一個可重複使用的HTML區塊瀏覽器不會直接顯示它需要在運行時使用JavaScript實例化。
另一種方法是只用一個 HTML 檔,並使用 `<template>` 元素定義多個 [HTML 模板](https://developer.mozilla.org/docs/Web/HTML/Element/template)。模板是瀏覽器不會直接顯示的可重用 HTML 區塊,需要用 JavaScript 在執行時實例化。
### 任務
### 讓我們來建構
我們將創建一個具有兩個畫面的銀行應用登入頁面和儀表板。首先在HTML的`body`中添加一個佔位元素,我們將用它來實例化應用的不同畫面:
我們將建置一個銀行應用程式,有兩個主要畫面:登入頁與儀表板。先在 HTML 的 body 裡加入一個佔位元素——各種畫面會在此呈現
```html
<div id="app">Loading...</div>
```
我們給它一個`id`以便稍後用JavaScript更容易找到它。
**說明這個佔位元素:**
- **建立** 一個 ID 為 "app" 的容器,用來顯示所有畫面
- **顯示** 載入中的訊息,直到 JavaScript 初始化第一個畫面
- **提供** 動態內容的單一掛載點
- **方便** JavaScript 透過 `document.getElementById()` 目標定位
> 提示:由於這個元素的內容會被替換,我們可以在其中放置一個加載消息或指示器,當應用加載時會顯示它。
> 💡 **小技巧**:因為這個元素的內容會被替換,我們可以放入載入訊息或指示器,在應用載入時顯示
接下來在下面添加登入頁面的HTML模板。目前我們只在其中放置一個標題和一個包含導航連結的區塊。
著,在 HTML 裡添加一個登入頁的模板。暫時只放一個標題與一個包含連結的區段,連結用來切換畫面
```html
<template id="login">
@ -75,11 +187,17 @@ CO_OP_TRANSLATOR_METADATA:
</template>
```
然後我們再添加一個儀表板頁面的HTML模板。這個頁面將包含不同的區塊
**解析這個登入模板內容:**
- **定義** 一個具唯一標識 ID 「login」的模板供 JavaScript 選取
- **包含** 主要標題,確立應用品牌形象
- **使用** 語義化的 `<section>` 元素將相關內容分組
- **提供** 一個導向儀表板的導覽連結
- 一個包含標題和登出連結的標頭
- 銀行帳戶的當前餘額
- 一個以表格形式顯示的交易記錄
然後我們新增另一個儀表板頁模板,包含下列不同區域:
- 標頭有標題與登出連結
- 顯示銀行帳戶當前餘額
- 以表格呈現交易清單
```html
<template id="dashboard">
@ -106,31 +224,88 @@ CO_OP_TRANSLATOR_METADATA:
</template>
```
> 提示在創建HTML模板時如果你想查看它的外觀可以將`<template>`和`</template>`行用`<!-- -->`註解掉。
✅ 你認為我們為什麼要在模板上使用`id`屬性?我們是否可以使用其他東西,比如類別?
## 使用JavaScript顯示模板
如果你在瀏覽器中嘗試當前的HTML檔案你會看到它卡在顯示`Loading...`。這是因為我們需要添加一些JavaScript代碼來實例化並顯示HTML模板。
實例化模板通常分為三個步驟:
1. 在DOM中檢索模板元素例如使用[`document.getElementById`](https://developer.mozilla.org/docs/Web/API/Document/getElementById)。
2. 使用[`cloneNode`](https://developer.mozilla.org/docs/Web/API/Node/cloneNode)克隆模板元素。
3. 將其附加到可見元素下的DOM中例如使用[`appendChild`](https://developer.mozilla.org/docs/Web/API/Node/appendChild)。
**解析此儀表板各部分:**
- **使用** 語義化 `<header>` 元素結構頁面及導覽
- **在各畫面** 一致顯示應用標題以維持品牌識別
- **提供** 連結導回登入頁
- **顯示** 專門區域呈現帳戶餘額
- **運用** HTML 表格組織交易資料
- **定義** 表頭為日期、對象及金額欄位
- **表格主體** 保留空白,後續動態插入內容
> 💡 **小技巧**:製作 HTML 模板時,若想查看其呈現,可用 `<!-- -->``<template>``</template>` 標籤註解起來。
### 🔄 **教學檢測**
**模板系統理解**:在寫 JavaScript 前,確保你知道:
- ✅ 模板與一般 HTML 元素的不同
- ✅ 模板會被瀏覽器隱藏,直到 JavaScript 啟動
- ✅ 模板內使用語義化 HTML 結構的重要性
- ✅ 模板如何使 UI 元件可重用
**快速自測**:如果將 HTML 內容的 `<template>` 標籤移除會發生什麼?
*答:內容會立即顯示,失去模板功能*
**架構好處**:模板提供:
- **可重用性**:一次定義,多次實例
- **效能**:避免重複 HTML 解析
- **維護性**:集中管理 UI 結構
- **彈性**:動態內容切換
✅ 你認為為什麼模板用 `id` 屬性?可否用 class 代替?
## 用 JavaScript 讓模板活起來
現在我們要讓模板運作。就如 3D 印表機將數位藍圖打造出實體物品般JavaScript 將我們隱藏的模板產生成可見且可互動的元素。
這個流程遵循三個固定步驟,是現代 Web 開發的基礎。理解這種模式後,你會在許多框架和程式庫中看到。
在瀏覽器嘗試目前 HTML 檔時,你會看到 `Loading...` 長時間停住,因為我們還沒加 JavaScript 來實例化並顯示 HTML 模板。
實例化模板一般三步:
1. 透過[`document.getElementById`](https://developer.mozilla.org/docs/Web/API/Document/getElementById) 取得 DOM 內模板元素。
2. 用 [`cloneNode`](https://developer.mozilla.org/docs/Web/API/Node/cloneNode) 複製模板內容。
3. 用 [`appendChild`](https://developer.mozilla.org/docs/Web/API/Node/appendChild) 將複製內容掛載到頁面中的可見元素下。
```mermaid
flowchart TD
A[🔍 步驟 1: 尋找模板] --> B[📋 步驟 2: 複製模板]
B --> C[🔗 步驟 3: 附加到 DOM]
A1["document.getElementById('login')"] --> A
B1["template.content.cloneNode(true)"] --> B
C1["app.appendChild(view)"] --> C
C --> D[👁️ 模板對用戶可見]
style A fill:#e1f5fe
style B fill:#f3e5f5
style C fill:#e8f5e8
style D fill:#fff3e0
```
**步驟視覺解析:**
- **第 1 步** 找到隱藏的模板元素
- **第 2 步** 複製一份可以任意編輯的副本
- **第 3 步** 將副本插入可見頁面區域
- **結果** 是一個可互動的畫面呈現給使用者
✅ 為什麼我們需要在附加到DOM之前克隆模板如果跳過這一步你認為會發生什麼
✅ 為何要先複製再掛載?如果跳過這步驟會怎樣
### 任務
在你的專案資料夾中創建一個名為`app.js`的新檔案並在HTML的`<head>`部分中導入該檔案:
專案資料夾新建 `app.js`,並在 HTML `<head>` 區段引入
```html
<script src="app.js" defer></script>
```
現在在`app.js`中,我們將創建一個新函數`updateRoute`
**理解這段 script 引入的重點:**
- **連結** JavaScript 檔與 HTML 文件
- **使用** `defer` 屬性確保腳本在 HTML 解析後執行
- **確保** DOM 元素完全載入後才能使用
- **符合** 現代腳本載入與效能最佳實踐
接著在 `app.js` 中創建一個 `updateRoute` 函式:
```js
function updateRoute(templateId) {
@ -142,19 +317,71 @@ function updateRoute(templateId) {
}
```
這裡我們執行的正是上述的三個步驟。我們實例化具有`templateId`的模板,並將其克隆的內容放入應用的佔位元素中。注意,我們需要使用`cloneNode(true)`來複製模板的整個子樹。
**一步步解釋:**
- **查找** 具有指定 ID 的模板元素
- **用** `cloneNode(true)` 建立該模板內容的深複製
- **尋找** 容器元素以顯示內容
- **清空** 容器中現有內容
- **插入** 複製的模板內容到可見的 DOM
現在用其中一個模板調用這個函數,並查看結果。
接著用其中一個模板 ID 呼叫函式,看看結果。
```js
updateRoute('login');
```
✅ 這段代碼`app.innerHTML = '';`的目的是什麼?如果沒有它會發生什麼?
## 創建路由
**此函式呼叫達成:**
- **啟用** 傳入的登入模板 ID
- **展示** 如何以程式方式切換不同畫面
- **取代** 「Loading...」訊息呈現登入頁面
✅ 這段 `app.innerHTML = '';` 的目的為何?刪除後會如何?
## 建立路由
路由本質是將網址連結到正確內容。想像早期電話接線生使用交換台接通電話他們接受來電請求並轉接到目的地。Web 路由類似,依 URL 請求決定該顯示什麼內容。
```mermaid
flowchart LR
A["🌐 URL 路徑<br/>/dashboard"] --> B["🗺️ 路由物件<br/>查找"]
B --> C["🎯 模板 ID<br/>'dashboard'"]
C --> D["📌 尋找模板<br/>getElementById"]
D --> E["👁️ 顯示畫面<br/>複製並附加"]
F["📍 /login"] --> G["🎯 'login'"]
H["📍 /unknown"] --> I["❌ 找不到"]
I --> J["🔄 重定向至 /login"]
style B fill:#e3f2fd
style E fill:#e8f5e8
style I fill:#ffebee
style J fill:#fff3e0
```
傳統上Web 伺服器依不同 URL 傳回不同 HTML 檔案。由於我們建置單頁應用,需要用 JavaScript 自行管理路由。這讓我們能更好控制使用者體驗與效能。
```mermaid
flowchart LR
A["🌐 URL 路徑<br/>/dashboard"] --> B["🗺️ 路由物件<br/>查找"]
B --> C["🎯 範本 ID<br/>'dashboard'"]
C --> D["📄 找範本<br/>getElementById"]
D --> E["👁️ 顯示畫面<br/>複製並附加"]
F["📍 /login"] --> G["🎯 'login'"]
H["📍 /unknown"] --> I["❌ 未找到"]
I --> J["🔄 重新導向至 /login"]
style B fill:#e3f2fd
style E fill:#e8f5e8
style I fill:#ffebee
style J fill:#fff3e0
```
**路由流程說明:**
- **URL 改變** 觸發查詢路由對應表
- **有效路由** 映射到特定模板 ID 以渲染畫面
- **無效路由** 採取後備行為避免破壞狀態
- **渲染模板** 依先前學習三步驟進行
在談到網頁應用時,我們稱*路由*為將**URL**映射到應顯示的特定畫面的意圖。在具有多個HTML檔案的網站中這是自動完成的因為檔案路徑會反映在URL中。例如在你的專案資料夾中有以下檔案
Web 應用路由是指映射 **網址** 至應顯示的指定畫面。多頁網站由於檔案路徑反映在 URL 上,因此自動完成。例如,下列專案中的檔案:
```
mywebsite/index.html
@ -162,7 +389,7 @@ mywebsite/login.html
mywebsite/admin/index.html
```
如果你以`mywebsite`作為根目錄創建一個網頁伺服器URL映射將是:
如果建立以 `mywebsite` 為根的 Web 伺服器URL 映射會是:
```
https://site.com --> mywebsite/index.html
@ -170,11 +397,11 @@ https://site.com/login.html --> mywebsite/login.html
https://site.com/admin/ --> mywebsite/admin/index.html
```
然而對於我們的網頁應用我們使用的是包含所有畫面的單一HTML檔案因此這種默認行為對我們沒有幫助。我們必須手動創建這個映射並使用JavaScript更新顯示的模板。
但我們的 Web 應用使用單一 HTML 檔案包含所有畫面,故無法使用預設行為。必須手動建立此對照表,並用 JavaScript 更新顯示的模板。
### 任務
我們將使用一個簡單的物件來實現一個[映射](https://en.wikipedia.org/wiki/Associative_array)將URL路徑與我們的模板對應起來。在`app.js`檔案的頂部添加這個物件。
我們將用一個簡單物件實作 URL 路徑與模板間的 [映射](https://en.wikipedia.org/wiki/Associative_array)。在 `app.js` 頂部新增此物件。
```js
const routes = {
@ -183,7 +410,12 @@ const routes = {
};
```
現在讓我們稍微修改一下`updateRoute`函數。我們不再直接將`templateId`作為參數傳遞而是希望通過首先查看當前URL然後使用我們的映射來獲取對應的模板ID值。我們可以使用[`window.location.pathname`](https://developer.mozilla.org/docs/Web/API/Location/pathname)來僅獲取URL中的路徑部分。
**解讀此路由設定:**
- **定義** URL 路徑與模板識別 ID 的映射
- **用** 物件鍵值對,鍵為路徑,值包含模板資訊
- **方便** 查詢指定 URL 應使用何模板
- **提供** 擴充路由的彈性結構
現在讓我們稍微修改一下 `updateRoute` 函數。我們不會直接傳遞 `templateId` 作為參數,而是先從目前的 URL 中取出它,然後使用我們的映射來取得對應的 template ID 值。我們可以使用 [`window.location.pathname`](https://developer.mozilla.org/docs/Web/API/Location/pathname) 只取得 URL 中的路徑部分。
```js
function updateRoute() {
@ -198,26 +430,100 @@ function updateRoute() {
}
```
這裡我們將聲明的路由映射到對應的模板。你可以嘗試手動更改瀏覽器中的URL檢查它是否正確運作。
✅ 如果你在URL中輸入一個未知的路徑會發生什麼我們該如何解決這個問題
## 添加導航功能
我們應用的下一步是添加在頁面之間導航的功能而無需手動更改URL。這涉及兩件事
1. 更新當前URL
2. 根據新URL更新顯示的模板
第二部分我們已經通過`updateRoute`函數處理了所以我們需要弄清楚如何更新當前URL。
我們需要使用JavaScript具體來說是[`history.pushState`](https://developer.mozilla.org/docs/Web/API/History/pushState)它允許在不重新載入HTML的情況下更新URL並在瀏覽歷史中創建新條目。
> 注意雖然HTML的錨點元素[`<a href>`](https://developer.mozilla.org/docs/Web/HTML/Element/a)本身可以用來創建指向不同URL的超連結但它默認會使瀏覽器重新載入HTML。在處理自定義JavaScript路由時需要使用`preventDefault()`函數來防止這種行為。
**拆解這裡發生的事情:**
- **擷取** 瀏覽器 URL 中的當前路徑,使用 `window.location.pathname`
- **查找** 在我們的 routes 物件中對應的路由配置
- **取得** 路由配置中的 template ID
- **按照** 之前相同的模板渲染流程進行
- **創建** 一個能響應 URL 變化的動態系統
這裡我們將宣告的路由映射到對應的模板。你可以試著在瀏覽器手動更改 URL看看它是否能正常工作。
✅ 輸入一個未知路徑到 URL 中會發生什麼?我們該怎麼解決這個問題?
## 新增導覽功能
建立路由後,用戶需要一種方式在應用程式中導航。傳統網站點擊連結會重新載入整個頁面,但我們想在不刷新頁面的情況下同時更新 URL 和內容。這樣會帶來更流暢的體驗,類似桌面應用程式在不同視圖間切換。
我們需要協調兩件事:更新瀏覽器的 URL讓用戶能書籤頁面並分享連結以及顯示適當的內容。正確實作時這能創造出用戶對現代應用程式所期待的無縫導航體驗。
```mermaid
sequenceDiagram
participant User
participant Browser
participant App
participant Template
User->>Browser: 點擊「登入」連結
Browser->>App: onclick 事件觸發
App->>App: preventDefault() 並導航到 '/dashboard'
App->>Browser: history.pushState('/dashboard')
Browser->>Browser: URL 更新至 /dashboard
App->>App: 呼叫 updateRoute()
App->>Template: 找到並複製 dashboard 範本
Template->>App: 返回複製內容
App->>Browser: 用範本替換應用程式內容
Browser->>User: 顯示 dashboard 畫面
Note over User,Template: 使用者點擊瀏覽器返回按鈕
User->>Browser: 點擊返回按鈕
Browser->>Browser: 歷史記錄回到 /login
Browser->>App: popstate 事件觸發
App->>App: 自動呼叫 updateRoute()
App->>Template: 找到並複製 login 範本
Template->>App: 返回複製內容
App->>Browser: 用範本替換應用程式內容
Browser->>User: 顯示登入畫面
```
### 🔄 **學習自我檢視**
**單頁應用架構**:驗證你對完整系統的理解:
- ✅ 用戶端路由與傳統伺服器端路由有什麼不同?
- ✅ 為什麼 History API 對於正確的 SPA 導航至關重要?
- ✅ 模板如何在不重新載入頁面下實現動態內容?
- ✅ 事件處理在攔截導航中扮演什麼角色?
**系統整合**:你的 SPA 展示了:
- **模板管理**:可重用的 UI 元件與動態內容
- **用戶端路由**:不用伺服器請求進行 URL 管理
- **事件驅動架構**:響應式導航和用戶互動
- **瀏覽器整合**:正確支持歷史和前進/返回按鈕
- **效能優化**:快速切換與降低伺服器負擔
**專業模式**:你已實作:
- **模型-視圖分離**:模板與應用邏輯分開
- **狀態管理**URL 狀態與顯示內容同步
- **漸進增強**JavaScript 強化基本 HTML 功能
- **用戶體驗**:沒有頁面重新載入的流暢應用導航
> <20> **架構見解**:導航系統組件
>
> **你正在建立的:**
> - **🔄 URL 管理**:更新瀏覽器地址欄且無須重新載入頁面
> - **📋 模板系統**:根據當前路由動態切換內容
> - **📚 歷史紀錄整合**:維持瀏覽器前進/返回按鈕功能
> - **🛡️ 錯誤處理**:無效或缺失路由的優雅回退
>
> **組件如何協同工作:**
> - **監聽** 導航事件(點擊、歷史變更)
> - **使用** History API 更新 URL
> - **渲染** 新路由的對應模板
> - **維持** 用戶體驗的流暢性
下一步,我們的應用程式要新增能自動切換頁面而不用手動更改 URL 的能力。這包含兩件事:
1. 更新目前的 URL
2. 根據新 URL 更新呈現的模板
第二點我們已用 `updateRoute` 函式處理完了,現在要想想怎麼更新目前的 URL。
我們需要用 JavaScript特別是 [`history.pushState`](https://developer.mozilla.org/docs/Web/API/History/pushState),它允許更新 URL 並在瀏覽歷史中創建新條目,而不重新載入 HTML。
> ⚠️ **重要提示**:雖然 HTML 超連結元素 [`<a href>`](https://developer.mozilla.org/docs/Web/HTML/Element/a) 可獨立用來建立指向不同 URL 的鏈結,但預設會讓瀏覽器重新載入 HTML。用自訂 JavaScript 處理路由時,必須用點擊事件的 preventDefault() 方法阻止此行為。
### 任務
讓我們創建一個新函數,用於在應用中導航:
我們來建立一個可以在應用中導航的新函式
```js
function navigate(path) {
@ -226,9 +532,15 @@ function navigate(path) {
}
```
這個方法首先根據給定的路徑更新當前URL然後更新模板。屬性`window.location.origin`返回URL的根目錄允許我們從給定路徑重建完整URL。
**理解這個導航函式:**
- **更新** 瀏覽器 URL 至新路徑,使用 `history.pushState`
- **新增** 條目到瀏覽器歷史堆疊,支援前進/返回按鈕
- **觸發** `updateRoute()` 函式來呈現對應模板
- **維持** 單頁應用體驗,不重新載入頁面
此方法會先根據給定路徑更新目前 URL再更新模板。`window.location.origin` 屬性返回 URL 根部,讓我們能從路徑重建完整 URL。
現在我們有了這個函數,我們可以解決當路徑與任何定義的路由不匹配時的問題。我們將通過在`updateRoute`函數中添加一個回退到現有路由的功能來解決這個問題。
現在我們有這個函式了,可以解決路徑找不到匹配時的問題。我們會修改 `updateRoute` 函式,在找不到路由時回退到某個現有路由
```js
function updateRoute() {
@ -239,12 +551,23 @@ function updateRoute() {
return navigate('/login');
}
...
const template = document.getElementById(route.templateId);
const view = template.content.cloneNode(true);
const app = document.getElementById('app');
app.innerHTML = '';
app.appendChild(view);
}
```
如果找不到路由,我們現在會重定向到`login`頁面。
**重點回顧:**
- **檢查** 當前路徑是否有對應路由
- **無效路由時** 重定向到登入頁面
- **提供** 回退機制防止導航錯誤
- **確保** 用戶即使 URL 錯誤仍看到有效畫面
找不到路由時,我們現在會導向 `login` 頁面。
接下來我們創建一個函數來獲取點擊連結時的URL並防止瀏覽器的默認連結行為
著建立一個函式,當點擊連結時取得 URL並阻止瀏覽器的預設跳轉行為:
```js
function onLinkClick(event) {
@ -253,7 +576,11 @@ function onLinkClick(event) {
}
```
最後通過在HTML中的*登入*和*登出*連結上添加綁定來完成導航系統。
**拆解這個點擊處理器:**
- **用** `preventDefault()` 阻止瀏覽器預設連結行為
- **從** 被點擊連結元素取出目標 URL
- **調用** 自訂的導航函式,而非重新載入頁面
- **維持** 平滑的單頁應用體驗
```html
<a href="/dashboard" onclick="onLinkClick(event)">Login</a>
@ -261,56 +588,237 @@ function onLinkClick(event) {
<a href="/login" onclick="onLinkClick(event)">Logout</a>
```
上述代碼中的`event`物件捕獲`click`事件並將其傳遞給我們的`onLinkClick`函數。
使用[`onclick`](https://developer.mozilla.org/docs/Web/API/GlobalEventHandlers/onclick)屬性將`click`事件綁定到JavaScript代碼這裡是對`navigate()`函數的調用。
嘗試點擊這些連結,你現在應該能夠在應用的不同畫面之間導航。
`history.pushState`方法是HTML5標準的一部分並在[所有現代瀏覽器](https://caniuse.com/?search=pushState)中實現。如果你正在為舊版瀏覽器構建網頁應用有一個替代這個API的技巧使用路徑前的[哈希符號(`#`](https://en.wikipedia.org/wiki/URI_fragment),你可以實現基於常規錨點導航的路由,且不會重新載入頁面,因為它的目的是在頁面內創建內部連結。
## 處理瀏覽器的返回與前進按鈕
**這個 onclick 綁定的效果:**
- **將** 每個連結與自訂導航系統連結起來
- **將** 點擊事件傳給 `onLinkClick` 函式處理
- **實現** 無頁面重新載入的平滑導航
- **保持** 適當的 URL 結構,方便用戶書籤或分享
[`onclick`](https://developer.mozilla.org/docs/Web/API/GlobalEventHandlers/onclick) 屬性綁定 `click` 事件到 JavaScript 程式碼,這裡是呼叫 `navigate()` 函式。
試點擊這些連結,你應該能順利切換應用中不同頁面。
`history.pushState` 方法屬於 HTML5 標準,在[所有現代瀏覽器](https://caniuse.com/?search=pushState)都有實作。如果你開發面向舊瀏覽器的網頁應用,有個技巧是用 [hash (`#`)](https://en.wikipedia.org/wiki/URI_fragment) 放在路徑前頭,能用普通超連結實現不重新載入頁面的路由,因為它本來用來做頁面內部連結。
## 讓前進和返回按鈕正常運作
前進和返回按鈕是瀏覽網頁的基礎,就像 NASA 任務控制員可以查看過去系統狀態一樣。用戶期望這些按鈕能正常運作,不然上下瀏覽體驗就會大亂。
我們的單頁應用需要額外設定才能支援這點。瀏覽器維護有歷史堆疊(我們一直在用 `history.pushState` 新增),但當用戶利用歷史導覽時,我們必須回應並更新顯示內容。
```mermaid
sequenceDiagram
participant User
participant Browser
participant App
participant Template
User->>Browser: 點擊「登入」連結
Browser->>App: onclick 事件被觸發
App->>App: preventDefault() & navigate('/dashboard')
App->>Browser: history.pushState('/dashboard')
Browser->>Browser: URL 更新為 /dashboard
App->>App: updateRoute() 被呼叫
App->>Template: 找到並複製 dashboard 模板
Template->>App: 回傳複製的內容
App->>Browser: 用模板取代 app 內容
Browser->>User: 顯示 dashboard 畫面
Note over User,Template: 使用者點擊瀏覽器返回鍵
User->>Browser: 點擊返回鍵
Browser->>Browser: 歷史記錄後退至 /login
Browser->>App: popstate 事件被觸發
App->>App: updateRoute() 自動被呼叫
App->>Template: 找到並複製登入模板
Template->>App: 回傳複製的內容
App->>Browser: 用模板取代 app 內容
Browser->>User: 顯示登入畫面
```
**重要互動點:**
- **用戶行為** 透過點擊或瀏覽器按鈕觸發導航
- **應用攔截** 超連結點擊防止頁面重載
- **History API** 管理 URL 變更與瀏覽歷史堆疊
- **模板** 提供每個畫面內容架構
- **事件監聽** 確保 app 回應所有導航類型
使用`history.pushState`會在瀏覽器的導航歷史中創建新條目。你可以通過按住瀏覽器的*返回按鈕*來檢查,它應該顯示如下內容:
使用 `history.pushState` 會在瀏覽器導航歷史中新增條目。你可按住瀏覽器「返回按鈕」,會看到類似這樣的視窗
![導航歷史截圖](../../../../translated_images/history.7fdabbafa521e06455b738d3dafa3ff41d3071deae60ead8c7e0844b9ed987d8.hk.png)
![截圖:導航歷史](../../../../translated_images/history.7fdabbafa521e064.hk.png)
如果你嘗試多次點擊返回按鈕你會看到當前URL發生了變化歷史也被更新但顯示的模板保持不變。
若連按幾次返回按鈕,你會發現目前 URL 改變、歷史更新,但模板不斷顯示相同內容
這是因為應用不知道每次歷史變化時需要調用`updateRoute()`。如果你查看[`history.pushState`文檔](https://developer.mozilla.org/docs/Web/API/History/pushState)你會看到當狀態改變時即我們移動到不同的URL會觸發[`popstate`](https://developer.mozilla.org/docs/Web/API/Window/popstate_event)事件。我們將利用這一點來解決這個問題。
原因是應用程式不知道每次歷史變動時都該呼叫 `updateRoute()`。查看 [`history.pushState` 文件](https://developer.mozilla.org/docs/Web/API/History/pushState) 可知,若狀態改變-也就是換成另一 URL -會觸發 [`popstate`](https://developer.mozilla.org/docs/Web/API/Window/popstate_event) 事件。我們將用它來解決問題。
### 任務
為了確保當瀏覽器歷史變化時顯示的模板被更新,我們將附加一個新函數來調用`updateRoute()`。我們會在`app.js`檔案的底部完成這件事:
確保瀏覽器歷史變動時模板也跟著更新,我們要附加一個函式呼叫 `updateRoute()`。把它放在 `app.js` 檔案的底部
```js
window.onpopstate = () => updateRoute();
updateRoute();
```
> 注意:我們在這裡使用了一個[箭頭函數](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Functions/Arrow_functions)來聲明`popstate`事件處理器以簡潔,但普通函數也可以正常工作。
**理解這個歷史狀態整合:**
- **監聽** 使用者用瀏覽器按鈕導航時發生的 `popstate` 事件
- **使用** 箭頭函式簡潔寫法作為事件處理器
- **自動** 在歷史狀態變更時呼叫 `updateRoute()`
- **初始化** 頁面一載入就呼叫 `updateRoute()`
- **確保** 無論如何導航都呈現正確模板
> 💡 **專家提示**:我們這裡用 [箭頭函式](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Functions/Arrow_functions)來宣告 `popstate` 事件處理函式,簡潔明瞭,但普通函式效果相同。
這裡有段箭頭函式的教學影片回顧:
[![箭頭函式](https://img.youtube.com/vi/OP6eEbOj2sc/0.jpg)](https://youtube.com/watch?v=OP6eEbOj2sc "Arrow Functions")
> 🎥 點擊上方圖片觀看箭頭函式教學影片。
現在試著使用瀏覽器的返回與前進按鈕,檢查看看此次呈現的路由是否正確更新。
### ⚡ **接下來 5 分鐘你可以做**
- [ ] 使用瀏覽器返回/前進按鈕測試你的銀行應用導航
- [ ] 嘗試在位址欄手動輸入不同 URL 測試路由
- [ ] 開啟瀏覽器 DevTools檢視模板如何被複製加入 DOM
- [ ] 嘗試加上 console.log 追蹤路由流程
### 🎯 **這小時能完成的事**
- [ ] 完成課後小測驗,理解 SPA 架構概念
- [ ] 加 CSS 美化銀行應用模板外觀
- [ ] 完成 404 頁面錯誤處理挑戰
- [ ] 實作附加路由功能的信用頁面挑戰
- [ ] 加入載入狀態與模板切換過場動畫
### 📅 **你的週長 SPA 開發旅程**
- [ ] 完成具表單、資料管理與持久化功能的完整銀行應用
- [ ] 增加高階路由功能,如路由參數、巢狀路由
- [ ] 實作導航守衛與基於身份驗證的路由
- [ ] 建立可重複使用的模板元件與元件庫
- [ ] 加動畫與過場提升使用體驗流暢度
- [ ] 部署 SPA 至托管平台並妥善設定路由
### 🌟 **你的月長前端架構高手養成**
- [ ] 使用 React、Vue 或 Angular 等現代框架構建複雜 SPA
- [ ] 學習高階狀態管理模式與函式庫
- [ ] 精通 SPA 開發的建構工具與開發流程
- [ ] 實作進階網頁應用 (PWA) 功能及離線支援
- [ ] 研習大型 SPA 的效能優化技巧
- [ ] 參與開源 SPA 專案並分享知識
## 🎯 你的單頁應用精通時間軸
```mermaid
timeline
title SPA 開發與現代網頁架構學習進度
section 基礎 (20 分鐘)
範本系統HTML 範本元素
DOM 操作
:內容複製
:動態渲染
section 路由基礎 (30 分鐘)
客戶端導航URL 管理
:歷史紀錄 API
:路由映射
:事件處理
section 使用者體驗 (40 分鐘)
導航優化:瀏覽器整合
:返回按鈕支援
:錯誤處理
:順暢過渡
section 架構模式 (50 分鐘)
專業 SPA元件系統
:狀態管理
:效能優化
:錯誤邊界
section 高級技術 (1 週)
框架整合React Router
Vue Router
Angular Router
:狀態庫
section 生產技能 (1 個月)
企業開發:建構系統
:測試策略
:部署流程
:效能監控
```
### 🛠️ 你的 SPA 開發工具箱總結
完成本課程後,你已精通:
- **模板架構**:可重複使用的 HTML 元件與動態內容渲染
- **用戶端路由**:無需頁面重新載入的 URL 管理與導航
- **瀏覽器整合**:使用 History API 並支援返回/前進按鈕
- **事件驅動系統**:導航處理與用戶互動管理
- **DOM 操作**:模板複製、內容切換及元素管理
- **錯誤處理**:無效路由與缺失內容的優雅回退
- **效能模式**:高效的內容載入與渲染策略
**實務應用**:你的 SPA 開發技能直接適用於:
- **現代網頁應用**React、Vue、Angular 等框架開發
- **進階網頁應用 (PWA)**:具離線能力與應用程式體驗
- **企業儀表板**:多視圖的複雜商業應用
- **電子商務平台**:商品目錄、購物車及結帳流程
- **內容管理**:動態內容建立與編輯介面
- **行動端開發**:使用網頁技術的混合應用
**專業技能掌握**:你現在可以:
- **架構** 單頁應用程式,並實現適當的關注點分離
- **實作** 可隨應用程式複雜度擴展的客戶端路由系統
- **除錯** 使用瀏覽器開發者工具偵錯複雜的導航流程
- **優化** 透過高效的模板管理提升應用程式效能
- **設計** 感覺原生且反應靈敏的使用者體驗
**掌握的前端開發概念**
- **元件架構**:可重複使用的 UI 範式和模板系統
- **狀態同步**URL 狀態管理與瀏覽器歷史紀錄
- **事件驅動程式設計**:使用者互動處理與導航
- **效能優化**:高效的 DOM 操作與內容載入
- **使用者體驗設計**:流暢過渡與直覺導航
**下一階段**:你已經準備好探索現代前端框架、高級狀態管理,或打造複雜的企業應用程式!
🌟 **成就解鎖**:你已建立專業的單頁應用程式基礎,採用現代網路架構模式!
這裡有一個關於箭頭函數的回顧影片:
---
[![箭頭函數](https://img.youtube.com/vi/OP6eEbOj2sc/0.jpg)](https://youtube.com/watch?v=OP6eEbOj2sc "箭頭函數")
## GitHub Copilot Agent 挑戰 🚀
> 🎥 點擊上方圖片觀看關於箭頭函數的影片。
使用 Agent 模式完成以下挑戰:
現在嘗試使用瀏覽器的返回與前進按鈕,檢查這次顯示的路由是否正確更新。
**描述:** 強化銀行應用程式,實作錯誤處理及 404 頁面模板,提升使用者瀏覽不存在頁面時的體驗
---
**提示:** 建立一個 id 為 "not-found" 的新 HTML 模板,展示友善的 404 錯誤頁面並帶有樣式。接著修改 JavaScript 路由邏輯,在用戶瀏覽無效 URL 時顯示該模板,並新增一個「返回首頁」按鈕,點擊後導回登入頁面。
在此了解更多有關 [agent 模式](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode)。
## 🚀 挑戰
為這個應用新增一個模板和路由,顯示應用的製作團隊名單。
為第三個頁面新增一個模板與路由,該頁面用於顯示此應用程式的製作名單。
**挑戰目標:**
- **建立** 一個擁有適當內容架構的新 HTML 模板
- **將** 新路由加入你的路由設定物件
- **包含** 從及前往製作名單頁面的導航連結
- **測試** 確認所有導航與瀏覽器歷史操作正常
## 課後小測驗
## 課後測驗
[課後小測驗](https://ff-quizzes.netlify.app/web/quiz/42)
[課後測驗](https://ff-quizzes.netlify.app/web/quiz/42)
## 複習與自學
## 回顧與自學
路由是 Web 開發中意外棘手的部分之一,尤其隨著網頁從頁面重新整理行為轉向單頁應用程式頁面更新。閱讀一下 [Azure 靜態 Web 應用服務](https://docs.microsoft.com/azure/static-web-apps/routes/?WT.mc_id=academic-77807-sagibbon) 如何處理路由。你能解釋為何該文件中描述的一些決策是必要的嗎?
路由是網頁開發中出乎意料的棘手部分之一,特別是隨著網頁從頁面刷新行為轉向單頁應用的頁面刷新。閱讀一些關於[Azure靜態網頁應用服務如何處理路由](https://docs.microsoft.com/azure/static-web-apps/routes/?WT.mc_id=academic-77807-sagibbon)的內容。你能解釋為什麼文檔中描述的一些決策是必要的嗎?
**附加學習資源:**
- **探索** 流行框架如 React Router 與 Vue Router 如何實作客戶端路由
- **研究** hash 路由與 history API 路由的差異
- **了解** 伺服器端渲染 (SSR) 及其對路由策略的影響
- **調查** 進階網頁應用程式 (PWA) 如何處理路由與導航
## 作業
@ -318,5 +826,7 @@ updateRoute();
---
**免責聲明**
此文件已使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋不承擔責任。
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件由人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 所翻譯。雖然我們致力於提供準確的翻譯,請注意自動翻譯可能包含錯誤或不準確之處。原始文件的原文版本應被視為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們不對使用本翻譯所引起的任何誤解或誤譯承擔責任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->
Loading…
Cancel
Save