chore(i18n): sync translations with latest source changes (chunk 2/2, 261 changes)

update-translations
localizeflow[bot] 5 days ago
parent a33848619b
commit a5481015bd

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

@ -0,0 +1,584 @@
{
"1-getting-started-lessons/1-intro-to-programming-languages/README.md": {
"original_hash": "d45ddcc54eb9232a76d08328b09d792e",
"translation_date": "2025-11-06T12:32:35+00:00",
"source_file": "1-getting-started-lessons/1-intro-to-programming-languages/README.md",
"language_code": "ja"
},
"1-getting-started-lessons/1-intro-to-programming-languages/assignment.md": {
"original_hash": "17b8ec8e85d99e27dcb3f73842e583be",
"translation_date": "2025-10-24T14:51:36+00:00",
"source_file": "1-getting-started-lessons/1-intro-to-programming-languages/assignment.md",
"language_code": "ja"
},
"1-getting-started-lessons/2-github-basics/README.md": {
"original_hash": "5c383cc2cc23bb164b06417d1c107a44",
"translation_date": "2025-11-18T18:44:50+00:00",
"source_file": "1-getting-started-lessons/2-github-basics/README.md",
"language_code": "ja"
},
"1-getting-started-lessons/3-accessibility/README.md": {
"original_hash": "7f2c48e04754724123ea100a822765e5",
"translation_date": "2025-11-06T12:36:04+00:00",
"source_file": "1-getting-started-lessons/3-accessibility/README.md",
"language_code": "ja"
},
"1-getting-started-lessons/3-accessibility/assignment.md": {
"original_hash": "e6d0f456dfc22afb41bbdefeb5ec179d",
"translation_date": "2025-10-24T14:54:35+00:00",
"source_file": "1-getting-started-lessons/3-accessibility/assignment.md",
"language_code": "ja"
},
"1-getting-started-lessons/README.md": {
"original_hash": "770d9f83dddc841c19f210dee5fe0712",
"translation_date": "2025-10-03T13:26:18+00:00",
"source_file": "1-getting-started-lessons/README.md",
"language_code": "ja"
},
"10-ai-framework-project/README.md": {
"original_hash": "3925b6a1c31c60755eaae4d578232c25",
"translation_date": "2025-11-06T12:39:58+00:00",
"source_file": "10-ai-framework-project/README.md",
"language_code": "ja"
},
"10-ai-framework-project/solution/README.md": {
"original_hash": "d41d8cd98f00b204e9800998ecf8427e",
"translation_date": "2026-01-29T17:48:27+00:00",
"source_file": "10-ai-framework-project/solution/README.md",
"language_code": "ja"
},
"2-js-basics/1-data-types/README.md": {
"original_hash": "672b0bb6e8b431075f3bdb7130590d2d",
"translation_date": "2025-11-06T12:24:55+00:00",
"source_file": "2-js-basics/1-data-types/README.md",
"language_code": "ja"
},
"2-js-basics/1-data-types/assignment.md": {
"original_hash": "6fd645e97c48cd5eb5a3d290815ec8b5",
"translation_date": "2025-10-24T14:45:54+00:00",
"source_file": "2-js-basics/1-data-types/assignment.md",
"language_code": "ja"
},
"2-js-basics/2-functions-methods/README.md": {
"original_hash": "71f7d7dafa1c7194d79ddac87f669ff9",
"translation_date": "2025-11-06T12:26:27+00:00",
"source_file": "2-js-basics/2-functions-methods/README.md",
"language_code": "ja"
},
"2-js-basics/2-functions-methods/assignment.md": {
"original_hash": "8328f58f4593b4671656ff8f4b2edbd9",
"translation_date": "2025-10-24T14:46:28+00:00",
"source_file": "2-js-basics/2-functions-methods/assignment.md",
"language_code": "ja"
},
"2-js-basics/3-making-decisions/README.md": {
"original_hash": "c688385d15dd3645e924ea0ffee8967f",
"translation_date": "2025-11-06T12:27:52+00:00",
"source_file": "2-js-basics/3-making-decisions/README.md",
"language_code": "ja"
},
"2-js-basics/3-making-decisions/assignment.md": {
"original_hash": "ffe366b2d1f037b99fbadbe1dc81083d",
"translation_date": "2025-10-24T14:47:46+00:00",
"source_file": "2-js-basics/3-making-decisions/assignment.md",
"language_code": "ja"
},
"2-js-basics/4-arrays-loops/README.md": {
"original_hash": "1710a50a519a6e4a1b40a5638783018d",
"translation_date": "2025-11-06T12:27:11+00:00",
"source_file": "2-js-basics/4-arrays-loops/README.md",
"language_code": "ja"
},
"2-js-basics/4-arrays-loops/assignment.md": {
"original_hash": "8abcada0534e0fb3a7556ea3c5a2a8a4",
"translation_date": "2025-10-24T14:47:06+00:00",
"source_file": "2-js-basics/4-arrays-loops/assignment.md",
"language_code": "ja"
},
"2-js-basics/README.md": {
"original_hash": "cc9e70a2f096c67389c8acff1521fc27",
"translation_date": "2025-08-23T22:37:03+00:00",
"source_file": "2-js-basics/README.md",
"language_code": "ja"
},
"3-terrarium/1-intro-to-html/README.md": {
"original_hash": "3fcfa99c4897e051b558b5eaf1e8cc74",
"translation_date": "2025-11-06T12:48:23+00:00",
"source_file": "3-terrarium/1-intro-to-html/README.md",
"language_code": "ja"
},
"3-terrarium/1-intro-to-html/assignment.md": {
"original_hash": "650e63282e1dfa032890fcf5c1c4119d",
"translation_date": "2025-10-24T15:04:40+00:00",
"source_file": "3-terrarium/1-intro-to-html/assignment.md",
"language_code": "ja"
},
"3-terrarium/2-intro-to-css/README.md": {
"original_hash": "e39f3a4e3bcccf94639e3af1248f8a4d",
"translation_date": "2025-11-06T12:47:26+00:00",
"source_file": "3-terrarium/2-intro-to-css/README.md",
"language_code": "ja"
},
"3-terrarium/2-intro-to-css/assignment.md": {
"original_hash": "bee6762d4092a13fc7c338814963f980",
"translation_date": "2025-10-24T15:03:43+00:00",
"source_file": "3-terrarium/2-intro-to-css/assignment.md",
"language_code": "ja"
},
"3-terrarium/3-intro-to-DOM-and-closures/README.md": {
"original_hash": "973e48ad87d67bf5bb819746c9f8e302",
"translation_date": "2025-11-06T12:45:24+00:00",
"source_file": "3-terrarium/3-intro-to-DOM-and-closures/README.md",
"language_code": "ja"
},
"3-terrarium/3-intro-to-DOM-and-closures/assignment.md": {
"original_hash": "947ca5ce7c94aee9c7de7034e762bc17",
"translation_date": "2025-10-24T15:02:41+00:00",
"source_file": "3-terrarium/3-intro-to-DOM-and-closures/assignment.md",
"language_code": "ja"
},
"3-terrarium/README.md": {
"original_hash": "bc5c5550f79d10add90ce419ee34abb3",
"translation_date": "2026-01-06T14:31:31+00:00",
"source_file": "3-terrarium/README.md",
"language_code": "ja"
},
"3-terrarium/solution/README.md": {
"original_hash": "6329fbe8bd936068debd78cca6f09c0a",
"translation_date": "2025-08-23T22:36:25+00:00",
"source_file": "3-terrarium/solution/README.md",
"language_code": "ja"
},
"4-typing-game/README.md": {
"original_hash": "efa2ab875b8bb5a7883816506da6b6d2",
"translation_date": "2025-11-06T12:58:26+00:00",
"source_file": "4-typing-game/README.md",
"language_code": "ja"
},
"4-typing-game/solution/README.md": {
"original_hash": "068cbb9b3c10a96d503f6cdd6c9ace8c",
"translation_date": "2025-08-24T00:17:57+00:00",
"source_file": "4-typing-game/solution/README.md",
"language_code": "ja"
},
"4-typing-game/typing-game/README.md": {
"original_hash": "da8bc72041a2bb3826a54654ee1a8844",
"translation_date": "2025-11-06T12:58:42+00:00",
"source_file": "4-typing-game/typing-game/README.md",
"language_code": "ja"
},
"4-typing-game/typing-game/assignment.md": {
"original_hash": "3eac59d70e2532a677a2ce6bf765485a",
"translation_date": "2025-10-24T15:12:54+00:00",
"source_file": "4-typing-game/typing-game/assignment.md",
"language_code": "ja"
},
"5-browser-extension/1-about-browsers/README.md": {
"original_hash": "00aa85715e1efd4930c17a23e3012e69",
"translation_date": "2025-11-06T13:10:34+00:00",
"source_file": "5-browser-extension/1-about-browsers/README.md",
"language_code": "ja"
},
"5-browser-extension/1-about-browsers/assignment.md": {
"original_hash": "b6897c02603d0045dd6d8256e8714baa",
"translation_date": "2025-10-24T15:20:10+00:00",
"source_file": "5-browser-extension/1-about-browsers/assignment.md",
"language_code": "ja"
},
"5-browser-extension/2-forms-browsers-local-storage/README.md": {
"original_hash": "2b6203a48c48d8234e0948353b47d84e",
"translation_date": "2025-11-06T13:08:41+00:00",
"source_file": "5-browser-extension/2-forms-browsers-local-storage/README.md",
"language_code": "ja"
},
"5-browser-extension/2-forms-browsers-local-storage/assignment.md": {
"original_hash": "25b8d28b8531352d4eb67291fd7824c4",
"translation_date": "2025-10-24T15:18:50+00:00",
"source_file": "5-browser-extension/2-forms-browsers-local-storage/assignment.md",
"language_code": "ja"
},
"5-browser-extension/3-background-tasks-and-performance/README.md": {
"original_hash": "b275fed2c6fc90d2b9b6661a3225faa2",
"translation_date": "2025-11-06T13:07:02+00:00",
"source_file": "5-browser-extension/3-background-tasks-and-performance/README.md",
"language_code": "ja"
},
"5-browser-extension/3-background-tasks-and-performance/assignment.md": {
"original_hash": "a203e560e58ccc6ba68bffc40c7c8676",
"translation_date": "2025-10-24T15:17:41+00:00",
"source_file": "5-browser-extension/3-background-tasks-and-performance/assignment.md",
"language_code": "ja"
},
"5-browser-extension/README.md": {
"original_hash": "b121a279a6ab39878491f3e572673515",
"translation_date": "2025-08-23T23:37:40+00:00",
"source_file": "5-browser-extension/README.md",
"language_code": "ja"
},
"5-browser-extension/solution/README.md": {
"original_hash": "fab4e6b4f0efcd587a9029d82991f597",
"translation_date": "2025-08-23T23:48:47+00:00",
"source_file": "5-browser-extension/solution/README.md",
"language_code": "ja"
},
"5-browser-extension/solution/translation/README.es.md": {
"original_hash": "cbaf73f94a9ab4c680a10ef871e92948",
"translation_date": "2025-08-23T23:49:43+00:00",
"source_file": "5-browser-extension/solution/translation/README.es.md",
"language_code": "ja"
},
"5-browser-extension/solution/translation/README.fr.md": {
"original_hash": "9361268ca430b2579375009e1eceb5e5",
"translation_date": "2025-08-23T23:52:24+00:00",
"source_file": "5-browser-extension/solution/translation/README.fr.md",
"language_code": "ja"
},
"5-browser-extension/solution/translation/README.hi.md": {
"original_hash": "dd58ae1b7707034f055718c1b68bc8de",
"translation_date": "2025-08-23T23:50:38+00:00",
"source_file": "5-browser-extension/solution/translation/README.hi.md",
"language_code": "ja"
},
"5-browser-extension/solution/translation/README.it.md": {
"original_hash": "9a6b22a2eff0f499b66236be973b24ad",
"translation_date": "2025-08-23T23:53:14+00:00",
"source_file": "5-browser-extension/solution/translation/README.it.md",
"language_code": "ja"
},
"5-browser-extension/solution/translation/README.ja.md": {
"original_hash": "3f5e6821e0febccfc5d05e7c944d9e3d",
"translation_date": "2025-08-23T23:54:07+00:00",
"source_file": "5-browser-extension/solution/translation/README.ja.md",
"language_code": "ja"
},
"5-browser-extension/solution/translation/README.ms.md": {
"original_hash": "21b364c158c8e4f698de65eeac16c9fe",
"translation_date": "2025-08-23T23:51:32+00:00",
"source_file": "5-browser-extension/solution/translation/README.ms.md",
"language_code": "ja"
},
"5-browser-extension/start/README.md": {
"original_hash": "26fd39046d264ba185dcb086d3a8cf3e",
"translation_date": "2025-08-23T23:41:50+00:00",
"source_file": "5-browser-extension/start/README.md",
"language_code": "ja"
},
"6-space-game/1-introduction/README.md": {
"original_hash": "a6332a7bb4d0be3bfd24199c83993777",
"translation_date": "2025-11-06T13:04:54+00:00",
"source_file": "6-space-game/1-introduction/README.md",
"language_code": "ja"
},
"6-space-game/1-introduction/assignment.md": {
"original_hash": "c8fc39a014d08247c082878122e2ba73",
"translation_date": "2025-10-24T15:16:36+00:00",
"source_file": "6-space-game/1-introduction/assignment.md",
"language_code": "ja"
},
"6-space-game/2-drawing-to-canvas/README.md": {
"original_hash": "7994743c5b21fdcceb36307916ef249a",
"translation_date": "2025-11-06T13:01:41+00:00",
"source_file": "6-space-game/2-drawing-to-canvas/README.md",
"language_code": "ja"
},
"6-space-game/2-drawing-to-canvas/assignment.md": {
"original_hash": "87cd43afe5b69dbbffb5c4b209ea6791",
"translation_date": "2025-10-24T15:14:30+00:00",
"source_file": "6-space-game/2-drawing-to-canvas/assignment.md",
"language_code": "ja"
},
"6-space-game/3-moving-elements-around/README.md": {
"original_hash": "8c55a2bd4bc0ebe4c88198fd563a9e09",
"translation_date": "2025-11-06T13:00:41+00:00",
"source_file": "6-space-game/3-moving-elements-around/README.md",
"language_code": "ja"
},
"6-space-game/3-moving-elements-around/assignment.md": {
"original_hash": "c162b3b3a1cafc1483c8015e9b266f0d",
"translation_date": "2025-10-24T15:13:50+00:00",
"source_file": "6-space-game/3-moving-elements-around/assignment.md",
"language_code": "ja"
},
"6-space-game/4-collision-detection/README.md": {
"original_hash": "039b4d8ce65f5edd82cf48d9c3e6728c",
"translation_date": "2025-11-06T13:04:03+00:00",
"source_file": "6-space-game/4-collision-detection/README.md",
"language_code": "ja"
},
"6-space-game/4-collision-detection/assignment.md": {
"original_hash": "124efddbb65166cddb38075ad6dae324",
"translation_date": "2025-10-24T15:15:59+00:00",
"source_file": "6-space-game/4-collision-detection/assignment.md",
"language_code": "ja"
},
"6-space-game/4-collision-detection/solution/README.md": {
"original_hash": "068cbb9b3c10a96d503f6cdd6c9ace8c",
"translation_date": "2025-08-23T23:04:13+00:00",
"source_file": "6-space-game/4-collision-detection/solution/README.md",
"language_code": "ja"
},
"6-space-game/4-collision-detection/your-work/README.md": {
"original_hash": "068cbb9b3c10a96d503f6cdd6c9ace8c",
"translation_date": "2025-08-23T23:03:44+00:00",
"source_file": "6-space-game/4-collision-detection/your-work/README.md",
"language_code": "ja"
},
"6-space-game/5-keeping-score/README.md": {
"original_hash": "2ed9145a16cf576faa2a973dff84d099",
"translation_date": "2025-11-06T13:05:36+00:00",
"source_file": "6-space-game/5-keeping-score/README.md",
"language_code": "ja"
},
"6-space-game/5-keeping-score/assignment.md": {
"original_hash": "81f292dbda01685b91735e0398dc0504",
"translation_date": "2025-08-23T22:53:51+00:00",
"source_file": "6-space-game/5-keeping-score/assignment.md",
"language_code": "ja"
},
"6-space-game/5-keeping-score/solution/README.md": {
"original_hash": "068cbb9b3c10a96d503f6cdd6c9ace8c",
"translation_date": "2025-08-23T22:54:50+00:00",
"source_file": "6-space-game/5-keeping-score/solution/README.md",
"language_code": "ja"
},
"6-space-game/5-keeping-score/your-work/README.md": {
"original_hash": "068cbb9b3c10a96d503f6cdd6c9ace8c",
"translation_date": "2025-08-23T22:54:22+00:00",
"source_file": "6-space-game/5-keeping-score/your-work/README.md",
"language_code": "ja"
},
"6-space-game/6-end-condition/README.md": {
"original_hash": "a4b78043f4d64bf3ee24e0689b8b391d",
"translation_date": "2025-11-06T13:02:28+00:00",
"source_file": "6-space-game/6-end-condition/README.md",
"language_code": "ja"
},
"6-space-game/6-end-condition/assignment.md": {
"original_hash": "232d592791465c1678cab3a2bb6cd3e8",
"translation_date": "2025-10-24T15:15:11+00:00",
"source_file": "6-space-game/6-end-condition/assignment.md",
"language_code": "ja"
},
"6-space-game/6-end-condition/solution/README.md": {
"original_hash": "068cbb9b3c10a96d503f6cdd6c9ace8c",
"translation_date": "2025-08-23T23:10:17+00:00",
"source_file": "6-space-game/6-end-condition/solution/README.md",
"language_code": "ja"
},
"6-space-game/6-end-condition/your-work/README.md": {
"original_hash": "068cbb9b3c10a96d503f6cdd6c9ace8c",
"translation_date": "2025-08-23T23:09:48+00:00",
"source_file": "6-space-game/6-end-condition/your-work/README.md",
"language_code": "ja"
},
"6-space-game/README.md": {
"original_hash": "c40a698395ee5102715f7880bba3f2e7",
"translation_date": "2025-08-23T22:51:30+00:00",
"source_file": "6-space-game/README.md",
"language_code": "ja"
},
"6-space-game/solution/README.md": {
"original_hash": "068cbb9b3c10a96d503f6cdd6c9ace8c",
"translation_date": "2025-08-23T23:11:05+00:00",
"source_file": "6-space-game/solution/README.md",
"language_code": "ja"
},
"7-bank-project/1-template-route/README.md": {
"original_hash": "351678bece18f07d9daa987a881fb062",
"translation_date": "2025-11-06T12:55:02+00:00",
"source_file": "7-bank-project/1-template-route/README.md",
"language_code": "ja"
},
"7-bank-project/1-template-route/assignment.md": {
"original_hash": "df0dcecddcd28ea8cbf6ede0ad57d673",
"translation_date": "2025-10-24T15:09:25+00:00",
"source_file": "7-bank-project/1-template-route/assignment.md",
"language_code": "ja"
},
"7-bank-project/2-forms/README.md": {
"original_hash": "7cbdbd132d39a2bb493e85bc2a9387cc",
"translation_date": "2025-11-06T12:57:22+00:00",
"source_file": "7-bank-project/2-forms/README.md",
"language_code": "ja"
},
"7-bank-project/2-forms/assignment.md": {
"original_hash": "efb01fcafd2ef40c593a6e662fc938a8",
"translation_date": "2025-10-24T15:11:33+00:00",
"source_file": "7-bank-project/2-forms/assignment.md",
"language_code": "ja"
},
"7-bank-project/3-data/README.md": {
"original_hash": "86ee5069f27ea3151389d8687c95fac9",
"translation_date": "2025-11-06T12:52:30+00:00",
"source_file": "7-bank-project/3-data/README.md",
"language_code": "ja"
},
"7-bank-project/3-data/assignment.md": {
"original_hash": "d0a02cb117e91a5b5f24178080068a3d",
"translation_date": "2025-10-24T15:08:18+00:00",
"source_file": "7-bank-project/3-data/assignment.md",
"language_code": "ja"
},
"7-bank-project/4-state-management/README.md": {
"original_hash": "b807b09df716dc48a2b750835bf8e933",
"translation_date": "2025-11-06T12:50:08+00:00",
"source_file": "7-bank-project/4-state-management/README.md",
"language_code": "ja"
},
"7-bank-project/4-state-management/assignment.md": {
"original_hash": "50a7783473b39a2e0f133e271a102231",
"translation_date": "2025-10-24T15:05:51+00:00",
"source_file": "7-bank-project/4-state-management/assignment.md",
"language_code": "ja"
},
"7-bank-project/README.md": {
"original_hash": "830359535306594b448db6575ce5cdee",
"translation_date": "2025-08-23T23:54:55+00:00",
"source_file": "7-bank-project/README.md",
"language_code": "ja"
},
"7-bank-project/api/README.md": {
"original_hash": "9884f8c8a61cf56214450f8b16a094ce",
"translation_date": "2025-08-23T23:55:53+00:00",
"source_file": "7-bank-project/api/README.md",
"language_code": "ja"
},
"7-bank-project/solution/README.md": {
"original_hash": "461aa4fc74c6b1789c3a13b5d82c0cd9",
"translation_date": "2025-08-24T00:10:47+00:00",
"source_file": "7-bank-project/solution/README.md",
"language_code": "ja"
},
"8-code-editor/1-using-a-code-editor/README.md": {
"original_hash": "a9a3bcc037a447e2d8994d99e871cd9f",
"translation_date": "2025-11-06T12:29:39+00:00",
"source_file": "8-code-editor/1-using-a-code-editor/README.md",
"language_code": "ja"
},
"8-code-editor/1-using-a-code-editor/assignment.md": {
"original_hash": "effe56ba51c38d7bdfad1ea38288666b",
"translation_date": "2025-10-24T14:49:24+00:00",
"source_file": "8-code-editor/1-using-a-code-editor/assignment.md",
"language_code": "ja"
},
"9-chat-project/README.md": {
"original_hash": "2066c17078e9d18b5e309f31d8e8bc24",
"translation_date": "2025-11-06T12:43:11+00:00",
"source_file": "9-chat-project/README.md",
"language_code": "ja"
},
"9-chat-project/solution/README.md": {
"original_hash": "cb549dcad8eea3221cb89793aeaa3bb3",
"translation_date": "2025-09-01T15:24:00+00:00",
"source_file": "9-chat-project/solution/README.md",
"language_code": "ja"
},
"9-chat-project/solution/backend/README.md": {
"original_hash": "bcd2c2bbaae71151b1ed1b9170aa78af",
"translation_date": "2025-09-01T15:42:39+00:00",
"source_file": "9-chat-project/solution/backend/README.md",
"language_code": "ja"
},
"9-chat-project/solution/backend/python/README.md": {
"original_hash": "0aaa930f076f2d83cc872ad157f8ffd3",
"translation_date": "2025-10-24T15:01:48+00:00",
"source_file": "9-chat-project/solution/backend/python/README.md",
"language_code": "ja"
},
"9-chat-project/solution/frontend/README.md": {
"original_hash": "7746a470be8fc7f736eb1b43ebb710ee",
"translation_date": "2025-09-01T15:51:26+00:00",
"source_file": "9-chat-project/solution/frontend/README.md",
"language_code": "ja"
},
"AGENTS.md": {
"original_hash": "a362efd06d64d4134a0cfe8515a86d34",
"translation_date": "2025-10-03T11:18:51+00:00",
"source_file": "AGENTS.md",
"language_code": "ja"
},
"CODE_OF_CONDUCT.md": {
"original_hash": "b0a9b4cccd918195f58224d5793da1a6",
"translation_date": "2025-08-23T22:20:29+00:00",
"source_file": "CODE_OF_CONDUCT.md",
"language_code": "ja"
},
"CONTRIBUTING.md": {
"original_hash": "777400e9f0336c7ee2f9a1200a88478f",
"translation_date": "2025-08-23T22:22:50+00:00",
"source_file": "CONTRIBUTING.md",
"language_code": "ja"
},
"Git-Basics/README.md": {
"original_hash": "5cf5e1ed51455fefed4895fcc4d6ba2a",
"translation_date": "2025-10-03T15:48:08+00:00",
"source_file": "Git-Basics/README.md",
"language_code": "ja"
},
"README.md": {
"original_hash": "490c83786552d7fab6390991517bef09",
"translation_date": "2026-01-29T17:54:08+00:00",
"source_file": "README.md",
"language_code": "ja"
},
"SECURITY.md": {
"original_hash": "4ecc3bf2e27983d4c780be6f26ee6228",
"translation_date": "2025-08-23T22:21:58+00:00",
"source_file": "SECURITY.md",
"language_code": "ja"
},
"SUPPORT.md": {
"original_hash": "c9d207ff77b4bb46e46dc2b607a8ec1a",
"translation_date": "2025-08-23T22:18:48+00:00",
"source_file": "SUPPORT.md",
"language_code": "ja"
},
"_404.md": {
"original_hash": "ea9f0804bd62f46d9808e953ec7fc459",
"translation_date": "2025-08-23T22:21:02+00:00",
"source_file": "_404.md",
"language_code": "ja"
},
"docs/_navbar.md": {
"original_hash": "3bd2f51ecf4ac9b39277cba748943793",
"translation_date": "2025-08-23T22:50:47+00:00",
"source_file": "docs/_navbar.md",
"language_code": "ja"
},
"docs/_sidebar.md": {
"original_hash": "655c91b5979de46f1d70d97f0c5f1d14",
"translation_date": "2025-08-23T22:50:00+00:00",
"source_file": "docs/_sidebar.md",
"language_code": "ja"
},
"for-teachers.md": {
"original_hash": "71009af209f81cc01a1f2d324200375f",
"translation_date": "2025-10-03T09:01:05+00:00",
"source_file": "for-teachers.md",
"language_code": "ja"
},
"lesson-template/README.md": {
"original_hash": "0494be70ad7fadd13a8c3d549c23e355",
"translation_date": "2025-08-24T00:16:11+00:00",
"source_file": "lesson-template/README.md",
"language_code": "ja"
},
"lesson-template/assignment.md": {
"original_hash": "b5f62ec256c7e43e771f0d3b4e1a9130",
"translation_date": "2025-08-24T00:16:43+00:00",
"source_file": "lesson-template/assignment.md",
"language_code": "ja"
},
"memory-game/README.md": {
"original_hash": "ff47271e53637b2ba6ba72ad2b70f6d7",
"translation_date": "2025-10-03T11:54:39+00:00",
"source_file": "memory-game/README.md",
"language_code": "ja"
},
"quiz-app/README.md": {
"original_hash": "5301875c55bb305e6046bed3a4fd06d2",
"translation_date": "2025-08-24T00:23:41+00:00",
"source_file": "quiz-app/README.md",
"language_code": "ja"
}
}

@ -0,0 +1,584 @@
{
"1-getting-started-lessons/1-intro-to-programming-languages/README.md": {
"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": "zh-HK"
},
"1-getting-started-lessons/1-intro-to-programming-languages/assignment.md": {
"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": "zh-HK"
},
"1-getting-started-lessons/2-github-basics/README.md": {
"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": "zh-HK"
},
"1-getting-started-lessons/3-accessibility/README.md": {
"original_hash": "7f2c48e04754724123ea100a822765e5",
"translation_date": "2026-01-06T13:06:57+00:00",
"source_file": "1-getting-started-lessons/3-accessibility/README.md",
"language_code": "zh-HK"
},
"1-getting-started-lessons/3-accessibility/assignment.md": {
"original_hash": "e6d0f456dfc22afb41bbdefeb5ec179d",
"translation_date": "2026-01-06T13:10:46+00:00",
"source_file": "1-getting-started-lessons/3-accessibility/assignment.md",
"language_code": "zh-HK"
},
"1-getting-started-lessons/README.md": {
"original_hash": "770d9f83dddc841c19f210dee5fe0712",
"translation_date": "2025-10-03T13:26:07+00:00",
"source_file": "1-getting-started-lessons/README.md",
"language_code": "zh-HK"
},
"10-ai-framework-project/README.md": {
"original_hash": "3925b6a1c31c60755eaae4d578232c25",
"translation_date": "2026-01-06T12:06:41+00:00",
"source_file": "10-ai-framework-project/README.md",
"language_code": "zh-HK"
},
"10-ai-framework-project/solution/README.md": {
"original_hash": "d41d8cd98f00b204e9800998ecf8427e",
"translation_date": "2026-01-29T17:48:27+00:00",
"source_file": "10-ai-framework-project/solution/README.md",
"language_code": "zh-HK"
},
"2-js-basics/1-data-types/README.md": {
"original_hash": "672b0bb6e8b431075f3bdb7130590d2d",
"translation_date": "2026-01-06T12:16:34+00:00",
"source_file": "2-js-basics/1-data-types/README.md",
"language_code": "zh-HK"
},
"2-js-basics/1-data-types/assignment.md": {
"original_hash": "6fd645e97c48cd5eb5a3d290815ec8b5",
"translation_date": "2026-01-06T12:18:33+00:00",
"source_file": "2-js-basics/1-data-types/assignment.md",
"language_code": "zh-HK"
},
"2-js-basics/2-functions-methods/README.md": {
"original_hash": "71f7d7dafa1c7194d79ddac87f669ff9",
"translation_date": "2026-01-06T12:14:28+00:00",
"source_file": "2-js-basics/2-functions-methods/README.md",
"language_code": "zh-HK"
},
"2-js-basics/2-functions-methods/assignment.md": {
"original_hash": "8328f58f4593b4671656ff8f4b2edbd9",
"translation_date": "2026-01-06T12:16:19+00:00",
"source_file": "2-js-basics/2-functions-methods/assignment.md",
"language_code": "zh-HK"
},
"2-js-basics/3-making-decisions/README.md": {
"original_hash": "c688385d15dd3645e924ea0ffee8967f",
"translation_date": "2026-01-06T12:18:41+00:00",
"source_file": "2-js-basics/3-making-decisions/README.md",
"language_code": "zh-HK"
},
"2-js-basics/3-making-decisions/assignment.md": {
"original_hash": "ffe366b2d1f037b99fbadbe1dc81083d",
"translation_date": "2026-01-06T12:20:58+00:00",
"source_file": "2-js-basics/3-making-decisions/assignment.md",
"language_code": "zh-HK"
},
"2-js-basics/4-arrays-loops/README.md": {
"original_hash": "1710a50a519a6e4a1b40a5638783018d",
"translation_date": "2026-01-06T12:21:18+00:00",
"source_file": "2-js-basics/4-arrays-loops/README.md",
"language_code": "zh-HK"
},
"2-js-basics/4-arrays-loops/assignment.md": {
"original_hash": "8abcada0534e0fb3a7556ea3c5a2a8a4",
"translation_date": "2026-01-06T12:23:34+00:00",
"source_file": "2-js-basics/4-arrays-loops/assignment.md",
"language_code": "zh-HK"
},
"2-js-basics/README.md": {
"original_hash": "cc9e70a2f096c67389c8acff1521fc27",
"translation_date": "2025-08-23T22:36:52+00:00",
"source_file": "2-js-basics/README.md",
"language_code": "zh-HK"
},
"3-terrarium/1-intro-to-html/README.md": {
"original_hash": "3fcfa99c4897e051b558b5eaf1e8cc74",
"translation_date": "2026-01-06T12:59:30+00:00",
"source_file": "3-terrarium/1-intro-to-html/README.md",
"language_code": "zh-HK"
},
"3-terrarium/1-intro-to-html/assignment.md": {
"original_hash": "650e63282e1dfa032890fcf5c1c4119d",
"translation_date": "2026-01-06T13:01:24+00:00",
"source_file": "3-terrarium/1-intro-to-html/assignment.md",
"language_code": "zh-HK"
},
"3-terrarium/2-intro-to-css/README.md": {
"original_hash": "e39f3a4e3bcccf94639e3af1248f8a4d",
"translation_date": "2026-01-06T13:04:33+00:00",
"source_file": "3-terrarium/2-intro-to-css/README.md",
"language_code": "zh-HK"
},
"3-terrarium/2-intro-to-css/assignment.md": {
"original_hash": "bee6762d4092a13fc7c338814963f980",
"translation_date": "2026-01-06T13:06:33+00:00",
"source_file": "3-terrarium/2-intro-to-css/assignment.md",
"language_code": "zh-HK"
},
"3-terrarium/3-intro-to-DOM-and-closures/README.md": {
"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": "zh-HK"
},
"3-terrarium/3-intro-to-DOM-and-closures/assignment.md": {
"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": "zh-HK"
},
"3-terrarium/README.md": {
"original_hash": "bc5c5550f79d10add90ce419ee34abb3",
"translation_date": "2026-01-06T12:09:40+00:00",
"source_file": "3-terrarium/README.md",
"language_code": "zh-HK"
},
"3-terrarium/solution/README.md": {
"original_hash": "6329fbe8bd936068debd78cca6f09c0a",
"translation_date": "2025-08-23T22:36:06+00:00",
"source_file": "3-terrarium/solution/README.md",
"language_code": "zh-HK"
},
"4-typing-game/README.md": {
"original_hash": "efa2ab875b8bb5a7883816506da6b6d2",
"translation_date": "2026-01-06T12:08:49+00:00",
"source_file": "4-typing-game/README.md",
"language_code": "zh-HK"
},
"4-typing-game/solution/README.md": {
"original_hash": "068cbb9b3c10a96d503f6cdd6c9ace8c",
"translation_date": "2025-08-24T00:17:49+00:00",
"source_file": "4-typing-game/solution/README.md",
"language_code": "zh-HK"
},
"4-typing-game/typing-game/README.md": {
"original_hash": "da8bc72041a2bb3826a54654ee1a8844",
"translation_date": "2026-01-06T12:57:28+00:00",
"source_file": "4-typing-game/typing-game/README.md",
"language_code": "zh-HK"
},
"4-typing-game/typing-game/assignment.md": {
"original_hash": "3eac59d70e2532a677a2ce6bf765485a",
"translation_date": "2026-01-06T12:59:16+00:00",
"source_file": "4-typing-game/typing-game/assignment.md",
"language_code": "zh-HK"
},
"5-browser-extension/1-about-browsers/README.md": {
"original_hash": "00aa85715e1efd4930c17a23e3012e69",
"translation_date": "2026-01-06T12:44:16+00:00",
"source_file": "5-browser-extension/1-about-browsers/README.md",
"language_code": "zh-HK"
},
"5-browser-extension/1-about-browsers/assignment.md": {
"original_hash": "b6897c02603d0045dd6d8256e8714baa",
"translation_date": "2026-01-06T12:46:05+00:00",
"source_file": "5-browser-extension/1-about-browsers/assignment.md",
"language_code": "zh-HK"
},
"5-browser-extension/2-forms-browsers-local-storage/README.md": {
"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": "zh-HK"
},
"5-browser-extension/2-forms-browsers-local-storage/assignment.md": {
"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": "zh-HK"
},
"5-browser-extension/3-background-tasks-and-performance/README.md": {
"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": "zh-HK"
},
"5-browser-extension/3-background-tasks-and-performance/assignment.md": {
"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": "zh-HK"
},
"5-browser-extension/README.md": {
"original_hash": "b121a279a6ab39878491f3e572673515",
"translation_date": "2025-08-23T23:37:23+00:00",
"source_file": "5-browser-extension/README.md",
"language_code": "zh-HK"
},
"5-browser-extension/solution/README.md": {
"original_hash": "fab4e6b4f0efcd587a9029d82991f597",
"translation_date": "2025-08-23T23:48:31+00:00",
"source_file": "5-browser-extension/solution/README.md",
"language_code": "zh-HK"
},
"5-browser-extension/solution/translation/README.es.md": {
"original_hash": "cbaf73f94a9ab4c680a10ef871e92948",
"translation_date": "2025-08-23T23:49:24+00:00",
"source_file": "5-browser-extension/solution/translation/README.es.md",
"language_code": "zh-HK"
},
"5-browser-extension/solution/translation/README.fr.md": {
"original_hash": "9361268ca430b2579375009e1eceb5e5",
"translation_date": "2025-08-23T23:52:05+00:00",
"source_file": "5-browser-extension/solution/translation/README.fr.md",
"language_code": "zh-HK"
},
"5-browser-extension/solution/translation/README.hi.md": {
"original_hash": "dd58ae1b7707034f055718c1b68bc8de",
"translation_date": "2025-08-23T23:50:20+00:00",
"source_file": "5-browser-extension/solution/translation/README.hi.md",
"language_code": "zh-HK"
},
"5-browser-extension/solution/translation/README.it.md": {
"original_hash": "9a6b22a2eff0f499b66236be973b24ad",
"translation_date": "2025-08-23T23:52:57+00:00",
"source_file": "5-browser-extension/solution/translation/README.it.md",
"language_code": "zh-HK"
},
"5-browser-extension/solution/translation/README.ja.md": {
"original_hash": "3f5e6821e0febccfc5d05e7c944d9e3d",
"translation_date": "2025-08-23T23:53:50+00:00",
"source_file": "5-browser-extension/solution/translation/README.ja.md",
"language_code": "zh-HK"
},
"5-browser-extension/solution/translation/README.ms.md": {
"original_hash": "21b364c158c8e4f698de65eeac16c9fe",
"translation_date": "2025-08-23T23:51:13+00:00",
"source_file": "5-browser-extension/solution/translation/README.ms.md",
"language_code": "zh-HK"
},
"5-browser-extension/start/README.md": {
"original_hash": "26fd39046d264ba185dcb086d3a8cf3e",
"translation_date": "2025-08-23T23:41:32+00:00",
"source_file": "5-browser-extension/start/README.md",
"language_code": "zh-HK"
},
"6-space-game/1-introduction/README.md": {
"original_hash": "a6332a7bb4d0be3bfd24199c83993777",
"translation_date": "2026-01-06T12:35:19+00:00",
"source_file": "6-space-game/1-introduction/README.md",
"language_code": "zh-HK"
},
"6-space-game/1-introduction/assignment.md": {
"original_hash": "c8fc39a014d08247c082878122e2ba73",
"translation_date": "2026-01-06T12:37:04+00:00",
"source_file": "6-space-game/1-introduction/assignment.md",
"language_code": "zh-HK"
},
"6-space-game/2-drawing-to-canvas/README.md": {
"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": "zh-HK"
},
"6-space-game/2-drawing-to-canvas/assignment.md": {
"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": "zh-HK"
},
"6-space-game/3-moving-elements-around/README.md": {
"original_hash": "8c55a2bd4bc0ebe4c88198fd563a9e09",
"translation_date": "2026-01-06T12:37:20+00:00",
"source_file": "6-space-game/3-moving-elements-around/README.md",
"language_code": "zh-HK"
},
"6-space-game/3-moving-elements-around/assignment.md": {
"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": "zh-HK"
},
"6-space-game/4-collision-detection/README.md": {
"original_hash": "039b4d8ce65f5edd82cf48d9c3e6728c",
"translation_date": "2026-01-06T12:27:34+00:00",
"source_file": "6-space-game/4-collision-detection/README.md",
"language_code": "zh-HK"
},
"6-space-game/4-collision-detection/assignment.md": {
"original_hash": "124efddbb65166cddb38075ad6dae324",
"translation_date": "2026-01-06T12:29:14+00:00",
"source_file": "6-space-game/4-collision-detection/assignment.md",
"language_code": "zh-HK"
},
"6-space-game/4-collision-detection/solution/README.md": {
"original_hash": "068cbb9b3c10a96d503f6cdd6c9ace8c",
"translation_date": "2025-08-23T23:04:04+00:00",
"source_file": "6-space-game/4-collision-detection/solution/README.md",
"language_code": "zh-HK"
},
"6-space-game/4-collision-detection/your-work/README.md": {
"original_hash": "068cbb9b3c10a96d503f6cdd6c9ace8c",
"translation_date": "2025-08-23T23:03:34+00:00",
"source_file": "6-space-game/4-collision-detection/your-work/README.md",
"language_code": "zh-HK"
},
"6-space-game/5-keeping-score/README.md": {
"original_hash": "2ed9145a16cf576faa2a973dff84d099",
"translation_date": "2026-01-06T12:34:17+00:00",
"source_file": "6-space-game/5-keeping-score/README.md",
"language_code": "zh-HK"
},
"6-space-game/5-keeping-score/assignment.md": {
"original_hash": "81f292dbda01685b91735e0398dc0504",
"translation_date": "2025-08-23T22:53:40+00:00",
"source_file": "6-space-game/5-keeping-score/assignment.md",
"language_code": "zh-HK"
},
"6-space-game/5-keeping-score/solution/README.md": {
"original_hash": "068cbb9b3c10a96d503f6cdd6c9ace8c",
"translation_date": "2025-08-23T22:54:41+00:00",
"source_file": "6-space-game/5-keeping-score/solution/README.md",
"language_code": "zh-HK"
},
"6-space-game/5-keeping-score/your-work/README.md": {
"original_hash": "068cbb9b3c10a96d503f6cdd6c9ace8c",
"translation_date": "2025-08-23T22:54:13+00:00",
"source_file": "6-space-game/5-keeping-score/your-work/README.md",
"language_code": "zh-HK"
},
"6-space-game/6-end-condition/README.md": {
"original_hash": "a4b78043f4d64bf3ee24e0689b8b391d",
"translation_date": "2026-01-06T12:31:42+00:00",
"source_file": "6-space-game/6-end-condition/README.md",
"language_code": "zh-HK"
},
"6-space-game/6-end-condition/assignment.md": {
"original_hash": "232d592791465c1678cab3a2bb6cd3e8",
"translation_date": "2026-01-06T12:33:53+00:00",
"source_file": "6-space-game/6-end-condition/assignment.md",
"language_code": "zh-HK"
},
"6-space-game/6-end-condition/solution/README.md": {
"original_hash": "068cbb9b3c10a96d503f6cdd6c9ace8c",
"translation_date": "2025-08-23T23:10:07+00:00",
"source_file": "6-space-game/6-end-condition/solution/README.md",
"language_code": "zh-HK"
},
"6-space-game/6-end-condition/your-work/README.md": {
"original_hash": "068cbb9b3c10a96d503f6cdd6c9ace8c",
"translation_date": "2025-08-23T23:09:37+00:00",
"source_file": "6-space-game/6-end-condition/your-work/README.md",
"language_code": "zh-HK"
},
"6-space-game/README.md": {
"original_hash": "c40a698395ee5102715f7880bba3f2e7",
"translation_date": "2025-08-23T22:51:15+00:00",
"source_file": "6-space-game/README.md",
"language_code": "zh-HK"
},
"6-space-game/solution/README.md": {
"original_hash": "068cbb9b3c10a96d503f6cdd6c9ace8c",
"translation_date": "2025-08-23T23:10:37+00:00",
"source_file": "6-space-game/solution/README.md",
"language_code": "zh-HK"
},
"7-bank-project/1-template-route/README.md": {
"original_hash": "351678bece18f07d9daa987a881fb062",
"translation_date": "2026-01-06T12:48:54+00:00",
"source_file": "7-bank-project/1-template-route/README.md",
"language_code": "zh-HK"
},
"7-bank-project/1-template-route/assignment.md": {
"original_hash": "df0dcecddcd28ea8cbf6ede0ad57d673",
"translation_date": "2026-01-06T12:51:16+00:00",
"source_file": "7-bank-project/1-template-route/assignment.md",
"language_code": "zh-HK"
},
"7-bank-project/2-forms/README.md": {
"original_hash": "7cbdbd132d39a2bb493e85bc2a9387cc",
"translation_date": "2026-01-06T12:46:26+00:00",
"source_file": "7-bank-project/2-forms/README.md",
"language_code": "zh-HK"
},
"7-bank-project/2-forms/assignment.md": {
"original_hash": "efb01fcafd2ef40c593a6e662fc938a8",
"translation_date": "2026-01-06T12:48:34+00:00",
"source_file": "7-bank-project/2-forms/assignment.md",
"language_code": "zh-HK"
},
"7-bank-project/3-data/README.md": {
"original_hash": "86ee5069f27ea3151389d8687c95fac9",
"translation_date": "2026-01-06T12:51:26+00:00",
"source_file": "7-bank-project/3-data/README.md",
"language_code": "zh-HK"
},
"7-bank-project/3-data/assignment.md": {
"original_hash": "d0a02cb117e91a5b5f24178080068a3d",
"translation_date": "2026-01-06T12:54:17+00:00",
"source_file": "7-bank-project/3-data/assignment.md",
"language_code": "zh-HK"
},
"7-bank-project/4-state-management/README.md": {
"original_hash": "b807b09df716dc48a2b750835bf8e933",
"translation_date": "2026-01-06T12:54:43+00:00",
"source_file": "7-bank-project/4-state-management/README.md",
"language_code": "zh-HK"
},
"7-bank-project/4-state-management/assignment.md": {
"original_hash": "50a7783473b39a2e0f133e271a102231",
"translation_date": "2026-01-06T12:57:07+00:00",
"source_file": "7-bank-project/4-state-management/assignment.md",
"language_code": "zh-HK"
},
"7-bank-project/README.md": {
"original_hash": "830359535306594b448db6575ce5cdee",
"translation_date": "2025-08-23T23:54:39+00:00",
"source_file": "7-bank-project/README.md",
"language_code": "zh-HK"
},
"7-bank-project/api/README.md": {
"original_hash": "9884f8c8a61cf56214450f8b16a094ce",
"translation_date": "2025-08-23T23:55:32+00:00",
"source_file": "7-bank-project/api/README.md",
"language_code": "zh-HK"
},
"7-bank-project/solution/README.md": {
"original_hash": "461aa4fc74c6b1789c3a13b5d82c0cd9",
"translation_date": "2025-08-24T00:10:34+00:00",
"source_file": "7-bank-project/solution/README.md",
"language_code": "zh-HK"
},
"8-code-editor/1-using-a-code-editor/README.md": {
"original_hash": "a9a3bcc037a447e2d8994d99e871cd9f",
"translation_date": "2026-01-06T12:23:51+00:00",
"source_file": "8-code-editor/1-using-a-code-editor/README.md",
"language_code": "zh-HK"
},
"8-code-editor/1-using-a-code-editor/assignment.md": {
"original_hash": "effe56ba51c38d7bdfad1ea38288666b",
"translation_date": "2026-01-06T12:26:51+00:00",
"source_file": "8-code-editor/1-using-a-code-editor/assignment.md",
"language_code": "zh-HK"
},
"9-chat-project/README.md": {
"original_hash": "2066c17078e9d18b5e309f31d8e8bc24",
"translation_date": "2026-01-06T12:09:46+00:00",
"source_file": "9-chat-project/README.md",
"language_code": "zh-HK"
},
"9-chat-project/solution/README.md": {
"original_hash": "cb549dcad8eea3221cb89793aeaa3bb3",
"translation_date": "2025-09-01T15:22:57+00:00",
"source_file": "9-chat-project/solution/README.md",
"language_code": "zh-HK"
},
"9-chat-project/solution/backend/README.md": {
"original_hash": "bcd2c2bbaae71151b1ed1b9170aa78af",
"translation_date": "2025-09-01T15:42:31+00:00",
"source_file": "9-chat-project/solution/backend/README.md",
"language_code": "zh-HK"
},
"9-chat-project/solution/backend/python/README.md": {
"original_hash": "0aaa930f076f2d83cc872ad157f8ffd3",
"translation_date": "2026-01-06T13:17:10+00:00",
"source_file": "9-chat-project/solution/backend/python/README.md",
"language_code": "zh-HK"
},
"9-chat-project/solution/frontend/README.md": {
"original_hash": "7746a470be8fc7f736eb1b43ebb710ee",
"translation_date": "2025-09-01T15:51:18+00:00",
"source_file": "9-chat-project/solution/frontend/README.md",
"language_code": "zh-HK"
},
"AGENTS.md": {
"original_hash": "a362efd06d64d4134a0cfe8515a86d34",
"translation_date": "2025-10-03T11:17:40+00:00",
"source_file": "AGENTS.md",
"language_code": "zh-HK"
},
"CODE_OF_CONDUCT.md": {
"original_hash": "b0a9b4cccd918195f58224d5793da1a6",
"translation_date": "2025-08-23T22:20:18+00:00",
"source_file": "CODE_OF_CONDUCT.md",
"language_code": "zh-HK"
},
"CONTRIBUTING.md": {
"original_hash": "777400e9f0336c7ee2f9a1200a88478f",
"translation_date": "2025-08-23T22:22:35+00:00",
"source_file": "CONTRIBUTING.md",
"language_code": "zh-HK"
},
"Git-Basics/README.md": {
"original_hash": "5cf5e1ed51455fefed4895fcc4d6ba2a",
"translation_date": "2025-10-03T15:47:27+00:00",
"source_file": "Git-Basics/README.md",
"language_code": "zh-HK"
},
"README.md": {
"original_hash": "490c83786552d7fab6390991517bef09",
"translation_date": "2026-01-29T17:50:10+00:00",
"source_file": "README.md",
"language_code": "zh-HK"
},
"SECURITY.md": {
"original_hash": "4ecc3bf2e27983d4c780be6f26ee6228",
"translation_date": "2025-08-23T22:21:34+00:00",
"source_file": "SECURITY.md",
"language_code": "zh-HK"
},
"SUPPORT.md": {
"original_hash": "c9d207ff77b4bb46e46dc2b607a8ec1a",
"translation_date": "2025-08-23T22:18:37+00:00",
"source_file": "SUPPORT.md",
"language_code": "zh-HK"
},
"_404.md": {
"original_hash": "ea9f0804bd62f46d9808e953ec7fc459",
"translation_date": "2025-08-23T22:20:52+00:00",
"source_file": "_404.md",
"language_code": "zh-HK"
},
"docs/_navbar.md": {
"original_hash": "3bd2f51ecf4ac9b39277cba748943793",
"translation_date": "2025-08-23T22:50:35+00:00",
"source_file": "docs/_navbar.md",
"language_code": "zh-HK"
},
"docs/_sidebar.md": {
"original_hash": "655c91b5979de46f1d70d97f0c5f1d14",
"translation_date": "2025-08-23T22:49:41+00:00",
"source_file": "docs/_sidebar.md",
"language_code": "zh-HK"
},
"for-teachers.md": {
"original_hash": "71009af209f81cc01a1f2d324200375f",
"translation_date": "2025-10-03T08:53:49+00:00",
"source_file": "for-teachers.md",
"language_code": "zh-HK"
},
"lesson-template/README.md": {
"original_hash": "0494be70ad7fadd13a8c3d549c23e355",
"translation_date": "2025-08-24T00:15:57+00:00",
"source_file": "lesson-template/README.md",
"language_code": "zh-HK"
},
"lesson-template/assignment.md": {
"original_hash": "b5f62ec256c7e43e771f0d3b4e1a9130",
"translation_date": "2025-08-24T00:16:33+00:00",
"source_file": "lesson-template/assignment.md",
"language_code": "zh-HK"
},
"memory-game/README.md": {
"original_hash": "ff47271e53637b2ba6ba72ad2b70f6d7",
"translation_date": "2025-10-03T11:54:24+00:00",
"source_file": "memory-game/README.md",
"language_code": "zh-HK"
},
"quiz-app/README.md": {
"original_hash": "5301875c55bb305e6046bed3a4fd06d2",
"translation_date": "2025-08-24T00:23:11+00:00",
"source_file": "quiz-app/README.md",
"language_code": "zh-HK"
}
}

@ -0,0 +1,853 @@
# 程式語言與現代開發工具簡介
嗨,未來的開發者!👋 可不可以跟你說件每天都讓我雞皮疙瘩掉滿地的事?你即將發現,程式設計不只是關於電腦——它是擁有真正超能力,把你最狂野的想法實現出來的能力!
你知道那種用你最喜歡的 app所有操作都剛剛好完美連接的瞬間嗎當你按一個按鈕然後發生了某種完全神奇的事讓你忍不住想說「哇他們到底怎麼做到的」剛好某個跟你一樣的人——可能正坐在他最喜歡的咖啡店裡凌晨兩點喝著第三杯濃縮咖啡——寫了造就那魔法的程式碼。你將親眼見證的令人震驚的事是到了這節課結束你不僅會理解他們是怎麼做到的還會迫不及待想要自己試試看
聽著,如果你覺得程式設計現在很可怕,我完全可以理解。當我剛開始時,我真的以為你需要是某種數學天才,或者從五歲就開始寫程式。但徹底改變我觀念的是這件事:程式設計就像學一門語言溝通一樣。你從「你好」和「謝謝」學起,然後開始點咖啡,不知不覺你開始進行深刻的哲學討論!只不過在這案例裡,你是在和電腦對話。說真的?它們是你能找到的最有耐性的對話夥伴——它們從不責備你的錯誤,而是永遠準備好再試一次!
今天,我們將探索現代網頁開發令人驚嘆的工具,讓它不僅僅是可行,而是讓人嚴重上癮。我說的就是 Netflix、Spotify 和你喜歡的獨立應用工作室每天使用的同一套編輯器、瀏覽器和工作流程。最棒的是:這些專業級、業界標準的工具大多數都是完全免費的!
![Intro Programming](../../../../translated_images/zh-HK/webdev101-programming.d6e3f98e61ac4bff.webp)
> 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)
## 我們即將一起展開的冒險
好啦,我真心為我們今天將探討的東西興奮到蹦蹦跳!說真的,我希望能看到當你理解這些概念的那張臉。這就是我們一起踏上的驚奇旅程:
- **什麼是程式設計(以及為什麼它超酷!)** 我們要了解程式碼如何成為背後無形魔法,驅動你周遭一切,從那個能準時知道是星期一早晨的鬧鐘,到完美推薦你 Netflix 節目的算法
- **程式語言和它們奇妙的個性** 想像走進一場派對,每個人都有完全不同的超能力和解決問題的方法。這就是程式語言的世界,你會很享受認識它們!
- **數位魔法的基本構件** 想像這是終極創意樂高組合套件。一旦你了解這些積木怎麼拼組,你會發現你實際上能打造出你想像的任何東西
- **讓你感覺像握到魔法棒的專業工具** 我不是誇張——這些工具真的會讓你覺得你有超能力,最棒的是?它們是專業人士每天使用的!
> 💡 **重點是**:今天不要想著要全部記住!我現在只想讓你感受到對可能性的那股興奮。細節會隨著我們一起練習自然記住——真正的學習就是這樣發生的!
> 你也可以在 [Microsoft Learn](https://docs.microsoft.com/learn/modules/web-development-101/introduction-programming/?WT.mc_id=academic-77807-sagibbon) 上進行這堂課程!
## 那麼究竟什麼是*程式設計*
好,來回答百萬美元的問題:程式設計到底是什麼?
我來分享一個徹底改變我思考的故事。上週我試著向媽媽解釋如何使用我們的新智慧電視遙控器。結果我發現自己一直在說「按紅色的按鈕,不是大紅色的,是左邊那個小紅色的…不,是另一個左邊…好,現在按住兩秒,不是一秒,不是三秒…」聽起來很熟悉嗎?😅
這就是程式設計!它是對一個非常強大的東西,給出極度詳細、逐步的指令,而它需要所有步驟都說得非常清楚的藝術。只不過你不是在解釋給媽媽聽(她可以問「哪個紅色按鈕?!」),而是在解釋給電腦——電腦會精確執行你說的每一句話,即便你真正想說的不是這樣。
第一次學到這點時讓我震撼的是電腦其實本質非常簡單。它們字面上只懂兩個東西——1 和 0基本上就是「是」和「否」或「開」和「關」。就這樣但神奇之處在於——我們不必像《駭客任務》裡那樣只說 1 和 0 。這時就是**程式語言**大顯身手的地方。它們就像世界上最棒的翻譯官,把你正常的人類思考完美地轉換成電腦語言。
而每天早上醒來仍讓我起雞皮疙瘩的是:你生活中所有數位東西都是由跟你一樣的人開始的,可能他們穿著睡衣,手裡拿著咖啡,在筆電前敲著程式碼。讓你看起來完美無瑕的 Instagram 濾鏡?有人寫了那程式碼。推推薦你發現新歌的算法?一位開發者設計了它。幫你和朋友分餐費用的 app沒錯有人想「這很煩我一定能解決它」結果…他們做到了
學程式不僅是學新技術——你是加入這個超棒的問題解決社群,他們每天的想法是,「如果我能做出些什麼讓別人的生活更好一點點呢?」說真的,還有比這更帥的事嗎?
**趣味知識探索**:有空時去查查世界上第一位程式設計師是誰?提示一下:她可能不是你預期的人!她的故事非常吸引人,而且證明了程式設計一直是關於創意解決問題和跳出框架思考。
### 🧠 **回顧時間:你感覺如何?**
**花點時間反思:**
- 「給電腦下指令」的概念現在對你來說有意義嗎?
- 你能想到想用程式自動化的日常任務嗎?
- 關於程式設計,你腦中冒出什麼問題?
> **記住**:如果某些概念現在還模糊是完全正常的。學程式就像學一門新語言——你的大腦需要時間建立神經通路。你做得很棒!
## 程式語言就像不同口味的魔法
好啦,這聽起來怪怪的,但跟我走——程式語言很像不同類型的音樂。想想看:有浪漫即興的爵士樂、強而有力的搖滾、典雅嚴謹的古典樂和充滿創造力與表達的嘻哈。每種風格都有自己的氛圍、熱情粉絲社群,也適合不同心情和場合。
程式語言也是如此!你不會用同一種語言寫一款有趣的手機遊戲和處理大量氣候數據的程式,就像你不會在瑜伽課播放死亡金屬(嗯,大多數瑜伽課至少是這樣😄)。
每次想起這點,我都覺得超神奇:這些語言就像世界上最有耐性的、最聰明的口譯員坐在你旁邊。你以對你腦子自然的方式表達想法,它們處理把這些轉成電腦真正懂的 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
// 第一步:基本斐波那契設置
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;
}
```
**分解運作過程:**
- **使用** `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
adr r0,thumb+1
bx r0
code16
thumb
mov r0,#00
sub r0,r0,#01
mov r1,#01
mov r4,#10
ldr r2,=0x40000000
back add r0,r1
str r0,[r2]
add r2,#04
mov r3,r0
mov r0,r1
mov r1,r3
sub r4,#01
cmp r4,#00
bne back
end
```
注意 JavaScript 幾乎像英文指令一樣易讀,而組合語言用神秘指令直接控制電腦處理器。兩者完成完全相同的任務,但高階語言對人類來說更容易理解、撰寫和維護。
**你會注意到的主要差異:**
- **可讀性**JavaScript 使用描述性名稱如 `fibonacciCount`,組合語言用難懂標籤如 `r0`、`r1`
- **註解**:高階語言鼓勵使用解釋性的註解,使程式碼本身成為自我說明
- **結構**JavaScript 的邏輯流程與人類逐步思考問題的方式相符合
- **維護**:根據不同需求更新 JavaScript 版本是直觀且清晰的
**關於費波那契數列**這個絕美的數字模式每個數字是前兩項的和0、1、1、2、3、5、8……幾乎在自然界到處可見你會在向日葵螺旋、松果排列、鳳梨貝殼的弧度甚至樹枝的生長方式中看到它。數學與程式碼如何幫助我們理解並重現自然用來創造美的模式實在令人驚嘆
## 讓魔法發生的基本元素
好了,現在你已經見識過程式語言的實際運作,我們來拆解構成每一段程式碼的基本部分。把這些想成你最愛食譜中的關鍵材料──一旦瞭解每個元素的功能,你就能閱讀並撰寫幾乎任何語言的程式碼!
這有點像學習程式設計的文法。還記得學校時學過名詞、動詞以及如何組成句子嗎?程式語言也有自己的文法,老實說,它比英文文法邏輯多也更寬容!😄
### 陳述句:逐步指令
先從 **陳述句** 開始──它們就像與電腦對話中的單句話。每句陳述告訴電腦要做一件具體的事,就像指示:「這裡左轉」、「紅燈停下」、「停在那個車位」。
我喜歡陳述句的是它們通常很易讀。看看這個:
```javascript
// 執行單一操作的基本語句
const userName = "Alex";
console.log("Hello, world!");
const sum = 5 + 3;
```
**這段程式碼做了什麼:**
- **宣告**一個常數變數來存放使用者名稱
- **顯示**歡迎訊息到控制台輸出
- **計算**並儲存一個數學運算結果
```javascript
// 與網頁互動的語句
document.title = "My Awesome Website";
document.body.style.backgroundColor = "lightblue";
```
**一步一步看發生了什麼:**
- **修改**瀏覽器頁籤上顯示的網頁標題
- **變更**整個頁面內容的背景顏色
### 變數:程式的記憶體系統
說實話,**變數** 是我最喜歡教的概念之一,因為它們就像你每天都會用到的東西!
想像一下你的手機聯絡人列表。你不會記得每個人的電話號碼──而是將「媽媽」、「最好的朋友」或「凌晨兩點還送披薩的店」存起來,讓手機記住實際的號碼。變數就是這樣!它們是有標籤的容器,你的程式可以使用有意義的名稱存取資訊。
更酷的是:變數會隨著程式運行而改變(所以叫「變數」──你懂的吧?)。就像發現更好的披薩店時會更新聯絡資訊一樣,變數能隨著程式得到新資訊或情況改變而更新!
讓我示範這有多簡單又美妙:
```javascript
// 第一步:建立基本變量
const siteName = "Weather Dashboard";
let currentWeather = "sunny";
let temperature = 75;
let isRaining = false;
```
**理解這些概念:**
- **存放**不變的數值到 `const` 變數(比如網站名稱)
- **使用** `let` 定義可變動的數值
- **賦予**不同的資料型態:字串(文字)、數字及布林值(真/假)
- **選擇**描述性的名稱說明變數內容
```javascript
// 第 2 步:使用物件來群組相關資料
const weatherData = {
location: "San Francisco",
humidity: 65,
windSpeed: 12
};
```
**上面我們:**
- **建立**一個物件來群組相關的天氣資訊
- **整理**多筆資料放在同一個變數名稱下
- **用**鍵值對清楚標示每筆資訊
```javascript
// 第3步使用及更新變數
console.log(`${siteName}: Today is ${currentWeather} and ${temperature}°F`);
console.log(`Wind speed: ${weatherData.windSpeed} mph`);
// 更新可變變數
currentWeather = "cloudy";
temperature = 68;
```
**理解每個部分:**
- **顯示**內容使用帶有 `${}` 語法的模板字串
- **使用**點記法 (`weatherData.windSpeed`) 取物件屬性
- **更新**使用 `let` 宣告的變數以反映變化
- **結合**多個變數創造有意義的訊息
```javascript
// 第4步使用現代解構賦值以使代碼更清晰
const { location, humidity } = weatherData;
console.log(`${location} humidity: ${humidity}%`);
```
**你需要知道的:**
- **從物件中解構賦值以擷取特定屬性**
- **創建與物件鍵相同名稱的新變數**
- **簡化程式碼,避免反覆使用點記法**
### 控制流程:教你的程式思考
好了,這就是程式設計讓人驚嘆的地方!**控制流程** 就是教你的程式如何做出智慧決策,就像你每天不假思索地做的一樣。
想像一下:今天早上你可能心裡想「如果下雨,我就帶傘;如果很冷,我會穿外套;如果我遲到了,我會跳過早餐順路買杯咖啡。」你的大腦天然遵循這種如果──那麼的邏輯,天天做好幾十次!
這就是為什麼程式感覺聰明又活潑,而不只是乏味、可預測的腳本。它們真能看情況、評估狀況,並適當回應。就像給你的程式一顆能適應並做決定的大腦!
想看看這個邏輯怎麼精采運作?讓我示範:
```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
```
### 程式碼編輯器與整合開發環境:你的新數位好夥伴
談談程式碼編輯器──這可真快成為你最愛聚集的地方!把它想成你的程式碼聖地,你會花最多時間在這裡編寫和優化數位創作。
而且現代編輯器超魔法──它們不只是花俏的文本編輯器。它們就像全天候坐在你旁邊、無比聰明而且支持你寫程式的導師。它們會在你發現錯字之前找到它們,建議改善讓你看起來更厲害,還幫你理解每段程式碼在做什麼,其中一些甚至能預測你下一步打什麼與你的想法!
我還記得第一次發現自動完成功能──感覺彷彿生活在未來。你開始打字,編輯器就跳出來說:「诶,你是不是想用這個,剛好能幫你完成需要的功能?」就像有個心靈感應的程式碼夥伴!
**令這些編輯器不可思議的原因?**
現代程式碼編輯器提供一系列功能,設計來提升你的生產力:
| 功能 | 功能說明 | 為何有用 |
|---------|--------------|--------------|
| **語法高亮** | 為程式碼不同部分著色 | 讓程式碼更易讀與找錯 |
| **自動完成** | 打字時推薦程式碼 | 加快寫程式速度並減少錯字 |
| **除錯工具** | 幫你找到並修正錯誤 | 節省無數除錯時間 |
| **擴充功能** | 新增專業化能力 | 讓你能依技術需求客製編輯器 |
| **AI 助手** | 建議程式碼與解說 | 加速學習與工作效率 |
> 🎥 **影片資源**:想實際看看這些工具的運作?請觀看這部 [Tools of the Trade video](https://youtube.com/watch?v=69WJeXGBdxg) 獲得完整介紹。
#### 推薦給網頁開發者的編輯器
**[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) - 抓出程式碼錯字
**[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──它免費行業使用率高社群及教學資源豐富。
### 網頁瀏覽器:你的秘密開發實驗室
準備好徹底被震撼了嗎!你知道一直以來用瀏覽器滑社交媒體和看影片,其實它們藏著一個巨大的秘密開發實驗室?
每次你在網頁上點右鍵選「檢查」元素,就是在開啟一個隱藏的開發者工具世界──它們強大到比我以前花大錢買的某些軟體還厲害。就像發現你平凡廚房後面藏著一座專業大廚實驗室的秘密通道!
第一次有人向我展示瀏覽器 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。列出三個幫助你更有效率寫代碼、除錯或維護代碼嘅功能或擴展。對每個功能提供簡短說明講解佢點樣增強你嘅工作效率。
---
## 🚀 挑戰
**好啦,偵探,你準備好攪第一宗案未?**
依家你有咗呢個超棒嘅基礎,我會畀你一個冒險,幫你見識到編程世界嘅多元同精采。聽好──呢個唔係寫代碼嘅挑戰,所以唔使擔心!想像自己係編程語言嘅偵探,展開你第一單振奮人心嘅案件!
**你嘅任務,如果你願意接受:**
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 看一些適合初學者的程式教學影片。那裡有很多優秀的創作者,懂得新手的感受。
- 思考加入本地的見面會或線上社群。相信我,開發者們都很樂意幫助新手!
> 🎯 **聽著,請記得這點**:你不需要一夜之間成為程式高手!現在你只是開始認識這個你即將成為一份子的精彩新世界。慢慢來,享受這段旅程,並記得——每一位你敬佩的開發者,曾經都坐在你現在的位置上,感到興奮並可能有些不知所措。這非常正常,這表示你做得很對!
## 作業
[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 -->
**免責聲明**
本文件由 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於翻譯準確性,但請注意自動翻譯可能包含錯誤或不準確之處。原始文件的原文版本應視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們不對因使用本翻譯而引起的任何誤解或誤譯負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -0,0 +1,69 @@
# Assignment: Exploring Modern Web Development Tools
## Instructions
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.
**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) 進行翻譯。雖然我們力求準確,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的本地語言版本應被視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用本翻譯而引起的任何誤解或誤讀概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -0,0 +1,768 @@
# GitHub 簡介
嗨,未來的開發者!👋 準備好加入全球數百萬程式員的行列了嗎?我非常興奮地向你介紹 GitHub——把它想像成程式員的社交媒體平台不同的是我們分享的不是午餐照而是程式碼一起打造令人驚嘆的作品
這點真的讓我震撼不已:你手機上的每個應用程式、你瀏覽的每個網站,以及你將學習使用的大多數工具,都是由團隊開發者在像 GitHub 這樣的平台上共同協作完成的。你喜愛的音樂應用程式?可能就有人跟你一樣貢獻了程式碼。那款玩不膩的遊戲?沒錯,可能就是用 GitHub 協作建造出來的。現在,你也將學會如何成為這個精彩社群的一員!
我知道剛開始可能會覺得很多——說真的,我還記得第一次看到 GitHub 頁面時想:「這到底是什麼意思?」但事實是:每個開發者都是從你現在的狀態開始的。結束這堂課時,你會有自己的 GitHub 倉庫(把它想像成你個人在雲端的專案展示台),你會知道如何儲存工作、與他人分享,甚至參與數百萬人使用的專案。
我們會一步步一起走這趟旅程,沒有急躁,沒有壓力——只有你我和一些超酷的工具,它們將成為你新的好朋友!
![Intro to GitHub](../../../../translated_images/zh-HK/webdev101-github.8846d7971abef6f9.webp)
> 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 的魔法吧!想像這就像在創作傑作之前整理藝術工具——有了合適的工具,一切都會變得更順暢、更有趣。
我會親自引導你完成每個設定步驟,我保證並沒有看起來那麼可怕。如果一開始有些地方不太懂,那完全正常!我還記得設定第一個開發環境時,感覺像在解讀古埃及象形文字。每個開發者都曾經跟你現在一樣,不知道自己有沒有做對。劇透一下:你正在學習,已經做對了!🌟
本課將涵蓋:
- 追蹤你在電腦上的工作
- 與他人合作專案
- 如何貢獻開源軟體
### 先決條件
先讓你的電腦準備好 GitHub 的魔法吧!別擔心--這個設定只需做一次,之後你的程式之旅都將順利進行。
好了,讓我們從基礎開始!首先,我們需要確認你的電腦上是否已經安裝了 Git。Git 基本上就像一個超聰明的助手,記錄你對程式碼的每個變更,比起每兩秒猛按 Ctrl+S我們都做過好太多了
在終端機輸入這行魔法指令,看看你的電腦是否有安裝 Git
`git --version`
如果還沒安裝,別擔心!到 [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 是否已設好,可以輸入:
```bash
git config --list
```
你還需要有 GitHub 帳號、程式碼編輯器(如 Visual Studio Code還有打開終端機或命令提示字元
造訪 [github.com](https://github.com/),還沒帳號就註冊,或登入並完善個人資料。
💡 **現代提示**:考慮設定 [SSH 金鑰](https://docs.github.com/en/authentication/connecting-to-github-with-ssh) 或使用 [GitHub CLI](https://cli.github.com/) 來無密碼更輕鬆認證。
✅ GitHub 並非唯一的程式碼倉庫,市場上還有其他選擇,但 GitHub 最出名。
### 準備工作
你需要在本機(筆電或電腦)準備一個有程式碼的資料夾,還有 GitHub 上一個公開倉庫,作為示範如何貢獻他人專案的例子。
### 保護你的程式碼安全
說點安全性的事情——別擔心,不會嚇到你!把安全習慣想成鎖車或鎖房門。這些簡單習慣會成為你的第二天性,保護你辛苦的作品。
我們會從一開始就展示如何用現代且安全的方式與 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 and GitHub basics video](https://img.youtube.com/vi/9R31OUPpxU4/0.jpg)](https://www.youtube.com/watch?v=9R31OUPpxU4)
**一起動手做:**
1. **在 GitHub 建立你的倉庫**。前往 GitHub.com找那個明亮的綠色 **New** 按鈕(或右上角的 **+** 號),點擊並選擇 **New repository**
以下是操作步驟:
1. 幫你的倉庫取個名字──取一個對你有意義的!
1. 可以加個描述(讓別人知道專案是什麼)
1. 決定是公開(大家都能看到)還是私密(只限你)
1. 建議勾選新增 README 檔案——這是你專案的封面頁
1. 點擊 **Create repository**,慶祝一下-你剛創建了第一個倉庫!🎉
2. **進入你的專案資料夾**。打開終端機(別怕,真的沒那麼可怕!)。我們要指示電腦專案檔案位置。輸入以下指令:
```bash
cd [name of your folder]
```
**我們這樣做是因為:**
- 其實是在告訴電腦「嘿,帶我到專案資料夾」
- 這就像在桌面打開指定資料夾,但用文字指令執行
- 把 `[name of your folder]` 換成你專案資料夾的真實名稱
3. **將資料夾變成 Git 倉庫**。魔法就在此發生!打:
```bash
git init
```
**這步驟的意義(很酷吧!):**
- 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 restore <file>..." to discard changes in working directory)
modified: file.txt
modified: file2.txt
```
**別慌!這是什麼意思:**
- 紅色的檔案代表有變更但還沒準備好要儲存
- 綠色的檔案(如果有看到)代表已準備好儲存
- Git 很貼心,告訴你下一步該怎麼做
> 💡 **專業提示**`git status` 是你最好的朋友!任何時候搞不懂狀況就用它。它就像問 Git 「嘿,情況怎樣?」
5. **準備好要儲存的檔案**(稱為「暫存」):
```bash
git add .
```
**我們剛做了什麼:**
- 告訴 Git 「我想把這資料夾裡的所有檔案都加入下一次儲存」
- `.` 就像說「資料夾裡所有的東西」
- 現在檔案都「暫存」了,準備下一步
**想挑選性加入?** 你也可以只加入特定檔案:
```bash
git add [file or folder name]
```
**為什麼會想這樣做?**
- 有時候你想一併保存相關修改
- 幫助你把工作化整為零
- 更容易理解什麼時候改了什麼
**改主意了?** 沒問題!可以這樣取消暫存:
```bash
# 取消暫存所有項目
git reset
# 只取消暫存一個檔案
git reset [file name]
```
別擔心-這不會刪除你的檔案,只是把檔案從「準備儲存」的列表移除。
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」。未來的你會感謝現在用心的你
7. **連接本機專案到 GitHub**。目前專案只在你電腦裡,我們要連到 GitHub 倉庫,才能分享給全世界!
先去你的 GitHub 倉庫頁面,複製 URL 回來,然後打:
```bash
git remote add origin https://github.com/username/repository_name.git
```
(把這個 URL 換成你實際的倉庫網址!)
**我們做了什麼:**
- 我們已建立你本地專案和你的 GitHub 倉庫之間的連接
- 「origin」只是你 GitHub 倉庫的暱稱——就像把聯絡人加到你的電話裡一樣
- 現在本地的 Git 知道該把你的程式碼傳送到哪裡,當你準備好分享時
💡 **更簡易的方法**:如果你已安裝 GitHub CLI可以用一條指令完成這些動作
```bash
gh repo create my-repo --public --push --source=.
```
8. **將你的程式碼傳送到 GitHub**(重要時刻!):
```bash
git push -u origin main
```
**🚀 就是現在!你要將程式碼上傳到 GitHub 了!**
**發生了什麼:**
- 你的提交從電腦傳到 GitHub
- `-u` 旗標建立永久連接,未來推送更方便
- 「main」是你的主要分支名稱就像主資料夾
- 之後你只要打 `git push` 就能上傳!
💡 **小提示**如果你的分支叫別的名字例如「master」請用那個名稱。可用 `git branch --show-current` 確認。
9. **你的全新日常編程節奏**(這會讓你上癮!):
從此以後,當你對專案作出改動時,有一個簡單的三步舞:
```bash
git add .
git commit -m "describe what you changed"
git push
```
**這會成為你的編程節奏:**
- 作出很棒的程式碼改動 ✨
- 用 `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**:經常、用描述性訊息提交,避免一次大量、少次提交
#### Commit 訊息
一個優秀的 Git commit 主旨句應完成以下句子:
If applied, this commit will <你的主旨句>
主旨用命令式、現在時用「change」非「changed」或「changes」。
正文(可選)也用命令式、現在時,應說明為何改動並和之前行為對比。是解釋「為什麼」不是「怎麼做」。
✅ 花幾分鐘逛逛 GitHub。能找到非常好的 commit 訊息嗎?或非常簡短的訊息?你覺得 commit 訊息中最重要且有用的資訊是什麼?
## 與他人合作(有趣的部分!)
戴好帽子,因為這正是 GitHub 魔法開始的地方!🪄 你已掌握管理自己程式碼,但現在我們要進入我最愛的部分——與來自世界各地的優秀開發者合作。
想像一下:明天醒來時,你發現有人在東京幫你改進了程式碼;有人在柏林修復你卡住的 bug下午時聖保羅的一位開發者加入了你從未想過的功能。這不是科幻——在 GitHub 世界裡,這就是日常!
讓我最興奮的是,你即將學到的協作技能,就是 Google、微軟以及你喜歡的初創公司每天使用的 EXACT 工作流程。你不只是在學一個酷工具——你在學讓全球軟體世界協同運作的祕密語言。
說真的,一旦你體驗到有人合併你的第一個 Pull Request 的快感,就會懂為什麼開發者對開源充滿熱情。就像參與全球最大、最具創意的團隊專案!
> 參考影片
>
> [![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 功能:**
🤖 **自動化與 CI/CD**
- **GitHub Actions** 自動化測試與部署
- **Dependabot** 依賴自動更新
💬 **社群與專案管理:**
- **GitHub Discussions** 問題之外的社群討論空間
- **GitHub Projects** 看板式專案管理
- **分支保護規則** 強制程式碼品質標準
這些資源能幫助新團隊成員加入時更快上手。通常新貢獻者在看程式碼前會先看這些,判斷專案是否值得他們花時間。
✅ README 檔雖然準備起來花時間,但常被忙碌的維護者忽略。能找到特別有描述性的範例嗎?備註:有一些[工具幫助創建優質 README](https://www.makeareadme.com/)值得嘗試。
### 任務:合併一些程式碼
貢獻文件幫助他人參與專案。說明你找什麼樣的貢獻及流程如何。貢獻者需經過一系列步驟,才能在 GitHub 貢獻給你的倉庫:
1. **Fork 你的倉庫** 大概你會希望大家先對你的專案 _fork_。Fork 是建立你倉庫的複製品在他們的 GitHub 帳號上。
1. **Clone**。接著他們會 clone 專案到本地機器。
1. **建立分支**。你會想請他們為他們的工作建立一個 _branch_
1. **專注於一件事**。請貢獻者一次專注於一個改動內容——這樣你合併他們工作的機率比較高。想像他們修 bug、新增功能、更新多項測試——如果你只能接受其中兩項或一項怎麼辦
✅ 想像在寫和交付優質程式碼時,分支扮演關鍵角色。你能想到哪些應用場景?
> 註:成為你想看到的改變,也建立分支管理自己的工作吧。你所做的 commit 預設是在你當前「checkout」的分支上。用 `git status` 查看你在哪個分支。
讓我們來看一下貢獻者的工作流程。假設貢獻者已經 _fork__clone_ 好 repo且本地已有 Git 環境:
1. **建立分支**。用命令 `git branch` 建立一個準備提交改動的分支:
```bash
git branch [branch-name]
```
> 💡 **現代做法**:你也可以用一條命令同時建立並切換分支:
```bash
git switch -c [branch-name]
```
1. **切換到工作分支**。用 `git switch` 切換到剛建立的分支並更新工作目錄:
```bash
git switch [branch-name]
```
> 💡 **現代提示**`git switch` 是改用於切分支的更清晰替代 `git checkout`,對初學者更安全。
1. **開始工作**。此時你要新增改動。別忘告訴 Git命令如下
```bash
git add .
git commit -m "my changes"
```
> ⚠️ **提交訊息品質**:務必為 commit 取個好名字,無論是為自己還是你協助的 repo 維護者。具體說明你改了什麼!
1. **合併你的工作到 `main` 分支**。完成工作後,想結合你的工作和 `main` 分支的內容。`main` 可能已有更新,先用以下命令拉最新版本:
```bash
git switch main
git pull
```
現在你要確保任何 _衝突_,即 Git 不知如何 _合併_ 的情況,都處理在你的工作分支。執行:
```bash
git switch [branch_name]
git merge main
```
`git merge main` 會把 `main` 最新變更帶入你的分支。希望能直接繼續。如果不行VS Code 會告訴你 Git 混淆在哪裡,並讓你修改檔案指定正確版本。
💡 **現代替代法**:考慮用 `git rebase` 取得更乾淨的歷史:
```bash
git rebase main
```
這會把你的提交依序放到最新的 main 上,形成線性歷史。
1. **推送你的工作到 GitHub**。推送工作到 GitHub 具包含兩件事。先把分支推送到你的遠端 fork然後開 Pull Request。
```bash
git push --set-upstream origin [branch-name]
```
上述指令會在你的 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"
```
🔧 **PR 最佳實踐**
- 用關鍵字連結相關 issue如 "Fixes #123"
- UI 改動補充截圖
- 指定特定審查者
- 進行中可用草稿 PR
- 發出審查請求前確保所有 CI 通過
1. **清理**。成功合併 PR 後,進行 _清理_ 是良好做法。你需要清理本地分支和推送到 GitHub 的分支。首先,使用以下命令在本地刪除它:
```bash
git branch -d [branch-name]
```
接著請到該 fork 的 GitHub 頁面,刪除你剛剛推送過去的遠端分支。
`Pull request`拉取請求聽起來怪怪的因為你真正想做的是把變更推送到專案裡。但維護者專案擁有者或核心團隊需要先審核你的變更才會將它合併進專案的「main」分支所以你其實是在請求維護者做變更決定。
拉取請求是用來比較和討論分支所引入差異的地方,可以包含審核、評論、整合測試等等。一個好的拉取請求大致遵循和提交說明相同的規則。如果你的工作是修復某個問題,可以加上 issue 的參考,格式是 `#` 後面接問題號碼。例如 `#97`
🤞希望所有檢查通過,且專案擁有者能將你的變更合併進專案🤞
用以下指令更新你當前的本地工作分支,將 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。你需要將它的內容複製到你的電腦。
✅ 找到「新手友好」的 repo 一個好方法是[使用 'good-first-issue' 標籤搜尋](https://github.blog/2020-01-22-browse-good-first-issues-to-start-contributing-to-open-source/)。
![Copy a repo locally](../../../../translated_images/zh-HK/clone_repo.5085c48d666ead57.webp)
複製代碼有好幾種方式。最常見的是使用 HTTPS、SSH 或 GitHub CLI命令列工具「克隆」該儲存庫內容。
打開你的終端機並這樣克隆儲存庫:
```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`
你也可以開啟整個專案:
- **[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 的更多有趣事
你可以對任何公開 repo 加星標、訂閱觀察和/或「fork」它們。加星的 repo 可以在右上角的下拉選單找到,這就像書籤,但用於程式碼。
專案有譯題追蹤器,通常是在 GitHub 的「Issues」標籤頁除非另有說明用來討論專案相關問題。拉取請求標籤是用來討論和審查正在進行的改動。
有些專案還會在論壇、郵件列表或聊天頻道(像 Slack、Discord 或 IRC進行討論。
🔧 **現代 GitHub 功能**
- **GitHub Discussions** - 內建的社群討論區
- **GitHub Sponsors** - 財務支持維護者
- **Security tab** - 漏洞報告與安全公告
- **Actions tab** - 查看自動化工作流程及 CI/CD 管線
- **Insights tab** - 關於貢獻者、提交和專案健康度的分析
- **Projects tab** - GitHub 內建的專案管理工具
✅ 四處逛逛你的新 GitHub repo試試修改設定、添加資訊、建立專案如看板、設定 GitHub Actions 來自動化。你能做的事非常多!
---
## 🚀 挑戰
好了,現在是測試你閃亮新 GitHub 超能力的時刻了!🚀 這項挑戰會讓一切以最令人滿足的方式連結起來:
找一個朋友(或那位一直問你在做什麼「電腦東西」的家人)一起展開協同編程冒險吧!這就是魔法所在 —— 建立一個專案,讓他們 fork建立一些分支像專業人士一樣合併變更。
說實話 —— 你們絕對會笑翻(尤其是當你們試圖同時更改同一行),可能還會被搞得抓頭困惑,但你一定會體驗到那些令人驚豔的「啊哈!」時刻,讓所有學習都變得值得。此外,與人分享第一次成功合併有著特別的意義 —— 它像是在慶祝你已走多遠!
還沒有編程夥伴完全沒問題GitHub 社群熱情友善他們都記得當初自己也是新手。找標記「good first issue」的儲存庫 —— 這基本上是在說「嘿,新手,來和我們一起學吧!」多棒啊!
## 課後小測
[Post-lecture quiz](https://ff-quizzes.netlify.app/web/en/)
## 複習並持續學習
呼!🎉 看你多棒 —— 你剛剛像冠軍一樣征服了 GitHub 基礎!如果你現在覺得腦子有點爆炸,那是很正常,說實話這也是好事。你剛剛學到的工具,是我當初花了好幾週才習慣的。
Git 和 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 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 -->
**免責聲明**
本文件由 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們雖力求準確,但請注意自動翻譯可能存在錯誤或不準確之處。原始文件的母語版本應視為權威來源。對於重要資訊,建議採用專業人工翻譯。因使用本翻譯所產生之任何誤解或誤讀,我們概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -0,0 +1,252 @@
# 全面網站無障礙測試報告
## 指引
在此作業中,你將運用所學原則與技術,對一個真實網站進行專業級的無障礙測試。這個實作經驗將加深你對無障礙障礙及解決方案的理解。
選擇一個看似存在無障礙問題的網站──這比分析已完美無瑕的網站更有學習價值。合適的對象包括較舊的網站、複雜的網頁應用程式或包含豐富多媒體內容的網站。
### 階段一:策略性人工評估
在使用自動化工具之前,先進行全面的人工評估。此以使用者為中心的方法常能揭露工具遺漏的問題,並幫助你理解真實使用者體驗。
**🔍 重要評估標準:**
**導覽與結構:**
- 能否僅使用鍵盤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 -->
**免責聲明**
本文件是使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們力求準確,但請注意自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要資訊,建議尋求專業人工翻譯。本公司不對因使用本翻譯而產生的任何誤解或錯誤詮釋承擔責任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -0,0 +1,22 @@
# 網頁開發入門
在本課程的這部分,你將學習一些非專案基礎的重要概念,這些概念對成為專業開發者至關重要。
### 主題
1. [程式語言與工具入門](1-intro-to-programming-languages/README.md)
2. [GitHub 基礎介紹](2-github-basics/README.md)
3. [無障礙設計基礎](3-accessibility/README.md)
### 致謝
程式語言與工具入門由 [Jasmine Greenaway](https://twitter.com/paladique) 用 ♥️ 撰寫
GitHub 基礎介紹由 [Floor Drees](https://twitter.com/floordrees) 用 ♥️ 撰寫
無障礙設計基礎由 [Christopher Harrison](https://twitter.com/geektrainer) 用 ♥️ 撰寫
---
**免責聲明**
本文件已使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,請注意自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。

File diff suppressed because it is too large Load Diff

@ -0,0 +1,609 @@
# JavaScript 基礎:資料類型
![JavaScript Basics - Data types](../../../../translated_images/zh-HK/webdev101-js-datatypes.4cc470179730702c.webp)
> 筆記作者:[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 其他部分變得更加清晰。正如建築師在建造大教堂之前需要了解不同的建材,這些基礎會支撐你日後所建構的所有東西。
## 課前測驗
[課前測驗](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) 上學習這課!
[![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` 表示可變變數,或 `const` 表示不變值。
- **變數名稱**,這是你自行選擇的描述性名稱。
`let` 是 ES6 引入的關鍵字,賦予你的變數所謂的 _區塊作用域_。建議你用 `let``const` 取代舊的 `var` 關鍵字。我們將在後續章節更深入講解區塊作用域。
### 任務 - 使用變數
1. **宣告變數**。我們先來建立第一個變數:
```javascript
let myVariable;
```
**這樣做的效果:**
- 這告訴 JavaScript 建立一個名為 `myVariable` 的儲存空間
- JavaScript 在記憶體中分配空間給此變數
- 變數目前沒有值undefined
2. **賦予它一個值**。現在給變數放入一個值:
```javascript
myVariable = 123;
```
**指派的工作原理:**
- `=` 運算子將值 123 指派給變數
- 變數現在包含這個值,不再是 undefined
- 你可以在程式中用 `myVariable` 參考這個值
> 注意:本課中使用的 `=` 是「指派運算子」,用於設定變數值,並非等號。
3. **智慧地合併步驟**。實際上,我們可以將兩步合成一步:
```javascript
let myVariable = 123;
```
**此方式較有效率:**
- 你同時宣告變數並賦值,一行搞定
- 這是開發者的標準作法
- 減少程式碼長度,同時保持清晰易懂
4. **改變值**。若想儲存不同的數字怎麼辦?
```javascript
myVariable = 321;
```
**了解重新指派:**
- 變數現在包含 321取代了之前的 123
- 先前的值被覆蓋——變數一次只存一個值
- 此可變性是用 `let` 宣告變數的主要特性
✅ 試試看!你可以直接在瀏覽器中寫 JavaScript。打開瀏覽器開發者工具進入控制台console輸入 `let myVariable = 123`,按下 Enter再輸入 `myVariable`。會發生什麼事情?後續課程還會學到更多這些概念。
### 🧠 **變數掌握檢測:熟悉度測驗**
**來測試你對變數的理解:**
- 你能解釋宣告和賦值變數的差別嗎?
- 如果你在宣告之前使用變數,會發生什麼?
- 什麼情況下會選擇用 `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; // 不允許
```
**需要記住的是:**
- **嘗試** 重新指派常數會造成錯誤
- **保護** 重要值免於意外變更
- **確保** 值在程式中保持一致
**物件參考被保護** - 以下是不允許的:
```javascript
const obj = { a: 3 };
obj = { b: 5 } // 不允許
```
**了解這些概念:**
- **阻止** 用新物件取代整個物件
- **保護** 原始物件的參考不變
- **維持** 記憶體中物件的一致身份
**物件內容不被保護** - 以下是允許的:
```javascript
const obj = { a: 3 };
obj.a = 5; // 允許
```
**解析發生了什麼:**
- **修改** 物件內部屬性值
- **保留** 相同的物件參考
- **顯示** 物件內容可以更改,而參考保持不變
> 注意,`const` 意味著參考受保護免於重新指派,但該值本身並不 _不可變_,特別是當它是物件等複雜結構時,內容可以更改。
## 資料類型
JavaScript 將資訊組織成不同類別,稱為資料類型。這個概念類似古代學者分類知識的方式——亞里斯多德區分不同的推理類型,知道邏輯原則不能同樣套用於詩歌、數學和自然哲學。
資料類型很重要因為不同的運算需對應不同的資訊類型。就像你不能在人的名字上做數學運算或對數學公式做字母排序一樣JavaScript 也需要適合的資料類型以避免錯誤並提升可靠度。
變數可以存儲許多不同類型的值,比如數字和文字。這些不同類型的值稱為**資料類型**。資料類型在軟體開發中非常重要,幫助開發者決定如何撰寫程式碼和軟體的運作方式。此外,有些資料類型具有獨特的功能,幫助轉換或提取額外資訊。
✅ 資料類型亦稱為 JavaScript 原始資料類型,因為它們是語言提供的最低階資料類型。共有 7 種原始類型string、number、bigint、boolean、undefined、null 與 symbol。花點時間想像這些原始類型各代表什麼。`zebra` 是什麼?`0` 呢?`true` 又是什麼?
### 數字Numbers
數字是 JavaScript 最直觀的資料類型。無論你處理的是整數例如 42、小數例如 3.14,或是負數例如 -5JavaScript 都能統一處理。
還記得前面存入的 123 嗎?那實際上是數字資料類型:
```javascript
let myVariable = 123;
```
**主要特徵:**
- JavaScript 自動辨識數字值
- 可用變數執行數學運算
- 不需明確宣告資料類型
變數可以存所有類型數字,包括小數和負數。數字也能跟數學運算子搭配使用,後面[數學運算子章節](../../../../2-js-basics/1-data-types)會介紹。
```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 所使用的符號。
這些運算子跟傳統數學相同:加號作加法,減號作減法,依此類推。
以下是部分可用運算子:
| 符號 | 說明 | 範例 |
| ------ | ------------------------------------------------------------------- | ------------------------------- |
| `+` | **加法**:計算兩數之和 | `1 + 2 // 預期結果是 3` |
| `-` | **減法**:計算兩數之差 | `1 - 2 // 預期結果是 -1` |
| `*` | **乘法**:計算兩數之積 | `1 * 2 // 預期結果是 2` |
| `/` | **除法**:計算兩數之商 | `1 / 2 // 預期結果是 0.5` |
| `%` | **餘數**:計算兩數相除的餘數 | `1 % 2 // 預期結果是 1` |
✅ 試試看吧!在瀏覽器控制台中嘗試一個數學運算。結果會不會讓你驚訝?
### 🧮 **數學技能檢測:自信計算**
**測試你對算術的理解:**
- `/`(除法)和 `%`(餘數)有什麼差別?
- 你能預測 `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 + "!"; //哈囉世界!
myString1 + " " + myString2 + "!"; //哈囉 世界!
myString1 + ", " + myString2 + "!"; //哈囉,世界!
```
**逐步說明,發生了什麼:**
- **使用** `+` 運算子結合多個字串
- **直接** 將字串連接在一起,第一個範例中不加空格
- **加入** 字串間的空格字元 `" "` 以提升可讀性
- **插入** 標點符號如逗號,讓格式正確
✅ 為什麼在 JavaScript 中 `1 + 1 = 2`,但 `'1' + '1' = 11`?想想看。那 `'1' + 1` 又是什麼呢?
**模板字面量**是格式化字串的另一種方式,不過不是用引號,而是用反引號。任何非純文字的東西必須放入佔位符 `${}` 內,包括可能是字串的變數。
```javascript
let myString1 = "Hello";
let myString2 = "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)會協助設定布林值,你也會經常看到使用運算子來初始化或更新變數值。
```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 行為之一。熟悉這些特性將幫助你寫出更可靠的程式,並更有效地除錯。
## 課後測驗
[課後測驗](https://ff-quizzes.netlify.app)
## 複習與自學
看看[這份 JavaScript 練習清單](https://css-tricks.com/snippets/javascript/),試試其中一個。你學到了什麼?
## 作業
[資料型別練習](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 DISCLAIMER START -->
**免責聲明**
本文件係使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 所翻譯。雖然我們致力於準確性,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們不對因使用此翻譯而產生的任何誤解或誤釋承擔責任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -0,0 +1,94 @@
# Data Types Practice: E-commerce Shopping Cart
## Instructions
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) 翻譯而成。雖然我們力求準確,但請注意自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。如涉及重要資訊,建議使用專業人工翻譯。本公司不就使用此翻譯而產生的任何誤解或誤譯承擔責任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -0,0 +1,574 @@
# JavaScript 基礎:方法與函數
![JavaScript Basics - Functions](../../../../translated_images/zh-HK/webdev101-js-functions.be049c4726e94f8b.webp)
> 繪圖筆記由 [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` 關鍵字告訴 JavaScript「嘿我正在創建一個函數
- `nameOfFunction` 是你為函數取的描述性名稱
- 括號 `()` 可放入參數(稍後會講)
- 花括號 `{}` 裡包含你在呼叫函數時要執行的程式碼
讓我們來寫個簡單的問候函數,看看範例:
```javascript
function displayGreeting() {
console.log('Hello, world!');
}
```
此函數將在控制台列印 "Hello, world!"。定義完成後,你可以重複多次呼叫它。
執行或「呼叫」函數的方法是寫函數名稱加上括號。JavaScript 允許你在呼叫前或後定義函數——它的執行順序會自動處理。
```javascript
// 呼叫我們的函數
displayGreeting();
```
執行此行程式碼,即會執行 `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` 物件的函數。關鍵差異在於方法綁定於物件,而函數是獨立存在。許多開發者在日常對話中常互用這兩個詞。
### 函數最佳實踐
以下是寫出優秀函數的小技巧:
- 給函數取清晰且描述性名稱——未來的自己會感謝你!
- 使用 **camelCase** 命名多字函數(例如 `calculateTotal`,而非 `calculate_total`
- 保持每個函數專注完成一件事
## 傳遞資訊給函數
我們剛剛的 `displayGreeting` 函數很有限——它只能對所有人顯示 "Hello, world!"。參數使函數變得更靈活有用。
**參數** 就像佔位符,每次使用函數時你可填入不同數值。如此一來,同一函數可針對不同資訊運作。
當你定義函數時,將參數列在括號中,多個參數用逗號分隔:
```javascript
function name(param, param2, param3) {
}
```
每個參數就像佔位符,當呼叫函數時會提供實際值,填入這些位置。
讓我們改寫問候函數,接受一個名字參數:
```javascript
function displayGreeting(name) {
const message = `Hello, ${name}!`;
console.log(message);
}
```
注意我們使用反引號(`` ` ``)和 `${}` 直接將 `name` 插入訊息中——這種方式叫做模板字串,是建立帶有變數字串的好工具。
呼叫函數時,我們可以傳入任何名字:
```javascript
displayGreeting('Christopher');
// 執行時顯示「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
```
## 預設值
如果我們想讓參數可選怎麼辦?這時預設值非常有用!
假設我們想讓使用者能自訂問候語,但若沒指定,就使用 "Hello" 這個備用詞彙。你可用等號設定預設值,就像設定變數一樣:
```javascript
function displayGreeting(name, salutation='Hello') {
console.log(`${salutation}, ${name}`);
}
```
這裡 `name` 仍是必填,`salutation` 如果沒給值則默認為 `'Hello'`
現在我們可用兩種方式呼叫這個函數:
```javascript
displayGreeting('Christopher');
// 顯示「Hello, Christopher」
displayGreeting('Christopher', 'Hi');
// 顯示「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
```
> **專家提示**:預設參數讓函數更友善。使用者可以用合理預設值快速上手,仍可依需求自訂!
## 回傳值
到目前為止,我們的函數只在控制台輸出訊息,但如果你想讓函數計算結果並回傳呢?
這就是**回傳值**的用處。函數不只是輸出,也能把值回傳給呼叫它的程式,你可以存進變數或在其他地方使用。
回傳值用 `return` 關鍵字,後面接你想回傳的內容:
```javascript
return myVariable;
```
重要一點:當函數執行到 `return`,會立刻停止並把該值送回呼叫者。
改寫問候函數,讓它回傳訊息而非直接列印:
```javascript
function createGreetingMessage(name) {
const message = `Hello, ${name}`;
return message;
}
```
現在函數不列印,只建立訊息並回傳。
使用回傳值,就像其他值一樣存入變數:
```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`,它是內建函數,會等待一段時間後執行程式碼。我們需要告訴它要執行什麼程式碼——這正是傳入函數的好例子!
試試看這段程式碼——3 秒後會看到訊息:
```javascript
function displayDone() {
console.log('3 seconds has elapsed');
}
// 計時器數值以毫秒為單位
setTimeout(displayDone, 3000);
```
注意我們傳入 `displayDone`(沒有括號)給 `setTimeout`。我們不是自己呼叫函數,而是把函數交給 `setTimeout`告訴它「3 秒後呼叫它。」
### 匿名函數
有時你只需要一個函數做一件事,不想特別命名。想想看——如果只用一次,為什麼要多個名稱搞亂程式碼?
JavaScript 允許你創建 **匿名函數**——沒有名字的函數,直接寫在需要它的地方。
下面是用匿名函數改寫計時例子:
```javascript
setTimeout(function() {
console.log('3 seconds has elapsed');
}, 3000);
```
效果相同,但函數直接定義在 `setTimeout` 裡,省掉了另写一個函數宣告。
### 箭頭函數
現代 JavaScript 有更精簡寫函數的方法,稱為 **箭頭函數**。符號是 `=>`(像個箭頭,你懂的),非常受開發者歡迎。
箭頭函數省略了 `function` 關鍵字,寫起來更簡潔。
這是我們計時例子改用箭頭函數寫法:
```javascript
setTimeout(() => {
console.log('3 seconds has elapsed');
}, 3000);
```
`()` 是放參數的地方(這例子是空的),接著是箭頭 `=>`,最後是用花括號包住的函數內容。兩者功能相同,但語法更簡潔。
```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 挑戰 🚀
使用 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),因為它們越來越多於程式碼中使用。練習編寫一個函數,然後用這語法重寫。
## 作業
[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 DISCLAIMER START -->
**免責聲明**
本文件由 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於確保翻譯的準確性,但請注意自動翻譯可能存在錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要資訊,建議採用專業人工翻譯。我們對因使用本翻譯而產生的任何誤解或誤譯概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -0,0 +1,73 @@
# 函數樂趣
## 指示
在這個作業中,你將練習創建不同類型的函數,以加強你對 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 -->
**免責聲明**
本文件乃使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 所翻譯。儘管我們力求準確,但請注意自動翻譯可能包含錯誤或不準確之處。原始文件之原文版本應被視為權威來源。對於重要資訊,建議採用專業人工翻譯。本公司不對因使用本翻譯所引致的任何誤解或曲解承擔責任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -0,0 +1,663 @@
# JavaScript 基礎:作出決策
![JavaScript Basics - Making decisions](../../../../translated_images/zh-HK/webdev101-js-decisions.69e1b20f272dd1f0.webp)
> 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 程式需要根據不同情況做出選擇。這種分支和決策的能力讓靜態程式碼轉變為回應式且智能的應用。
在這堂課中,你將學習如何在程式中實作條件邏輯。我們將探討條件語句、比較運算符和邏輯表達式,使你的程式能評估情況並作出適當回應。
## 課前測驗
[課前測驗](https://ff-quizzes.netlify.app/web/quiz/11)
能夠做出決策並控制程式流程是程式設計的基本面向。本節涵蓋如何使用布林值與條件邏輯控制 JavaScript 程式的執行路徑。
[![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) 上學習此課程!
```mermaid
mindmap
root((決策制定))
Boolean Logic
true/false
比較結果
邏輯表達式
Conditional Statements
if statements
單一條件
程式碼執行
if-else
兩個路徑
替代動作
switch
多個選項
清晰結構
Operators
Comparison
=== !== < > <= >=
價值關係
Logical
&& || !
結合條件
Advanced Patterns
Ternary
? : 語法
內嵌決策
Complex Logic
嵌套條件
多重標準
```
## 布林值簡短回顧
在探索決策前,讓我們回顧前一課提過的布林值。此值以數學家喬治·布爾命名,代表二元狀態——`true` 或 `false`,無模糊空間,沒有中間值。
這些二元值構成所有計算邏輯的基礎。你程式做出的每個決策最終都會被簡化為布林評估。
建立布林變數相當簡單:
```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` 語句就像在程式碼裡問問題:「如果這個條件成立,就執行這件事。」這應該是你在 JavaScript 中做決策最重要的工具。
它的工作原理如下:
```javascript
if (condition) {
// 條件為真。此區塊內的代碼將會執行。
}
```
條件放在括號內,如果為 `true`JavaScript 就會執行大括號內的程式碼。若為 `false`,則跳過整個區塊。
你通常會使用比較運算子來建立這些條件。來看看一個實例:
```javascript
let currentMoney = 1000;
let laptopPrice = 800;
if (currentMoney >= laptopPrice) {
// 條件為真。此區塊內的代碼將會執行。
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 語句
但是如果你想讓程式在條件為 `false` 時做不同的事呢?這時就用到 `else`——它就像備案。
`else` 語句讓你說:「如果這個條件不成立,改做這件事。」
```javascript
let currentMoney = 500;
let laptopPrice = 800;
if (currentMoney >= laptopPrice) {
// 條件為真。此區塊內的程式碼將會執行。
console.log("Getting a new laptop!");
} else {
// 條件為假。此區塊內的程式碼將會執行。
console.log("Can't afford a new laptop, yet!");
}
```
因為 `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 語句
有時候你需要將一個值與多個選項比較。雖然可以用多個 `if..else` 串接,但寫起來會很難管理。`switch` 語句提供更清晰的結構來處理多個離散值。
這個概念類似早期電話交換機中的機械切換系統——一個輸入值決定執行哪條路徑。
```javascript
switch (expression) {
case x:
// 程式碼區塊
break;
case y:
// 程式碼區塊
break;
default:
// 程式碼區塊
}
```
其結構如下:
- JavaScript 執行表達式一次
- 搜尋每個 `case` 以尋找匹配
- 找到匹配時執行該代碼塊
- `break` 告訴 JavaScript 停止並跳出 switch
- 若沒有匹配,執行 `default` 區塊(若有)
```javascript
// 使用 switch 陳述式處理星期幾的程式
let dayNumber = 2;
let dayName;
switch (dayNumber) {
case 1:
dayName = "Monday";
break;
case 2:
dayName = "Tuesday";
break;
case 3:
dayName = "Wednesday";
break;
default:
dayName = "Unknown day";
break;
}
console.log(`Today is ${dayName}`);
```
在此範例中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 語句掌握:多重選擇**
**測試你對 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
```
## 結合邏輯運算子的條件與決策
我們用一個較實際的範例來看看這些邏輯運算子:
```javascript
let currentMoney = 600;
let laptopPrice = 800;
let laptopDiscountPrice = laptopPrice - (laptopPrice * 0.2); // 筆記型電腦價格打八折
if (currentMoney >= laptopPrice || currentMoney >= laptopDiscountPrice) {
// 條件為真。此區塊內的程式碼會執行。
console.log("Getting a new laptop!");
} else {
// 條件為假。此區塊內的程式碼會執行。
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 就不會被評估。善用此特性!
### 否定運算子
有時候反過來思考更容易。比如不是問「使用者有沒有登入?」而是「使用者沒有登入嗎?」驚嘆號 (`!`) 運算子會幫你反轉邏輯。
```javascript
if (!condition) {
// 當條件為假時執行
} else {
// 當條件為真時執行
}
```
`!` 運算子就像說「相反的…」——如果是 `true`,它變 `false`,反之亦然。
### 三元運算子
對簡單條件賦值JavaScript 提供了 **三元運算子**。這種簡潔語法讓你能在一行寫條件,適合根據條件賦予兩種值之一。
```javascript
let variable = condition ? returnThisIfTrue : returnThisIfFalse;
```
它讀起來像個問題:「這個條件成立嗎?若是,用這個值。否則用那個值。」
下面是一個更具體例子:
```javascript
let firstNumber = 20;
let secondNumber = 10;
let biggestNumber = firstNumber > secondNumber ? firstNumber : secondNumber;
```
✅ 花點時間多讀幾遍這段程式碼。你理解這些運算子是如何運作的嗎?
這行程式碼的意思是:「`firstNumber` 是否大於 `secondNumber`?是的話,`biggestNumber` 等於 `firstNumber`;否則等於 `secondNumber`。」
三元運算子其實是這段傳統 `if..else` 程式碼的簡短寫法:
```javascript
let biggestNumber;
if (firstNumber > secondNumber) {
biggestNumber = firstNumber;
} else {
biggestNumber = 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 以下
需求:
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)。
瀏覽 Josh Comeau 精彩的 [operator lookup](https://joshwcomeau.com/operator-lookup/)
## 作業
[運算子](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) 進行翻譯。雖然我們致力於確保準確性,但請注意自動翻譯可能存在錯誤或不準確之處。原始語言版本的文件應被視為具權威性的資料來源。對於重要資訊,建議尋求專業人工翻譯。我們不對使用本翻譯所引致的任何誤解或誤釋承擔責任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -0,0 +1,111 @@
# Making Decisions: Student Grade Processor
## Learning Objectives
在這個作業中,你將透過建構一個處理不同評分系統中學生成績的程式,練習本課所學的決策製定概念。你將使用 `if...else` 陳述式、比較運算子及邏輯運算子以判斷哪些學生通過了課程。
## The Challenge
你為一所剛與另一機構合併的學校工作。現在你需要處理來自兩個完全不同評分系統的學生成績,並判斷哪些學生是及格的。這是練習條件邏輯的絕佳機會!
### Understanding the Grading Systems
#### First Grading System (Numeric)
- 成績以數字 1-5 表示
- **及格分數**3 及以上3、4 或 5
- **不及格分數**:低於 31 或 2
#### Second Grading System (Letter Grades)
- 成績使用字母:`A`、`A-`、`B`、`B-`、`C`、`C-`
- **及格成績**`A`、`A-`、`B`、`B-`、`C`、`C-`(此系統列出的所有成績均為及格)
- **注意**:此系統不包含像 `D``F` 這類不及格成績
### Your Task
給定以下的陣列 `allStudents` ,表示所有學生及其成績,請建立一個新陣列 `studentsWhoPass` ,其中包含根據各自評分系統判斷及格的所有學生。
```javascript
let allStudents = [
'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 -->
**免責聲明**
本文件由 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們力求準確,請注意自動翻譯可能包含錯誤或不準確之處。原文文件以其母語版本為準。如涉及重要資訊,建議尋求專業人工翻譯。本公司不對因使用本翻譯所引致的任何誤解或曲解承擔責任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -0,0 +1,741 @@
# JavaScript 基礎:陣列與迴圈
![JavaScript Basics - Arrays](../../../../translated_images/zh-HK/webdev101-js-arrays.439d7528b8a29455.webp)
> 筆記作者:[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)
有沒有想過網站如何追蹤購物車的商品或顯示你的好友清單?這就是陣列與迴圈發揮作用的地方。陣列就像數位容器,可以存放多個資訊項目,而迴圈則讓你有效率地處理所有資料,避免重複撰寫程式碼。
這兩個觀念共同構成了處理程式中資訊的基礎。你將學會如何從手動撰寫每一步,轉變為創造聰明且高效率的程式碼,快速處理上百甚至上千個項目。
學完本課程後,你將能了解如何用少量程式碼完成複雜資料處理任務。讓我們來探索這些重要的程式設計概念。
[![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
清單
功能表
圖庫
```
## 陣列
把陣列想像成數位檔案櫃——不像一個抽屜只能放一份文件,你可以在一個結構化的容器中組織多個相關項目。在程式語言中,陣列讓你能把多項資訊集中存放於一個有序的套件。
無論你在建立相片集、管理待辦事項清單,或是追蹤遊戲中的高分,陣列都提供了資料組織的基礎。讓我們看看它們的運作方式。
✅ 陣列就在我們周遭!你能舉出生活中陣列的例子嗎?例如太陽能電池板陣列?
### 建立陣列
建立陣列非常簡單——只要用中括號!
```javascript
// 空陣列 - 就好似一個空購物車等待加入商品
const myArray = [];
```
**這裡發生了什麼?**
你用中括號 `[]` 建立了一個空容器。就像一個空的圖書架,準備好放進你想整理的書本。
你也可以直接在建立的時候填入初始值:
```javascript
// 你的雪糕店口味菜單
const iceCreamFlavors = ["Chocolate", "Strawberry", "Vanilla", "Pistachio", "Rocky Road"];
// 用戶的個人資料資訊(混合不同類型的數據)
const userData = ["John", 25, true, "developer"];
// 你最喜愛課程的考試分數
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
```
### 陣列索引
這點一開始可能會覺得奇怪:陣列裡的項目編號從 0 開始,而非 1。這種從零開始的編號方式源於電腦記憶體的運作原理是電腦語言如 C 語言)早期慣例。陣列中每個位置都有自己的位址編號,稱為**索引**。
| 索引 | 值 | 說明 |
|-------|-------|-------------|
| 0 | "Chocolate" | 第一個元素 |
| 1 | "Strawberry" | 第二個元素 |
| 2 | "Vanilla" | 第三個元素 |
| 3 | "Pistachio" | 第四個元素 |
| 4 | "Rocky Road" | 第五個元素 |
✅ 陣列從零開始編號會讓你感到驚訝嗎?有些程式語言從 1 開始編號。這背後有有趣故事,你可以在[維基百科](https://en.wikipedia.org/wiki/Zero-based_numbering)閱讀了解。
**存取陣列元素:**
```javascript
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";
console.log(iceCreamFlavors[4]); // "牛油胡桃"
// 在結尾新增元素
iceCreamFlavors[5] = "Cookie Dough";
console.log(iceCreamFlavors[5]); // "曲奇麵糰"
```
**上例中,我們:**
- **將**索引 4 處的元素從 "Rocky Road" 改為 "Butter Pecan"
- **新增**了一個索引 5 的元素 "Cookie Dough"
- **新增**元素時,陣列長度會自動擴展
### 陣列長度與常用方法
陣列內建屬性和方法,讓你更輕鬆地操作資料。
**取得陣列長度:**
```javascript
const iceCreamFlavors = ["Chocolate", "Strawberry", "Vanilla", "Pistachio", "Rocky Road"];
console.log(iceCreamFlavors.length); // 5
// 長度會隨陣列變動自動更新
iceCreamFlavors.push("Mint Chip");
console.log(iceCreamFlavors.length); // 6
```
**重點摘要:**
- **回傳**陣列中元素的總數
- **元素增加或刪除時,長度會自動更新**
- **可用於迴圈或驗證元素數量**
**重要的陣列方法:**
```javascript
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
```
> **實際應用觀察**:陣列在程式設計中無處不在!社群動態、購物車、相簿、播放清單歌曲 - 這些背後都是陣列!
## 迴圈
想像狄更斯小說中學生懲罰要在黑板上不停抄寫句子。試想你若能簡單指示「把這句話寫 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` 迴圈就像設定計時器一樣——你確定想執行幾次。它十分有組織且可預期,特別適合操作陣列或計數任務。
**For 迴圈結構:**
| 元件 | 目的 | 範例 |
|-----------|---------|----------|
| **初始化** | 設定起點 | `let i = 0` |
| **條件** | 何時繼續 | `i < 10` |
| **增量** | 如何更新 | `i++` |
```javascript
// 從0數到9
for (let i = 0; i < 10; 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 迴圈
`while` 迴圈就像說「繼續做這件事直到...」——你可能不知道究竟會跑幾次,但知道何時停下。它非常適用向使用者重複要求輸入直到符合條件,或搜尋資料直到找到目標。
**While 迴圈特性:**
- **只要條件為真**就會持續執行
- **需要自行管理**計數器變數
- **條件判斷**在每次迴圈前執行
- **若條件不會成假值,可能造成無限迴圈**
```javascript
// 基本計數範例
let i = 0;
while (i < 10) {
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!");
}
```
**看懂這些範例:**
- **計數器 `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
const iceCreamFlavors = ["Chocolate", "Strawberry", "Vanilla", "Pistachio", "Rocky Road"];
// 傳統 for 迴圈方法
for (let i = 0; i < iceCreamFlavors.length; i++) {
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)。
**你的挑戰:** 使用至少三種不同的陣列方法重構學生成績範例。注意使用現代 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在你自己創建的陣列上。
## 作業
[循環陣列](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 -->
**免責聲明**
本文件由 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 自動翻譯而成。儘管我們盡力確保準確性,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應視為權威來源。對於關鍵資訊,建議採用專業人工翻譯。我們對因使用本翻譯而引起的任何誤解或誤釋概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -0,0 +1,115 @@
# 陣列與迴圈練習
## 指示
完成以下練習以練習使用陣列和迴圈。每個練習以課程中的概念為基礎,並鼓勵你應用不同的迴圈類型和陣列方法。
### 練習 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 -->
**免責聲明**
本文件由 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 所翻譯。儘管我們致力於確保準確性,但請注意,自動翻譯可能存在錯誤或不準確之處。原始文件的母語版本應視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們不對因使用本翻譯而引起的任何誤解或誤釋承擔責任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -0,0 +1,17 @@
# JavaScript 簡介
JavaScript 是網絡的語言。在這四節課中,你將學習它的基礎知識。
### 主題
1. [變數和數據類型](1-data-types/README.md)
2. [函數和方法](2-functions-methods/README.md)
3. [使用 JavaScript 作出決策](3-making-decisions/README.md)
4. [陣列和迴圈](4-arrays-loops/README.md)
### 致謝
這些課程由 [Jasmine Greenaway](https://twitter.com/paladique)、[Christopher Harrison](https://twitter.com/geektrainer) 和 [Chris Noring](https://twitter.com/chris_noring) 用 ♥️ 編寫。
**免責聲明**
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。

@ -0,0 +1,585 @@
# 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/zh-HK/webdev101-html.4389c2067af68e98.webp)
> 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)
> 📺 **觀看學習**:看看這支有用的影片概覽
>
> [![HTML Fundamentals Video](https://img.youtube.com/vi/1TvxJKBzhyQ/0.jpg)](https://www.youtube.com/watch?v=1TvxJKBzhyQ)
## 設定你的專案
在深入 HTML 程式碼之前,讓我們先為你的玻璃花房專案建立一個適當的工作空間。從一開始就建立有條理的檔案結構,是個非常重要的習慣,對你的整個網頁開發旅程都會有幫助。
### 任務:建立你的專案結構
你將為玻璃花房專案建立一個專用資料夾,並加入你的第一個 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`
![VS Code Explorer showing new file creation](../../../../translated_images/zh-HK/vs-code-index.e2986cf919471eb9.webp)
**方案二:使用終端機指令**
```bash
mkdir terrarium
cd terrarium
touch index.html
code index.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 宣告與根元素
任何 HTML 檔案的前兩行,就像是文件對瀏覽器的「介紹」:
```html
<!DOCTYPE html>
<html></html>
```
**這段程式碼的作用是:**
- **宣告** 文件型態為 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>` 根元素包含什麼
- ✅ 這種結構如何幫助瀏覽器正確呈現頁面
**快速自我檢測**:你能用自己的話說明「符合標準的呈現」是什麼意思嗎?
## 新增必要的文件 Metadata
HTML 文件的 `<head>` 部分包含瀏覽器和搜尋引擎需要的重要資訊,但訪客不會直接在頁面上看到。可以把它想成「幕後」資料,幫助你的網頁運作正常,並在不同裝置和平台上正確顯示。
這些 Metadata 告訴瀏覽器如何呈現頁面、要使用哪種字元編碼、以及如何處理不同螢幕尺寸——這些都是打造專業且無障礙網頁的關鍵。
### 任務:新增文件 Head
在你的 `<html>` 開始和結束標籤之間,插入這段 `<head>`
```html
<head>
<title>Welcome to my Virtual Terrarium</title>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
```
**解釋每個元素的作用:**
- **設定** 頁面標題,會顯示在瀏覽器分頁標籤和搜尋結果中
- **指定** 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>` 元素包含所有你網頁中可見的內容——使用者會看到和互動的所有東西。雖然 `<head>` 是給瀏覽器的指令,`<body>` 是實際內容:文字、圖片、按鈕和其他組成使用者介面的元素。
讓我們加上 `body` 結構,並了解 HTML 標籤如何合作,創造有意義的內容。
### 理解 HTML 標籤結構
HTML 使用成對標籤來定義元素。大部分標籤有開始標籤例如 `<p>`,和結束標籤例如 `</p>`,中間夾帶內容:`<p>Hello, world!</p>`。這會建立一個包含「Hello, world!」文字的段落元素。
### 任務:新增 Body 元素
更新你的 HTML 檔案,加入 `<body>` 元素:
```html
<!DOCTYPE html>
<html>
<head>
<title>Welcome to my Virtual Terrarium</title>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body></body>
</html>
```
**完整結構提供:**
- **建立** 基本的 HTML5 文件框架
- **包含** 必要的 Metadata 以供瀏覽器正確呈現
- **創造** 空白的 body 以容納可見內容
- **採用** 現代網頁開發最佳實務
現在,你準備好加入玻璃花房的可見元素了。我們將使用 `<div>` 元素作為容器,組織不同區塊的內容,並使用 `<img>` 元素來顯示植物圖片。
### 操作圖片與版面容器
圖片在 HTML 中是特別的,使用「自我封閉」標籤。與像 `<p></p>` 這類包裹內容的元素不同,`<img>` 標籤本身帶有所需的所有資訊,用屬性如 `src` 指定圖片路徑,`alt` 屬性做無障礙使用說明。
在加入圖片到 HTML 之前,你需要先透過建立 images 資料夾並放入植物圖像,整理專案檔案。
**首先,準備你的圖片:**
1. 在你的玻璃花房專案資料夾中建立一個叫 `images` 的資料夾
2. 從[解答資料夾](../../../../3-terrarium/solution/images)下載植物圖片(共 14 張)
3. 將所有植物圖片複製到你新建立的 `images` 資料夾
### 任務:建立植物顯示版面
現在在你的 `<body></body>` 標籤之間,加入兩欄排列的植物圖片:
```html
<div id="page">
<div id="left-container" class="container">
<div class="plant-holder">
<img class="plant" alt="plant" id="plant1" src="../../../../translated_images/zh-HK/plant1.d87946a2ca70cc43.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant2" src="../../../../translated_images/zh-HK/plant2.8daa1606c9c1ad89.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant3" src="../../../../translated_images/zh-HK/plant3.8b0d484381a2a2a7.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant4" src="../../../../translated_images/zh-HK/plant4.656e16ae1df37be2.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant5" src="../../../../translated_images/zh-HK/plant5.2b41b9355f11ebcc.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant6" src="../../../../translated_images/zh-HK/plant6.3d1827d03b656994.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant7" src="../../../../translated_images/zh-HK/plant7.8152c302ac97f621.png" />
</div>
</div>
<div id="right-container" class="container">
<div class="plant-holder">
<img class="plant" alt="plant" id="plant8" src="../../../../translated_images/zh-HK/plant8.38d6428174ffa850.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant9" src="../../../../translated_images/zh-HK/plant9.f0e38d3327c37fc2.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant10" src="../../../../translated_images/zh-HK/plant10.b159d6d6e985595f.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant11" src="../../../../translated_images/zh-HK/plant11.2a03a1c2ec8ea84e.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant12" src="../../../../translated_images/zh-HK/plant12.60e9b53e538fbaf3.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant13" src="../../../../translated_images/zh-HK/plant13.07a51543c820bcf5.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant14" src="../../../../translated_images/zh-HK/plant14.6e486371ba7d36ba.png" />
</div>
</div>
</div>
```
**一步步來看這段程式碼的作用:**
- **建立** 一個主頁面容器,`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
```
這個作法讓你網站對有障礙的使用者更友善,也幫助搜尋引擎更好理解你的內容。這是現代網頁開發的基本原則,為所有人創造更佳體驗。
### 新增語義化頁面標題
讓我們為玻璃花房頁面加上一個適當標題。將此行插入在你的開頭 `<body>` 標籤後:
```html
<h1>My Terrarium</h1>
```
**為何語義化標記很重要:**
- **幫助** 螢幕閱讀器使用者導航和理解頁面結構
- **提升** 搜尋引擎排名SEO明確內容階層
- **加強** 視障或認知障礙使用者的無障礙體驗
- **創造** 跨所有裝置與平台更佳的使用者體驗
- **遵循** 網頁標準和專業開發最佳實務
**語義化與非語義化選擇範例:**
| 目的 | ✅ 語義化選擇 | ❌ 非語義化選擇 |
|---------|----------------------------|-------------------------------|
| 主標題 | `<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 樣式,這些元素暫時不會可見。
玻璃花房標記使用描述性的 class 名稱,方便下一課的 CSS 樣式設計既直覺又易維護。
### 任務:新增玻璃花房結構
將此標記插入在最後一個 `</div>` 標籤之前(即頁面容器關閉標籤前):
```html
<div id="terrarium">
<div class="jar-top"></div>
<div class="jar-walls">
<div class="jar-glossy-long"></div>
<div class="jar-glossy-short"></div>
</div>
<div class="dirt"></div>
<div class="jar-bottom"></div>
</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 歷史挑戰
**學習網頁演變**
自 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 工具包總結
完成本課程後,你已具備:
- **文件結構**:完整的 HTML5 基礎和正確 DOCTYPE
- **語意標記**:增強無障礙與 SEO 的意義明確標籤
- **圖片整合**:妥善組織文件與替代文字使用
- **版面容器**:策略性利用帶描述性類名的 div
- **無障礙意識**:理解螢幕閱讀器導航原理
- **現代標準**:掌握目前 HTML5 實務與被淘汰標籤知識
- **專案基礎**:為 CSS 樣式與 JavaScript 互動打下堅實基礎
**下一步**:你的 HTML 結構已準備好進行 CSS 樣式設計!你建立的語意基礎會讓下一課更容易理解。
## 作業
[練習你的 HTML建立一個部落格範本](assignment.md)
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件是使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們盡力確保準確性,但請注意自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要資料,建議採用專業人工翻譯。我們對因使用本翻譯所引致的任何誤解或誤釋概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -0,0 +1,145 @@
# HTML 練習作業:建立博客模型
## 學習目標
透過設計和編寫完整的博客首頁結構,應用你的 HTML 知識。這個實作作業將加強語意化 HTML 概念、無障礙最佳實踐以及專業的程式碼組織技能,這些都是你在整個網頁開發旅程中會使用到的。
**完成此作業後,你將能夠:**
- 練習在編碼前規劃網站佈局
- 適當應用語意化的 HTML 元素
- 創建無障礙且結構良好的標記
- 培養帶有註解和組織的專業編碼習慣
## 專案需求
### 第 1 部分:設計規劃(視覺模型)
**創建你的博客首頁視覺模型,內容包括:**
- 含網站標題和導覽的頁首
- 主要內容區域,至少有 2-3 篇博客文章預覽
- 側邊欄,包含額外資訊(關於區塊、最新文章、分類)
- 含聯絡資訊或連結的頁尾
**模型創建方式選項:**
- **手繪草圖**:使用紙張和鉛筆,然後拍照或掃描你的設計
- **數位工具**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 語法(可在瀏覽器測試)
- [ ] 回答指示問題的反思文字
## 評分標準
| 評量標準 | 優秀 (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 結構與語意。無需擔心視覺樣式——那是 CSS 的工作!你的頁面可能看起來樸素,但應該結構良好且有意義。
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件乃使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 所翻譯。儘管我們力求準確,請注意自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要資訊,建議採用專業人工翻譯。對於因使用本翻譯而引起的任何誤解或錯誤詮釋,我們概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -0,0 +1,726 @@
# 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/zh-HK/webdev101-css.3f7af5991bf53a20.webp)
> 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)
## CSS 基本入門
很多人認為 CSS 只是「讓東西漂亮」但它的用途遠不止於此。CSS 就像電影導演—不只控制外觀,還能決定移動方式、互動反應,以及如何適應不同情況。
現代 CSS 非常強大。你能撰寫自動調整手機、平板和桌面電腦布局的程式碼。還可以創造引導使用者注意力的流暢動畫。當一切協同運作時,效果令人驚豔。
> 💡 **專業提示**CSS 持續進化,加入新功能與能力。使用新 CSS 功能前,務必查看 [CanIUse.com](https://caniuse.com) 以確認瀏覽器支援度。
**本課程目標:**
- **用現代 CSS 技術** 創造完整的 terrarium 視覺設計
- **探索** 基本概念,例如層疊、繼承和 CSS 選擇器
- **實作** 響應式定位與佈局策略
- **建構** terrarium 容器,運用 CSS 形狀與樣式
### 先備條件
你應該已完成上堂課的 terrarium HTML 結構,並準備好進行樣式設定。
> 📺 **教學影片**:看看這個實用的影片導覽
>
> [![CSS Basics Tutorial](https://img.youtube.com/vi/6yIdOIV9p1I/0.jpg)](https://www.youtube.com/watch?v=6yIdOIV9p1I)
### 設定你的 CSS 檔案
開始設定樣式前,需將 CSS 連結到 HTML。這個連結告訴瀏覽器在哪裏尋找 terrarium 的樣式說明。
在 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>` 標籤加入內嵌樣式:
```html
<h1 style="color: red">My Terrarium</h1>
```
**此段程式碼功能說明:**
- **直接給予** `<h1>` 紅色文字,使用內嵌樣式
- **利用** `style` 屬性在 HTML 內嵌 CSS
- **創造** 最高優先權的樣式規則於該元素
接著在 `style.css` 加入這條規則:
```css
h1 {
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>` 的字型,來實作繼承效果:
```css
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
```
**說明:**
- **設定** 全頁字型,針對 `<body>` 元素
- **使用** 多種字型備援沾功能,增強瀏覽器的兼容性
- **採用** 現代系統字型,跨作業系統呈現佳
- **確保** 除非特別覆寫,所有子元素繼承此字型
打開瀏覽器開發者工具F12切換至 Elements檢查 `<h1>`,可看到它繼承 body 的字型:
![inherited font](../../../../translated_images/zh-HK/1.cc07a5cbe114ad1d.webp)
**實驗時間**:試著在 `<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 有多種精確程度,選擇正確的選擇器就像挑選合適工具。有時你想給整區所有門塗色,有時只要針對一扇門。
### 元素選擇器(標籤)
元素選擇器根據標籤名稱標的 HTML 元素,適合設定廣泛適用的基礎樣式:
```css
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 0;
padding: 0;
}
h1 {
color: #3a241d;
text-align: center;
font-size: 2.5rem;
margin-bottom: 1rem;
}
```
**理解這些樣式:**
- **對整頁** 用 `body` 設定一致的字型排版
- **移除** 瀏覽器預設的邊距與內距,方便控制
- **為** 所有標題元素設定顏色、對齊與間距
- **使用** `rem` 單位,確保字型大小可延展且易讀
元素選擇器非常適合設定通用風格,但對於 terrarium 內部像植物這類個別元件,需要更細緻選擇器。
### ID 選擇器:獨特元素
ID 選擇器使用 `#`,針對具有特定 `id` 屬性的元素。因 ID 在頁面中應唯一,非常適合為獨特元素(如左、右兩邊的植物容器)設定樣式。
讓我們設計 terrarium 側邊容器的樣式,放置植物:
```css
#left-container {
background-color: #f5f5f5;
width: 15%;
left: 0;
top: 0;
position: absolute;
height: 100vh;
padding: 1rem;
box-sizing: border-box;
}
#right-container {
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` 單位的零值,使程式碼更乾淨
- **搭配** 柔和背景色,較不刺眼
**程式碼質量挑戰**:此 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;
}
#left-container {
left: 0;
}
#right-container {
right: 0;
}
```
### 類別選擇器:可復用樣式
類別選擇器用 `.` 表示,適合將相同樣式套用到多個元素。與 ID 不同,類別可多處重複使用,非常適合樣式一致性的模式。
在 terrarium 裡每株植物需要共用樣式但位置不同。可用類別分享樣式ID 定位。
**這是每株植物的 HTML 結構:**
```html
<div class="plant-holder">
<img class="plant" alt="Decorative plant for terrarium" id="plant1" src="../../../../translated_images/zh-HK/plant1.d18b18ffe73da18f.png" />
</div>
```
**重點說明:**
- **用** `class="plant-holder"` 統一容器樣式
- **用** `class="plant"` 統一植物圖像樣式與行為
- **用** 唯一 `id="plant1"` 作特定定位與 JavaScript 操作
- **使用** 描述性 alt 文字供輔助工具使用
接著把這些樣式加進 `style.css`
```css
.plant-holder {
position: relative;
height: 13%;
left: -0.6rem;
}
.plant {
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 ... */
}
/* Plant holder positioning */
.plant-holder {
position: relative; /* Creates positioning context */
/* ... other styles ... */
}
/* Plant positioning */
.plant {
position: absolute; /* Allows precise placement within holder */
/* ... other styles ... */
}
```
**解讀定位策略:**
- **絕對定位容器** 脫離文件流,固定於螢幕邊緣
- **相對定位植物容器** 建立定位上下文,仍保留文件流
- **絕對定位植物** 可精準定位於其相對容器內
- **此組合** 可讓植物垂直堆疊,且可個別定位
> 🎯 **重要原因**`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
```
### 建造玻璃罐組件
讓我們逐一建造生態瓶罐的各部分。每個部分都使用絕對定位以及百分比大小以達到響應式設計:
```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);
}
.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;
}
.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;
}
```
**了解生態瓶的構造:**
- **使用**百分比尺寸,實現在所有螢幕尺寸上的響應式縮放
- **絕對定位**元素以精確堆疊和對齊
- **應用**不同透明度值來創造玻璃的透光效果
- **實施**`z-index`分層,讓植物看起來在罐子內部
- **加入**微妙的盒陰影和圓角,讓外觀更真實
### 使用百分比的響應式設計
注意所有尺寸都使用百分比,而非固定像素值:
**為什麼這很重要:**
- **確保**生態瓶在任意螢幕尺寸上按比例縮放
- **維持**罐子組件之間的視覺關係
- **提供**從手機到大螢幕桌面一致的體驗
- **允許**設計適應變化,不破壞視覺佈局
### CSS 單位的運用
我們使用 `rem` 單位為 border-radius 設定大小,這會相對於根字體大小縮放,創建更可及的設計,尊重使用者字體偏好。詳細可參考官方規範的 [CSS relative units](https://www.w3.org/TR/css-values-3/#font-relative-lengths)。
**視覺實驗**:嘗試修改這些值並觀察效果:
- 將罐子透明度從 0.5 改為 0.8 — 玻璃外觀如何變化?
- 調整污土顏色從 `#3a241d` 改為 `#8B4513` — 視覺影響是什麼?
- 將污土的 `z-index` 改為 2 — 分層順序會怎樣?
### 🔄 **教學檢查點**
**CSS 視覺設計理解**:確認你對視覺 CSS 的掌握:
- ✅ 百分比尺寸如何創造響應式設計?
- ✅ 為什麼透明度能營造玻璃透光效果?
- ✅ `z-index` 在元素分層中扮演什麼角色?
- ✅ border-radius 如何塑造罐子形狀?
**設計原則**:注意我們如何用簡單圖形構建複雜視覺:
1. **矩形** → **圓角矩形** → **罐子組件**
2. **純色** → **透明度** → **玻璃效果**
3. **單一元素** → **分層組合** → **3D 外觀**
---
## GitHub Copilot Agent 挑戰 🚀
使用 Agent 模式完成以下挑戰:
**說明:** 創建一個 CSS 動畫,讓生態瓶中的植物輕輕搖擺,模擬自然微風吹拂效果。這將幫助你練習 CSS 動畫、變形和關鍵影格,並提升生態瓶的視覺吸引力。
**提示:** 為 `.plant` 類添加 CSS 關鍵影格動畫,使植物輕輕左右擺動。創建一個擺動動畫,使每株植物沿水平方向旋轉約 2-3 度,持續時間 3-4 秒,且動畫無限循環,並使用平滑的緩動函數。
在這了解更多[Agent 模式](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode)。
## 🚀 挑戰:加入玻璃反光效果
準備好用逼真的玻璃反光效果增強你的生態瓶了嗎?這技術將增加層次感與真實感。
你將創建細膩的高光,模擬光線如何在玻璃面反射。這類似文藝復興畫家 Jan van Eyck 利用光影與反射使繪畫中的玻璃呈現立體感。目標如下:
![finished terrarium](../../../../translated_images/zh-HK/terrarium-final.2f07047ffc597d0a.webp)
**你的挑戰:**
- **創建**細膩的白色或淺色橢圓形,用於玻璃反光
- **策略性定位**在罐子左側
- **應用**適當透明度和模糊效果,模擬真實光反射
- **使用**`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 結構
- **現代實踐**:運用相對單位與可及設計模式
**下一步**你的生態瓶已有結構HTML與樣式CSS。最後一課將用 JavaScript 加入互動!
## 作業
[CSS 重構](assignment.md)
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件由 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於追求準確性,請注意自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於關鍵資訊,建議聘請專業人工翻譯。因使用本翻譯所產生的任何誤解或誤譯,我們概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -0,0 +1,127 @@
# CSS 重構作業
## 目標
將你的玻璃花房項目轉換為使用現代 CSS 佈局技術!將目前的絕對定位方式重構為使用 **Flexbox****CSS Grid**,以實現更易於維護且響應式的設計。此作業挑戰你在保持玻璃花房視覺吸引力的同時,應用現代 CSS 標準。
理解什麼時候以及如何使用不同的佈局方法是現代網頁開發的重要技能。此練習將傳統定位技術與當代 CSS 佈局系統連接起來。
## 作業指示
### 第一階段:分析與規劃
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及其理由
- 重構過程中面臨的挑戰
- 瀏覽器相容性說明
- 執行程式碼的說明
## 評分標準
| 評分項目 | 優異 (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 自訂屬性的主題切換器
🌟 **容器查詢**:使用現代容器查詢技巧以達元件級響應式
> 💡 **記住**:目標不只是讓它能用,而是理解為何你選擇的佈局方法是這個設計挑戰的最佳解決方案!
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件經由 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意自動翻譯結果可能包含錯誤或不準確之處。原始文件的母語版本應被視為具權威性的來源。對於重要資訊,建議採用專業人工翻譯。我們不對因使用此翻譯而產生的任何誤解或誤釋承擔責任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -0,0 +1,762 @@
# 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/zh-HK/webdev101-js.10280393044d7eaa.webp)
> 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/zh-HK/dom-tree.7daf0e763cbbba92.webp)
> 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/zh-HK/dom-tree.7daf0e763cbbba92.webp)
> DOM 與對應 HTML 標記的示意圖。來自 [Olfa Nasraoui](https://www.researchgate.net/publication/221417012_Profile-Based_Focused_Crawler_for_Social_Media-Sharing_Websites)
在本課中,我們將完成互動植物箱專案,寫出 JavaScript 讓使用者能操控頁面上的植物。
## 開始之前:為成功做準備
你需要之前植物箱課程的 HTML 與 CSS 檔案——我們即將把靜態設計變成交互式。如果你是第一次加入,先完成那些課程會有重要的背景知識。
以下是我們要做的事:
- **為所有植物實現流暢的拖放功能**
- **追蹤座標,讓植物記住自己的位置**
- **建立完整的互動介面,使用純 JavaScript**
- **用閉包模式寫出乾淨、有組織的程式碼**
## 設定你的 JavaScript 檔案
讓我們開始建立讓你的植物箱互動的 JavaScript 檔案。
**步驟 1建立你的程式檔**
在你的植物箱資料夾中,建立一個新檔案命名為 `script.js`
**步驟 2將 JavaScript 連結到你的 HTML**
在你的 `index.html``<head>` 區塊加入這個 script 標籤:
```html
<script src="./script.js" defer></script>
```
**`defer` 屬性的重要性:**
- **確保** JavaScript 等待所有 HTML 載入完成後才執行
- **避免** JavaScript 在元素尚未準備好時尋找它們而造成錯誤
- **保證** 所有植物元素都可被互動使用
- **比把 script 放到頁面底部有更好的效能**
> ⚠️ **重要提醒**`defer` 屬性可以避免常見的時序問題。沒有它JavaScript 可能嘗試存取還沒載入的 HTML 元素,導致錯誤。
---
## 將 JavaScript 與 HTML 元素連結
在讓元素可拖放前JavaScript 需要找到它們,在 DOM 中定位它們。可以把這看成圖書館的目錄系統——只要有目錄號碼,你就能準確找到想要的書。
我們會使用 `document.getElementById()` 來建立這連結。這就像有一個精確的檔案系統——你給出 ID它就能找到你 HTML 中的正確元素。
### 讓所有植物都能拖曳
在你的 `script.js` 檔案加入以下程式碼:
```javascript
// 啟用所有14種植物的拖曳功能
dragElement(document.getElementById('plant1'));
dragElement(document.getElementById('plant2'));
dragElement(document.getElementById('plant3'));
dragElement(document.getElementById('plant4'));
dragElement(document.getElementById('plant5'));
dragElement(document.getElementById('plant6'));
dragElement(document.getElementById('plant7'));
dragElement(document.getElementById('plant8'));
dragElement(document.getElementById('plant9'));
dragElement(document.getElementById('plant10'));
dragElement(document.getElementById('plant11'));
dragElement(document.getElementById('plant12'));
dragElement(document.getElementById('plant13'));
dragElement(document.getElementById('plant14'));
```
**這段程式碼完成了什麼:**
- **定位** 每個植物元素於 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會發生什麼事為什麼 `getElementById()` 只回傳一個元素?
*答案ID 應該唯一;如果重複,只有第一個元素會被找到*
---
## 建立拖曳元素的閉包函數
現在我們來打造拖曳功能的核心:一個閉包,用來管理每棵植物的拖曳行為。這閉包包含多個內部函數,協同追蹤滑鼠移動並更新元素位置。
閉包很適合這項任務,因為它能讓我們創造「私有」變數,在函數調用間持續存在,給每棵植物獨立的座標追蹤系統。
### 用簡單範例理解閉包
讓我用一個簡單範例說明閉包的概念:
```javascript
function createCounter() {
let count = 0; // 這就像一個私有變量
function increment() {
count++; // 內部函數會記住外部變量
return count;
}
return increment; // 我們將內部函數返回
}
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 函數
現在我們寫出控制拖曳邏輯的主要函數。把它加入你聲明植物元素的程式碼下面:
```javascript
function dragElement(terrariumElement) {
// 初始化位置追蹤變數
let pos1 = 0, // 之前的滑鼠 X 位置
pos2 = 0, // 之前的滑鼠 Y 位置
pos3 = 0, // 目前的滑鼠 X 位置
pos4 = 0; // 目前的滑鼠 Y 位置
// 設定初始拖曳事件監聽器
terrariumElement.onpointerdown = pointerDrag;
}
```
**位置追蹤系統解說:**
- **`pos1``pos2`**:儲存舊滑鼠位置和新滑鼠位置之間的差距
- **`pos3``pos4`**:追蹤滑鼠目前的座標
- **`terrariumElement`**:我們正在改造成可拖曳的那個植物元素
- **`onpointerdown`**:使用者開始拖曳時觸發的事件
**閉包模式運作方式:**
- **為每個植物元素建立** 私有位置變數
- **在拖曳過程中維持** 這些變數
- **確保** 每棵植物獨立追蹤自己的座標
- **透過 `dragElement` 函數提供** 乾淨的介面
### 為什麼使用 pointer 事件?
你可能會想,為什麼我們用 `onpointerdown` 而不是較常見的 `onclick`。原因如下:
| 事件類型 | 適用情境 | 注意事項 |
|------------|----------|-------------|
| `onclick` | 簡單按鈕點擊 | 只能處理點擊與放開,無法拖曳 |
| `onpointerdown` | 滑鼠和觸控都適用 | 新穎且目前廣泛支援 |
| `onmousedown` | 只適用桌面滑鼠 | 忽略行動裝置使用者 |
**為什麼 pointer 事件很適合我們的需求:**
- **不論是滑鼠、手指或觸控筆** 都能正常運作
- **無論用筆電、平板或手機都一樣流暢**
- **能處理真正的拖曳動作,而非只是點擊放開**
- **帶來使用者期望的順暢體驗**
> 💡 **未來趨勢**Pointer 事件是最新的使用者互動標準。比起寫分開的滑鼠與觸控程式碼,這種方式同時涵蓋兩者。非常酷對吧?
### 🔄 **教學回顧檢查**
**事件處理理解**:暫停確認你掌握:
- ✅ 為何用 pointer 事件而非 mouse 事件?
- ✅ 閉包變數如何在函數調用間持續存在?
- ✅ `preventDefault()` 在流暢拖曳中扮演什麼角色?
- ✅ 為什麼事件監聽器是綁在文件document而不是各個元素上
**與現實連結**:想想你每天使用過的拖放介面:
- **檔案上傳**:將檔案拖到瀏覽器視窗
- **看板任務板**:任務在欄目間移動
- **圖片集**:重新排列照片順序
- **行動介面**:觸控螢幕上的滑動與拖曳
---
## pointerDrag 函數:捕捉拖曳的開始
當使用者按下植物(不論是滑鼠點擊或手指觸控),`pointerDrag` 函數就會啟動。它會存下初始座標並準備拖曳系統。
把這個函數加到你的 `dragElement` 閉包裡,緊接在 `terrariumElement.onpointerdown = pointerDrag;` 這行後面:
```javascript
function pointerDrag(e) {
// 阻止瀏覽器預設行為(例如文字選取)
e.preventDefault();
// 捕捉初始鼠標/觸摸位置
pos3 = e.clientX; // 拖曳開始的 X 座標
pos4 = e.clientY; // 拖曳開始的 Y 座標
// 設置拖曳過程的事件監聽器
document.onpointermove = elementDrag;
document.onpointerup = stopElementDrag;
}
```
**逐步解說:**
- **防止** 瀏覽器的預設行為,以免干擾拖曳
- **記錄** 使用者開始拖曳時的確切座標
- **建立** 對持續拖曳移動的事件監聽器
- **準備** 追蹤整個文件中的滑鼠或手指動作
### 理解事件預防
`e.preventDefault()` 這行對順暢拖曳至關重要:
**若不防止,瀏覽器可能會:**
- **選取** 用戶拖曳時劃過的文字
- **觸發** 右鍵拖曳時的選單
- **干擾** 我們自訂的拖曳功能
- **造成** 拖動過程中的視覺異常
> 🔍 **實驗**:完成本課後,試著移除 `e.preventDefault()`,觀察拖曳體驗如何改變。你會很快知道這行為什麼必要!
### 座標追蹤系統
`e.clientX``e.clientY` 屬性給出滑鼠/觸控點的精確座標:
| 屬性 | 量測項目 | 使用案例 |
|----------|------------------|----------|
| `clientX` | 相對於視窗的水平位置 | 追蹤左右移動 |
| `clientY` | 相對於視窗的垂直位置 | 追蹤上下移動 |
**了解這些座標:**
- **提供** 像素級精確的定位資訊
- **隨使用者移動指標** 實時更新
- **跨不同螢幕尺寸和縮放等級** 保持一致
- **啟用** 平滑、響應式的拖放互動
### 設置文件級事件監聽器
注意我們如何將移動和停止事件附加在整個 `document` 上,而不僅是植物元素:
```javascript
document.onpointermove = elementDrag;
document.onpointerup = stopElementDrag;
```
**為何附加到 document**
- **即使滑鼠離開植物元素也能繼續追蹤**
- **防止使用者快速移動時拖動中斷**
- **在整個螢幕上提供平滑拖動體驗**
- **處理游標移出瀏覽器視窗的邊界情況**
> ⚡ **效能提示**:拖動停止時,我們會清理這些文件級監聽器,避免記憶體洩漏和效能問題。
## 完善拖曳系統:移動與清理
現在我們將新增兩個處理實際拖曳移動和拖曳停止時清理的函式。這些函式協同工作,為你的植物造景提供平滑、響應式的移動功能。
### elementDrag 函式:追蹤移動
`pointerDrag` 的結束花括號後面添加 `elementDrag` 函式:
```javascript
function elementDrag(e) {
// 計算自上次事件以來移動的距離
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';
}
```
**理解座標數學運算:**
- **`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` 的結束花括號後添加此清理函式:
```javascript
function stopElementDrag() {
// 移除文件層級的事件監聽器
document.onpointerup = null;
document.onpointermove = null;
}
```
**清理為何重要:**
- **防止因殘留事件監聽器導致的記憶體洩漏**
- **使用者釋放植物後停止拖曳行為**
- **允許其他元素獨立拖動**
- **重設系統以便下一次拖曳操作**
**未清理會發生什麼:**
- 事件監聽器在拖曳停止後仍持續執行
- 累積未使用的監聽器導致效能下降
- 與其他元素互動時產生意外行為
- 瀏覽器資源浪費於不必要的事件處理
### 理解 CSS 位置屬性
我們的拖動系統操作兩個關鍵 CSS 屬性:
| 屬性 | 控制內容 | 我們如何使用 |
|------|----------|--------------|
| `top` | 距離頂部邊緣的距離 | 拖動過程中的垂直定位 |
| `left` | 距離左側邊緣的距離 | 拖動過程中的水平定位 |
**關於 offset 屬性的重要觀察:**
- **`offsetTop`**:距離已定位祖先元素頂部的當前距離
- **`offsetLeft`**:距離已定位祖先元素左側的當前距離
- **定位上下文**:這些值相對於最近的已定位祖先元素
- **即時更新**:當修改 CSS 屬性時立即變動
> 🎯 **設計理念**:此拖曳系統刻意保持彈性—沒有「放置區域」或其他限制。用戶可自由將植物放置於任意位置,完全掌控自己的造景設計。
## 綜合整合:完整拖曳系統
恭喜!你剛完成了一個用純 JavaScript 製作的高級拖放系統。你的完整 `dragElement` 函式內含一個功能強大的閉包,管理著:
**你的閉包達成的功能:**
- **獨立維護** 每個植物的私有位置變數
- **處理** 從開始拖曳到結束的整個生命週期
- **提供** 跨整個螢幕的平滑、響應式移動
- **妥善清理** 資源以預防記憶體洩漏
- **創造** 直覺且具創造力的造景介面
### 測試你的互動式植物瓶景觀
現在測試你的互動式植物瓶景觀!在瀏覽器中打開你的 `index.html` 並嘗試操作:
1. **點擊並按住** 任一植物開始拖曳
2. **移動滑鼠或手指** 看著植物平滑跟隨
3. **放開** 以將植物放置在新位置
4. **嘗試** 不同的排列方式探索介面
🥇 **成就感**:你已創建一個完全互動的網頁應用,掌握專業開發者每天使用的核心概念。這拖放功能底層原理同時支撐著檔案上傳、看板列表及各種互動介面。
### 🔄 **教學檢視點**
**完整系統理解**:檢驗你對拖曳系統的掌握:
- ✅ 閉包如何維護每個植物的獨立狀態?
- ✅ 為什麼座標計算的數學對平滑移動必要?
- ✅ 忘記清理事件監聽器會導致什麼?
- ✅ 這種模式如何擴展到更複雜的互動?
**程式碼品質反思**:回顧完整方案:
- **模組化設計**:每個植物獲得自己的閉包實例
- **事件效能**:妥善設定與清理監聽器
- **跨裝置支援**:適用桌面與行動裝置
- **效能意識**:無記憶體洩漏或多餘計算
![finished terrarium](../../../../translated_images/zh-HK/terrarium-final.0920f16e87c13a84.webp)
---
## 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 工具包總結
完成本課後,你已具備:
- **DOM 精通**:元素選取、屬性操作、樹狀結構導覽
- **事件專業**:跨裝置指標事件互動處理
- **閉包理解**:私有狀態管理與函式持續性
- **互動系統**:從零打造完整拖放實作
- **效能意識**:妥善清理事件避免記憶體洩漏
- **現代模式**:專業開發中的程式碼組織技巧
- **用戶體驗**:打造直覺、響應式界面
**你已習得的專業技能:**
- **Trello/看板列表**:欄位間的卡片拖曳
- **檔案上傳系統**:拖放檔案處理
- **圖片庫**:照片重新排列介面
- **行動應用**:基於觸控的互動模式
**下一階段**:你已準備好探索 React、Vue 或 Angular 等現代框架,這些框架基於你目前 DOM 操控的基本概念建立!
## 作業
[多練習操作 DOM](assignment.md)
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件乃使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們力求準確,但請注意自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應視為權威資料來源。對於重要資訊,建議採用專業人工翻譯。我們不對因使用本翻譯而產生的任何誤解或誤釋負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -0,0 +1,130 @@
# DOM 元素調查作業
## 概覽
現在你已經親身體驗了 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 -->
**免責聲明**
本文件由 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 協助翻譯。雖然我們致力於確保準確性,但請注意自動翻譯可能包含錯誤或不準確之處。原始語言版本的文件應被視為權威來源。對於關鍵資訊,建議採用專業人工翻譯。我們不對因使用本翻譯而產生的任何誤解或誤釋負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -0,0 +1,21 @@
## 部署你的 Terrarium
你可以使用 **Azure Static Web Apps** 在網絡上部署或發佈你的 Terrarium。
1. 分叉此倉庫
2. 按下此按鈕 👇
[![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 構建和發佈你的應用。
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件乃使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於確保準確性,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應視為權威來源。對於重要資訊,建議尋求專業人工翻譯。我們不對因使用本翻譯而產生的任何誤解或誤釋負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -0,0 +1,28 @@
# 我的微型植物園:一個學習 HTML、CSS 和使用 JavaScript 操控 DOM 的項目 🌵🌱
一個小型的拖放代碼冥想項目。通過一點 HTML、JS 和 CSS你可以構建一個網頁界面為其設計樣式並添加互動功能。
![我的微型植物園](../../../../3-terrarium/images/screenshot_gray.png)
## 鳴謝
由 [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](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋不承擔責任。

@ -0,0 +1,281 @@
# 事件驅動程式設計 - 建立一個打字遊戲
```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
```
想知道提升這項技能的最佳方法之一嗎?你猜對了——我們要做一款遊戲!
```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 與使用類別設定樣式
- 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 美化遊戲介面,使之更吸引人
### 📅 **這一週你的遊戲開發路線**
- [ ] 完成具備所有功能與細節的完整打字遊戲
- [ ] 新增難度等級,包含不同複雜度的字詞
- [ ] 實作用戶統計追蹤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) 以 ♥️ 撰寫
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件乃使用人工智能翻譯服務【Co-op Translator】(https://github.com/Azure/co-op-translator) 翻譯而成。雖然我們致力於確保準確性,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威資料。對於重要資訊,建議使用專業人工翻譯。對於因使用本翻譯而引致的任何誤解或誤釋,我們概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -0,0 +1,4 @@
這是一個佔位符,故意留空
**免責聲明**
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為具權威性的來源。對於重要資訊,建議使用專業的人類翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。

@ -0,0 +1,604 @@
# 使用事件創建一個遊戲
你是否曾經想過網站如何知道你何時點按按鈕或在文字框中輸入?這就是事件驅動程式設計的魔力!有什麼比構建一個有用的東西更好的方式來學習這項基本技能呢——一個會對你每次敲打的按鍵作出反應的打字速度遊戲。
你將親身看到網頁瀏覽器如何「與」你的 JavaScript 代碼「對話」。每次你點按、輸入或移動滑鼠時,瀏覽器都會向你的代碼發送小訊息(我們稱之為事件),而你則決定如何回應!
到本文結束時,你將建立一個真實的打字遊戲,追蹤你的速度和準確度。更重要的是,你將理解驅動每一個你曾使用過的互動網站的基本概念。讓我們開始吧!
## 課前測驗
[Pre-lecture quiz](https://ff-quizzes.netlify.app/web/quiz/21)
## 事件驅動程式設計
想想你最喜歡的應用程式或網站——是什麼令它感覺生動且反應靈敏?全在於它如何回應你的操作!每一次點按、點擊、滑動或鍵入都會創造一個我們稱之為「事件」的東西,而這正是網頁開發中真正的魔法所在。
這就是為什麼針對網頁編程如此有趣的原因:我們永遠不知道何時有人會點擊按鈕或開始輸入文字。他們可能立即點擊,也可能等待五分鐘,或者可能根本不點擊!這種不可預測性意味著我們需要用不同的方法來撰寫代碼。
我們不是寫一段從上到下運行的程式碼(就像食譜一樣),而是寫程式碼耐心地等待事情發生。這就像 1800 年代的電報操作員守候在他們的機器旁,準備在訊息通過電線時立即回應一樣。
那麼,「事件」究竟是什麼?簡單地說,它是發生的事情!你點擊一個按鈕——這就是一個事件。你輸入一個字母——這是另一個事件。你移動滑鼠——還有另一個事件。
事件驅動程式設計讓我們能夠設定代碼來聆聽和回應。我們創建特殊函數稱為**事件監聽器event listeners**,它們耐心等待特定事件發生,然後在事件發生時立即執行。
把事件監聽器想像成你的代碼的門鈴。你安裝門鈴 (`addEventListener()`)告訴它要聽哪種聲音如「click」或「keypress」然後指定誰按鐘時該做什麼你的自訂函數
**事件監聽器的工作原理:**
- **監聽**特定的用戶操作,如點擊、鍵擊或滑鼠移動
- **執行**指定事件發生時的自訂代碼
- **立即回應**用戶互動,創造無縫體驗
- **可處理**同一元素上的多個不同事件監聽器
> **NOTE:** 值得一提的是,創建事件監聽器的方法有很多。你可以使用匿名函數,或者建立有名稱的函數。你也可以用各種快捷方式,比如設定 `click` 屬性,或者使用 `addEventListener()`。在我們的練習中,我們將專注於 `addEventListener()` 和匿名函數,因為這可能是網頁開發者最常用的技術。它也是最靈活的,因為 `addEventListener()` 適用於所有事件,且事件名稱可作為參數傳入。
### 常見事件
雖然網頁瀏覽器提供數十種不同的事件可供監聽,但大多數互動應用程式只依賴幾個基本事件。理解這些核心事件將為你建立複雜的用戶互動奠定基礎。
有[數十種事件](https://developer.mozilla.org/docs/Web/Events)可供你監聽,幾乎用戶在頁面上做的任何事情都可以引發事件,這讓你能夠掌控用戶體驗。幸運的是,你通常只需要少數幾種事件。以下是一些常見的(包括我們創建遊戲時將使用的兩種):
| Event | 說明 | 常見用例 |
|-------|-------|---------|
| `click` | 用戶點擊了某些東西 | 按鈕、鏈結、互動元素 |
| `contextmenu` | 用戶點擊了滑鼠右鍵 | 自訂右鍵選單 |
| `select` | 用戶選取了部分文字 | 文字編輯、複製操作 |
| `input` | 用戶輸入了文字 | 表單驗證、即時搜尋 |
**理解這些事件類型:**
- **觸發**用戶與頁面上特定元素互動
- **提供**關於用戶操作的詳細事件對象資訊
- **啟用**你創造反應迅速、互動性強的網頁應用程式
- **跨瀏覽器裝置**皆可一致運作
## 創建遊戲
既然你了解事件如何運作,讓我們將這些知識付諸實踐,做一些有用的東西。我們將建立一個打字速度遊戲,演示事件處理的同時幫助你培養重要的開發技能。
我們要創建一個遊戲來探索 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` 讓頁面美觀。這是網頁開發最經典的三劍客!
**打開命令列或終端機視窗,輸入以下指令,來為你的工作建立一個新資料夾:**
```bash
# Linux 或 macOS
mkdir typing-game && cd typing-game
# Windows
md typing-game && cd typing-game
```
**這些指令做了什麼:**
- **創建**一個名為 `typing-game` 的新目錄,用來存放你的專案檔案
- **自動切換到**新建的目錄中
- **為你的遊戲開發搭建了**一個乾淨的工作環境
**打開 Visual Studio Code**
```bash
code .
```
**此指令作用:**
- **在目前目錄啟動** Visual Studio Code
- **打開**你的專案資料夾到編輯器中
- **提供**所有你需要的開發工具
**在 Visual Studio Code 中新增三個檔案,命名如下:**
- `index.html` - 包含遊戲的結構和內容
- `script.js` - 處理所有遊戲邏輯和事件監聽器
- `style.css` - 定義視覺外觀和樣式
## 創建使用者介面
現在讓我們搭建實際進行遊戲操作的舞台!把它想成為飛船設計控制面板——我們要確保玩家需要的所有東西都在他們預期的位置。
先想想遊戲需要些什麼。如果你在玩打字遊戲,你希望畫面上看到什麼?這裡是我們需要的:
| UI 元素 | 用途 | HTML 元素 |
|---------|-------|-----------|
| 引言顯示區 | 顯示要輸入的文字 | `<p>`,具有 `id="quote"` |
| 訊息區域 | 顯示狀態及成功訊息 | `<p>`,具有 `id="message"` |
| 文字輸入框 | 玩家輸入引言 | `<input>`,具有 `id="typed-value"` |
| 開始按鈕 | 開始遊戲 | `<button>`,具有 `id="start"` |
**UI 結構理解:**
- **內容**從上到下有邏輯地組織
- **元素**有唯一 ID方便 JavaScript 操作
- **提供**清晰的視覺階層,提升用戶體驗
- **使用**語意化 HTML增強輔助功能支援
這些元素都需要 ID方便我們在 JavaScript 中操作。我們也會在這裡加入 CSS 和 JavaScript 檔案的連結。
建立新檔案命名為 `index.html`,加入以下 HTML
```html
<!-- inside index.html -->
<html>
<head>
<title>Typing game</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Typing game!</h1>
<p>Practice your typing skills with a quote from Sherlock Holmes. Click **start** to begin!</p>
<p id="quote"></p> <!-- This will display our quote -->
<p id="message"></p> <!-- This will display any status messages -->
<div>
<input type="text" aria-label="current word" id="typed-value" /> <!-- The textbox for typing -->
<button type="button" id="start">Start</button> <!-- To start the game -->
</div>
<script src="script.js"></script>
</body>
</html>
```
**這段 HTML 結構做了這些事:**
- **`<head>` 連結** CSS 樣式表,提供樣式
- **建立**明確標題和說明,方便使用者理解
- **設置**具有特定 ID 的佔位段落,用於動態內容
- **包含**具無障礙特性的輸入欄位
- **提供**一個開始按鈕來觸發遊戲
- **在末尾載入** JavaScript 檔案,達到最佳載入效能
### 啟動應用程式
在開發過程中經常測試應用程式能幫助你早期發現問題並且即時看到成果。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 會做什麼:**
- **啟動**本地開發伺服器服務你的專案
- **你儲存檔案時**自動刷新瀏覽器頁面
- **從本地網址**(通常是 `localhost:5500`)提供服務
**打開瀏覽器並造訪 `https://localhost:5500`**
你應該會看到剛才建立的頁面!現在讓我們新增功能。
## 新增 CSS
接下來讓畫面好看一些視覺回饋自計算機早期時代起就是用戶介面的關鍵。在1980年代研究者發現即時的視覺回饋大幅提升使用者效能並且減少錯誤。這正是我們要創建的效果。
我們的遊戲需要清晰明確地呈現目前狀況。玩家應立即知道該輸入哪個字詞,若出錯也應見到即時提示。我們來做一些簡單有效的樣式:
建立新檔案命名為 `style.css`,加入以下語法。
```css
/* inside style.css */
.highlight {
background-color: yellow;
}
.error {
background-color: lightcoral;
border: red;
}
```
**這些 CSS 類別的作用:**
- **以黃色背景**標示當前字詞,提供清楚視覺引導
- **用淡珊瑚色背景**提示輸入錯誤
- **提供**即時回饋而不干擾輸入流程
- **使用**對比鮮明的顏色提升無障礙性與可讀性
✅ CSS 部分你可以自由調整頁面佈局。花點時間讓頁面更吸引人:
- 選用不同字體
- 為標題增添顏色
- 調整元素大小
## JavaScript
現在開始有趣了!🎉 我們已建立 HTML 結構和 CSS 樣式但遊戲目前如同漂亮的汽車卻沒有引擎。JavaScript 就是這個引擎——它讓一切實際運作並對玩家操作產生反應。
這是你將看到你的創作活起來的地方。我們會按步驟進行,讓一切不感到繁雜:
| 步驟 | 目的 | 你將學習什麼 |
|------|-------|----------------|
| [新增常數](../../../../4-typing-game/typing-game) | 設定引言與 DOM 參考 | 變數管理與 DOM 選取 |
| [新增開始遊戲的事件監聽器](../../../../4-typing-game/typing-game) | 處理遊戲初始化 | 事件處理與 UI 更新 |
| [新增輸入事件監聽器](../../../../4-typing-game/typing-game) | 實時處理使用者輸入 | 輸入驗證與動態回饋 |
**這種結構化方法幫助你:**
- **把程式碼**組織成合乎邏輯、可管理的部分
- **逐步構建**功能,便於除錯
- **理解**應用各部分如何協同工作
- **創造**未來專案可重用模式
但首先,建立新檔案命名為 `script.js`
### 新增常數
在開始動手前,先整理所有資源!就像 NASA 任務控制中心會在發射前設置所有監控系統一樣,事先準備整齊省事很多,也避免後續找資料時東翻西找、打錯字。
以下是我們要先設定的:
| 資料類型 | 用途 | 例子 |
|-----------|-------|-----|
| 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
// 在 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.',
'I ought to know by this time that when a fact appears to be opposed to a long train of deductions it invariably proves to be capable of bearing some other interpretation.',
'I never make exceptions. An exception disproves the rule.',
'What one man can invent another can discover.',
'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.',
];
// 儲存詞彙列表同玩家而家打緊嘅詞彙索引
let words = [];
let wordIndex = 0;
// 開始時間
let startTime = Date.now();
// 頁面元素
const quoteElement = document.getElementById('quote');
const messageElement = document.getElementById('message');
const typedValueElement = document.getElementById('typed-value');
```
**拆解這段設置程式碼的功用:**
- **使用 `const` 儲存** 一組 Sherlock Holmes 的引用語,因為引用語不會改變
- **使用 `let` 初始化** 追蹤變數,因為這些值在遊戲進行時會更新
- **透過 `document.getElementById()` 抓取** DOM 元素的引用以提升存取效率
- **建立遊戲功能基礎**,使用清晰且描述性變數名稱
- **將相關資料與元素組織** 起來便於後續維護程式碼
✅ 馬上為你的遊戲加入更多引用語吧
> 💡 **專家小提示**:我們可以在程式碼中隨時用 `document.getElementById()` 取得元素。因為會經常用到這些元素,我們會用常數避免字串拼寫錯誤。像是 [Vue.js](https://vuejs.org/) 或 [React](https://reactjs.org/) 這類框架能幫你更好管理及集中你的程式碼。
>
**這種作法為什麼這麼好:**
- **防止** 多次引用元素時拼寫錯誤
- **提升** 程式碼可讀性,使用描述性常數名稱
- **啟用** IDE 的自動完成與錯誤檢查功能
- **未來** 元素 ID 改變時更易重構程式碼
花一點時間觀看有關使用 `const`、`let` 與 `var` 的影片
[![Types of variables](https://img.youtube.com/vi/JNIXfGiDWM8/0.jpg)](https://youtube.com/watch?v=JNIXfGiDWM8 "Types of variables")
> 🎥 點擊上方圖片觀看變數相關教學影片。
### 新增開始邏輯
這是全部運作的關鍵時刻!🚀 你即將寫出第一個真正的事件監聽器,看到你的程式碼回應按鈕點擊會非常有成就感。
想想看某處某人會按下「Start」按鈕程式必須準備好因應。我們不知道他什麼時候按 — 可能立刻,也可能喝完咖啡才按 — 但當按下去時,你的遊戲就啟動了。
當使用者點擊 `start`,我們需要選擇一段引用,設置界面,並追蹤目前輸入的字與計時。以下是你需要加入的 JavaScript後面會解釋。
```javascript
// 在 script.js 的末尾
document.getElementById('start').addEventListener('click', () => {
// 獲取一個引言
const quoteIndex = Math.floor(Math.random() * quotes.length);
const quote = quotes[quoteIndex];
// 將引言放入字詞陣列中
words = quote.split(' ');
// 重置字詞索引以便追蹤
wordIndex = 0;
// 使用者界面更新
// 建立一個 span 元素陣列,以便設定類別
const spanWords = words.map(function(word) { return `<span>${word} </span>`});
// 轉換為字串並設為引言顯示的 innerHTML
quoteElement.innerHTML = spanWords.join('');
// 強調第一個字詞
quoteElement.childNodes[0].className = 'highlight';
// 清除所有先前訊息
messageElement.innerText = '';
// 設置文字框
// 清空文字框
typedValueElement.value = '';
// 設置焦點
typedValueElement.focus();
// 設置事件處理函式
// 啟動計時器
startTime = new Date().getTime();
});
```
**解構這段程式碼成合理的區塊:**
**📊 單字追蹤設定:**
- **隨機選擇** 一段引用,利用 `Math.floor()``Math.random()` 增加變化度
- **利用 `split(' ')`** 將引用切割成單字陣列
- **重置 `wordIndex`** 為 0讓玩家從第一個單字開始
- **準備** 遊戲狀態為全新的一回合
**🎨 使用者介面設定與顯示:**
- **攏成 `<span>` 元素陣列包住每個單字,方便個別樣式設定**
- **將這些 span 合併成單一字串以便快速更新 DOM**
- **透過加上 `highlight` CSS 類,標示第一個單字**
- **清空之前的遊戲訊息,保持畫面清爽**
**⌨️ 文字框準備:**
- **清除輸入欄現有文字**
- **聚焦文字框使玩家能立即開始輸入**
- **為新一場遊戲做好輸入區準備**
**⏱️ 計時器初始化:**
- **使用 `new Date().getTime()` 記錄當前時間戳**
- **能精準計算輸入速度及完成時間**
- **開始追蹤這回合的遊戲時間**
### 新增輸入邏輯
現在要開始進入遊戲的核心!別擔心,開始看似複雜,我們一步步拆解,最後你會發現邏輯非常合理。
我們建立的邏輯是:每當有人輸入字母,我們的程式都會檢查他輸入了什麼,給予回饋,並判斷下一步要做什麼。這有點像 1970 年代早期的文字處理器 WordStar在輸入時即時提供回饋。
```javascript
// 在 script.js 的結尾
typedValueElement.addEventListener('input', () => {
// 取得目前的字詞
const currentWord = words[wordIndex];
// 取得目前的數值
const typedValue = typedValueElement.value;
if (typedValue === currentWord && wordIndex === words.length - 1) {
// 句子結束
// 顯示成功
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) {
// 字詞結束
// 為新字詞清除 typedValueElement
typedValueElement.value = '';
// 移至下一個字詞
wordIndex++;
// 重設 quote 中所有元素的類別名稱
for (const wordElement of quoteElement.childNodes) {
wordElement.className = '';
}
// 高亮新字詞
quoteElement.childNodes[wordIndex].className = 'highlight';
} else if (currentWord.startsWith(typedValue)) {
// 目前正確
// 高亮下一個字詞
typedValueElement.className = '';
} else {
// 錯誤狀態
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)
**測試內容建議:**
- **確認** 點擊開始時隨機顯示引用語
- **確認** 輸入時正確標示出目前單字
- **檢查** 輸入錯誤時出現錯誤樣式
- **確定** 詞語完成後標示正確前進
- **測試** 完成整句後顯示完成訊息與計時
**常見除錯建議:**
- **查看** 瀏覽器控制台 (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 模態視窗範例
- **考慮** 在禁用和啟用表單控件時保持無障礙
## 課後測驗
[課後測驗](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)文件,並思考你會在什麼情況下使用它們。
## 作業
[建立一個新的鍵盤遊戲](assignment.md)
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件乃使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 所翻譯。儘管我們致力確保翻譯準確,但請注意自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要資訊,建議尋求專業人工翻譯。本公司對因使用本翻譯而導致的任何誤解或誤譯概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

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

@ -0,0 +1,546 @@
# 瀏覽器擴充功能專案第1部分瀏覽器入門
```mermaid
journey
title 你的瀏覽器擴充功能開發旅程
section 基礎
了解瀏覽器: 3: 學生
學習擴充功能類型: 4: 學生
設置開發環境: 4: 學生
section 開發
建立介面: 4: 學生
新增功能: 5: 學生
處理數據: 5: 學生
section 整合
在瀏覽器測試: 5: 學生
除錯問題: 4: 學生
精煉體驗: 5: 學生
```
![瀏覽器手繪筆記](../../../../translated_images/zh-HK/browser.60317c9be8b7f84a.webp)
> 手繪筆記作者:[Wassim Chegham](https://dev.to/wassimchegham/ever-wondered-what-happens-when-you-type-in-a-url-in-an-address-bar-in-a-browser-3dob)
## 課前測驗
[課前測驗](https://ff-quizzes.netlify.app/web/quiz/23)
### 簡介
瀏覽器擴充功能是能提升瀏覽體驗的小型應用程式。正如提姆·伯納斯-李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/zh-HK/earlybrowsers.d984b711cdf3a42d.webp)
> 一些早期瀏覽器,來源:[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/zh-HK/install-on-edge.d68781acaf0b3d3d.webp)
> **重要**:測試自製擴充功能時,務必切換至開發者模式,並允許來自其他商店的擴充功能。
### 開發時擴充功能安裝流程
當您正在開發與測試自己的擴充功能時,遵循此工作流程:
```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
```
**此命令執行的工作:**
- **編譯**您的原始碼成適用於瀏覽器的檔案
- **打包**JavaScript 模組成優化套件
- **產生**最終擴充功能檔案於 `/dist` 資料夾
- **準備**擴充功能以方便安裝與測試
**步驟 2前往瀏覽器擴充功能頁面**
1. **開啟**瀏覽器的擴充功能管理頁面
2. **點擊**右上角的「設定與更多」按鈕(`...` 圖示)
3. **從下拉選單選取**「擴充功能」
**步驟 3載入您的擴充功能**
- **新安裝**:選擇 `load unpacked` 並指定 `/dist` 資料夾
- **更新**:點選已安裝擴充功能旁的 `reload`
- **測試**:啟用「開發者模式」以存取額外除錯功能
### 正式版擴充功能安裝
> ✅ **備註**:以上開發指令專供您自行設計擴充功能測試。安裝已發佈擴充功能請至官方瀏覽器擴充功能商店,例如 [Microsoft Edge 加載程式商店](https://microsoftedge.microsoft.com/addons/Microsoft-Edge-Extensions-Home)。
**理解差異:**
- **開發安裝**允許您在開發階段測試未發佈擴充功能
- **商店安裝**提供經審核、持續更新的已發佈擴充功能
- **側載安裝**允許從官方商店以外安裝擴充功能(需開發者模式)
## 建立您的碳足跡擴充功能
我們將建立一款顯示您所在地區能源使用碳足跡的瀏覽器擴充功能。這個專案展示了擴充功能開發的基本概念,同時為環境意識提供實用工具。
此做法遵循約翰·杜威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) 增強您的套件管理技能
### 理解專案架構
了解專案架構有助於有效規劃開發工作。就像亞歷山大圖書館設計易於檢索的知識庫,一個良好結構的程式碼庫讓開發更有效率:
```
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 金鑰與區域代碼記錄在安全筆記,方便開發過程中參考。您將需要這些數值來測試擴充功能功能。
**安全提醒**:切勿將 API 金鑰或敏感憑證提交至代碼版本庫。我們將在後續步驟示範如何安全管理。
## 建立擴充功能介面
接著我們打造使用者介面元件。擴充功能採用兩個畫面:設定畫面用於初次配置,結果畫面用於顯示資料。
這遵循自早期運算介面設計以來推行的漸進披露原則——以邏輯序列揭露資訊與選項,避免用戶被過多訊息淹沒。
### 擴充功能視圖概述
**設定畫面** - 用戶首次配置:
![完成的擴充功能在瀏覽器中顯示含地區名稱及 API 金鑰輸入欄位的表單截圖。](../../../../translated_images/zh-HK/1.b6da8c1394b07491.webp)
**結果畫面** - 顯示碳足跡資料:
![完成的擴充功能顯示 US-NEISO 地區碳使用量及化石燃料百分比數值的截圖。](../../../../translated_images/zh-HK/2.1dae52ff08042246.webp)
### 建構設定表單
設定表單於初次使用時收集用戶配置資料。配置後資料將存於瀏覽器儲存供日後使用。
`/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>
```
**此表單實現了以下功能:**
- **建立**語義正確的表單結構,包含適切標籤與輸入關聯
- **啟用**瀏覽器自動完成以提升用戶體驗
- **須填寫**兩欄位方可提交,透過 `required` 屬性實現
- **以描述性類名**組織輸入欄位,方便樣式設計與 JavaScript 操控
- **提供**清楚指引給首次設定擴充功能的用戶
### 建構結果展示區
接下來,建立用於顯示碳足跡資料的結果區。將以下 HTML 加在表單下方:
```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>
```
**此結構提供了以下功能:**
- **`loading`**:在獲取 API 資料時顯示讀取訊息
- **`errors`**API 呼叫失敗或資料無效時顯示錯誤訊息
- **`data`**:用來持有原始資料,方便開發階段調試
- **`result-container`**:友善地呈現碳足跡資訊給使用者
- **`clear-btn`**:允許用戶變更地區,重新配置擴充功能
### 設定建置流程
接著安裝專案依賴,並測試建置流程:
```bash
npm install
```
**此安裝流程達成的工作:**
- **下載** Webpack 及 `package.json` 指定的其他開發依賴
- **設定**建置工具鏈以編譯現代 JavaScript
- **準備**開發環境用於擴充功能建置與測試
- **啟用**代碼打包、優化及跨瀏覽器相容功能
> 💡 **建置流程洞察**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://www.mozilla.org/firefox/browsers/browser-history/)
[網路的歷史](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)
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件是使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯的。雖然我們力求準確,但請注意自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們不對因使用本翻譯而引起的任何誤解或誤譯承擔責任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -0,0 +1,137 @@
# Assignment: 重新設計你的瀏覽器擴充功能風格
## 概覽
現在你已經建立了碳足跡瀏覽器擴充功能的 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 -->
**免責聲明**
本文件乃使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 所翻譯。雖然我們致力於確保準確性,但請注意,機器翻譯可能存在錯誤或不準確之處。原始文件以其母語版本為準。對於重要資訊,建議採用專業人工翻譯。本公司對因使用本翻譯而引致的任何誤解或誤譯概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -0,0 +1,654 @@
# 瀏覽器擴充功能專案第二部分:呼叫 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');
// 結果
const errors = document.querySelector('.errors');
const loading = document.querySelector('.loading');
const results = document.querySelector('.result-container');
const usage = document.querySelector('.carbon-usage');
const fossilfuel = document.querySelector('.fossil-fuel');
const myregion = document.querySelector('.my-region');
const clearBtn = document.querySelector('.clear-btn');
```
**這段程式碼做了什麼:**
- **捕捉** 用 `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();
```
**理解以下概念:**
- **附加** submit 監聽器到表單,當使用者按 Enter 或點擊送出時觸發
- **連接** 點擊監聽器給清除按鈕,用於重置表單
- **把** 事件物件 `(e)` 傳給處理函式以做額外控制
- **立即** 呼叫 `init()` 函式,設定擴充功能的初始狀態
✅ 注意這裡使用的箭頭函式語法shorthand arrow function。這是現代 JavaScript 的寫法,比傳統函式表達式更簡潔,但兩者都運作正常!
### 🔄 **教學檢核點**
**事件處理理解**:開始初始化前,請確認你能:
- ✅ 解釋 `addEventListener` 如何將使用者行為與 JavaScript 函式連結
- ✅ 理解為何將事件物件 `(e)` 傳給處理函式
- ✅ 辨識 `submit``click` 事件的差異
- ✅ 描述 `init()` 函式何時執行以及原因
**小自測**:如果忘記 `e.preventDefault()` 在表單送出時會發生什麼?
*答案:頁面會重新載入,導致所有 JavaScript 狀態消失並中斷使用者體驗*
## 編寫初始化和重置功能
讓我們建立擴充功能的初始化邏輯。`init()` 函式就像船隻的導航系統檢查儀器一樣——判斷當前狀態並相應調整介面。它會檢查是否首次使用你的擴充功能,並載入之前的設定。
`reset()` 函式則提供使用者重新開始的機會——就像科學家在不同實驗間重置儀器,確保資料乾淨。
```javascript
function init() {
// 檢查用戶是否之前已儲存 API 認證資料
const storedApiKey = localStorage.getItem('apiKey');
const storedRegion = localStorage.getItem('regionName');
// 將擴充功能圖示設為通用綠色(未來教學的佔位符)
// 待辦事項:在下一課中實作圖示更新
if (storedApiKey === null || storedRegion === null) {
// 首次使用者:顯示設定表單
form.style.display = 'block';
results.style.display = 'none';
loading.style.display = 'none';
clearBtn.style.display = 'none';
errors.textContent = '';
} else {
// 回歸使用者:自動載入其已儲存的資料
displayCarbonUsage(storedApiKey, storedRegion);
results.style.display = 'none';
form.style.display = 'none';
clearBtn.style.display = 'block';
}
}
function reset(e) {
e.preventDefault();
// 清除已儲存的地區設定以允許用戶選擇新位置
localStorage.removeItem('regionName');
// 重新啟動初始化過程
init();
}
```
**解析這裡發生了什麼:**
- **從** 瀏覽器本地儲存取回儲存的 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/zh-HK/localstorage.472f8147b6a3f8d1.webp)
> ⚠️ **安全注意**:在正式應用中,將 API 金鑰儲存在 LocalStorage 有安全風險,因為任何 JavaScript 都能讀取。學習用途可以暫時使用,但實際應用應採用安全的伺服器端儲存敏感憑證。
## 處理表單提交事件
現在我們來處理使用者提交表單時的行為。瀏覽器預設表單送出會重整頁面,我們將攔截這個行為,創造更順暢的體驗。
這類似任務控制中心管理太空船通訊——不會為每次通訊重置整個系統,而是維持持續運作並同步處理新資訊。
建立一個函式來捕捉表單提交事件,並提取使用者輸入:
```javascript
function handleSubmit(e) {
e.preventDefault();
setUpUser(apiKey.value, region.value);
}
```
**上述程式碼中,我們:**
- **阻止** 預設的表單送出重整行為
- **擷取** API 金鑰與區域兩個輸入欄位的使用者值
- **傳遞** 表單資料給 `setUpUser()` 函式處理
- **保持** 單頁應用行為,不重新載入頁面
✅ 請記得你的 HTML 表單欄位包含 `required` 屬性,所以瀏覽器會自動驗證使用者必填 API 金鑰和區域,確保此函式執行時輸入值有效。
## 設定使用者偏好
`setUpUser` 函式負責儲存使用者憑證並啟動第一次 API 呼叫。這為從設定跳轉到顯示結果提供流暢體驗。
```javascript
function setUpUser(apiKey, regionName) {
// 儲存用戶憑證以供未來使用
localStorage.setItem('apiKey', apiKey);
localStorage.setItem('regionName', regionName);
// 更新用戶介面以顯示加載狀態
loading.style.display = 'block';
errors.textContent = '';
clearBtn.style.display = 'block';
// 使用戶憑證獲取碳排放使用數據
displayCarbonUsage(apiKey, regionName);
}
```
**步驟解析:**
- **把** 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 {
// 從 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.error('Error fetching carbon data:', error);
// 顯示易明的錯誤訊息
loading.style.display = 'none';
results.style.display = 'none';
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` 然後在瀏覽器重新載入擴充功能以測試。你現在擁有一個功能完整的碳足跡追蹤器。下一課會加上動態圖示功能讓擴充功能更完整。
---
## GitHub Copilot Agent 挑戰 🚀
使用 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 選擇一個,並建立一個小型示範:
- [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 的現代替代方案
**研究時可思考的問題:**
- 這個 API 解決了哪些現實世界問題?
- API 如何處理錯誤及極端狀況?
- 使用此 API 有哪些安全考量?
- 這個 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)
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件乃透過 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於確保準確性,但請注意自動翻譯可能包含錯誤或不準確之處。原始語言文件應視為權威資料來源。對於重要資訊,建議採用專業人工翻譯。我們對於因使用本翻譯而導致的任何誤解或誤譯概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -0,0 +1,100 @@
# 採用 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 實務
## 評分標準
| 標準 | 優秀 (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 -->
**免責聲明**
本文件由 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻譯而成。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原文文件的母語版本應被視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用本翻譯而引致的任何誤解或誤釋概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -0,0 +1,588 @@
# 瀏覽器擴充套件專案 第三部分:了解背景任務與效能
```mermaid
journey
title 你的效能優化旅程
section 基礎
學習瀏覽器工具: 3: Student
理解剖析工具: 4: Student
識別瓶頸: 4: Student
section 擴展功能
建立色彩系統: 4: Student
建立背景任務: 5: Student
動態更新圖標: 5: Student
section 優化
監控效能: 5: Student
調試問題: 4: Student
打磨體驗: 5: Student
```
是否曾想過,為何有些瀏覽器擴充套件感覺反應靈敏,而有些則顯得遲緩?秘密就在背後正在發生的事情。當使用者在你的擴充套件介面中點擊時,背後有一整個世界的背景程序正悄悄管理資料擷取、圖示更新和系統資源。
這是我們瀏覽器擴充套件系列的最後一課,我們將讓你的碳足跡追蹤器順暢運作。你將新增動態圖示更新,並學會如何在效能問題成為麻煩前就發現它們。這就像調校賽車—小小的優化可以讓一切運行更順暢。
完成後,你會有一個磨練好的擴充套件,並了解區分好與優秀 Web 應用的效能原則。讓我們潛入瀏覽器優化的世界吧。
## 課前小測驗
[課前小測驗](https://ff-quizzes.netlify.app/web/quiz/27)
### 簡介
在我們之前的課程中,你建立了表單,連接到 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/zh-HK/profiler.5a4a62479c5df01c.webp)
✅ 若想深入學習,參考 [Microsoft 文件](https://docs.microsoft.com/microsoft-edge/devtools-guide/performance/?WT.mc_id=academic-77807-sagibbon) 裡有豐富細節
> 專家小撇步:測試前先清除瀏覽器快取,看看第一次造訪者的效能通常和重複造訪有何不同!
選取剖面時間軸的元素,可放大查看載入過程中發生的事件。
選取剖面時間軸的一部分,查看摘要面板,以獲取頁面效能快照:
![Edge profiler snapshot](../../../../translated_images/zh-HK/snapshot.97750180ebcad737.webp)
查看事件紀錄面板,看看是否有事件耗時超過 15 毫秒:
![Edge event log](../../../../translated_images/zh-HK/log.804026979f3707e0.webp)
✅ 熟悉你的剖析器!打開本網站的開發者工具,看看是否有哪些瓶頸。最慢載入的資源是什麼?最快的是什麼?
```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) {
// 定義二氧化碳強度範圍(每千瓦時克數)
const co2Scale = [0, 150, 600, 750, 800];
// 對應顏色從綠色(清潔)到深棕色(高碳)
const colors = ['#2AA364', '#F5EB4D', '#9E4229', '#381D02', '#381D02'];
// 尋找最接近輸入的強度數值
const closestNum = co2Scale.sort((a, b) => {
return Math.abs(a - value) - Math.abs(b - value);
})[0];
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 } });
}
```
**來拆解這個巧妙的小函數:**
- **建立** 兩個陣列—一個存放 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()` 函數裡設置預設的綠色圖示:
```javascript
chrome.runtime.sendMessage({
action: 'updateIcon',
value: {
color: 'green',
},
});
```
**這次初始化達到的效果:**
- **設定** 中性的綠色作為預設狀態
- **提供** 擴充套件啟動時立即的視覺回饋
- **建立** 與背景腳本的通訊模式
- **確保** 在資料載入前用戶能看到可用的擴充套件
### 呼叫函數,執行更新
現在將所有東西連結起來,當新 CO2 資料到達時,你的圖示會自動用正確顏色更新。就像電子裝置接通最後一條電路—各部分突然如一個系統般協同運作。
在取得 API 的 CO2 資料後,加入這行:
```javascript
// 從 API 取得二氧化碳數據後
// 讓 CO2 = data.data[0].intensity.actual;
calculateColor(CO2);
```
**這種整合具備:**
- **連結** API 資料流程與視覺指標系統
- **當新資料到達**,自動觸發圖示更新
- **確保** 依現行碳強度即時視覺回饋
- **保持** 資料擷取與顯示邏輯的職責分離
最後,在 `/dist/background.js` 加入監聽這些背景操作請求的事件監聽器:
```javascript
// 監聽來自內容腳本的訊息
chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
if (msg.action === 'updateIcon') {
chrome.action.setIcon({ imageData: drawIcon(msg.value) });
}
});
// 使用 Canvas API 繪製動態圖示
// 借用自 energy lollipop 擴充功能 - 很好的功能!
function drawIcon(value) {
// 建立一個離屏畫布以提升效能
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 動態創建新圖示
- **繪製** 表示當前碳強度的簡單彩色圓圈
- **更新** 你的瀏覽器工具列上的圖示
- **使用** 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 在擴充功能架構中扮演什麼角色?
- ✅ 顏色計算算法如何將資料映射到視覺反饋?
**效能考量**:你的擴充功能現在展現了:
- **高效訊息傳遞**:腳本語境間的乾淨通訊
- **優化渲染**OffscreenCanvas 防止 UI 阻塞
- **即時更新**:基於即時資料動態改變圖示
- **記憶體管理**:適當的清理與資源處理
**現在是測試你的擴充功能的時候了:**
- **用** `npm run build` **編譯**所有內容
- **在瀏覽器重新載入**你的擴充功能(別忘了這一步)
- **打開**你的擴充功能,觀察圖示顏色變化
- **檢查**它如何對全球的實時碳排放資料反應
現在你一眼就能知道洗衣服是否適合時機,還是應該等更乾淨的能源。你剛完成了真正有用的東西,並且在過程中學習了瀏覽器效能。
## GitHub Copilot 代理人挑戰 🚀
使用代理人模式完成以下挑戰:
**描述:** 增強瀏覽器擴充功能的效能監控能力,新增追蹤並顯示擴充功能各組件載入時間的功能。
**提示:** 建立一個性能監控系統,用於測量和記錄從 API 抓取 CO2 資料、計算顏色及更新圖示所需的時間。新增名為 `performanceTracker` 的函式,利用 Performance API 來測量這些操作,並在瀏覽器控制台中顯示帶有時間戳和持續時間指標的結果。
在此了解更多 [agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode)。
## 🚀 挑戰
來個有趣的偵探任務:挑幾個存在多年的開源網站(想想 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)
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件是使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻譯而成。雖然我們盡力確保準確性,但請注意,自動翻譯可能包含錯誤或不準確之處。文件的原始語言版本應被視為權威來源。對於重要資訊,建議採用專業人工翻譯。本公司不會對使用本翻譯所引起的任何誤解或曲解負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save