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

pull/1668/head
localizeflow[bot] 1 week ago
parent 2de47cc7f9
commit 1546839c2d

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 774 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 537 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 538 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 688 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 716 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 430 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 430 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 430 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 430 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 430 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 430 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 831 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 831 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 828 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 833 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 823 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 830 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

@ -1,23 +1,38 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "ccfcd8c2932761359fbaff3d6b01ace4",
"translation_date": "2025-08-23T22:57:20+00:00",
"original_hash": "c162b3b3a1cafc1483c8015e9b266f0d",
"translation_date": "2026-01-06T11:32:45+00:00",
"source_file": "6-space-game/3-moving-elements-around/assignment.md",
"language_code": "zh"
}
-->
# 注释你的代码
# 评论你的代码
## 指
## 指
查看游戏文件夹中的当前 /app.js 文件,寻找方法为其添加注释并整理代码。代码很容易变得混乱,现在是一个好机会添加注释,确保代码可读,以便以后使用。
清晰、良好注释的代码对于维护和共享项目至关重要。在本作业中,您将练习专业开发者的一个重要习惯:编写清晰、有帮助的注释,解释代码的目的和功能。
检查你游戏文件夹中的当前 `app.js` 文件,找出为其添加注释和整理代码的方法。代码很容易失控,现在是添加注释的好机会,以确保你拥有可读的代码,便于以后使用。
**你的任务包括:**
- **添加注释**,解释每个主要代码部分的作用
- **文档化函数**,清晰描述其目的和参数
- **将代码组织** 成逻辑模块,并添加部分标题
- **删除** 任何未使用或冗余的代码
- **使用一致的** 变量和函数命名规范
## 评分标准
| 标准 | 卓越表现 | 合格表现 | 需要改进 |
| -------- | ---------------------------------------------------------------- | ---------------------------------- | ---------------------------------------------------------- |
| | `app.js` 代码完全注释并组织成逻辑块 | `app.js` 代码有适当的注释 | `app.js` 代码有些混乱,缺乏良好的注释 |
| 评估标准 | 优秀 | 及格 | 需要改进 |
| -------- | --------- | -------- | ----------------- |
| **代码文档** | `app.js` 代码完全注释,对所有主要部分和函数有清晰、有帮助的解释 | `app.js` 代码有充分注释,对大多数部分有基本说明 | `app.js` 代码注释很少,缺乏清晰的说明 |
| **代码组织** | 代码组织成逻辑模块,有清晰的部分标题和一致的结构 | 代码有一定组织,对相关功能进行了基本分组 | 代码有些杂乱,难以理解 |
| **代码质量** | 所有变量和函数使用描述性名称,无未使用代码,风格一致 | 大部分代码使用良好命名,未使用代码较少 | 变量命名不清晰,含未使用代码,风格不一致 |
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免责声明**
本文档使用AI翻译服务 [Co-op Translator](https://github.com/Azure/co-op-translator) 进行翻译。尽管我们努力确保翻译的准确性,但请注意,自动翻译可能包含错误或不准确之处。应以原始语言的文档作为权威来源。对于关键信息,建议使用专业人工翻译。因使用本翻译而导致的任何误解或误读,我们概不负责。
本文件使用 AI 翻译服务 [Co-op Translator](https://github.com/Azure/co-op-translator) 进行翻译。虽然我们力求准确,但请注意自动翻译可能包含错误或不准确之处。原始语言的文档应被视为权威来源。对于重要信息,建议使用专业人工翻译。我们不对因使用本翻译而产生的任何误解或误释承担责任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,131 +1,325 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "a6ce295ff03bb49df7a3e17e6e7100a0",
"translation_date": "2025-08-29T14:46:26+00:00",
"original_hash": "039b4d8ce65f5edd82cf48d9c3e6728c",
"translation_date": "2026-01-06T11:22:28+00:00",
"source_file": "6-space-game/4-collision-detection/README.md",
"language_code": "zh"
}
-->
# 构建太空游戏第四部分:添加激光和检测碰撞
# 构建太空游戏 第4部分添加激光枪并检测碰撞
```mermaid
journey
title 你的碰撞检测之旅
section 物理基础
理解矩形: 3: Student
学习交叉数学: 4: Student
掌握坐标系: 4: Student
section 游戏机制
实现激光发射: 4: Student
添加对象生命周期: 5: Student
创建碰撞规则: 5: Student
section 系统集成
构建碰撞检测: 5: Student
优化性能: 5: Student
测试交互系统: 5: Student
```
## 课前测验
[课前测验](https://ff-quizzes.netlify.app/web/quiz/35)
在本课中,你将学习如何使用 JavaScript 发射激光!我们将为游戏添加以下两项内容:
- **激光**:从英雄的飞船发射,垂直向上移动。
- **碰撞检测**:作为实现射击功能的一部分,我们还会添加一些有趣的游戏规则:
- **激光击中敌人**:敌人被激光击中后会死亡。
- **激光击中屏幕顶部**:激光击中屏幕顶部后会被销毁。
- **敌人与英雄碰撞**:敌人与英雄相撞后双方都会被销毁。
- **敌人到达屏幕底部**:敌人到达屏幕底部后,敌人和英雄都会被销毁。
简而言之,你——*英雄*——需要在敌人到达屏幕底部之前用激光击中所有敌人。
✅ 研究一下历史上第一个计算机游戏的功能是什么?
让我们一起成为英雄吧!
想想《星球大战》中卢克的质子鱼雷击中死星排气口的那一刻。那精确的碰撞检测改变了银河系的命运!在游戏中,碰撞检测也一样——它决定了物体何时交互以及接下来发生什么。
在本课中,你将为你的太空游戏添加激光武器并实现碰撞检测。就像 NASA 任务规划人员计算航天器轨迹以避免碎片一样,你将学会检测游戏对象何时相交。我们会分解成相互构建的可管理步骤。
到课末,你将拥有一个功能完整的战斗系统,激光可以摧毁敌人,碰撞会触发游戏事件。这些碰撞原理被广泛应用于从物理模拟到互动网页界面的所有领域。
```mermaid
mindmap
root((碰撞检测))
Physics Concepts
Rectangle Boundaries
Intersection Testing
Coordinate Systems
Separation Logic
Game Objects
Laser Projectiles
Enemy Ships
Hero Character
Collision Zones
Lifecycle Management
Object Creation
Movement Updates
Destruction Marking
Memory Cleanup
Event Systems
Keyboard Input
Collision Events
Game State Changes
Audio/Visual Effects
Performance
Efficient Algorithms
Frame Rate Optimization
Memory Management
Spatial Partitioning
```
✅ 做一些关于有史以来第一个计算机游戏的调研。它的功能是什么?
## 碰撞检测
如何进行碰撞检测?我们需要将游戏中的对象视为移动的矩形。为什么要这样做呢?因为用于绘制游戏对象的图像是一个矩形:它有 `x`、`y`、`width` 和 `height`
碰撞检测就像阿波罗登月舱的接近传感器——它不断检查距离,当物体靠得太近时触发警报。在游戏中,这个系统判断物体何时交互及后续动作
如果两个矩形(例如英雄和敌人)*相交*,就发生了碰撞。碰撞后应该发生什么取决于游戏规则。为了实现碰撞检测,你需要以下内容:
我们使用的方法将每个游戏对象视为矩形,就像空中交通控制系统使用简化的几何形状来跟踪飞机一样。这个矩形方法看似简单,但计算效率高,适合大多数游戏场景。
1. 获取游戏对象的矩形表示方法,例如:
### 矩形表示
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width
}
}
```
每个游戏对象都需要坐标边界,类似火星探路者漫游车如何映射其在火星表面的位置。以下是我们定义边界坐标的方法:
2. 比较函数,这个函数可以像这样:
```mermaid
flowchart TD
A["🎯 游戏对象"] --> B["📍 位置 (x, y)"]
A --> C["📏 尺寸 (宽度, 高度)"]
B --> D["顶部: y"]
B --> E["左侧: x"]
C --> F["底部: y + 高度"]
C --> G["右侧: x + 宽度"]
D --> H["🔲 矩形边界"]
E --> H
F --> H
G --> H
H --> I["碰撞检测准备就绪"]
style A fill:#e3f2fd
style H fill:#e8f5e8
style I fill:#fff3e0
```
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width
}
}
```
**我们来拆解一下:**
- **上边缘**:就是对象的垂直起点(它的 y 坐标)
- **左边缘**:对象的水平起点(它的 x 坐标)
- **下边缘**:加上高度后的 y 坐标,知道它的底部位置!
- **右边缘**:加上宽度后的 x 坐标,完整边界就确定了
```javascript
function intersectRect(r1, r2) {
return !(r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top);
}
```
### 相交算法
## 如何销毁对象
检测矩形相交的逻辑类似哈勃太空望远镜判断天体是否重叠视场。算法检查两个矩形是否存在分离:
在游戏中销毁对象需要让游戏知道在某个时间间隔触发的游戏循环中不再绘制该对象。实现方法是当某些事件发生时将游戏对象标记为*死亡*,例如:
```mermaid
flowchart LR
A["矩形 1"] --> B{"分离测试"}
C["矩形 2"] --> B
B --> D["R2 左侧 > R1 右侧?"]
B --> E["R2 右侧 < R1 "]
B --> F["R2 顶部 > R1 底部?"]
B --> G["R2 底部 < R1 "]
D --> H{"有任一为真?"}
E --> H
F --> H
G --> H
H -->|是| I["❌ 无碰撞"]
H -->|否| J["✅ 检测到碰撞"]
style B fill:#e3f2fd
style I fill:#ffebee
style J fill:#e8f5e8
```
```javascript
function intersectRect(r1, r2) {
return !(r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top);
}
```
**分离测试工作原理就像雷达系统:**
- 矩形 2 是否完全在矩形 1 的右侧?
- 矩形 2 是否完全在矩形 1 的左侧?
- 矩形 2 是否完全在矩形 1 的下面?
- 矩形 2 是否完全在矩形 1 的上面?
如果上述条件都不成立,矩形必然重叠。这种方法类似雷达操作员确定两架飞机是否保持安全距离。
## 管理对象生命周期
当激光击中敌人时,两个对象都需要从游戏中删除。然而,在循环过程中直接删除对象可能导致崩溃——这是早期计算机系统如阿波罗导航计算机走过的弯路。我们采用“标记删除”的方法,在帧与帧之间安全移除对象。
```mermaid
stateDiagram-v2
[*] --> Active: 对象创建
Active --> Collided: 检测到碰撞
Collided --> MarkedDead: 设定 dead = true
MarkedDead --> Filtered: 下一帧
Filtered --> [*]: 对象移除
Active --> OutOfBounds: 离开屏幕
OutOfBounds --> MarkedDead
note right of MarkedDead
当前帧
继续安全
end note
note right of Filtered
帧间
移除对象
end note
```
这是我们标记删除的方法:
```javascript
// collision happened
enemy.dead = true
// 标记对象待移除
enemy.dead = true;
```
**这种方法的优点:**
- 标记对象为“死亡”,但暂不删除
- 允许当前游戏帧安全完成
- 避免因使用已删除对象而崩溃!
然后在重新绘制屏幕之前处理*死亡*对象,例如:
然后在下一渲染周期前过滤标记的对象
```javascript
gameObjects = gameObject.filter(go => !go.dead);
gameObjects = gameObjects.filter(go => !go.dead);
```
**此过滤操作作用:**
- 创建只包含“存活”对象的新列表
- 丢弃被标记为死亡的对象
- 保持游戏流畅运行
- 防止销毁对象累积导致内存膨胀
## 实现激光机制
游戏中的激光投射物类似于《星际迷航》中的光子鱼雷——它们是沿直线飞行的离散物体直到命中目标。每次按空格键会创建一个新的激光对象,在屏幕上移动。
## 如何发射激光
要实现这个功能,我们需要协调几个部分:
发射激光意味着响应按键事件并创建一个向某个方向移动的对象。因此,我们需要完成以下步骤:
**关键组件:**
- **创建** 从英雄位置生成的激光对象
- **处理** 键盘输入以触发激光创建
- **管理** 激光运动和生命周期
- **实现** 激光投射物的视觉表现
1. **创建激光对象**:从英雄飞船顶部发射,创建后开始向屏幕顶部移动。
2. **绑定按键事件代码**:选择键盘上的某个按键来代表玩家发射激光。
3. **创建一个看起来像激光的游戏对象**:当按键被按下时。
## 实现开火速率控制
## 激光的冷却时间
无限制的开火速率会让游戏引擎超负荷,也让游戏太容易。真实武器系统也有类似限制——即使是企业号的相位枪也需要时间充能。
激光需要在每次按键时发射,例如按下*空格键*。为了防止游戏在短时间内生成过多激光,我们需要解决这个问题。解决方法是实现所谓的*冷却时间*,即一个计时器,确保激光只能以一定频率发射。可以这样实现:
我们实现冷却系统防止速射刷屏,同时保持响应
```mermaid
sequenceDiagram
participant Player
participant Weapon
participant Cooldown
participant Game
Player->>Weapon: 按下空格键
Weapon->>Cooldown: 检查是否冷却
alt 武器已准备好
Cooldown->>Weapon: cool = true
Weapon->>Game: 创建激光
Weapon->>Cooldown: 开始新的冷却
Cooldown->>Cooldown: cool = false
Note over Cooldown: 等待 500 毫秒
Cooldown->>Cooldown: cool = true
else 武器正在冷却
Cooldown->>Weapon: cool = false
Weapon->>Player: 无操作
end
```
```javascript
class Cooldown {
constructor(time) {
this.cool = false;
setTimeout(() => {
this.cool = true;
}, time)
}, time);
}
}
class Weapon {
constructor {
constructor() {
this.cooldown = null;
}
fire() {
if (!this.cooldown || this.cooldown.cool) {
// produce a laser
// 创建激光投射物
this.cooldown = new Cooldown(500);
} else {
// do nothing - it hasn't cooled down yet.
// 武器仍在冷却中
}
}
}
```
**冷却系统原理:**
- 创建时武器处于“热”状态(暂不可发射)
- 经过超时时间后变“冷”状态(准备发射)
- 发射前检查:武器是否处于“冷”状态?
- 防止连点刷屏,响应仍然灵敏
✅ 参考太空游戏第一课复习冷却机制。
## 构建碰撞系统
你将扩展已有太空游戏代码,实现碰撞检测系统。像国际空间站自动避碰系统一样,你的游戏会持续监控对象位置并响应相交。
从之前的课程代码出发,添加带规则的碰撞检测,管理对象交互。
> 💡 **小技巧**:激光精灵已包含在资源文件夹并在代码中引用,准备好使用。
✅ 回顾太空游戏系列第一课,了解有关*冷却时间*的内容。
### 要实现的碰撞规则
## 要构建的内容
**游戏机制:**
1. **激光击中敌人**:激光击中敌人时,敌人被摧毁
2. **激光触及屏幕边界**:激光到达屏幕顶部时被移除
3. **敌人与英雄碰撞**:两者交叉时均被摧毁
4. **敌人到底部**:敌人到达屏幕底部时游戏结束
你将使用上一课中清理和重构过的代码进行扩展。可以从第二部分的代码开始,也可以使用[第三部分的起始代码](../../../../../../../../../your-work)。
### 🔄 **教学检查点**
**碰撞检测基础**:实现前确认自己理解:
- ✅ 矩形边界如何定义碰撞区域
- ✅ 为什么分离测试比求交效率高
- ✅ 对象生命周期管理为何游戏循环关键
- ✅ 事件驱动系统协调碰撞响应的方法
> 提示:你将使用的激光已经在你的资源文件夹中,并且代码中已经引用了它。
**快速自测**:如果你立即删除对象会发生什么?
*答:循环中间删除可能导致崩溃或漏遍历*
- **添加碰撞检测**:当激光与某物体碰撞时,应该遵循以下规则:
1. **激光击中敌人**:敌人被激光击中后会死亡。
2. **激光击中屏幕顶部**:激光击中屏幕顶部后会被销毁。
3. **敌人与英雄碰撞**:敌人与英雄相撞后双方都会被销毁。
4. **敌人到达屏幕底部**:敌人到达屏幕底部后,敌人和英雄都会被销毁。
**物理理解**:你现已掌握:
- **坐标系**:位置与尺寸定义边界
- **相交逻辑**:碰撞检测中的数学原理
- **性能优化**:实时系统中高效算法意义
- **内存管理**:保证稳定性的安全生命周期模式
## 推荐步骤
## 设置开发环境
找到在 `your-work` 子文件夹中为你创建的文件。它应该包含以下内容:
好消息——大部分基础已帮你搭好!所有游戏资源和基本结构都位于 `your-work` 子文件夹,等待你添加酷炫的碰撞功能。
### 项目结构
```bash
-| assets
@ -136,162 +330,431 @@ class Weapon {
-| app.js
-| package.json
```
**文件结构说明:**
- **包含** 游戏对象所需全部精灵图像
- **含有** 主要 HTML 文档和 JavaScript 应用文件
- **提供** 本地开发服务器的配置文件
### 启动开发服务器
通过输入以下命令启动项目:
进入项目文件夹启动本地服务器
```bash
cd your-work
npm start
```
**此命令执行步骤:**
- **切换** 到你的工作项目目录
- **启动** 本地 HTTP 服务器,地址为 `http://localhost:5000`
- **提供** 游戏文件供测试和开发使用
- **支持** 自动刷新实现实时开发
打开浏览器访问 `http://localhost:5000`,你将看到当前游戏状态,英雄和敌人呈现屏幕上。
### 逐步实现
如同 NASA 规划航海者号软件那样,我们将有系统地逐步实现碰撞检测功能。
```mermaid
flowchart TD
A["1. 矩形边界"] --> B["2. 相交检测"]
B --> C["3. 激光系统"]
C --> D["4. 事件处理"]
D --> E["5. 碰撞规则"]
E --> F["6. 冷却系统"]
G["对象边界"] --> A
H["物理算法"] --> B
I["投射物创建"] --> C
J["键盘输入"] --> D
K["游戏逻辑"] --> E
L["速率限制"] --> F
F --> M["🎮 完整游戏"]
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
style E fill:#e0f2f1
style F fill:#fce4ec
style M fill:#e1f5fe
```
#### 1. 添加矩形碰撞边界
首先教你的游戏对象如何描述自身边界。将此方法添加至你的 `GameObject` 类:
上述命令将在地址 `http://localhost:5000` 上启动一个 HTTP 服务器。打开浏览器并输入该地址,目前应该可以渲染英雄和所有敌人,但它们还不会移动。
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width,
};
}
```
**此方法作用:**
- **创建** 带精确边界坐标的矩形对象
- **计算** 底边和右边坐标(位置加尺寸)
- **返回** 可供碰撞检测算法使用的对象
- **提供** 给所有游戏对象统一接口
#### 2. 实现相交检测
接下来创建碰撞侦测函数,用于判断两个矩形是否重叠:
```javascript
function intersectRect(r1, r2) {
return !(
r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top
);
}
```
**算法逻辑:**
- **测试** 矩形间四种分离情况
- **如果** 存在分离,则返回 `false`(无碰撞)
- **无分离时** 表示发生碰撞,返回 `true`
- **使用** 取反逻辑提高效率
### 添加代码
#### 3. 实现激光发射系统
现在进入令人激动的部分!我们设置激光发射功能。
##### 消息常量
先定义消息类型,便于游戏各部分通信:
```javascript
KEY_EVENT_SPACE: "KEY_EVENT_SPACE",
COLLISION_ENEMY_LASER: "COLLISION_ENEMY_LASER",
COLLISION_ENEMY_HERO: "COLLISION_ENEMY_HERO",
```
**这些常量提供:**
- **统一** 应用程序内事件命名
- **确保** 各系统通信一致性
- **避免** 事件处理时拼写错误
1. **设置游戏对象的矩形表示方法以处理碰撞** 以下代码允许你获取 `GameObject` 的矩形表示方法。编辑你的 GameObject 类以扩展它:
##### 键盘输入处理
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width,
};
在键盘事件监听器中添加空格键检测:
```javascript
} else if(evt.keyCode === 32) {
eventEmitter.emit(Messages.KEY_EVENT_SPACE);
}
```
**该输入处理器:**
- **监听** keyCode 32 空格键
- **发送** 标准化事件消息
- **实现** 触发发射逻辑的解耦
##### 事件监听设置
在你的 `initGame()` 函数中注册发射行为:
```javascript
eventEmitter.on(Messages.KEY_EVENT_SPACE, () => {
if (hero.canFire()) {
hero.fire();
}
});
```
**事件监听器:**
- **响应** 空格键事件
- **检查** 冷却状态是否允许发射
- **允许时** 触发激光对象创建
添加激光与敌人碰撞事件处理:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
});
```
**碰撞处理器:**
- **接收** 碰撞事件数据,包括两个对象
- **标记** 两个对象为待删除
- **确保** 碰撞后正确清理
#### 4. 创建激光类
实现激光投射物类,使其向上移动并管理生命周期:
```javascript
class Laser extends GameObject {
constructor(x, y) {
super(x, y);
this.width = 9;
this.height = 33;
this.type = 'Laser';
this.img = laserImg;
let id = setInterval(() => {
if (this.y > 0) {
this.y -= 15;
} else {
this.dead = true;
clearInterval(id);
}
```
2. **添加检测碰撞的代码** 这是一个新函数,用于测试两个矩形是否相交:
```javascript
function intersectRect(r1, r2) {
return !(
r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top
);
}
```
3. **添加激光发射功能**
1. **添加按键事件消息**。按下*空格键*时应该在英雄飞船上方创建一个激光。为 Messages 对象添加三个常量:
```javascript
KEY_EVENT_SPACE: "KEY_EVENT_SPACE",
COLLISION_ENEMY_LASER: "COLLISION_ENEMY_LASER",
COLLISION_ENEMY_HERO: "COLLISION_ENEMY_HERO",
```
1. **处理空格键**。编辑 `window.addEventListener` 的 keyup 函数以处理空格键:
```javascript
} else if(evt.keyCode === 32) {
eventEmitter.emit(Messages.KEY_EVENT_SPACE);
}
```
1. **添加监听器**。编辑 `initGame()` 函数以确保按下空格键时英雄可以发射激光:
```javascript
eventEmitter.on(Messages.KEY_EVENT_SPACE, () => {
if (hero.canFire()) {
hero.fire();
}
```
并添加一个新的 `eventEmitter.on()` 函数以确保当敌人与激光碰撞时的行为:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
})
```
1. **移动对象**,确保激光逐渐移动到屏幕顶部。你将创建一个新的 Laser 类,继承 `GameObject`,如之前所做:
```javascript
class Laser extends GameObject {
constructor(x, y) {
super(x,y);
(this.width = 9), (this.height = 33);
this.type = 'Laser';
this.img = laserImg;
let id = setInterval(() => {
if (this.y > 0) {
this.y -= 15;
} else {
this.dead = true;
clearInterval(id);
}
}, 100)
}
}, 100);
}
}
```
**此类实现:**
- **继承** GameObject 以获得基础功能
- **设置** 合适的激光精灵尺寸
- **使用** setInterval() 实现自动向上移动
- **在** 到达顶部时自我销毁
- **管理** 自己的动画时间和清理
#### 5. 实现碰撞检测系统
创建完整碰撞检测函数:
```javascript
function updateGameObjects() {
const enemies = gameObjects.filter(go => go.type === 'Enemy');
const lasers = gameObjects.filter(go => go.type === "Laser");
// 测试激光与敌人的碰撞
lasers.forEach((laser) => {
enemies.forEach((enemy) => {
if (intersectRect(laser.rectFromGameObject(), enemy.rectFromGameObject())) {
eventEmitter.emit(Messages.COLLISION_ENEMY_LASER, {
first: laser,
second: enemy,
});
}
```
1. **处理碰撞**,实现激光的碰撞规则。添加一个 `updateGameObjects()` 函数,用于测试碰撞对象是否发生击中:
```javascript
function updateGameObjects() {
const enemies = gameObjects.filter(go => go.type === 'Enemy');
const lasers = gameObjects.filter((go) => go.type === "Laser");
// laser hit something
lasers.forEach((l) => {
enemies.forEach((m) => {
if (intersectRect(l.rectFromGameObject(), m.rectFromGameObject())) {
eventEmitter.emit(Messages.COLLISION_ENEMY_LASER, {
first: l,
second: m,
});
}
});
});
gameObjects = gameObjects.filter(go => !go.dead);
}
```
确保在 `window.onload` 的游戏循环中添加 `updateGameObjects()`
4. **实现激光的冷却时间**,确保激光只能以一定频率发射。
最后,编辑 Hero 类以实现冷却功能:
```javascript
class Hero extends GameObject {
constructor(x, y) {
super(x, y);
(this.width = 99), (this.height = 75);
this.type = "Hero";
this.speed = { x: 0, y: 0 };
this.cooldown = 0;
}
fire() {
gameObjects.push(new Laser(this.x + 45, this.y - 10));
this.cooldown = 500;
let id = setInterval(() => {
if (this.cooldown > 0) {
this.cooldown -= 100;
} else {
clearInterval(id);
}
}, 200);
}
canFire() {
return this.cooldown === 0;
}
});
});
// 移除已销毁的对象
gameObjects = gameObjects.filter(go => !go.dead);
}
```
**该碰撞系统:**
- **根据类型** 过滤游戏对象提高测试效率
- **测试** 所有激光与所有敌人是否相交
- **发现碰撞时** 发送碰撞事件
- **碰撞处理后** 清理已销毁对象
> ⚠️ **重要**:在主游戏循环(`window.onload`)中添加 `updateGameObjects()` 以启用碰撞检测。
#### 6. 为英雄类添加冷却系统
为英雄类增强发射机制和速率控制:
```javascript
class Hero extends GameObject {
constructor(x, y) {
super(x, y);
this.width = 99;
this.height = 75;
this.type = "Hero";
this.speed = { x: 0, y: 0 };
this.cooldown = 0;
}
fire() {
gameObjects.push(new Laser(this.x + 45, this.y - 10));
this.cooldown = 500;
let id = setInterval(() => {
if (this.cooldown > 0) {
this.cooldown -= 100;
} else {
clearInterval(id);
}
```
}, 200);
}
canFire() {
return this.cooldown === 0;
}
}
```
**增强后的英雄类解析:**
- **初始化** 冷却计时器为零(处于可发射状态)
- **创建** 激光对象,位置设定于英雄舰船上方
- **设置** 冷却周期防止连续快速发射
- **通过** 定时器递减冷却时间
- **提供** `canFire()` 方法判断是否可发射
### 🔄 **教学检查点**
**完整系统理解**:确认你掌握碰撞系统:
- ✅ 矩形边界怎样实现高效碰撞检测?
- ✅ 为什么对象生命周期管理对游戏稳定关键?
- ✅ 冷却系统如何避免性能问题?
- ✅ 事件驱动框架在碰撞处理中的作用?
**系统整合**:你的碰撞检测系统体现了:
- **数学精度**:矩形相交算法
- **性能优化**:高效碰撞测试模式
- **内存管理**:安全创建与销毁对象
- **事件协调**:解耦系统通信
- **实时处理**:基于帧的更新机制
**专业模式**:你已经实现了:
- **职责分离**:物理、渲染与输入解耦
- **面向对象设计**:继承与多态
- **状态管理**:对象生命周期和游戏状态跟踪
- **性能优化**:适合实时的高效算法
### 测试你的实现
你的太空游戏现已具备完整碰撞检测与战斗机制。🚀 测试这些新功能:
- **用箭头键** 验证移动控制
- **按空格键** 发射激光,注意冷却防止速射刷屏
- **观察碰撞效果** :激光击中敌人触发移除
- **验证清理** :被摧毁对象从游戏中消失
你已成功实现一个基于航天导航与机器人技术数学原理的碰撞检测系统。
### ⚡ **你可以在接下来5分钟内做的事**
- [ ] 打开浏览器开发者工具,在碰撞检测函数设置断点
- [ ] 尝试调整激光速度或敌人移动,观察碰撞效果
- [ ] 实验不同的冷却时间以测试开火速率
- [ ] 添加 `console.log` 语句以实时跟踪碰撞事件
### 🎯 **你这一小时可以完成的目标**
- [ ] 完成课后测验并理解碰撞检测算法
- [ ] 添加碰撞时的视觉特效,如爆炸效果
- [ ] 实现具有不同属性的各种类型投射物
- [ ] 创建可暂时增强玩家能力的强化道具
- [ ] 添加碰撞音效,使体验更具满足感
### 📅 **你的周长物理编程计划**
- [ ] 完成带有完善碰撞系统的完整太空游戏
- [ ] 实现矩形之外的高级碰撞形状(圆形、多边形)
- [ ] 添加粒子系统以实现逼真的爆炸效果
- [ ] 创建具有碰撞规避的复杂敌人行为
- [ ] 优化碰撞检测以支持大量对象的高性能运行
- [ ] 添加动量和真实运动的物理模拟
### 🌟 **你的月度游戏物理精通计划**
- [ ] 构建使用高级物理引擎和真实模拟的游戏
- [ ] 学习3D碰撞检测和空间划分算法
- [ ] 为开源物理库和游戏引擎做出贡献
- [ ] 掌握图形密集型应用的性能优化
- [ ] 创建关于游戏物理和碰撞检测的教育内容
- [ ] 构建展示高级物理编程技能的作品集
## 🎯 你的碰撞检测精通时间轴
```mermaid
timeline
title 碰撞检测与游戏物理学习进度
section 基础10分钟
Rectangle Math: 坐标系
: 边界计算
: 位置跟踪
: 尺寸管理
section 算法设计20分钟
Intersection Logic: 分离测试
: 重叠检测
: 性能优化
: 边界情况处理
section 游戏实现30分钟
Object Systems: 生命周期管理
: 事件协调
: 状态跟踪
: 内存清理
section 交互功能40分钟
Combat Mechanics: 投射物系统
: 武器冷却
: 伤害计算
: 视觉反馈
section 高级物理50分钟
Real-time Systems: 帧率优化
: 空间划分
: 碰撞响应
: 物理模拟
section 专业技巧(一周)
Game Engine Concepts: 组件系统
: 物理流程
: 性能分析
: 跨平台优化
section 行业应用(一月)
Production Skills: 大规模优化
: 团队协作
: 引擎开发
: 平台部署
```
### 🛠️ 你的游戏物理工具包总结
完成本课后,你已掌握:
- **碰撞数学**:矩形交叉算法和坐标系统
- **性能优化**:适用于实时应用的高效碰撞检测
- **对象生命周期管理**:安全的创建、更新与销毁模式
- **事件驱动架构**:解耦的碰撞响应系统
- **游戏循环集成**:基于帧的物理更新和渲染协调
- **输入系统**:响应式控制,带频率限制和反馈
- **内存管理**:高效的对象池和清理策略
**现实应用场景**:你的碰撞检测技能直接适用于:
- **交互式模拟**:科学建模与教育工具
- **用户界面设计**:拖放交互和触摸检测
- **数据可视化**:交互图表和可点击元素
- **移动开发**:触控手势识别和碰撞处理
- **机器人编程**:路径规划和障碍规避
- **计算机图形学**:光线追踪和空间算法
**职业技能提升**:你现在能够:
- **设计** 实时碰撞检测的高效算法
- **实现** 能适应对象复杂度的物理系统
- **调试** 复杂交互系统,应用数学原理
- **优化** 适配不同硬件和浏览器性能
- **架构** 使用成熟设计模式构建可维护游戏系统
**掌握的游戏开发概念**
- **物理模拟**:实时碰撞检测与响应
- **性能工程**:交互应用的优化算法
- **事件系统**:游戏组件间解耦通信
- **对象管理**:动态内容的高效生命周期模式
- **输入处理**:带反馈的响应式控制
**下一阶段**:你已准备好探索如 Matter.js 等高级物理引擎实现3D碰撞检测或构建复杂粒子系统
到此为止,你的游戏已经具备了一些功能!你可以使用箭头键导航,用空格键发射激光,并且击中敌人时敌人会消失。干得好!
🌟 **成就解锁**:你已构建了具备专业级碰撞检测的完整物理交互系统!
## GitHub Copilot Agent 挑战 🚀
使用 Agent 模式完成以下挑战:
**描述:** 通过实现随机生成的强化道具提升碰撞检测系统,当英雄飞船收集时可提供临时能力。
**提示:** 创建一个继承于 GameObject 的 PowerUp 类,实现英雄与强化道具之间的碰撞检测。至少添加两种强化道具:一种提高射速(减少冷却时间),另一种产生临时护盾。包含在随机时间和位置生成强化道具的逻辑。
---
## 🚀 挑战
添加爆炸效果!查看[太空艺术资源库](../../../../6-space-game/solution/spaceArt/readme.txt)中的游戏资源,尝试在激光击中外星人时添加爆炸效果。
添加一个爆炸效果!查看[Space Art 资源库](../../../../6-space-game/solution/spaceArt/readme.txt)中的游戏素材,尝试在激光击中外星人时添加爆炸效果。
## 课后测验
@ -299,7 +762,7 @@ npm start
## 复习与自学
尝试调整游戏中的时间间隔,观察会发生什么变化?阅读更多关于[JavaScript 定时事件](https://www.freecodecamp.org/news/javascript-timing-events-settimeout-and-setinterval/)的内容。
尝试调整游戏中目前的间隔时间。改变后会发生什么变化?了解更多关于[JavaScript 定时事件](https://www.freecodecamp.org/news/javascript-timing-events-settimeout-and-setinterval/)的内容。
## 作业
@ -307,5 +770,7 @@ npm start
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免责声明**
本文档使用AI翻译服务[Co-op Translator](https://github.com/Azure/co-op-translator)进行翻译。尽管我们努力确保准确性,但请注意,自动翻译可能包含错误或不准确之处。应以原始语言的文档作为权威来源。对于关键信息,建议使用专业人工翻译。对于因使用本翻译而引起的任何误解或误读,我们概不负责。
本文件由 AI 翻译服务 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻译。虽然我们努力保证准确性,但请注意,自动翻译可能存在错误或不准确之处。原始文档的原语言版本应被视为权威来源。对于重要信息,建议采用专业人工翻译。因使用本翻译而产生的任何误解或误释,我们概不负责。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,23 +1,64 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "8a0a097b45e7c75a611e2795e4013f16",
"translation_date": "2025-08-23T23:02:48+00:00",
"original_hash": "124efddbb65166cddb38075ad6dae324",
"translation_date": "2026-01-06T11:23:55+00:00",
"source_file": "6-space-game/4-collision-detection/assignment.md",
"language_code": "zh"
}
-->
# 探索碰撞
## 指导
## 说明
为了更好地理解碰撞的工作原理,制作一个包含少量碰撞物的小型游戏。通过按键或鼠标点击让它们移动,并在其中一个物体被撞击时触发某些事件。可以是类似陨石撞击地球,或者碰碰车的场景。发挥你的创造力!
通过创建一个自定义迷你游戏来应用你的碰撞检测知识,展示不同类型的对象交互。此作业将通过富有创意的实现和实验,帮助你理解碰撞机制。
### 项目要求
**创建一个小型互动游戏,特点包括:**
- **多个移动对象**,可以通过键盘或鼠标输入进行控制
- **使用课程中的矩形相交原理实现的碰撞检测系统**
- **发生碰撞时的视觉反馈**(如对象销毁、颜色变化、特效)
- **让碰撞有意义且引人入胜的游戏规则**
### 创意建议
**考虑实现以下场景之一:**
- **小行星带**:驾驶飞船穿越危险的太空碎片
- **碰碰车**:创建一个基于物理的碰撞竞技场
- **陨石防御**:保护地球免受陨石侵袭
- **收集游戏**:收集物品,同时避开障碍
- **领地控制**:竞争对象争夺空间
### 技术实现
**你的解决方案应展示:**
- 矩形碰撞检测的正确使用
- 基于事件的用户输入编程
- 对象生命周期管理(创建与销毁)
- 干净的代码组织和适当的类结构
### 额外挑战
**为你的游戏增加以下功能:**
- **碰撞时的粒子特效**
- **不同碰撞类型的音效**
- **基于碰撞结果的得分系统**
- **具有不同行为的多种碰撞类型**
- **随时间增加的渐进式难度**
## 评分标准
| 标准 | 卓越表现 | 合格表现 | 需要改进 |
| -------- | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------ | ----------------- |
| | 提供完整的可运行代码示例,物体绘制到画布上,发生基本碰撞,并触发反应 | 代码在某些方面不完整 | 代码运行异常 |
| 标准 | 优秀 | 合格 | 需改进 |
|----------|-----------|----------|-------------------|
| **碰撞检测** | 实现了基于矩形的精准碰撞检测,支持多种对象类型和复杂交互规则 | 基本的碰撞检测正常,简单对象交互正确 | 碰撞检测存在问题或不稳定 |
| **代码质量** | 代码干净且组织良好,具有适当的类结构、有意义的变量名和适当注释 | 代码可用,但组织或文档有待改进 | 代码难以理解或结构差 |
| **用户交互** | 响应灵敏,玩法流畅,视觉反馈明确,机制吸引人 | 基本控制可用,反馈足够 | 控制响应迟钝或令人困惑 |
| **创意** | 原创概念,具独特功能、视觉精致和创新的碰撞行为 | 标准实现,有一些创意元素 | 基本功能,无创意增强 |
---
**免责声明**
本文档使用AI翻译服务 [Co-op Translator](https://github.com/Azure/co-op-translator) 进行翻译。尽管我们努力确保翻译的准确性,但请注意,自动翻译可能包含错误或不准确之处。应以原始语言的文档作为权威来源。对于关键信息,建议使用专业人工翻译。因使用本翻译而引起的任何误解或误读,我们概不负责。
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免责声明**
本文件使用AI翻译服务[Co-op Translator](https://github.com/Azure/co-op-translator)进行翻译。尽管我们力求准确,但请注意,自动翻译可能存在错误或不准确之处。原始文件的母语版本应被视为权威来源。对于重要信息,建议使用专业人工翻译。因使用本翻译而引起的任何误解或误释,我们不承担任何责任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,23 +1,89 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "adda95e02afa3fbee67b6e385b1109e1",
"translation_date": "2025-08-29T14:46:10+00:00",
"original_hash": "2ed9145a16cf576faa2a973dff84d099",
"translation_date": "2026-01-06T11:28:05+00:00",
"source_file": "6-space-game/5-keeping-score/README.md",
"language_code": "zh"
}
-->
# 构建太空游戏第五部分:得分和生命
# 构建太空游戏 第5部分计分与生命
```mermaid
journey
title 你的游戏设计之旅
section 玩家反馈
了解评分心理学: 3: Student
学习视觉传达: 4: Student
设计奖励系统: 4: Student
section 技术实现
画布文本渲染: 4: Student
状态管理: 5: Student
事件驱动更新: 5: Student
section 游戏润色
用户体验设计: 5: Student
平衡挑战与奖励: 5: Student
创建引人入胜的游戏玩法: 5: Student
```
## 课前测验
[课前测验](https://ff-quizzes.netlify.app/web/quiz/37)
在本课中,你将学习如何为游戏添加得分功能以及计算生命值。
准备让你的太空游戏感觉像一个真正的游戏了吗让我们添加计分和生命管理——这些核心机制将早期街机游戏如《Space Invaders》从简单演示转变为让人上瘾的娱乐。这是你游戏真正可玩起来的关键。
```mermaid
mindmap
root((游戏反馈系统))
Visual Communication
Text Rendering
Icon Display
Color Psychology
Layout Design
Scoring Mechanics
Point Values
Reward Timing
Progress Tracking
Achievement Systems
Life Management
Risk vs Reward
Player Agency
Difficulty Balance
Recovery Mechanics
User Experience
Immediate Feedback
Clear Information
Emotional Response
Engagement Loops
Implementation
Canvas API
State Management
Event Systems
Performance
```
## 在屏幕上绘制文本——游戏的声音
## 在屏幕上绘制文本
要显示你的分数,我们需要学习如何在画布上渲染文字。`fillText()` 方法是你的主要工具——这也是经典街机游戏用来显示分数和状态信息的技术。
为了能够在屏幕上显示游戏得分,你需要知道如何在屏幕上放置文本。答案是使用画布对象的 `fillText()` 方法。你还可以控制其他方面,比如使用什么字体、文本的颜色,甚至文本的对齐方式(左对齐、右对齐、居中)。下面是一些代码示例,用于在屏幕上绘制文本。
```mermaid
flowchart LR
A["📝 文本内容"] --> B["🎨 样式"]
B --> C["📍 位置"]
C --> D["🖼️ 画布渲染"]
E["字体"] --> B
F["字号"] --> B
G["颜色"] --> B
H["对齐"] --> B
I["X 坐标"] --> C
J["Y 坐标"] --> C
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
```
你可以完全控制文本的外观:
```javascript
ctx.font = "30px Arial";
@ -26,22 +92,74 @@ ctx.textAlign = "right";
ctx.fillText("show this on the screen", 0, 0);
```
阅读更多关于[如何在画布上添加文本](https://developer.mozilla.org/docs/Web/API/Canvas_API/Tutorial/Drawing_text)的信息,并随意让你的文本看起来更有趣
深入了解 [向画布添加文本](https://developer.mozilla.org/docs/Web/API/Canvas_API/Tutorial/Drawing_text) ——你可能会惊讶于字体和样式可以多么富有创造性
## 生命值,作为游戏概念
## 生命值——不仅仅是一个数字
在游戏中,生命值的概念只是一个数字。在太空游戏的背景下,通常会分配一组生命值,当你的飞船受到伤害时,生命值会逐个减少。如果能用图形化的方式展示生命值,比如小型飞船或心形图标,而不是简单的数字,会更直观
在游戏设计中“生命”代表玩家的容错空间。这个概念可以追溯到弹珠机时代你会获得多个球来游戏。在早期视频游戏如《Asteroids》中生命给玩家提供了冒险和从错误中学习的许可
## 要构建的内容
```mermaid
flowchart TD
A["🎮 玩家行动"] --> B{"风险评估"}
B --> C["高风险,高回报"]
B --> D["安全策略"]
C --> E{"结果"}
D --> F["稳步进展"]
E -->|成功| G["🏆 大分数"]
E -->|失败| H["💔 失去生命"]
H --> I{"剩余生命?"}
I -->|是| J["🔄 再试一次"]
I -->|否| K["💀 游戏结束"]
J --> B
G --> B
F --> B
style C fill:#ffebee
style D fill:#e8f5e8
style G fill:#e3f2fd
style H fill:#fff3e0
```
视觉表现尤为重要——展示飞船图标而不是简单的“生命3”可以立即产生视觉识别就像早期街机机台用图标来跨越语言障碍传达信息一样。
让我们为你的游戏添加以下内容:
## 构建游戏的奖励系统
- **游戏得分**每摧毁一艘敌方飞船英雄应该获得一些分数我们建议每艘飞船奖励100分。游戏得分应显示在屏幕左下角。
- **生命值**:你的飞船有三条生命。每当敌方飞船与你碰撞时,你会失去一条生命。生命值应显示在屏幕右下角,并由以下图形表示 ![生命图标](../../../../translated_images/life.6fb9f50d53ee0413cd91aa411f7c296e10a1a6de5c4a4197c718b49bf7d63ebf.zh.png)。
现在我们将实现保持玩家投入的核心反馈系统:
```mermaid
sequenceDiagram
participant Player
participant GameEngine
participant ScoreSystem
participant LifeSystem
participant Display
Player->>GameEngine: 射击敌人
GameEngine->>ScoreSystem: 奖励积分
ScoreSystem->>ScoreSystem: +100 分
ScoreSystem->>Display: 更新分数
Player->>GameEngine: 与敌人碰撞
GameEngine->>LifeSystem: 失去生命
LifeSystem->>LifeSystem: -1 生命
LifeSystem->>Display: 更新生命值
alt 生命值 > 0
LifeSystem->>Player: 继续游戏
else 生命值 = 0
LifeSystem->>GameEngine: 游戏结束
end
```
- **计分系统**每摧毁一艘敌舰奖励100分圆整数字更便于玩家心算。分数显示在左下角。
- **生命计数器**:你的英雄从三条命开始——这是早期街机游戏确立的标准,平衡挑战和可玩性。每次与敌人碰撞会损失一条命。我们将在右下用飞船图标显示剩余生命 ![life image](../../../../translated_images/life.6fb9f50d53ee0413.zh.png)。
## 推荐步骤
## 开始构建吧!
找到在 `your-work` 子文件夹中为你创建的文件。它应该包含以下内容:
首先,设置你的工作区。进入 `your-work` 子文件夹中的文件。你应该能看到这些文件
```bash
-| assets
@ -53,24 +171,49 @@ ctx.fillText("show this on the screen", 0, 0);
-| package.json
```
通过输入以下命令启动你的项目
要测试你的游戏,从 `your_work` 文件夹启动开发服务器
```bash
cd your-work
npm start
```
上述命令将在地址 `http://localhost:5000` 上启动一个 HTTP 服务器。打开浏览器并输入该地址,现在它应该渲染英雄和所有敌人,并且当你按下左右箭头时,英雄会移动并可以击落敌人
这会在 `http://localhost:5000` 运行本地服务器。打开这个地址查看你的游戏。用箭头键测试控制,尝试射击敌人以验证一切正常
### 添加代码
```mermaid
flowchart TD
A["1. 资源加载"] --> B["2. 游戏变量"]
B --> C["3. 碰撞检测"]
C --> D["4. 英雄强化"]
D --> E["5. 显示功能"]
E --> F["6. 事件处理器"]
G["生命图标图片"] --> A
H["得分与生命追踪"] --> B
I["英雄与敌人交集"] --> C
J["积分与生命方法"] --> D
K["文本与图标渲染"] --> E
L["奖励与惩罚逻辑"] --> F
F --> M["🎮 完整游戏"]
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
style E fill:#e0f2f1
style F fill:#fce4ec
style M fill:#e1f5fe
```
### 开始编码!
1. **复制所需资源** 从 `solution/assets/` 文件夹复制到 `your-work` 文件夹;你需要添加一个 `life.png` 资源。在 `window.onload` 函数中添加 lifeImg
1. **获取所需的视觉素材**。将 `solution/assets/` 文件夹里的 `life.png` 复制到你的 `your-work` 文件夹。然后将 lifeImg 添加到你的 window.onload 函数中
```javascript
lifeImg = await loadTexture("assets/life.png");
```
1. 将 `lifeImg` 添加到资源列表中:
1. 别忘了将 `lifeImg` 添加到你的素材列表中:
```javascript
let heroImg,
@ -80,9 +223,9 @@ npm start
eventEmitter = new EventEmitter();
```
2. **添加变量**。添加代码以表示你的总得分初始为0和剩余生命值初始为3并在屏幕上显示这些分数
2. **设置游戏变量**。添加代码以跟踪总分从0开始和剩余生命从3开始。我们将在屏幕上显示这些信息让玩家随时了解自己的状态
3. **扩展 `updateGameObjects()` 函数**。扩展 `updateGameObjects()` 函数以处理敌人碰撞:
3. **实现碰撞检测**。扩展你的 `updateGameObjects()` 函数以检测敌人与英雄的碰撞:
```javascript
enemies.forEach(enemy => {
@ -93,19 +236,19 @@ npm start
})
```
4. **添加生命值和得分**。
1. **初始化变量**。在 `Hero` 类中的 `this.cooldown = 0` 下设置生命值和得分:
4. **为英雄添加生命和积分追踪**。
1. **初始化计数器**。在 `Hero` 类中 `this.cooldown = 0` 下,设置生命和积分:
```javascript
this.life = 3;
this.points = 0;
```
1. **在屏幕上绘制变量**。将这些值绘制到屏幕上
1. **向玩家显示这些数值**。创建函数以在屏幕上绘制这些值:
```javascript
function drawLife() {
// TODO, 35, 27
// 待办事项3527
const START_POS = canvas.width - 180;
for(let i=0; i < hero.life; i++ ) {
ctx.drawImage(
@ -128,18 +271,34 @@ npm start
```
1. **方法添加到游戏循环中**。确保在 `window.onload` 函数中将这些函数添加到 `updateGameObjects()`
1. **所有内容挂钩到你的游戏循环中**。在 window.onload 函数中 `updateGameObjects()` 之后调用这些函数
```javascript
drawPoints();
drawLife();
```
1. **实现游戏规则**。实现以下游戏规则:
### 🔄 **教学检查点**
**游戏设计理解**:在实施后果之前,确保你理解:
- ✅ 视觉反馈如何向玩家传递游戏状态
- ✅ UI 元素一致放置为何提升可用性
- ✅ 分值和生命管理背后的心理学
- ✅ 画布文字渲染与 HTML 文字的不同
**快速自测**:为何街机游戏通常使用圆整的点数?
*答案:圆整数字便于玩家心算,且带来心理上的满足感*
**用户体验原则**:你正在应用:
- **视觉层级**:重要信息醒目展示
- **即时反馈**:玩家操作即时更新
- **认知负荷**:信息简洁明了呈现
- **情感设计**:图标和颜色营造玩家连接感
1. **实现游戏后果与奖励**。现在我们添加让玩家行为有意义的反馈系统:
1. **每次英雄与敌人碰撞**,扣除一条生命。
1. **碰撞消耗生命**。每次英雄撞上敌人,你应该失去一条命。
扩展 `Hero` 类以实现生命值扣除:
将此方法添加到 `Hero` 类中
```javascript
decrementLife() {
@ -150,17 +309,17 @@ npm start
}
```
2. **每次激光击中敌人**游戏得分增加100分。
2. **射击敌人得分**。每命中一次奖励100分为精准射击提供即时正反馈。
在 Hero 类中扩展此增加函数:
扩展 `Hero` 类以实现得分增加:
```javascript
incrementPoints() {
this.points += 100;
}
```
将这些函数添加到碰撞事件发射器中
现将这些函数与碰撞事件关联
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
@ -175,15 +334,159 @@ npm start
});
```
✅ 进行一些研究,了解其他使用 JavaScript/Canvas 创建的游戏。它们有哪些共同特点?
完成这部分工作后,你应该能在屏幕右下角看到小型“生命”飞船,左下角看到得分,并且当你与敌人碰撞时生命值会减少,当你击中敌人时得分会增加。干得好!你的游戏已经接近完成。
✅ 想了解更多用 JavaScript 和 Canvas 构建的游戏?多做探索——你可能会惊讶于其可能性!
实现这些功能后,测试你的游戏,观察完整反馈系统的运行。你应该能看到右下的生命图标,左下的分数,并见证碰撞减少生命、成功击中增加分数。
你的游戏现在拥有了早期街机游戏令人着迷的核心机制——明确目标、即时反馈和玩家操作的有意义后果。
### 🔄 **教学检查点**
**完整游戏设计系统**:检验你对玩家反馈系统的掌握:
- ✅ 计分机制如何激发玩家动力和投入?
- ✅ 视觉一致性为何对用户界面设计重要?
- ✅ 生命系统如何平衡挑战与玩家留存?
- ✅ 即时反馈在创造令人满意的游戏体验中起什么作用?
**系统集成**:你的反馈系统体现了:
- **用户体验设计**:清晰视觉传达和信息层级
- **事件驱动架构**:对玩家操作的响应更新
- **状态管理**:追踪并显示动态游戏数据
- **画布掌握**:文字渲染和精灵定位
- **游戏心理学**:理解玩家动力和投入
**专业模式**:你已实现:
- **MVC 架构**:游戏逻辑、数据和表现分离
- **观察者模式**:游戏状态变更的事件驱动更新
- **组件设计**:可复用的渲染和逻辑函数
- **性能优化**:游戏循环中的高效渲染
### ⚡ **未来5分钟可做的事**
- [ ] 试验不同的字体大小和颜色显示分数
- [ ] 改变点数值,观察对游戏感觉的影响
- [ ] 添加 console.log 跟踪分数和生命的变动
- [ ] 测试边缘情况,如生命耗尽或高分达成
### 🎯 **本小时可完成的目标**
- [ ] 完成课后测验并理解游戏设计心理学
- [ ] 增加得分和失生命的音效
- [ ] 利用 localStorage 实现高分系统
- [ ] 为不同敌人类型设置不同点数
- [ ] 添加失去生命时的屏幕震动视觉效果
### 📅 **你的周长游戏设计之旅**
- [ ] 完成具有完善反馈系统的完整太空游戏
- [ ] 实现高级计分机制如连击倍率
- [ ] 增加成就和可解锁内容
- [ ] 创建难度递增和平衡系统
- [ ] 设计菜单和游戏结束界面
- [ ] 学习其他游戏以理解用户投入机制
### 🌟 **你的月长游戏开发精通**
- [ ] 构建配有复杂进程系统的完整游戏
- [ ] 学习游戏分析与玩家行为测量
- [ ] 贡献开源游戏开发项目
- [ ] 掌握高级游戏设计模式和变现策略
- [ ] 制作关于游戏设计和用户体验的教育内容
- [ ] 建立展示游戏设计与开发技能的作品集
## 🎯 你的游戏设计精通时间线
```mermaid
timeline
title 游戏设计与玩家反馈学习进程
section 基础10分钟
视觉传达:文本渲染
:图标设计
:布局原则
:色彩心理学
section 玩家心理20分钟
动机系统:积分值
:风险与回报
:进度反馈
:成就设计
section 技术实现30分钟
画布精通:文本定位
:精灵渲染
:状态管理
:性能优化
section 游戏平衡40分钟
难度设计:生命管理
:评分曲线
:玩家留存
:无障碍设计
section 用户体验50分钟
界面设计:信息层级
:响应反馈
:情感设计
:可用性测试
section 高级系统(一周)
游戏机制:进程系统
:分析集成
:变现设计
:社区功能
section 行业技能(一月)
职业发展:团队协作
:设计文档
:玩家研究
:平台优化
```
### 🛠️ 你的游戏设计工具包总结
完成本课后,你已掌握:
- **玩家心理学**:理解动机、风险/奖励与投入循环
- **视觉传达**使用文本、图标和布局进行有效UI设计
- **反馈系统**:实时响应玩家动作和游戏事件
- **状态管理**:高效追踪及展示动态游戏数据
- **画布文本渲染**:专业文字展示及样式定位
- **事件整合**:将用户操作连接至有意义的游戏后果
- **游戏平衡**:设计难度曲线和玩家进程系统
**现实应用**:你的游戏设计技能直接适用于:
- **用户界面设计**:创造引人入胜、直观界面
- **产品开发**:理解用户动机和反馈循环
- **教育技术**:游戏化和学习投入系统
- **数据可视化**:让复杂信息易于理解和吸引人
- **移动应用开发**:留存机制和用户体验设计
- **市场技术**:理解用户行为和转化优化
**获得的专业技能**
- **设计** 激励和吸引用户的用户体验
- **实现** 指导用户行为的反馈系统
- **平衡** 互动系统的挑战与可达性
- **创建** 跨用户群有效的视觉传达
- **分析** 用户行为并迭代设计改进
**掌握的游戏开发概念**
- **玩家动机**:理解驱动投入与留存的因素
- **视觉设计**:创建清晰、美观且实用的界面
- **系统集成**:多系统连接以实现一致体验
- **性能优化**:高效渲染与状态管理
- **无障碍设计**:面向不同技能水平和玩家需求设计
**下一步**:你已准备探索高级游戏设计模式,实施分析系统,或研究游戏变现和玩家留存策略!
🌟 **成就达成**:你已经构建了一个带有专业游戏设计原则的完整玩家反馈系统!
---
## GitHub Copilot Agent 挑战 🚀
使用代理模式完成以下挑战:
**描述:** 增强太空游戏的计分系统,实现带持久存储的最高分功能和连击奖励机制。
**提示:** 创建一个最高分系统,将玩家最高分保存到 localStorage。新增连击得分奖励连杀系统并针对不同敌人类型设定不同点值。新增视觉指示提示玩家刷新最高分并在游戏画面显示当前最高分。
## 🚀 挑战
你的代码几乎完成了。你能想象下一步该做什么吗?
现在拥有一个功能齐全的带计分和生命的游戏。思考还有哪些额外功能可以提升玩家体验。
## 课后测验
@ -191,13 +494,15 @@ npm start
## 复习与自学
研究一些可以增加和减少游戏得分和生命值的方法。有一些有趣的游戏引擎,比如 [PlayFab](https://playfab.com)。使用这些引擎如何提升你的游戏?
想深入探索?研究不同的计分和生命系统方法。有许多有趣的游戏引擎,比如 [PlayFab](https://playfab.com),它们管理计分、排行榜和玩家进程。集成类似功能将怎样提升你的游戏?
## 作业
[构建一个分游戏](assignment.md)
[构建一个分游戏](assignment.md)
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免责声明**
本文档使用AI翻译服务[Co-op Translator](https://github.com/Azure/co-op-translator)进行翻译。尽管我们努力确保准确性,但请注意,自动翻译可能包含错误或不准确之处。应以原始语言的文档作为权威来源。对于关键信息,建议使用专业人工翻译。因使用本翻译而引起的任何误解或误读,我们概不负责。
本文件由人工智能翻译服务 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻译。尽管我们力求准确,但请注意自动翻译可能存在错误或不准确之处。原始文档的母语版本应视为权威来源。对于重要信息,建议采用专业人工翻译。因使用本翻译而产生的任何误解或曲解,我们不承担任何责任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,41 +1,163 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "05be6c37791668e3719c4fba94566367",
"translation_date": "2025-08-29T14:47:07+00:00",
"original_hash": "a4b78043f4d64bf3ee24e0689b8b391d",
"translation_date": "2026-01-06T11:25:54+00:00",
"source_file": "6-space-game/6-end-condition/README.md",
"language_code": "zh"
}
-->
# 构建太空游戏第六部分:结束与重启
# 构建太空游戏第6部分结束与重启
```mermaid
journey
title 你的游戏完成旅程
section 结束条件
定义胜负状态: 3: Student
实现条件检测: 4: Student
处理状态转换: 4: Student
section 玩家体验
设计反馈系统: 4: Student
创建重启机制: 5: Student
优化用户界面: 5: Student
section 系统集成
管理游戏生命周期: 5: Student
处理内存清理: 5: Student
创建完整体验: 5: Student
```
每个伟大的游戏都需要清晰的结束条件和流畅的重启机制。你已经打造了一个具有移动、战斗和计分功能的令人印象深刻的太空游戏——现在是时候添加最终的部分,让它感觉完整了。
你的游戏当前无限运行就像美国宇航局1977年发射的旅行者探测器——几十年后仍在太空中旅行。虽然这对于太空探索很好但游戏需要定义好的终点来创造令人满意的体验。
今天,我们将实现合适的胜负条件和重启系统。课程结束时,你将拥有一个抛光完善的游戏,玩家可以完成并重玩,就像定义了媒介的经典街机游戏一样。
```mermaid
mindmap
root((游戏完成))
End Conditions
Victory States[胜利状态]
Defeat Conditions[失败条件]
Progress Tracking[进度跟踪]
State Validation[状态验证]
Player Feedback
Visual Messages[视觉信息]
Color Psychology[色彩心理学]
Clear Communication[清晰传达]
Emotional Response[情感反应]
State Management
Game Loop Control[游戏循环控制]
Memory Cleanup[内存清理]
Object Lifecycle[对象生命周期]
Event Handling[事件处理]
Restart Systems
Input Handling[输入处理]
State Reset[状态重置]
Fresh Initialization[全新初始化]
User Experience[用户体验]
Polish Elements
Message Display[消息显示]
Smooth Transitions[平滑过渡]
Error Prevention[错误预防]
Accessibility[无障碍]
```
## 课前测验
[课前测验](https://ff-quizzes.netlify.app/web/quiz/39)
在游戏中,有多种方式来表达*结束条件*。作为游戏的创作者,你需要决定游戏为何结束。以下是一些可能的原因,假设我们正在讨论你目前正在构建的太空游戏:
## 理解游戏结束条件
- **摧毁了`N`艘敌方飞船**:如果你将游戏分为不同的关卡,那么通常需要摧毁`N`艘敌方飞船才能完成一个关卡。
- **你的飞船被摧毁**:有些游戏中,如果你的飞船被摧毁,你就会输掉游戏。另一种常见的方法是引入“生命”的概念。每次飞船被摧毁时,扣除一条生命。当所有生命耗尽时,游戏结束。
- **收集了`N`分**:另一种常见的结束条件是收集一定的分数。如何获得分数取决于你,但通常会为各种活动分配分数,比如摧毁敌方飞船,或者收集敌方飞船被摧毁后掉落的物品。
- **完成一个关卡**:这可能涉及多个条件,比如摧毁`X`艘敌方飞船、收集`Y`分,或者收集某个特定的物品。
你的游戏应该何时结束?这个基础问题自早期街机时代以来影响了游戏设计。吃豆人被鬼抓住或吃掉所有豆子即结束,太空入侵者则是在外星人到达底部或全部被击毁时结束。
## 重启
作为游戏创作者,你定义胜利和失败条件。对于我们的太空游戏,以下是创造引人入胜玩法的常见方式:
如果玩家喜欢你的游戏,他们可能会想要重新玩一次。当游戏因某种原因结束时,你应该提供一个重启的选项。
```mermaid
flowchart TD
A["🎮 游戏开始"] --> B{"检查条件"}
B --> C["敌人数"]
B --> D["英雄生命"]
B --> E["得分阈值"]
B --> F["关卡进度"]
C --> C1{"敌人 = 0?"}
D --> D1{"生命 = 0?"}
E --> E1{"得分 ≥ 目标?"}
F --> F1{"目标完成?"}
C1 -->|是| G["🏆 胜利"]
D1 -->|是| H["💀 失败"]
E1 -->|是| G
F1 -->|是| G
C1 -->|否| B
D1 -->|否| B
E1 -->|否| B
F1 -->|否| B
G --> I["🔄 重新开始选项"]
H --> I
style G fill:#e8f5e8
style H fill:#ffebee
style I fill:#e3f2fd
```
- **已击毁 `N` 艘敌舰**:如果你将游戏分为不同关卡,通常需要击毁 `N` 艘敌舰以完成关卡
- **你的飞船被击毁**:有些游戏中,如果你的船被毁你就会输。一种常见方式是生命值机制。每当你的船被毁损失一条命,命数耗尽时游戏失败。
- **你获得了 `N` 分**:另一种常见的结束条件是你收集到足够的分数。分数来源由你决定,通常分数赋予消灭敌舰或收集敌舰被击毁后掉落的物品。
- **完成一个关卡**:这可能涉及多个条件,如击毁 `X` 艘敌舰、收集 `Y` 分数,或收集特定道具。
✅ 想一想,在什么条件下你认为游戏会结束,然后玩家会如何被提示重启。
## 实现游戏重启功能
## 要构建的内容
好的游戏通过流畅的重启机制提高重玩价值。当玩家完成游戏(或失败)时,通常希望立即再试一次——无论是为了超越分数还是提升表现。
你需要为游戏添加以下规则:
```mermaid
stateDiagram-v2
[*] --> Playing: 游戏开始
Playing --> Victory: 消灭所有敌人
Playing --> Defeat: 生命值 = 0
Victory --> MessageDisplay: 显示胜利信息
Defeat --> MessageDisplay: 显示失败信息
MessageDisplay --> WaitingRestart: 按回车提示
WaitingRestart --> Resetting: 按下回车键
Resetting --> CleanupMemory: 清除定时器
CleanupMemory --> ClearEvents: 移除监听器
ClearEvents --> InitializeGame: 全新开始
InitializeGame --> Playing: 新游戏开始
note right of MessageDisplay
颜色编码反馈:
绿色 = 胜利
红色 = 失败
end note
note right of Resetting
完成状态重置
防止内存泄漏
end note
```
俄罗斯方块是这方面的典范:当方块堆满顶部时,你可以立即开始新游戏,无需复杂菜单操作。我们将构建类似的重启系统,清晰地重置游戏状态,让玩家迅速回归游戏。
**思考**:想想你玩过的游戏。它们在什么情况下结束?如何提示你重启?什么样的重启体验让人感觉顺畅而非烦躁?
## 你将构建的内容
你将实现最终的功能,把项目打造成完整的游戏体验。这些元素让精致游戏区别于基础原型。
**今天我们添加的内容:**
1. **胜利条件**:消灭全部敌人并庆祝胜利(你应得的!)
2. **失败条件**:耗尽所有生命,面对失败画面
3. **重启机制**:按回车键即可重新开始——因为一场游戏永远不够
4. **状态管理**:每次都是崭新开始——无遗留敌人或奇怪故障
1. **赢得游戏**。当所有敌方飞船被摧毁时,玩家赢得游戏。此外,显示某种胜利信息。
2. **重启**。当所有生命耗尽或游戏胜利时,你应该提供一种方式来重启游戏。记住!你需要重新初始化游戏,并清除之前的游戏状态。
## 准备工作
## 推荐步骤
让我们准备开发环境。你应该已经有上节课的所有太空游戏文件。
找到在`your-work`子文件夹中为你创建的文件。它应该包含以下内容:
**你的项目文件结构应大致如下:**
```bash
-| assets
@ -48,175 +170,499 @@ CO_OP_TRANSLATOR_METADATA:
-| package.json
```
通过输入以下命令启动你的项目:
**启动你的开发服务器:**
```bash
cd your-work
npm start
```
上述命令将在地址`http://localhost:5000`上启动一个HTTP服务器。打开浏览器并输入该地址。你的游戏应该处于可玩的状态。
**该命令执行:**
- 在 `http://localhost:5000` 运行本地服务器
- 正确提供你的文件服务
- 当你修改时自动刷新浏览器
> 提示为了避免在Visual Studio Code中出现警告编辑`window.onload`函数,使其直接调用`gameLoopId`(不使用`let`),并在文件顶部独立声明`gameLoopId``let gameLoopId;`
在浏览器打开 `http://localhost:5000` 并验证游戏是否能运行。你应该可以移动,发射并与敌人互动。确认后,我们即可开始实现功能。
> 💡 **专业建议**为避免Visual Studio Code中警告请在文件顶部声明 `let gameLoopId;`,而不是在 `window.onload` 函数内声明。这符合现代JavaScript变量声明最佳实践。
```mermaid
flowchart TD
A["1. 条件追踪"] --> B["2. 事件处理器"]
B --> C["3. 消息常量"]
C --> D["4. 重启控制"]
D --> E["5. 消息显示"]
E --> F["6. 重置系统"]
G["isHeroDead()\nisEnemiesDead()"] --> A
H["碰撞事件\n结束游戏事件"] --> B
I["游戏结束_胜利\n游戏结束_失败"] --> C
J["回车键\n重启触发"] --> D
K["胜利/失败\n颜色编码文本"] --> E
L["状态清理\n全新初始化"] --> F
F --> M["🎮 完整游戏"]
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
style E fill:#e0f2f1
style F fill:#fce4ec
style M fill:#e1f5fe
```
## 实现步骤
### 添加代码
### 步骤1创建结束条件追踪函数
1. **追踪结束条件**。添加代码以追踪敌方飞船数量,或者英雄飞船是否被摧毁,方法是添加以下两个函数:
我们需要函数来监测游戏何时结束。就像国际空间站上的传感器不断监控关键系统一样,这些函数将持续检查游戏状态。
```javascript
function isHeroDead() {
return hero.life <= 0;
```javascript
function isHeroDead() {
return hero.life <= 0;
}
function isEnemiesDead() {
const enemies = gameObjects.filter((go) => go.type === "Enemy" && !go.dead);
return enemies.length === 0;
}
```
**幕后发生的事情:**
- **检查** 主角是否已无生命(糟糕!)
- **计算** 当前还活着并在战斗的敌人数量
- **返回** 当战场清空敌人时返回 `true`
- **使用** 简单的真假逻辑保持清晰明了
- **过滤** 所有游戏对象,找出存活者
### 步骤2更新事件处理器以检测结束条件
现在我们将把这些条件检测连接到游戏事件系统中。每当发生碰撞时,游戏会评估是否触发结束条件。这为关键游戏事件创建即时反馈。
```mermaid
sequenceDiagram
participant Collision
participant GameLogic
participant Conditions
participant EventSystem
participant Display
Collision->>GameLogic: 激光击中敌人
GameLogic->>GameLogic: 摧毁对象
GameLogic->>Conditions: 检查 isEnemiesDead()
alt 全部敌人被击败
Conditions->>EventSystem: 发送 GAME_END_WIN
EventSystem->>Display: 显示胜利消息
else 敌人仍然存在
Conditions->>GameLogic: 继续游戏
end
Collision->>GameLogic: 敌人攻击英雄
GameLogic->>GameLogic: 减少生命值
GameLogic->>Conditions: 检查 isHeroDead()
alt 生命值 = 0
Conditions->>EventSystem: 发送 GAME_END_LOSS
EventSystem->>Display: 显示失败消息
else 生命值仍然存在
GameLogic->>Conditions: 检查 isEnemiesDead()
alt 全部敌人被击败
Conditions->>EventSystem: 发送 GAME_END_WIN
end
end
```
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
hero.incrementPoints();
if (isEnemiesDead()) {
eventEmitter.emit(Messages.GAME_END_WIN);
}
});
eventEmitter.on(Messages.COLLISION_ENEMY_HERO, (_, { enemy }) => {
enemy.dead = true;
hero.decrementLife();
if (isHeroDead()) {
eventEmitter.emit(Messages.GAME_END_LOSS);
return; // 胜利之前的代价
}
if (isEnemiesDead()) {
eventEmitter.emit(Messages.GAME_END_WIN);
}
});
eventEmitter.on(Messages.GAME_END_WIN, () => {
endGame(true);
});
eventEmitter.on(Messages.GAME_END_LOSS, () => {
endGame(false);
});
```
**这里发生的事情:**
- **激光击中敌人**:双方消失,你得分,我们检查你是否获胜
- **敌人撞击你**:你失去一条命,我们检查你是否还活着
- **合理顺序**:先检查失败(没人想同时赢又输!)
- **即时反应**:重要事件发生时游戏立刻感知
### 步骤3添加新消息常量
你需要为 `Messages` 常量对象添加新的消息类型。这些常量帮助保持一致性,避免事件系统中的拼写错误。
```javascript
GAME_END_LOSS: "GAME_END_LOSS",
GAME_END_WIN: "GAME_END_WIN",
```
**上面内容:**
- **添加** 代表游戏结束事件的常量以保持一致性
- **使用** 描述性名称清晰标识事件目的
- **遵循** 现有消息类型命名规范
### 步骤4实现重启控制
接下来添加键盘控制,让玩家能重启游戏。回车键是自然选择,因其常用作确认与开始新游戏的按键。
**在现有keydown事件监听器中添加对回车键的检测**
```javascript
else if(evt.key === "Enter") {
eventEmitter.emit(Messages.KEY_EVENT_ENTER);
}
```
**添加新的消息常量:**
```javascript
KEY_EVENT_ENTER: "KEY_EVENT_ENTER",
```
**你需要知道的:**
- **扩展** 你现有的键盘事件处理系统
- **使用** 回车键作为重启触发器,提升用户体验直观性
- **发布** 自定义事件,供游戏的其他部分监听
- **保持** 与其他键盘控制相同模式
### 步骤5创建消息显示系统
function isEnemiesDead() {
const enemies = gameObjects.filter((go) => go.type === "Enemy" && !go.dead);
return enemies.length === 0;
游戏需要清楚地向玩家传达结果。我们将创建一个消息系统,使用颜色编码的文本显示胜利和失败状态,类似早期计算机终端界面绿色表示成功,红色表示错误。
**创建 `displayMessage()` 函数:**
```javascript
function displayMessage(message, color = "red") {
ctx.font = "30px Arial";
ctx.fillStyle = color;
ctx.textAlign = "center";
ctx.fillText(message, canvas.width / 2, canvas.height / 2);
}
```
**一步步,发生了什么:**
- **设置** 字号和字体,确保文字清晰易读
- **应用** 颜色参数,默认为“红色”表示警告
- **让** 文字在画布水平和垂直居中
- **利用** 现代JavaScript默认参数支持灵活颜色选择
- **借助** canvas 2D上下文直接渲染文本
**创建 `endGame()` 函数:**
```javascript
function endGame(win) {
clearInterval(gameLoopId);
// 设置延迟以确保所有待处理的渲染完成
setTimeout(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
if (win) {
displayMessage(
"Victory!!! Pew Pew... - Press [Enter] to start a new game Captain Pew Pew",
"green"
);
} else {
displayMessage(
"You died !!! Press [Enter] to start a new game Captain Pew Pew"
);
}
```
2. **添加逻辑到消息处理程序**。编辑`eventEmitter`以处理这些条件:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
hero.incrementPoints();
if (isEnemiesDead()) {
eventEmitter.emit(Messages.GAME_END_WIN);
}
});
eventEmitter.on(Messages.COLLISION_ENEMY_HERO, (_, { enemy }) => {
enemy.dead = true;
hero.decrementLife();
if (isHeroDead()) {
eventEmitter.emit(Messages.GAME_END_LOSS);
return; // loss before victory
}
if (isEnemiesDead()) {
eventEmitter.emit(Messages.GAME_END_WIN);
}
});
eventEmitter.on(Messages.GAME_END_WIN, () => {
endGame(true);
});
eventEmitter.on(Messages.GAME_END_LOSS, () => {
endGame(false);
});
```
3. **添加新的消息类型**。将这些消息添加到常量对象中:
```javascript
GAME_END_LOSS: "GAME_END_LOSS",
GAME_END_WIN: "GAME_END_WIN",
```
4. **添加重启代码**,以便在按下选定按钮时重启游戏。
1. **监听按键`Enter`**。编辑窗口的事件监听器以监听该按键:
```javascript
else if(evt.key === "Enter") {
eventEmitter.emit(Messages.KEY_EVENT_ENTER);
}
```
2. **添加重启消息**。将此消息添加到你的消息常量中:
```javascript
KEY_EVENT_ENTER: "KEY_EVENT_ENTER",
```
5. **实现游戏规则**。实现以下游戏规则:
1. **玩家胜利条件**。当所有敌方飞船被摧毁时,显示胜利信息。
1. 首先,创建一个`displayMessage()`函数:
```javascript
function displayMessage(message, color = "red") {
ctx.font = "30px Arial";
ctx.fillStyle = color;
ctx.textAlign = "center";
ctx.fillText(message, canvas.width / 2, canvas.height / 2);
}
```
2. 创建一个`endGame()`函数:
```javascript
function endGame(win) {
clearInterval(gameLoopId);
}, 200)
}
```
**此函数做了什么:**
- **冻结** 所有移动——不再有舰船或激光动作
- **暂停** 200毫秒让最后一帧完成绘制
- **清屏** 并将背景涂成黑色,效果更戏剧化
- **显示** 不同消息给胜利者和失败者
- **用色彩区分消息**——绿代表好消息,红代表……不太好
- **告诉** 玩家如何重新开始游戏
### 🔄 **教学反馈点**
**游戏状态管理**:在实现重置功能前确保理解:
- ✅ 结束条件如何创造清晰的游戏目标
- ✅ 为什么视觉反馈对玩家理解至关重要
- ✅ 合适清理对防止内存泄漏的重要性
- ✅ 事件驱动架构如何实现干净的状态切换
**自测速答**:如果重置时不清理事件监听器会怎样?
*答案:导致内存泄漏和重复事件处理器,引发不可预测的行为*
**游戏设计原则**:你正在实现的是:
- **清晰目标**:玩家明确什么是胜利、什么是失败
- **即时反馈**:游戏状态变化立即被传达
- **用户控制**:玩家准备好了就能重启
- **系统可靠性**适当清理避免bug和性能损耗
### 步骤6实现游戏重置功能
重置系统需彻底清理当前游戏状态,初始化清新的游戏会话。保证玩家得到干净的开局,无前一局遗留数据。
**创建 `resetGame()` 函数:**
```javascript
function resetGame() {
if (gameLoopId) {
clearInterval(gameLoopId);
eventEmitter.clear();
initGame();
gameLoopId = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawPoints();
drawLife();
updateGameObjects();
drawGameObjects(ctx);
}, 100);
}
}
```
**逐部分理解:**
- **检查** 当前是否有游戏循环正在运行
- **清除** 现有游戏循环以停止所有当前游戏活动
- **移除** 所有事件监听器防止内存泄漏
- **重新初始化** 游戏状态,生成全新对象和变量
- **启动** 新的游戏循环,包含必需的游戏逻辑
- **保持** 100毫秒间隔确保游戏性能一致
**将回车键事件处理器添加到 `initGame()` 函数:**
```javascript
eventEmitter.on(Messages.KEY_EVENT_ENTER, () => {
resetGame();
});
```
**为你的 EventEmitter 类添加 `clear()` 方法:**
```javascript
clear() {
this.listeners = {};
}
```
**关键点总结:**
- **将** 回车键按下连接到重置游戏功能
- **在** 游戏初始化时注册此事件监听器
- **提供** 一种清理所有事件监听器的干净方式,用于重置
- **通过** 清空事件处理器避免内存泄漏
- **将** 监听器对象重置为空以便重新初始化
## 恭喜!🎉
👽 💥 🚀 你成功从零构建了完整的游戏。像1970年代首批电子游戏程序员一样你把代码变成了带有合适游戏机制和用户反馈的互动体验。🚀 💥 👽
**你完成了:**
- **实现** 完整的胜负条件并向用户反馈
- **创建** 无缝重启系统,支持连续游戏
- **设计** 明确的游戏状态视觉传达
- **管理** 复杂的游戏状态切换和清理
- **整合** 所有组件成一个连贯、可玩的游戏
### 🔄 **教学反馈点**
**完整游戏开发系统**:庆祝你掌握了完整开发流程:
- ✅ 结束条件如何创造满足感玩家体验?
- ✅ 为什么正确的状态管理对游戏稳定性关键?
- ✅ 视觉反馈如何增强玩家理解?
- ✅ 重启系统在玩家留存中扮演什么角色?
**系统掌控力**:你的完整游戏展现了:
- **全栈游戏开发**:涵盖图形、输入和状态管理
- **专业架构**:事件驱动系统加上适当清理
- **用户体验设计**:清晰反馈与直观控制
- **性能优化**:高效渲染和内存管理
- **打磨完善**:所有细节让游戏感觉完美
**行业级技能**:你实现了:
- **游戏循环架构**:实时系统性能稳定
- **事件驱动编程**:解耦系统便于扩展
- **状态管理**:复杂数据处理与生命周期管控
- **用户界面设计**:清晰沟通与响应式操作
- **测试与调试**:迭代开发和问题解决
### ⚡ **下5分钟你可以做什么**
- [ ] 玩你的完整游戏,测试所有胜负条件
- [ ] 尝试修改不同结束条件参数
- [ ] 加入 console.log 语句追踪游戏状态变化
- [ ] 与朋友分享游戏并收集反馈
### 🎯 **这小时你可以完成什么**
- [ ] 完成课后测验,反思你的开发历程
- [ ] 为胜负状态添加音效效果
- [ ] 实现额外的结束条件,如限时或奖励目标
- [ ] 制作不同难度,调整敌人数量
- [ ] 美化视觉效果,使用更好字体和颜色
### 📅 **你的周度游戏开发精通计划**
- [ ] 完成增强版太空游戏,多关卡和进度系统
- [ ] 增加高级功能,如增强道具、多样敌人类型和特殊武器
- [ ] 创建保存的高分榜系统
- [ ] 设计菜单、设置和选项的用户界面
- [ ] 优化性能,适配不同设备和浏览器
- [ ] 在线部署你的游戏,与社区分享
### 🌟 **您的一个月游戏开发职业规划**
- [ ] 制作多个完整游戏,探索不同的类型和机制
- [ ] 学习高级游戏开发框架,如 Phaser 或 Three.js
- [ ] 参与开源游戏开发项目的贡献
- [ ] 研究游戏设计原则和玩家心理
- [ ] 创建展示您游戏开发技能的作品集
- [ ] 与游戏开发社区连接,持续学习
## 🎯 您的完整游戏开发精通时间表
```mermaid
timeline
title 完整游戏开发学习进度
section 基础课程1-2
游戏架构:项目结构
:资源管理
:画布基础
:事件系统
// set a delay so we are sure any paints have finished
setTimeout(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
if (win) {
displayMessage(
"Victory!!! Pew Pew... - Press [Enter] to start a new game Captain Pew Pew",
"green"
);
} else {
displayMessage(
"You died !!! Press [Enter] to start a new game Captain Pew Pew"
);
}
}, 200)
}
```
2. **重启逻辑**。当所有生命耗尽或玩家赢得游戏时,显示游戏可以重启。此外,当按下*重启*键时重启游戏(你可以决定哪个键映射到重启)。
1. 创建`resetGame()`函数:
```javascript
function resetGame() {
if (gameLoopId) {
clearInterval(gameLoopId);
eventEmitter.clear();
initGame();
gameLoopId = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawPoints();
drawLife();
updateGameObjects();
drawGameObjects(ctx);
}, 100);
}
}
```
2. 在`initGame()`中添加调用`eventEmitter`以重置游戏:
```javascript
eventEmitter.on(Messages.KEY_EVENT_ENTER, () => {
resetGame();
});
```
3. 为EventEmitter添加一个`clear()`函数:
```javascript
clear() {
this.listeners = {};
}
```
👽 💥 🚀 恭喜你,船长!你的游戏完成了!干得好!🚀 💥 👽
section 交互系统课程3-4
玩家控制:输入处理
:移动机制
:碰撞检测
:物理模拟
section 游戏机制课程5
反馈系统:得分机制
:生命管理
:视觉传达
:玩家激励
section 游戏完成课程6
精细化与流程:结束条件
:状态管理
:重启系统
:用户体验
section 高级功能(一周)
增强技能:音频整合
:视觉特效
:关卡进度
:性能优化
section 职业发展(一月)
行业准备:框架精通
:团队协作
:作品集开发
:社区参与
section 职业晋升(三月)
专业化:高级游戏引擎
:平台部署
:盈利策略
:行业网络
```
### 🛠️ 您的完整游戏开发工具包总结
---
完成整个太空游戏系列后,您现已掌握:
- **游戏架构**:事件驱动系统、游戏循环和状态管理
- **图形编程**Canvas API精灵渲染和视觉效果
- **输入系统**:键盘处理、碰撞检测和响应式控制
- **游戏设计**:玩家反馈、进度系统和参与机制
- **性能优化**:高效渲染、内存管理和帧率控制
- **用户体验**:清晰沟通、直观控制和细节打磨
- **专业模式**:整洁代码、调试技术和项目组织
**现实应用**:您的游戏开发技能直接适用于:
- **交互式网络应用**:动态界面和实时系统
- **数据可视化**:动画图表和交互图形
- **教育技术**:游戏化和吸引人的学习体验
- **移动开发**:基于触摸的交互和性能优化
- **模拟软件**:物理引擎和实时建模
- **创意产业**:互动艺术、娱乐和数字体验
**获得的专业技能**:您现在可以:
- **架构**复杂的交互系统从零开始
- **调试**使用系统化方法的实时应用
- **优化**性能以流畅用户体验
- **设计**吸引人的用户界面和交互模式
- **高效协作**处理技术项目并合理组织代码
**掌握的游戏开发概念**
- **实时系统**:游戏循环、帧率管理和性能
- **事件驱动架构**:解耦系统和消息传递
- **状态管理**:复杂数据处理和生命周期管理
- **用户界面编程**Canvas 图形和响应式设计
- **游戏设计理论**:玩家心理和参与机制
**下一阶段**您已准备好探索高级游戏框架、3D 图形、多玩家系统,或转向专业游戏开发职位!
🌟 **成就解锁**:您已完成完整的游戏开发旅程,从零打造出专业品质的互动体验!
**欢迎加入游戏开发社区!** 🎮✨
## GitHub Copilot Agent 挑战 🚀
使用 Agent 模式完成以下挑战:
**描述:** 通过实现一个关卡进度系统,增加难度和奖励功能,增强太空游戏。
**提示:** 创建一个多关卡太空游戏系统,每个关卡中敌舰数量增加,速度和生命值也提高。增加随关卡增长的得分倍增器,并实现当敌人被摧毁时随机出现的强化道具(如速射或护盾)。包括关卡完成奖励,并在屏幕上显示当前关卡与现有的得分和生命值。
了解更多关于[agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode)的信息。
## 🚀 可选增强挑战
**给您的游戏添加音效**:通过实现音效来增强游戏体验!考虑添加以下音效:
- **激光射击**,玩家开火时
- **敌舰摧毁**,敌舰被击中时
- **英雄受伤**,玩家受击时
- **胜利音乐**,游戏胜利时
- **失败音效**,游戏失败时
**音频实现示例:**
```javascript
// 创建音频对象
const laserSound = new Audio('assets/laser.wav');
const explosionSound = new Audio('assets/explosion.wav');
// 在游戏事件中播放声音
function playLaserSound() {
laserSound.currentTime = 0; // 重置到开始处
laserSound.play();
}
```
## 🚀 挑战
**您需要了解:**
- **创建**用于不同音效的 Audio 对象
- **重置**`currentTime`以支持快速连续音效播放
- **处理**浏览器自动播放策略,通过用户交互触发声音
- **管理**音量和时序以提升游戏体验
添加一个音效!你能为游戏添加音效以增强游戏体验吗?比如在激光命中、英雄飞船死亡或胜利时播放音效?查看这个[sandbox](https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_audio_play)来学习如何使用JavaScript播放音效。
> 💡 **学习资源**:探索这个[audio sandbox](https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_audio_play),了解更多 JavaScript 游戏中音频实现
## 课后测验
@ -224,13 +670,15 @@ npm start
## 复习与自学
你的任务是创建一个全新的样本游戏,因此探索一些有趣的游戏,看看你可能会构建哪种类型的游戏。
您的作业是创建一个新的示例游戏,您可以探索一些有趣的游戏,看看想制作哪种类型的游戏。
## 作业
## 任务
[构建一个样本游戏](assignment.md)
[构建示例游戏](assignment.md)
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免责声明**
本文档使用AI翻译服务[Co-op Translator](https://github.com/Azure/co-op-translator)进行翻译。尽管我们努力确保准确性,但请注意,自动翻译可能包含错误或不准确之处。应以原始语言的文档作为权威来源。对于关键信息,建议使用专业人工翻译。因使用本翻译而引起的任何误解或误读,我们概不负责。
本文件由 AI 翻译服务 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻译而成。尽管我们努力确保准确性,但请注意自动翻译可能包含错误或不准确之处。原始文本的原文版本应视为权威来源。对于重要信息,建议采用专业人工翻译。对于因使用本翻译而产生的任何误解或曲解,我们概不负责。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,31 +1,173 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "24201cf428c7edba1ccec2a78a0dd8f8",
"translation_date": "2025-08-23T23:08:34+00:00",
"original_hash": "232d592791465c1678cab3a2bb6cd3e8",
"translation_date": "2026-01-06T11:27:43+00:00",
"source_file": "6-space-game/6-end-condition/assignment.md",
"language_code": "zh"
}
-->
# 建一个示例游戏
# 建一个示例游戏
## 说明
## 作业概述
尝试制作一个小型游戏,练习不同的结束条件。可以设置不同的条件,比如达到一定的分数、英雄失去所有生命值或所有怪物被击败。制作一个简单的游戏,比如基于控制台的冒险游戏。以下游戏流程可以作为灵感:
现在你已经掌握了太空游戏中的游戏结束条件和重新开始功能,是时候将这些概念应用到一个全新的游戏体验中。你将设计并构建自己的游戏,展示不同的结束条件模式和重新开始机制。
本次作业挑战你在游戏设计上的创造性思维,同时练习所学的技术技能。你将探索不同的胜利和失败场景,实施玩家进度,并创造有趣的重新开始体验。
## 项目要求
### 核心游戏功能
你的游戏必须包含以下基本元素:
**结束条件多样性**:实现至少两种不同的游戏结束方式:
- **基于积分的胜利**:玩家达到目标分数或收集特定物品
- **基于生命的失败**:玩家失去所有可用生命或生命值
- **目标完成**:所有敌人被击败、特定物品被收集或目标达成
- **基于时间**:游戏在设定时间结束或倒计时归零
**重新开始功能**
- **清除游戏状态**:移除所有之前的游戏对象并重置变量
- **重新初始化系统**:以新的玩家状态、敌人和目标重新开始
- **用户友好控制**:提供清晰的重新开始游戏指令
**玩家反馈**
- **胜利信息**:用积极的反馈庆祝玩家成就
- **失败信息**:提供鼓励性消息激励重玩
- **进度指示**:显示当前分数、生命或目标状态
### 游戏创意与灵感
选择以下游戏概念之一或自创:
#### 1. 控制台冒险游戏
创建带有战斗机制的文本冒险:
```
Hero> Strikes with broadsword - orc takes 3p damage
Orc> Hits with club - hero takes 2p damage
Orc> Hits with club - hero takes 2p damage
Hero> Kicks - orc takes 1p damage
Game> Orc is defeated - Hero collects 2 coins
Game> ****No more monsters, you have conquered the evil fortress****
```
## 评分标准
**关键功能实现:**
- **回合制战斗**,具有不同攻击选项
- 玩家和敌人的**生命值**
- 用于收集金币或物品的**物品栏系统**
- 多种难度不同的**敌人类型**
- 当所有敌人被击败时**胜利条件**
#### 2. 收集游戏
- **目标**:收集特定物品同时避开障碍
- **结束条件**:达到目标收集数或失去所有生命
- **进度**:随着游戏进行,物品变得更难获取
#### 3. 拼图游戏
- **目标**:解决难度不断增加的谜题
- **结束条件**:完成所有关卡或用尽移动/时间
- **重新开始**:重置为第一关,清除进度
#### 4. 防御游戏
- **目标**:保护基地免受一波波敌人攻击
- **结束条件**:存活所有波次(胜利)或基地被摧毁(失败)
- **进度**:敌波难度和数量增加
## 实施指南
### 入门
1. **规划你的游戏设计**
- 绘制基本的游戏循环
- 明确你的结束条件
- 确定重新开始时需要重置的数据
2. **设置项目结构**
```
my-game/
├── index.html
├── style.css
├── game.js
└── README.md
```
3. **创建核心游戏循环**
- 初始化游戏状态
- 处理用户输入
- 更新游戏逻辑
- 检查结束条件
- 渲染当前状态
### 技术要求
**使用现代JavaScript**
- 使用`const`和`let`声明变量
- 适当使用箭头函数
- 实现ES6+功能,如模板字符串和解构赋值
**事件驱动架构**
- 创建用户交互事件处理器
- 通过事件实现游戏状态变化
- 使用事件监听实现重新开始功能
**清洁代码实践**
- 编写单一职责函数
- 使用描述性变量和函数名
- 添加注释说明游戏逻辑和规则
- 将代码组织成逻辑部分
## 提交要求
### 交付内容
1. **完整游戏文件**运行游戏所需的所有HTML、CSS和JavaScript文件
2. **README.md**:文档说明:
- 如何玩你的游戏
- 你实现了哪些结束条件
- 重新开始的操作说明
- 任何特别的功能或机制
3. **代码注释**:清晰解释游戏逻辑和算法
### 测试清单
提交前,请确认你的游戏:
- [ ] **在浏览器控制台无错误运行**
- [ ] **实现多种结束条件**
- [ ] **重新开始时正确重置状态**
- [ ] **向玩家提供清晰的游戏状态反馈**
- [ ] **使用现代JavaScript语法和最佳实践**
- [ ] **README.md中包含完整文档**
## 评估标准
| 评分标准 | 优秀 (4) | 良好 (3) | 中等 (2) | 初级 (1) |
|----------|----------|----------|----------|----------|
| **游戏功能** | 完整的游戏,多个结束条件,流畅重启,玩法体验优秀 | 完整游戏,基本结束条件,功能性重启机制 | 部分游戏,部分结束条件实现,重启存在小问题 | 不完整的游戏,功能受限且存在显著缺陷 |
| **代码质量** | 代码整洁组织良好使用现代JavaScript注释详尽结构优秀 | 代码组织良好,语法现代,注释充分,结构清晰 | 基础代码组织,部分现代语法,注释少 | 代码组织差,语法过时,缺少注释和结构 |
| **用户体验** | 游戏直观,指令清晰,反馈优秀,结束和重启体验吸引人 | 性能良好,有基本指令和反馈,结束和重启功能正常 | 基础玩法,指令少,游戏状态反馈有限 | 游戏混乱,指令不清,反馈差 |
| **技术实现** | 精通游戏开发概念,事件处理和状态管理 | 理解游戏概念,实施良好 | 基础理解,能实现但不完善 | 理解有限,实现较差 |
| **文档** | README详尽清晰代码注释充分测试证明完整 | 良好文档,指令清晰,注释足够 | 基础文档,指令少 | 缺少或无文档 |
### 评分等级
- **优秀 (16-20分)**:超出预期,创意丰富,实施完善
- **良好 (12-15分)**:满足所有要求,执行稳健
- **中等 (8-11分)**:大部分要求满足,有些小问题
- **初级 (4-7分)**:部分要求满足,需大幅改进
## 额外学习资源
- [MDN 游戏开发指南](https://developer.mozilla.org/en-US/docs/Games)
- [JavaScript 游戏开发教程](https://developer.mozilla.org/en-US/docs/Games/Tutorials)
- [Canvas API 文档](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API)
- [游戏设计原则](https://www.gamasutra.com/blogs/)
> 💡 **专业提示**:从简单开始,逐步添加功能。一个打磨精良的简单游戏胜过一个充满漏洞的复杂游戏!
| 标准 | 卓越表现 | 合格表现 | 需要改进 |
| -------- | ---------------------- | -------------------------- | -------------------------- |
| | 完整的游戏呈现 | 部分游戏呈现 | 部分游戏存在漏洞 |
---
**免责声明**
本文档使用AI翻译服务[Co-op Translator](https://github.com/Azure/co-op-translator)进行翻译。尽管我们努力确保翻译的准确性,但请注意,自动翻译可能包含错误或不准确之处。原始语言的文档应被视为权威来源。对于关键信息,建议使用专业人工翻译。我们不对因使用此翻译而产生的任何误解或误读承担责任。
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免责声明**
本文档使用AI翻译服务[Co-op Translator](https://github.com/Azure/co-op-translator)进行翻译。尽管我们力求准确,但请注意自动翻译可能包含错误或不准确之处。原始语言的文档应被视为权威来源。对于重要信息,建议采用专业人工翻译。我们不对因使用本翻译而产生的任何误解或曲解负责。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,31 +1,96 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "8a07db14e75ac62f013b7de5df05981d",
"translation_date": "2025-08-29T14:43:15+00:00",
"original_hash": "351678bece18f07d9daa987a881fb062",
"translation_date": "2026-01-06T11:40:39+00:00",
"source_file": "7-bank-project/1-template-route/README.md",
"language_code": "zh"
}
-->
# 构建银行应用程序第1部分Web应用中的HTML模板和路由
# 构建银行应用第1部分Web应用中的HTML模板与路由
```mermaid
journey
title 你的银行应用开发旅程
section SPA 基础
了解单页应用: 3: Student
学习模板概念: 4: Student
精通 DOM 操作: 4: Student
section 路由系统
实现客户端路由: 4: Student
处理浏览器历史: 5: Student
创建导航系统: 5: Student
section 专业模式
构建模块化架构: 5: Student
应用最佳实践: 5: Student
创建用户体验: 5: Student
```
1969年当阿波罗11号的导航计算机飞向月球时它必须在不重启整个系统的情况下切换不同程序。现代Web应用类似——它们改变你看到的内容而不重新加载所有内容。这创造了用户如今期望的流畅、响应式体验。
与每次交互都重新加载整个页面的传统网站不同现代Web应用只更新需要更改的部分。这种方法就像任务控制中心在保持持续通信的同时切换不同显示屏一样产生了我们习以为常的流畅体验。
以下是造成差异的关键:
| 传统多页面应用 | 现代单页面应用 |
|----------------------------|-------------------------|
| **导航** | 每个屏幕都全页重新加载 | 内容瞬时切换 |
| **性能** | 由于完整HTML下载较慢 | 通过局部更新更快 |
| **用户体验** | 令人不适的页面闪烁 | 平滑的类应用过渡 |
| **数据共享** | 页面间共享困难 | 状态管理简单 |
| **开发** | 维护多个HTML文件 | 单一HTML与动态模板 |
**理解演变:**
- **传统应用** 每次导航都需服务器请求
- **现代SPA** 一次加载使用JavaScript动态更新内容
- **用户期望** 现偏好即时、无缝交互
- **性能优势** 包括带宽减少和响应加快
本课将构建一个拥有多个无缝流转页面的银行应用。就像科学家使用可重构的模块化仪器进行不同实验一样我们将使用HTML模板作为可重复使用的组件根据需要显示。
你将使用HTML模板不同画面的可重用蓝图、JavaScript路由切换不同屏幕的系统和浏览器历史API保持返回按钮正常工作。这些是React、Vue和Angular等框架的基本技术。
最终,你将获得一个展示专业单页面应用原则的可用银行应用。
```mermaid
mindmap
root((单页应用程序))
Architecture
模板系统
客户端路由
状态管理
事件处理
Templates
可复用组件
动态内容
DOM操作
内容切换
Routing
URL管理
历史API
导航逻辑
浏览器集成
User Experience
快速导航
平滑过渡
状态一致
现代交互
Performance
减少服务器请求
更快的页面切换
高效资源使用
更佳响应性
```
## 课前测验
[课前测验](https://ff-quizzes.netlify.app/web/quiz/41)
### 简介
自从浏览器中出现JavaScript以来网站变得比以往更加互动和复杂。如今Web技术常被用来创建直接在浏览器中运行的功能齐全的应用程序我们称之为[Web应用程序](https://en.wikipedia.org/wiki/Web_application)。由于Web应用程序高度互动用户不希望每次执行操作时都需要等待整个页面重新加载。因此JavaScript被用来直接通过DOM更新HTML以提供更流畅的用户体验。
### 你需要准备的内容
在本课程中我们将为创建银行Web应用程序奠定基础使用HTML模板创建多个屏幕这些屏幕可以显示和更新而无需重新加载整个HTML页面。
### 前置条件
你需要一个本地Web服务器来测试我们将在本课程中构建的Web应用程序。如果你没有可以安装[Node.js](https://nodejs.org),然后在项目文件夹中使用命令`npx lite-server`。它会创建一个本地Web服务器并在浏览器中打开你的应用程序。
我们需要一个本地Web服务器来测试银行应用——别担心没你想象的难如果你还没有配置只需安装 [Node.js](https://nodejs.org) 并在项目文件夹运行 `npx lite-server`。这个命令会启动本地服务器并自动在浏览器打开应用。
### 准备工作
在你的电脑上创建一个名为`bank`的文件夹,并在其中创建一个名为`index.html`的文件。我们将从这个HTML[样板代码](https://en.wikipedia.org/wiki/Boilerplate_code)开始:
在你的电脑上创建一个名为 `bank` 的文件夹,里面放一个名为 `index.html` 的文件。我们将从这个HTML [模板](https://en.wikipedia.org/wiki/Boilerplate_code)开始:
```html
<!DOCTYPE html>
@ -41,30 +106,77 @@ CO_OP_TRANSLATOR_METADATA:
</html>
```
**这个模板提供了:**
- **建立** 正确DOCTYPE声明的HTML5文档结构
- **设置** 字符编码为UTF-8以支持国际文本
- **启用** 响应式设计使用viewport元标签兼容移动设备
- **设定** 浏览器标签页显示的描述性标题
- **创建** 一个干净的主体部分,用于构建应用
> 📁 **项目结构预览**
>
> **完成此课时后,项目将包含:**
> ```
> bank/
> ├── index.html <!-- Main HTML with templates -->
> ├── app.js <!-- Routing and navigation logic -->
> └── style.css <!-- (Optional for future lessons) -->
> ```
>
> **文件职责:**
> - **index.html**:包含所有模板和应用结构
> - **app.js**:负责路由、导航和模板管理
> - **模板**定义登录、仪表盘及其他屏幕的UI
---
## HTML模板
如果你想为一个网页创建多个屏幕一种解决方案是为每个屏幕创建一个HTML文件。然而这种解决方案有一些不便之处
模板解决了Web开发中的一个根本问题。古腾堡在1440年代发明活字印刷时意识到与其刻画整页他可以制作可复用的字母块并按需排列。HTML模板原理相同——不为每个屏幕创建单独HTML文件而是定义可复用结构按需显示。
```mermaid
flowchart TD
A["📋 模板定义"] --> B["💬 在DOM中隐藏"]
B --> C["🔍 JavaScript查找模板"]
C --> D["📋 克隆模板内容"]
D --> E["🔗 附加到可见DOM"]
E --> F["👁️ 用户看到内容"]
G["登录模板"] --> A
H["仪表板模板"] --> A
I["未来模板"] --> A
style A fill:#e3f2fd
style D fill:#e8f5e8
style F fill:#fff3e0
style B fill:#f3e5f5
```
把模板看作应用不同部分的蓝图。就像建筑师绘制一个蓝图多次使用而不是重复绘制相同房间我们创建模板一次按需实例化。浏览器会将这些模板隐藏直到JavaScript激活它们。
- 切换屏幕时需要重新加载整个HTML这可能会很慢。
- 在不同屏幕之间共享数据会变得困难。
如果想为网页创建多个屏幕一种办法是每个屏幕一个HTML文件。然而这样做有不便
另一种方法是只使用一个HTML文件并使用`<template>`元素定义多个[HTML模板](https://developer.mozilla.org/docs/Web/HTML/Element/template)。模板是一个可重复使用的HTML块浏览器不会显示它需要在运行时通过JavaScript实例化。
- 切换屏幕时必须重新加载整个HTML速度较慢
- 不同屏幕间共享数据困难
### 任务
另一种方法是只用一个HTML文件通过 `<template>` 元素定义多个 [HTML模板](https://developer.mozilla.org/docs/Web/HTML/Element/template)。模板是一个复用的HTML块浏览器不显示需使用JavaScript在运行时实例化。
### 我们开始构建
我们将创建一个银行应用程序包含两个屏幕登录页面和仪表盘页面。首先在HTML主体中添加一个占位元素我们将用它来实例化应用程序的不同屏幕
我们要创建一个包含两个主要屏幕的银行应用登录页和仪表盘。首先在HTML body中添加一个占位元素——所有不同屏幕将显示于此
```html
<div id="app">Loading...</div>
```
我们给它一个`id`以便稍后通过JavaScript更容易找到它。
**理解这个占位符:**
- **创建** 一个ID为“app”的容器将显示所有屏幕
- **显示** 加载消息直到JavaScript初始化第一个屏幕
- **提供** 动态内容的唯一挂载点
- **便于** JavaScript 使用 `document.getElementById()` 访问
> 提示:由于这个元素的内容会被替换,我们可以放置一个加载消息或指示器,在应用程序加载时显示。
> 💡 **专业提示**: 由于该元素内容将被替换,可以放置加载消息或指示器,应用加载时显示。
接下来在HTML中添加登录页面的模板。现在我们只在其中放置一个标题和一个包含导航链接的部分。
接下来在HTML模板下方添加登录页的模板。暂时只放置一个标题和一个包含用于导航的链接的部分。
```html
<template id="login">
@ -75,11 +187,17 @@ CO_OP_TRANSLATOR_METADATA:
</template>
```
然后我们添加仪表盘页面的HTML模板。这个页面将包含不同的部分
**分析此登录模板:**
- **定义** ID为“login”的模板便于JavaScript定位
- **包含** 一个主标题,确立应用品牌
- **用** 语义化的 `<section>` 元素分组相关内容
- **提供** 导航链接,引导用户跳转仪表盘
- 一个包含标题和注销链接的页眉
- 银行账户的当前余额
- 一个显示交易记录的表格
然后添加仪表盘的另一个HTML模板。此页面包含几个部分
- 带标题和注销链接的页眉
- 当前银行账户余额
- 一个使用表格展示的交易列表
```html
<template id="dashboard">
@ -106,31 +224,88 @@ CO_OP_TRANSLATOR_METADATA:
</template>
```
> 提示创建HTML模板时如果你想查看它的外观可以通过将`<template>`和`</template>`行用`<!-- -->`包裹来注释掉它们。
✅ 你认为我们为什么在模板上使用`id`属性?我们是否可以使用其他方法,比如类?
## 使用JavaScript显示模板
如果你在浏览器中尝试当前的HTML文件你会发现它停留在显示`Loading...`。这是因为我们需要添加一些JavaScript代码来实例化并显示HTML模板。
实例化模板通常分为三个步骤:
1. 在DOM中检索模板元素例如使用[`document.getElementById`](https://developer.mozilla.org/docs/Web/API/Document/getElementById)。
2. 使用[`cloneNode`](https://developer.mozilla.org/docs/Web/API/Node/cloneNode)克隆模板元素。
3. 将其附加到可见元素的DOM中例如使用[`appendChild`](https://developer.mozilla.org/docs/Web/API/Node/appendChild)。
**理解此仪表盘各部分:**
- **用** 语义 `<header>` 元素组织页面及导航
- **显示** 跨屏幕一致的应用标题,强化品牌
- **提供** 返回登录页的注销链接
- **展示** 专门区域内的当前账户余额
- **使用** 结构化的HTML表格组织交易数据
- **定义** 日期、对象和金额三栏表头
- **留空** 表体,待后续动态注入内容
> 💡 **专业提示**创建HTML模板时若想预览外观可以将 `<template>``</template>` 标签注释掉,使用 `<!-- -->` 包围。
### 🔄 **教学小检验**
**模板系统理解**在实现JavaScript前确保你了解
- ✅ 模板与普通HTML元素的区别
- ✅ 模板为何在JavaScript激活前保持隐藏
- ✅ 语义化HTML结构在模板中的重要性
- ✅ 模板如何支持可复用UI组件
**快速自测**若移除HTML外围的 `<template>` 标记会怎样?
*答:内容立即可见,却失去模板功能*
**架构优势**:模板提供:
- **重用性**:定义一次,多次实例
- **性能**避免重复HTML解析
- **可维护性**UI结构集中管理
- **灵活性**:动态切换内容
✅ 为什么我们给模板加了 `id` 属性?能否用类名代替?
## 用JavaScript激活模板
现在我们需要让模板具备功能。就像3D打印机根据数字蓝图制造实体JavaScript将我们的隐藏模板转换成用户可见且可交互的元素。
这个过程有三个一致步骤形成了现代Web开发的基础。掌握此模式你会在各框架和库中见到。
若在浏览器打开当前HTML文件屏幕会一直显示“Loading...”因为我们还没添加JavaScript来实例化并显示HTML模板。
实例化模板通常分三步:
1. 使用 [`document.getElementById`](https://developer.mozilla.org/docs/Web/API/Document/getElementById) 获取DOM中的模板元素。
2. 使用 [`cloneNode`](https://developer.mozilla.org/docs/Web/API/Node/cloneNode) 复制模板内容。
3. 使用 [`appendChild`](https://developer.mozilla.org/docs/Web/API/Node/appendChild) 将复制的内容附加到可见DOM元素。
```mermaid
flowchart TD
A[🔍 第1步查找模板] --> B[📋 第2步克隆模板]
B --> C[🔗 第3步附加到DOM]
A1["document.getElementById('login')"] --> A
B1["template.content.cloneNode(true)"] --> B
C1["app.appendChild(view)"] --> C
C --> D[👁️ 模板对用户可见]
style A fill:#e1f5fe
style B fill:#f3e5f5
style C fill:#e8f5e8
style D fill:#fff3e0
```
**过程视觉分解:**
- **第一步** 定位隐藏在DOM中的模板
- **第二步** 创建可安全修改的工作副本
- **第三步** 将副本插入可见页面区域
- **结果** 为用户提供可交互的功能屏幕
✅ 为什么我们需要在附加到DOM之前克隆模板如果跳过这一步会发生什么
✅ 为什么需要在附加到DOM前克隆模板若跳过这步会怎样
### 任务
在项目文件夹中创建一个名为`app.js`的新文件并在HTML的`<head>`部分导入该文件:
在项目文件夹内创建一个新文件 `app.js`并在HTML `<head>` 中引入它
```html
<script src="app.js" defer></script>
```
现在在`app.js`中,我们将创建一个新函数`updateRoute`
**理解此脚本引入:**
- **关联** JavaScript文件至HTML文档
- **使用** `defer` 属性确保脚本在HTML解析完成后执行
- **允许** 脚本执行时所有DOM元素已加载
- **遵循** 现代最佳脚本加载及性能实践
接下来在 `app.js` 中创建一个新函数 `updateRoute`
```js
function updateRoute(templateId) {
@ -142,19 +317,71 @@ function updateRoute(templateId) {
}
```
这里我们执行了上述的三个步骤。我们实例化`templateId`对应的模板,并将其克隆的内容放入应用程序的占位符中。注意,我们需要使用`cloneNode(true)`来复制模板的整个子树。
**逐步解析:**
- **通过唯一ID定位** 模板元素
- **使用** `cloneNode(true)` 深拷贝模板内容
- **定位** 应用容器,用于显示内容
- **清空** 应用容器已有内容
- **插入** 克隆后的模板内容至可见DOM
现在调用这个函数并传入一个模板,查看结果。
调用此函数传入其中一个模板ID看结果。
```js
updateRoute('login');
```
✅ 代码`app.innerHTML = '';`的作用是什么?如果没有它会发生什么?
**函数调用做了什么:**
- **激活** 传入的登录模板ID对应模板
- **展示** 如何程序化切换应用不同屏幕
- **将“Loading...”替换成登录界面**
✅ 代码中的 `app.innerHTML = '';` 是做什么的?没有它会怎样?
## 创建路由
在谈论Web应用程序时我们称*路由*为将**URL**映射到应该显示的特定屏幕的意图。在一个包含多个HTML文件的网站中这会自动完成因为文件路径会反映在URL中。例如在项目文件夹中有以下文件
路由本质上是把URL与正确内容连接起来。想想早期电话接线员如何使用配线板连接电话——他们接受请求并转接至正确目标。Web路由也类似根据URL请求决定展示何内容。
```mermaid
flowchart LR
A["🌐 URL 路径<br/>/dashboard"] --> B["🗺️ 路由对象<br/>查找"]
B --> C["🎯 模板 ID<br/>'dashboard'"]
C --> D["📌 查找模板<br/>getElementById"]
D --> E["👁️ 显示屏幕<br/>克隆并追加"]
F["📍 /login"] --> G["🎯 'login'"]
H["📍 /unknown"] --> I["❌ 未找到"]
I --> J["🔄 重定向到 /login"]
style B fill:#e3f2fd
style E fill:#e8f5e8
style I fill:#ffebee
style J fill:#fff3e0
```
传统上Web服务器通过不同HTML文件响应不同URL。我们构建的是单页面应用需用JavaScript自主管理路由。这样我们能更好地控制用户体验和性能。
```mermaid
flowchart LR
A["🌐 URL 路径<br/>/dashboard"] --> B["🗺️ 路由对象<br/>查找"]
B --> C["🎯 模板 ID<br/>'dashboard'"]
C --> D["📄 查找模板<br/>getElementById"]
D --> E["👁️ 显示屏幕<br/>克隆并追加"]
F["📍 /login"] --> G["🎯 'login'"]
H["📍 /unknown"] --> I["❌ 未找到"]
I --> J["🔄 重定向到 /login"]
style B fill:#e3f2fd
style E fill:#e8f5e8
style I fill:#ffebee
style J fill:#fff3e0
```
**理解路由流程:**
- **URL变更** 触发查看路由配置
- **有效路由** 映射至特定模板ID渲染
- **无效路由** 触发回退行为,避免错误状态
- **模板渲染** 按照之前学的三步完成
在Web应用中*路由*指意图将**URL**映射到应展示的特定屏幕。多HTML文件的网站会自动完成此映射因为文件路径和URL对应。例如假设项目有这些文件
```
mywebsite/index.html
@ -162,7 +389,7 @@ mywebsite/login.html
mywebsite/admin/index.html
```
如果你创建一个以`mywebsite`为根的Web服务器URL映射将是
若以 `mywebsite` 作为根目录运行Web服务器URL映射将是
```
https://site.com --> mywebsite/index.html
@ -170,11 +397,11 @@ https://site.com/login.html --> mywebsite/login.html
https://site.com/admin/ --> mywebsite/admin/index.html
```
然而对于我们的Web应用程序我们使用的是一个包含所有屏幕的单一HTML文件因此这种默认行为对我们没有帮助。我们必须手动创建这个映射并使用JavaScript更新显示的模板。
不过我们的Web应用只有一个HTML文件包含所有屏幕默认行为不适用。我们需手动创建映射并用JavaScript更新显示的模板。
### 任务
我们将使用一个简单的对象来实现一个[映射](https://en.wikipedia.org/wiki/Associative_array)将URL路径与我们的模板关联。在`app.js`文件顶部添加这个对象。
用一个简单对象实现URL路径和模板间的[映射](https://en.wikipedia.org/wiki/Associative_array)。在`app.js`文件顶部添加此对象。
```js
const routes = {
@ -183,7 +410,12 @@ const routes = {
};
```
现在稍微修改一下`updateRoute`函数。我们不再直接传递`templateId`作为参数而是先查看当前URL然后使用我们的映射获取对应的模板ID值。我们可以使用[`window.location.pathname`](https://developer.mozilla.org/docs/Web/API/Location/pathname)来仅获取URL中的路径部分。
**理解此路由配置:**
- **定义** URL路径至模板ID的映射关系
- **使用** 对象语法键为URL路径值为模板信息
- **方便** 对任意URL快速查找对应模板
- **提供** 未来添加新路由的可扩展结构
现在让我们稍微修改一下 `updateRoute` 函数。不是直接将 `templateId` 作为参数传递,而是先查看当前的 URL然后使用我们的映射关系来获取相应的模板 ID。我们可以使用 [`window.location.pathname`](https://developer.mozilla.org/docs/Web/API/Location/pathname) 来仅获取 URL 中的路径部分。
```js
function updateRoute() {
@ -198,26 +430,100 @@ function updateRoute() {
}
```
这里我们将声明的路由映射到对应的模板。你可以通过在浏览器中手动更改URL来测试它是否正常工作。
✅ 如果在URL中输入一个未知路径会发生什么我们如何解决这个问题
## 添加导航
应用程序的下一步是添加在页面之间导航的功能而无需手动更改URL。这包括两件事
**解析这里发生的事情:**
- **提取** 浏览器 URL 中当前的路径,使用 `window.location.pathname`
- **查找** 在我们的 routes 对象中对应的路由配置
- **获取** 路由配置中的模板 ID
- **遵循** 之前相同的模板渲染过程
- **创建** 一个响应 URL 变化的动态系统
1. 更新当前URL
2. 根据新URL更新显示的模板
这里我们将声明的路由映射到对应的模板。你可以通过手动更改浏览器中的 URL 来尝试检验其是否正确工作。
我们已经通过`updateRoute`函数解决了第二部分因此我们需要弄清楚如何更新当前URL。
✅ 如果你在 URL 中输入一个未知路径会发生什么?我们该如何解决这个问题?
我们需要使用JavaScript特别是[`history.pushState`](https://developer.mozilla.org/docs/Web/API/History/pushState)它允许更新URL并在浏览历史中创建新条目而无需重新加载HTML。
## 添加导航
> 注意虽然HTML锚元素[`<a href>`](https://developer.mozilla.org/docs/Web/HTML/Element/a)本身可以用于创建指向不同URL的超链接但默认情况下它会使浏览器重新加载HTML。在使用自定义JavaScript处理路由时需要使用`preventDefault()`函数阻止点击事件的默认行为。
有了路由,用户需要一种方式在应用内导航。传统网站点击链接时会重新加载整个页面,但我们希望在不刷新页面的情况下同时更新 URL 和内容。这会带来更流畅的体验,类似于桌面应用切换不同视图的方式。
我们需要协调两件事:更新浏览器的 URL 以便用户可以收藏页面和分享链接,以及显示恰当的内容。正确实现时,这将创建用户对现代应用所期待的无缝导航体验。
```mermaid
sequenceDiagram
participant User
participant Browser
participant App
participant Template
User->>Browser: 点击 "登录" 链接
Browser->>App: onclick 事件触发
App->>App: preventDefault() & navigate('/dashboard')
App->>Browser: history.pushState('/dashboard')
Browser->>Browser: URL 更新为 /dashboard
App->>App: 调用 updateRoute()
App->>Template: 查找并克隆仪表板模板
Template->>App: 返回克隆内容
App->>Browser: 用模板替换应用内容
Browser->>User: 显示仪表板界面
Note over User,Template: 用户点击浏览器后退按钮
User->>Browser: 点击后退按钮
Browser->>Browser: 历史记录回退到 /login
Browser->>App: 触发 popstate 事件
App->>App: 自动调用 updateRoute()
App->>Template: 查找并克隆登录模板
Template->>App: 返回克隆内容
App->>Browser: 用模板替换应用内容
Browser->>User: 显示登录界面
```
### 🔄 **教学核对点**
**单页应用架构**:验证你对完整系统的理解:
- ✅ 客户端路由与传统服务器端路由有何不同?
- ✅ History API 为什么对正确的 SPA 导航至关重要?
- ✅ 模板如何实现无刷新页面的动态内容?
- ✅ 事件处理在拦截导航中扮演什么角色?
**系统集成**:你的 SPA 展示了:
- **模板管理**:可复用的 UI 组件与动态内容
- **客户端路由**:无需服务器请求的 URL 管理
- **事件驱动架构**:响应式导航与用户交互
- **浏览器集成**:正确的历史记录和前进/后退按钮支持
- **性能优化**:快速切换,减少服务器负担
**专业模式**:你已实现:
- **模型-视图分离**:模板与应用逻辑分开
- **状态管理**URL 状态与显示内容同步
- **渐进增强**JavaScript 强化基础 HTML 功能
- **用户体验**:平滑类应用导航,无需刷新页面
> <20> **架构洞见**:导航系统组件
>
> **你构建的内容:**
> - **🔄 URL 管理**:无刷新更新浏览器地址栏
> - **📋 模板系统**:根据当前路由动态切换内容
> - **📚 历史集成**:支持浏览器前进和后退按钮
> - **🛡️ 错误处理**:无效或缺失路由时的优雅降级
>
> **组件协同工作方式:**
> - **监听** 导航事件(点击、历史变化)
> - **更新** URL使用 History API
> - **渲染** 新路由对应的模板
> - **维持** 用户无缝体验
下一步是让我们的应用可以在不手动更改 URL 的情况下导航页面。这包括两件事:
1. 更新当前 URL
2. 基于新 URL 更新显示的模板
我们已经用 `updateRoute` 处理了第二部分,所以需要弄清楚如何更新当前 URL。
我们将使用 JavaScript尤其是 [`history.pushState`](https://developer.mozilla.org/docs/Web/API/History/pushState),它允许更新 URL 以及在浏览历史中创建新条目,而无需重新加载 HTML。
> ⚠️ **重要说明**:虽然 HTML 的锚点元素 [`<a href>`](https://developer.mozilla.org/docs/Web/HTML/Element/a) 可以用来创建指向不同 URL 的超链接,但默认行为是浏览器重新加载页面。使用自定义 JavaScript 处理路由时,必须通过点击事件的 preventDefault() 函数来阻止这种行为。
### 任务
让我们创建一个新函数,用于在应用程序中导航:
让我们创建一个可以用来在应用内导航的新函数
```js
function navigate(path) {
@ -226,9 +532,15 @@ function navigate(path) {
}
```
此方法首先根据给定路径更新当前URL然后更新模板。属性`window.location.origin`返回URL根路径允许我们从给定路径重新构建完整URL。
**理解导航函数的功能:**
- **更新** 浏览器 URL使用 `history.pushState`
- **添加** 新条目到浏览器历史栈,支持前进/后退按钮
- **触发** `updateRoute()` 函数,显示对应模板
- **保持** 单页应用体验,无页面刷新
现在我们有了这个函数,可以解决路径与定义的路由不匹配时的问题。我们将通过添加一个回退到现有路由的功能来修改`updateRoute`函数。
此方法先根据给定路径更新当前 URL接着更新模板。`window.location.origin` 属性返回 URL 根地址,方便根据路径重新构建完整 URL。
既然有了这个函数,我们可以解决路径不匹配任何定义路由的问题。我们将在 `updateRoute` 函数中添加一个回退逻辑,当找不到匹配时导航至现有路由。
```js
function updateRoute() {
@ -239,12 +551,23 @@ function updateRoute() {
return navigate('/login');
}
...
const template = document.getElementById(route.templateId);
const view = template.content.cloneNode(true);
const app = document.getElementById('app');
app.innerHTML = '';
app.appendChild(view);
}
```
如果找不到路由,我们现在会重定向到`login`页面。
**关键点回顾:**
- **检查** 当前路径是否有对应路由
- **访问无效路由时,重定向** 到登录页
- **提供** 防止导航失败的回退机制
- **确保** 即使 URL 错误,用户也能看到有效页面
如果找不到路由,则重定向到 `login` 页面。
接下来创建一个函数用于获取点击链接时的URL并阻止浏览器的默认链接行为
接下来创建一个函数,能够在点击链接时获取 URL并阻止浏览器默认跳转行为:
```js
function onLinkClick(event) {
@ -253,7 +576,11 @@ function onLinkClick(event) {
}
```
通过在HTML中的*登录*和*注销*链接上添加绑定,完成导航系统。
**解析这个点击处理函数:**
- **阻止** 浏览器默认点击链接行为,调用 `preventDefault()`
- **提取** 所点击链接元素的目标 URL
- **调用** 自定义导航函数,代替页面重载
- **保持** 平滑的单页应用体验
```html
<a href="/dashboard" onclick="onLinkClick(event)">Login</a>
@ -261,56 +588,237 @@ function onLinkClick(event) {
<a href="/login" onclick="onLinkClick(event)">Logout</a>
```
上面的`event`对象捕获`click`事件并将其传递给我们的`onLinkClick`函数。
使用[`onclick`](https://developer.mozilla.org/docs/Web/API/GlobalEventHandlers/onclick)属性将`click`事件绑定到JavaScript代码这里是调用`navigate()`函数。
尝试点击这些链接,你现在应该能够在应用程序的不同屏幕之间导航。
`history.pushState`方法是HTML5标准的一部分并在[所有现代浏览器](https://caniuse.com/?search=pushState)中实现。如果你正在为旧版浏览器构建Web应用程序可以使用一个技巧代替此API在路径前使用[哈希(`#`](https://en.wikipedia.org/wiki/URI_fragment),可以实现与常规锚导航兼容的路由,并且不会重新加载页面,因为它的目的是在页面内创建内部链接。
## 处理浏览器的后退和前进按钮
**这个 onclick 绑定实现了什么:**
- **连接** 每个链接与自定义导航系统
- **将** 点击事件传递给 `onLinkClick` 函数处理
- **启用** 无刷新平滑导航
- **维持** 用户可收藏或分享的正确 URL 结构
[`onclick`](https://developer.mozilla.org/docs/Web/API/GlobalEventHandlers/onclick) 属性绑定了 `click` 事件,这里绑定的是调用 `navigate()` 函数。
试着点击这些链接,现在应该能在应用的不同界面间导航。
`history.pushState` 方法是 HTML5 标准的一部分,已被[所有现代浏览器](https://caniuse.com/?search=pushState)支持。如果你为较老浏览器构建应用,有个替代方案:使用 [hash#](https://en.wikipedia.org/wiki/URI_fragment) 作为路径前缀,你可以实现基于常规锚点导航的路由,页面不会重载,因为其本意是创建页内锚点链接。
## 让后退和前进按钮生效
后退和前进按钮是网页浏览的基础,就像 NASA 任务控制人员可以回顾太空任务中的系统状态一样。用户期望这些按钮正常工作,不然就破坏了预期的浏览体验。
我们的单页应用需要额外配置来支持这一点。浏览器维护一个历史记录栈(我们通过 `history.pushState` 持续添加),但在用户通过历史记录导航时,应用需要对应更新显示内容。
```mermaid
sequenceDiagram
participant User
participant Browser
participant App
participant Template
User->>Browser: 点击 "登录" 链接
Browser->>App: onclick 事件触发
App->>App: preventDefault() & navigate('/dashboard')
App->>Browser: history.pushState('/dashboard')
Browser->>Browser: URL 更新为 /dashboard
App->>App: 调用 updateRoute()
App->>Template: 查找并克隆仪表盘模板
Template->>App: 返回克隆内容
App->>Browser: 用模板替换应用内容
Browser->>User: 显示仪表盘屏幕
Note over User,Template: 用户点击浏览器后退按钮
User->>Browser: 点击后退按钮
Browser->>Browser: 历史记录后退至 /login
Browser->>App: popstate 事件触发
App->>App: 自动调用 updateRoute()
App->>Template: 查找并克隆登录模板
Template->>App: 返回克隆内容
App->>Browser: 用模板替换应用内容
Browser->>User: 显示登录屏幕
```
**关键交互点:**
- **用户操作** 通过点击或浏览器按钮触发导航
- **应用拦截** 链接点击,防止页面刷新
- **历史 API** 管理 URL 变化和浏览器历史栈
- **模板** 提供每个页面的内容结构
- **事件监听** 确保应用对所有导航操作响应
使用`history.pushState`会在浏览器的导航历史中创建新条目。你可以通过按住浏览器的*后退按钮*来检查,它应该显示如下内容:
使用 `history.pushState` 创建了新的浏览历史条目。你可以按住浏览器的 *后退按钮* 来查看,如下图所示
![导航历史截图](../../../../translated_images/history.7fdabbafa521e06455b738d3dafa3ff41d3071deae60ead8c7e0844b9ed987d8.zh.png)
![浏览历史截图](../../../../translated_images/history.7fdabbafa521e064.zh.png)
如果你尝试点击几次后退按钮你会发现当前URL发生了变化历史记录也更新了但显示的模板保持不变。
如果你尝试点击后退几次,你会发现当前 URL 变化了,历史记录也更新了,但显示的模板却没有变化
这是因为应用程序不知道每次历史记录更改时需要调用`updateRoute()`。如果你查看[`history.pushState`文档](https://developer.mozilla.org/docs/Web/API/History/pushState)你会发现当状态更改时——即我们移动到不同的URL——会触发[`popstate`](https://developer.mozilla.org/docs/Web/API/Window/popstate_event)事件。我们将利用这一点来解决问题。
这是因为应用并不知道每当历史变化时需要调用 `updateRoute()`。查看 [`history.pushState` 文档](https://developer.mozilla.org/docs/Web/API/History/pushState) 会看到,如果状态发生变化 —— 意味着我们跳转到了不同 URL —— 会触发 [`popstate`](https://developer.mozilla.org/docs/Web/API/Window/popstate_event) 事件。我们将用它来解决这个问题。
### 任务
为了确保浏览器历史记录更改时显示的模板得到更新,我们将附加一个新函数来调用`updateRoute()`。我们将在`app.js`文件底部完成此操作:
为了确保当浏览器历史变化时,显示的模板也会更新,我们将在 `app.js` 文件底部添加一个新函数,调用 `updateRoute()`
```js
window.onpopstate = () => updateRoute();
updateRoute();
```
> 注意:我们在这里使用了[箭头函数](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Functions/Arrow_functions)来声明`popstate`事件处理程序以简化代码,但普通函数也可以正常工作。
**理解历史集成:**
- **监听** 当用户通过浏览器按钮导航时触发的 `popstate` 事件
- **使用** 箭头函数简写事件处理器
- **自动调用** 每次历史状态改变就调用 `updateRoute()`
- **初始化** 页面首次加载时也调用 `updateRoute()`
- **保证** 无论用户如何导航,显示正确模板
以下是关于箭头函数的复习视频:
> 💡 **专业提示**:这里我们用 [箭头函数](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Functions/Arrow_functions) 简洁声明 `popstate` 事件处理器,但常规函数也同样有效。
[![箭头函数](https://img.youtube.com/vi/OP6eEbOj2sc/0.jpg)](https://youtube.com/watch?v=OP6eEbOj2sc "箭头函数")
这是一个箭头函数复习视频:
> 🎥 点击上方图片观看关于箭头函数的视频。
[![箭头函数](https://img.youtube.com/vi/OP6eEbOj2sc/0.jpg)](https://youtube.com/watch?v=OP6eEbOj2sc "箭头函数")
现在尝试使用浏览器的后退和前进按钮,检查显示的路由是否正确更新。
> 🎥 点击上图观看箭头函数视频。
现在尝试使用浏览器的后退和前进按钮,查看这次显示的路由是否正确更新。
### ⚡ **你可以在接下来的 5 分钟内做什么**
- [ ] 测试你的银行应用是否支持浏览器前进/后退按钮导航
- [ ] 尝试手动在地址栏输入不同的 URL 测试路由
- [ ] 打开浏览器开发者工具,查看模板如何被克隆到 DOM
- [ ] 尝试添加 console.log 来跟踪路由流程
### 🎯 **你可以在接下来的一小时内完成什么**
- [ ] 完成课后测验,理解 SPA 架构概念
- [ ] 添加 CSS 样式,让银行应用模板更专业
- [ ] 实现 404 错误页挑战,处理错误路由
- [ ] 创建鸣谢页面挑战,添加额外路由功能
- [ ] 添加加载状态和模板切换过渡动画
### 📅 **你的一周 SPA 开发路线**
- [ ] 完成包含表单、数据管理和持久化的完整银行应用
- [ ] 添加路由参数和嵌套路由等高级功能
- [ ] 实现导航守卫和基于认证的路由
- [ ] 创建可复用的模板组件和组件库
- [ ] 添加动画与过渡,提升用户体验
- [ ] 部署 SPA 至托管平台并正确配置路由
### 🌟 **你一个月的前端架构精通计划**
- [ ] 使用 React、Vue 或 Angular 构建复杂 SPA
- [ ] 学习高级状态管理模式与库
- [ ] 精通 SPA 构建工具与开发流程
- [ ] 实现渐进式 Web 应用与离线功能
- [ ] 研究大型 SPA 的性能优化技巧
- [ ] 参与开源 SPA 项目并分享经验
## 🎯 你的单页应用掌握时间线
```mermaid
timeline
title SPA开发与现代Web架构学习进程
section 基础20分钟
模板系统: HTML模板元素
: DOM操作
: 内容克隆
: 动态渲染
section 路由基础30分钟
客户端导航: URL管理
: 历史记录API
: 路由映射
: 事件处理
section 用户体验40分钟
导航优化: 浏览器集成
: 返回按钮支持
: 错误处理
: 平滑过渡
section 架构模式50分钟
专业SPA: 组件系统
: 状态管理
: 性能优化
: 错误边界
section 高级技术(一周)
框架集成: React Router
: Vue Router
: Angular Router
: 状态库
section 生产技能(一个月)
企业开发: 构建系统
: 测试策略
: 部署流程
: 性能监控
```
### 🛠️ 你的 SPA 开发工具总结
完成本课后,你已掌握:
- **模板架构**:可复用的 HTML 组件与动态内容渲染
- **客户端路由**:无刷新页面的 URL 管理与导航
- **浏览器集成**History API 使用及前进/后退支持
- **事件驱动系统**:导航处理与用户交互管理
- **DOM 操作**:模板克隆、内容切换与元素管理
- **错误处理**:无效路由与缺失内容的优雅降级
- **性能模式**:高效内容加载与渲染策略
**真实应用场景**:你的 SPA 开发技能直接应用于:
- **现代网络应用**React、Vue、Angular 等框架开发
- **渐进式 Web 应用**:支持离线和类应用体验
- **企业仪表盘**:多视图复杂业务应用
- **电子商务平台**:产品目录、购物车和结账流程
- **内容管理**:动态内容创建与编辑接口
- **移动开发**:使用 Web 技术的混合应用
**获得的职业技能**:你现在可以:
- **构建** 单页应用,合理实现关注点分离
- **实现** 可随应用复杂度扩展的客户端路由系统
- **调试** 复杂的导航流程,使用浏览器开发者工具
- **优化** 应用性能,通过高效的模板管理
- **设计** 原生且响应迅速的用户体验
**掌握的前端开发概念**
- **组件架构**可复用的UI模式和模板系统
- **状态同步**URL状态管理和浏览器历史记录
- **事件驱动编程**:用户交互处理和导航
- **性能优化**高效的DOM操作和内容加载
- **用户体验设计**:流畅的过渡和直观的导航
**下一阶段**:你已准备好探索现代前端框架、高级状态管理,或构建复杂的企业级应用!
🌟 **成就解锁**:你已使用现代网页架构模式构建了专业的单页应用基础!
---
## GitHub Copilot Agent 挑战 🚀
使用 Agent 模式完成以下挑战:
**描述:** 通过为无效路由实现错误处理和404页面模板提升银行应用的用户体验改善访问不存在页面时的表现。
**提示:** 创建一个id为 "not-found" 的新HTML模板显示用户友好的404错误页面并附加样式。然后修改JavaScript路由逻辑当用户导航到无效URL时显示此模板并添加一个 “回首页” 按钮,导航回登录页。
在这里了解更多关于[agent模式](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode)的信息。
## 🚀 挑战
为第三个页面添加一个新模板和路由,该页面显示此应用程序的致谢信息。
添加一个新的模板和路由,作为第三个页面,展示本应用的鸣谢内容。
**挑战目标:**
- **创建** 一个新的HTML模板包含合适的内容结构
- **添加** 新路由至你的路由配置对象中
- **包含** 指向鸣谢页及从其返回的导航链接
- **测试** 浏览器历史下所有导航的正常工作
## 课后测验
[课后测验](https://ff-quizzes.netlify.app/web/quiz/42)
## 复习与自学
## 复习与自主学习
路由是web开发中意外复杂的部分之一尤其随着网页从刷新行为转向单页应用的页面刷新。阅读一些关于[Azure静态网站服务](https://docs.microsoft.com/azure/static-web-apps/routes/?WT.mc_id=academic-77807-sagibbon)如何处理路由的内容。你能解释文档中描述的某些决策为什么是必要的吗?
路由是Web开发中一个令人惊讶的复杂部分尤其是在Web从页面刷新行为转向单页应用程序页面刷新时。阅读一些关于[Azure静态Web应用服务](https://docs.microsoft.com/azure/static-web-apps/routes/?WT.mc_id=academic-77807-sagibbon)如何处理路由的内容。你能解释为什么文档中描述的一些决策是必要的吗?
**额外学习资源:**
- **探索** React Router 和 Vue Router 等流行框架如何实现客户端路由
- **研究** 基于哈希的路由和历史API路由的区别
- **学习** 服务器端渲染SSR以及它如何影响路由策略
- **调查** 渐进式网页应用PWA如何处理路由和导航
## 作业
@ -318,5 +826,7 @@ updateRoute();
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免责声明**
本文档使用AI翻译服务 [Co-op Translator](https://github.com/Azure/co-op-translator) 进行翻译。尽管我们努力确保准确性,但请注意,自动翻译可能包含错误或不准确之处。应以原始语言的文档作为权威来源。对于关键信息,建议使用专业人工翻译。对于因使用本翻译而引起的任何误解或误读,我们概不负责。
本文件由人工智能翻译服务[Co-op Translator](https://github.com/Azure/co-op-translator)翻译而成。虽然我们努力保证翻译的准确性,但请注意自动翻译可能存在错误或不准确之处。原文应被视为权威版本。对于重要信息,建议采用专业人工翻译。因使用本翻译所产生的任何误解或误释,我们不承担任何责任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,26 +1,60 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "8223e429218befa731dd5bfd22299520",
"translation_date": "2025-08-24T00:09:43+00:00",
"original_hash": "df0dcecddcd28ea8cbf6ede0ad57d673",
"translation_date": "2026-01-06T11:42:27+00:00",
"source_file": "7-bank-project/1-template-route/assignment.md",
"language_code": "zh"
}
-->
# 改进路由
## 说明
## 指令
当前的路由声明仅包含模板 ID但在显示新页面时有时需要更多内容。让我们通过以下两个功能来改进路由实现
现在你已经构建了一个基本的路由系统,是时候通过专业功能来提升它,以改善用户体验并为开发者提供更好的工具。真实世界的应用不仅仅需要模板切换——它们需要动态的页面标题、生命周期钩子和可扩展的架构。
- 为每个模板设置标题,并在模板更改时更新窗口标题为新标题。
- 添加一个选项,在模板更改后运行一些代码。我们希望每次显示仪表盘页面时,在开发者控制台打印 `'Dashboard is shown'`
在本次作业中,你将通过实现两个在生产环境网页应用中常见的关键功能来扩展你的路由实现。这些增强功能将让你的银行应用更显完善,并为未来功能奠定基础。
当前的路由声明只包含要使用的模板 ID。但在显示新页面时有时需要更多信息。让我们用两个额外的功能来改进路由实现
### 功能 1动态页面标题
**目标:** 给每个模板一个标题,并在模板更改时动态更新窗口标题。
**重要性:**
- **提升**用户体验,显示描述性的浏览器标签标题
- **增强**屏幕阅读器和辅助技术的可访问性
- **提供**更好的书签和浏览器历史上下文
- **遵循**专业的网页开发最佳实践
**实现方法:**
- **扩展** routes 对象,包含每个路由的标题信息
- **修改** `updateRoute()` 函数以动态更新 `document.title`
- **测试**在不同页面间导航时标题是否正确变化
### 功能 2路由生命周期钩子
**目标:** 添加在模板更改后运行某些代码的选项。我们希望每次显示仪表板页面时,都在开发者控制台打印 `'Dashboard is shown'`
**重要性:**
- **支持**特定路由加载时执行自定义逻辑
- **提供**分析、日志记录或初始化代码的钩子
- **为更复杂的路由行为奠定基础**
- **展示**网页开发中的观察者模式
**实现方法:**
- **为**路由配置添加可选的回调函数属性
- **在**模板渲染完成后执行回调函数(如果存在)
- **确保**该功能适用于任何定义了回调的路由
- **测试**访问仪表板时,控制台消息是否出现
## 评分标准
| 标准 | 卓越 | 合格 | 需要改进 |
| -------- | ---------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- |
| | 两个功能均已实现并正常工作。标题和代码添加功能也适用于在 `routes` 声明中新增的路由。 | 两个功能正常工作,但行为是硬编码的,无法通过 `routes` 声明进行配置。添加第三个带标题和代码功能的路由无法正常工作或仅部分工作。 | 缺少一个功能或某个功能未正常工作。 |
| 评判标准 | 优秀示范 | 合格 | 需要改进 |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- |
| | 两个功能均已实现且正常工作。对 `routes` 声明中新增的路由,标题和代码部分也能生效。 | 两个功能可用,但行为写死,无法通过 `routes` 声明进行配置。对新增包含标题和代码的第三条路由不起作用或部分起作用。 | 有一个功能缺失或工作不正常。 |
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免责声明**
本文档使用AI翻译服务 [Co-op Translator](https://github.com/Azure/co-op-translator) 进行翻译。尽管我们努力确保翻译的准确性,但请注意,自动翻译可能包含错误或不准确之处。应以原始语言的文档作为权威来源。对于关键信息,建议使用专业人工翻译。因使用本翻译而引起的任何误解或误读,我们概不负责。
本文件由人工智能翻译服务 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻译。尽管我们力求准确,但请注意自动翻译可能包含错误或不准确之处。请以原文作为权威来源。对于重要信息,建议使用专业人工翻译。因使用本翻译所引起的任何误解或曲解,我们概不负责。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

File diff suppressed because it is too large Load Diff

@ -1,25 +1,166 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "474f3ab1ee755ca980fc9104a0316e17",
"translation_date": "2025-08-24T00:00:54+00:00",
"original_hash": "efb01fcafd2ef40c593a6e662fc938a8",
"translation_date": "2026-01-06T11:40:21+00:00",
"source_file": "7-bank-project/2-forms/assignment.md",
"language_code": "zh"
}
-->
# 为您的银行应用程序设计样式
# 使用现代 CSS 美化你的银行应用
## 指导说明
## 项目概述
创建一个新的 `styles.css` 文件,并在当前的 `index.html` 文件中添加一个链接到该文件。在您刚创建的 CSS 文件中,添加一些样式,使 *登录**仪表盘* 页面看起来整洁美观。尝试创建一个配色主题,为您的应用程序赋予独特的品牌风格
将你的功能性银行应用转变为视觉上吸引人、专业的网页应用,使用现代 CSS 技术。你将创建一个统一的设计系统,提升用户体验,同时保持无障碍和响应式设计原则
> 提示:如果需要,您可以修改 HTML 并添加新的元素和类
本任务挑战你应用当代网页设计模式,实现一致的视觉识别,并打造用户既觉得美观又直观易用的界面
## 评分标准
## 说明
| 标准 | 卓越 | 合格 | 需要改进 |
| -------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------- |
| | 所有页面看起来干净且易读,具有一致的配色主题,不同的部分清晰地突显出来。 | 页面有样式,但没有主题,或者部分没有清晰的分隔。 | 页面缺乏样式,部分看起来杂乱无章,信息难以阅读。 |
### 第一步:设置样式表
**创建你的 CSS 基础:**
1. **创建** 一个名为 `styles.css` 的新文件于项目根目录
2. **在** `index.html` 文件中链接该样式表:
```html
<link rel="stylesheet" href="styles.css">
```
3. **从** CSS 重置和现代默认样式开始:
```css
/* Modern CSS reset and base styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
line-height: 1.6;
color: #333;
}
```
### 第二步:设计系统要求
**实现以下核心设计元素:**
#### 颜色调色板
- **主色**:为按钮和高亮选择专业颜色
- **辅助色**:用于装饰和次要操作的互补色
- **中性色**:用于文字、边框和背景的灰色调
- **成功/错误色**:成功状态用绿色,错误用红色
#### 排版
- **标题层级**:清晰区分 H1、H2 和 H3 元素
- **正文文本**易读字体大小最小16px和适当行高
- **表单标签**:清晰、无障碍的文本样式
#### 布局与间距
- **一致的间距**使用间距刻度8px、16px、24px、32px
- **网格系统**:表单和内容区有序布局
- **响应式设计**:移动优先,设定断点
### 第三步:组件样式
**为以下具体组件设计样式:**
#### 表单
- **输入框**:专业边框,聚焦状态和校验样式
- **按钮**:悬停效果、禁用状态和加载指示
- **标签**:清晰定位和必填标识
- **错误信息**:明显错误样式和有用的提示信息
#### 导航
- **页头**:简洁且有品牌风格的导航区域
- **链接**:清晰的悬停状态和激活指示
- **徽标/标题**:醒目的品牌元素
#### 内容区
- **板块**:不同区域间清晰视觉分隔
- **卡片**:使用卡片布局时,包含阴影和边框
- **背景**:合理使用留白和细腻背景
### 第四步:增强功能(可选)
**可考虑实现以下高级功能:**
- **暗黑模式**:在亮色与暗色主题切换
- **动画效果**:细腻的过渡与微交互
- **加载状态**:表单提交过程的视觉反馈
- **响应式图片**:针对不同屏幕尺寸优化图片
## 设计灵感
**现代银行应用特点:**
- **简洁、极简设计**,留白充足
- **专业配色方案**(蓝色、绿色或高级中性色)
- **清晰视觉层次**,突出号召性用语按钮
- **无障碍对比度**,符合 WCAG 指南
- **移动响应式布局**,适配所有设备
## 技术要求
### CSS 组织
```css
/* 1. CSS Custom Properties (Variables) */
:root {
--primary-color: #007bff;
--secondary-color: #6c757d;
/* Add more variables */
}
/* 2. Base Styles */
/* Reset, typography, general elements */
/* 3. Layout */
/* Grid, flexbox, positioning */
/* 4. Components */
/* Forms, buttons, cards */
/* 5. Utilities */
/* Helper classes, responsive utilities */
/* 6. Media Queries */
/* Responsive breakpoints */
```
### 无障碍要求
- **色彩对比度**确保普通文本至少4.5:1比例
- **焦点指示器**:键盘导航时焦点状态明显
- **表单标签**:与输入框正确关联
- **响应式设计**:支持 320px 到 1920px 宽屏
## 评估标准
| 评估标准 | 杰出 (A) | 熟练 (B) | 发展中 (C) | 需改进 (F) |
|------------------|----------------------------|----------------------------|----------------------------|----------------------------|
| **设计系统** | 全面实现,颜色、排版及间距一致 | 使用一致样式,设计模式清晰,视觉层次良好 | 基础样式,存在一致性问题或缺失设计元素 | 样式极少,设计不一致或冲突 |
| **用户体验** | 创造直观专业界面,极佳可用性与美观性 | 提供良好用户体验,导航清晰,内容易读 | 基本可用,用户体验有待提升 | 可用性差,难以导航或阅读 |
| **技术实现** | 使用现代 CSS 技术,代码结构良好,符合最佳实践 | 有效实现 CSS组织合理技术适当 | CSS 功能正常,缺少组织或现代方法 | CSS 实现差,存在技术或浏览器兼容问题 |
| **响应式设计** | 设计完全响应,适用于所有设备尺寸 | 响应表现良好,部分屏幕尺寸有轻微问题 | 基础响应设计,存在部分布局问题 | 不响应或移动端存在明显问题 |
| **无障碍** | 遵循 WCAG 指南,键盘导航和屏幕阅读器支持优秀 | 良好无障碍实践,对比和焦点明显 | 基本考虑无障碍,部分元素缺失 | 无障碍差,残障用户难以使用 |
## 提交指南
**提交内容包括:**
- **styles.css**:完整样式表
- **更新后的 HTML**:所有修改的 HTML 文件
- **截图**:桌面及移动端设计效果图
- **README**:简述设计选择及配色方案
**加分项:**
- **CSS 自定义属性**,便于维护主题
- **高级 CSS 功能**,如 Grid、Flexbox 或 CSS 动画
- **性能考量**,如优化 CSS 及文件大小最小化
- **跨浏览器测试**,确保兼容多种浏览器
> 💡 **专业小贴士**:从移动端设计开始,再增强大屏体验。移动优先策略确保应用在所有设备上表现良好,并符合现代网页开发最佳实践。
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免责声明**
本文档使用AI翻译服务 [Co-op Translator](https://github.com/Azure/co-op-translator) 进行翻译。尽管我们努力确保翻译的准确性,但请注意,自动翻译可能包含错误或不准确之处。应以原始语言的文档作为权威来源。对于关键信息,建议使用专业人工翻译。因使用本翻译而引起的任何误解或误读,我们概不负责。
本文件由人工智能翻译服务[Co-op Translator](https://github.com/Azure/co-op-translator)翻译。虽然我们力求准确,但请注意,自动翻译可能存在错误或不准确之处。应以原文作为权威来源。对于重要信息,建议采用专业人工翻译。因使用本翻译而产生的任何误解或误释,我们不承担任何责任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

File diff suppressed because it is too large Load Diff

@ -1,27 +1,144 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "a4abf305ede1cfaadd56a8fab4b4c288",
"translation_date": "2025-08-24T00:05:52+00:00",
"original_hash": "d0a02cb117e91a5b5f24178080068a3d",
"translation_date": "2026-01-06T11:44:49+00:00",
"source_file": "7-bank-project/3-data/assignment.md",
"language_code": "zh"
}
-->
# 重构并注释你的代码
# 代码重构与文档作业
## 学习目标
完成本作业后,您将练习专业开发人员每天使用的关键软件开发技能。您将学习如何组织代码以便于维护,通过抽象减少重复,并为未来的开发者(包括您自己)编写文档。
干净且有良好文档的代码对于现实世界中多个开发者协作和代码库持续发展的 Web 开发项目至关重要。
## 作业概述
您的银行应用中的 `app.js` 文件随着登录、注册和仪表盘功能的增加变得庞大。现在是时候通过专业开发实践来重构这段代码,以改进可读性、可维护性并减少重复。
## 说明
随着代码库的增长,定期重构代码对于保持其可读性和长期可维护性至关重要。通过添加注释并重构你的 `app.js` 来提升代码质量:
通过实现以下三大核心重构技巧来改造您当前的 `app.js` 代码:
### 1. 提取配置常量
**任务**:在文件顶部创建一个配置区域,定义可重用的常量。
**实现指导:**
- 提取服务器 API 基础 URL当前在多个地方硬编码
- 为多个函数中出现的错误消息创建常量
- 考虑提取反复使用的路由路径和元素 ID
**示例结构:**
```javascript
// 配置常量
const API_BASE_URL = 'http://localhost:5000/api';
const ROUTES = {
LOGIN: '/login',
DASHBOARD: '/dashboard'
};
```
### 2. 创建统一请求函数
**任务**:构建一个可重用的 `sendRequest()` 函数,消除 `createAccount()``getAccount()` 之间的重复代码。
**要求:**
- 支持 GET 和 POST 请求
- 包含适当的错误处理
- 支持不同的 URL 端点
- 接受可选的请求体数据
**函数签名指导:**
```javascript
async function sendRequest(endpoint, method = 'GET', data = null) {
// 在这里实现您的代码
}
```
### 3. 添加专业代码文档
**任务**:用清晰、实用的注释为代码编写文档,解释逻辑背后的“为什么”。
**文档标准:**
- 为函数添加说明,解释用途、参数和返回值
- 为复杂逻辑或业务规则添加内联注释
- 用分节标题将相关函数归组
- 解释任何非显而易见的代码模式或浏览器特定的 workaround
**示例文档风格:**
```javascript
/**
* Authenticates user and redirects to dashboard
* @param {Event} event - Form submission event
* @returns {Promise<void>} - Resolves when login process completes
*/
async function login(event) {
// 防止默认表单提交以使用JavaScript处理
event.preventDefault();
// 你的实现...
}
```
## 成功标准
您的重构代码应体现以下专业开发实践:
### 优秀实现
- ✅ **常量**:所有魔法字符串和 URL 都提取到清晰命名的常量中
- ✅ **DRY 原则**:公共请求逻辑合并到一个可重用的 `sendRequest()` 函数
- ✅ **文档**:函数拥有清晰的 JSDoc 注释,解释目的和参数
- ✅ **组织**:代码用章节标题和统一格式进行逻辑分组
- ✅ **错误处理**:使用新请求函数改进了错误处理
### 合格实现
- ✅ **常量**:大多数重复值提取,仍有少量硬编码残留
- ✅ **提取**:创建了基本的 `sendRequest()` 函数,但可能未覆盖所有边缘案例
- ✅ **注释**:关键函数有文档,但部分解释不够完整
- ✅ **可读性**:代码整体较好,有少许改进空间
### 需要改进
- ❌ **常量**:许多魔法字符串和 URL 仍然散布在文件中硬编码
- ❌ **重复**:类似函数之间仍有大量重复代码
- ❌ **文档**:缺失或注释不足,没有解释代码目的
- ❌ **组织**:代码缺乏明确结构和逻辑分组
## 测试您的重构代码
重构完成后,确保您的银行应用仍能正常工作:
1. **测试所有用户流程**:注册、登录、仪表盘显示和错误处理
2. **验证 API 调用**:确认 `sendRequest()` 函数在账号创建和获取上都有效
3. **检查错误场景**:使用无效凭证和网络错误进行测试
4. **查看控制台输出**:确保重构期间未引入新的错误
## 提交指南
提交您的重构 `app.js` 文件,要求:
- 用清晰的章节标题组织不同功能
- 代码格式和缩进保持一致
- 所有函数均有完整 JSDoc 文档
- 顶部简短注释说明您的重构思路
**额外挑战**:创建一个简单的代码文档文件(`CODE_STRUCTURE.md`),解释您的应用架构及各函数如何协作。
## 现实世界联系
- 提取常量,例如服务器 API 基础 URL
- 将相似的代码进行整合:例如,你可以创建一个 `sendRequest()` 函数,将 `createAccount()``getAccount()` 中使用的代码集中起来
- 重新组织代码,使其更易于阅读,并添加注释
本作业反映了专业开发人员定期进行的代码维护工作。在行业环境中:
- **代码审查** 会评估代码的可读性和可维护性,就像本作业一样
- **技术债务** 会在代码未定期重构与文档化时累积
- **团队协作** 依赖清晰且有完整文档的代码,让新成员能快速理解
- **修复 Bug** 在结构良好、有适当抽象的代码库中更容易完成
## 评分标准
您在这里练习的技能——提取常量、消除重复和编写清晰文档——是专业软件开发的基础。
| 标准 | 卓越 | 合格 | 需要改进 |
| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| | 代码已添加注释,分为不同部分且易于阅读。常量已提取,并创建了一个整合的 `sendRequest()` 函数。 | 代码整洁,但仍可通过添加更多注释、提取常量或整合代码来进一步优化。 | 代码混乱,未添加注释,常量未提取,代码未整合。 |
---
**免责声明**
本文档使用AI翻译服务 [Co-op Translator](https://github.com/Azure/co-op-translator) 进行翻译。尽管我们努力确保翻译的准确性,但请注意,自动翻译可能包含错误或不准确之处。应以原文档的原始语言版本为权威来源。对于关键信息,建议使用专业人工翻译。我们对因使用此翻译而引起的任何误解或误读不承担责任。
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免责声明**
本文件由 AI 翻译服务 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻译。虽然我们努力确保准确性,但请注意自动翻译可能存在错误或不准确之处。原文应被视为权威来源。对于重要信息,建议使用专业人工翻译。我们对因使用本翻译而产生的任何误解或误释不承担责任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,73 +1,242 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "b46acf79da8550d76445eed00b06c878",
"translation_date": "2025-10-03T12:49:18+00:00",
"original_hash": "b807b09df716dc48a2b750835bf8e933",
"translation_date": "2026-01-06T11:45:06+00:00",
"source_file": "7-bank-project/4-state-management/README.md",
"language_code": "zh"
}
-->
# 构建银行应用程序第4部分状态管理概念
# 构建银行应用 第4部分状态管理概念
## 课前测验
## ⚡ 你可以在接下来的5分钟内完成的事情
[课前测验](https://ff-quizzes.netlify.app/web/quiz/47)
**忙碌开发者快速入门路径**
### 介绍
```mermaid
flowchart LR
A[⚡ 5 分钟] --> B[诊断状态问题]
B --> C[创建中央状态对象]
C --> D[添加 updateState 函数]
D --> E[看到即时改进]
```
- **第1分钟**:测试当前状态问题 - 登录,刷新页面,观察是否登出
- **第2分钟**:将 `let account = null` 替换为 `let state = { account: null }`
- **第3分钟**:创建一个简单的 `updateState()` 函数以实现受控更新
- **第4分钟**:更新一个函数以使用新的模式
- **第5分钟**:测试改进后的可预测性和调试能力
**快速诊断测试**
```javascript
// 之前:分散状态
let account = null; // 刷新后丢失!
// 之后:集中状态
let state = Object.freeze({ account: null }); // 可控且可追踪!
```
**为什么这很重要**在5分钟内你将体验从混乱的状态管理转变为可预测、易调试的模式。这是让复杂应用易于维护的基础。
## 🗺️ 你通过状态管理大师之路的学习旅程
```mermaid
journey
title 从分散状态到专业架构
section 诊断问题
识别状态丢失问题: 3: You
理解分散的更新: 4: You
认识架构需求: 6: You
section 集中控制
创建统一状态对象: 5: You
实施受控更新: 7: You
添加不可变模式: 8: You
section 增加持久性
实施 localStorage: 6: You
处理序列化: 7: You
创建会话连续性: 9: You
section 平衡新鲜度
解决数据陈旧问题: 5: You
构建刷新系统: 8: You
实现最佳平衡: 9: You
```
**你的旅程目标**:课程结束时,你将构建一个专业级别的状态管理系统,处理持久化、数据新鲜度和可预测的更新——这些模式同样用于生产应用。
## 课程前测验
[课程前测验](https://ff-quizzes.netlify.app/web/quiz/47)
## 介绍
随着 Web 应用程序的规模不断扩大,跟踪所有数据流变得越来越具有挑战性。哪些代码获取数据,哪些页面使用数据,数据需要在何时何地更新……很容易导致代码混乱,难以维护。尤其是当你需要在应用程序的不同页面之间共享数据时,比如用户数据。*状态管理*的概念一直存在于各种程序中,但随着 Web 应用程序的复杂性不断增加,它现在成为开发过程中需要重点考虑的问题。
状态管理就像旅行者号航天器的导航系统——当一切顺利时,你几乎感知不到它的存在。但当问题发生时,它决定了你是能够抵达星际空间,还是在宇宙虚无中迷失。在网页开发中,状态代表着应用需要记住的一切:用户登录状态、表单数据、导航历史和临时界面状态
在最后这一部分中,我们将重新审视我们构建的应用程序,重新思考如何管理状态,以支持浏览器在任何时候刷新,并在用户会话之间持久化数据。
随着你的银行应用从简单登录表单发展到更复杂的应用,你可能遇到了一些常见挑战。刷新页面后用户会意外登出。关闭浏览器后所有进度丢失。调试问题时,你需要在多个以不同方式修改同一数据的函数中穿梭
### 前置条件
这并非编码不佳的表现——而是应用达到一定复杂度时自然出现的痛点。每个开发者都会在应用从“概念验证”转向“生产就绪”过程中遇到这些问题。
你需要完成本课程的 Web 应用程序的[数据获取](../3-data/README.md)部分。此外,你需要安装 [Node.js](https://nodejs.org) 并[本地运行服务器 API](../api/README.md),以便管理账户数据。
在本课中,我们将实现一个集中式状态管理系统,把你的银行应用变成一个可靠、专业的应用。你将学习如何可预测地管理数据流,适当持久化用户会话,并创造现代网页应用所需的流畅用户体验
你可以通过在终端中执行以下命令来测试服务器是否正常运行:
## 先决条件
在深入状态管理概念之前,你需要正确设置开发环境并搭建银行应用的基础。本课直接构建在本系列前几部分的概念和代码上。
请确保在继续前准备好以下内容:
**必备设置:**
- 完成 [数据获取课程](../3-data/README.md) —— 应用应能成功加载并显示账户数据
- 在系统上安装 [Node.js](https://nodejs.org) 用于运行后端API
- 本地启动 [服务器API](../api/README.md) 以处理账户数据操作
**环境测试:**
通过终端执行以下命令确保API服务器正确运行
```sh
curl http://localhost:5000/api
# -> should return "Bank API v1.0.0" as a result
# -> 应该返回结果 "Bank API v1.0.0"
```
**此命令作用说明:**
- **发送** GET请求到本地API服务器
- **测试**连接,验证服务器响应
- **返回**如果一切正常则返回API版本信息
## 🧠 状态管理架构概览
```mermaid
mindmap
root((状态管理))
当前问题
会话丢失
页面刷新问题
浏览器关闭影响
变量重置问题
分散更新
多点修改
调试挑战
不可预测的行为
清理不完整
登出状态问题
内存泄漏
安全隐患
集中解决方案
统一状态对象
单一可信源
可预测的结构
可扩展的基础
受控更新
不可变模式
Object.freeze 使用
基于函数的变更
状态追踪
历史管理
调试可见性
变更审计
持久化策略
localStorage 集成
会话连续性
JSON 序列化
自动同步
数据新鲜度
服务器刷新
过期数据处理
平衡优化
存储优化
最小数据
性能关注
安全考量
```
**核心原则**:专业状态管理在可预测性、持久性和性能之间取得平衡,打造可靠的用户体验,能从简单交互扩展到复杂应用工作流。
---
## 重新思考状态管理
## 诊断当前状态问题
在[上一课](../3-data/README.md)中,我们在应用程序中引入了一个基本的状态概念,即全局变量 `account`,它包含当前登录用户的银行数据。然而,我们当前的实现存在一些缺陷。试着在仪表板页面刷新浏览器,会发生什么?
就像夏洛克·福尔摩斯检查犯罪现场一样,我们需要了解当前实现中到底发生了什么,才能解决用户会话消失的疑团。
当前代码存在以下三个问题:
让我们做一个简单实验,揭示底层的状态管理难题:
- 状态没有持久化,浏览器刷新会将你带回登录页面。
- 有多个函数修改状态。随着应用程序的增长,这会使跟踪更改变得困难,并且容易忘记更新某些部分。
- 状态没有清理,因此当你点击*注销*时,账户数据仍然存在,即使你已经回到登录页面。
**🧪 尝试这个诊断测试:**
1. 登录银行应用并进入仪表盘
2. 刷新浏览器页面
3. 观察登录状态发生了什么
我们可以逐一更新代码来解决这些问题,但这会导致代码重复增加,使应用程序更加复杂且难以维护。或者,我们可以暂停几分钟,重新思考我们的策略。
如果你被重定向回登录界面你就发现了经典的状态持久性问题。这种行为发生是因为我们当前实现将用户数据存储在随着页面加载而重置的JavaScript变量中
> 我们真正试图解决的问题是什么?
**当前实现的问题:**
[状态管理](https://en.wikipedia.org/wiki/State_management)的核心是找到一种好的方法来解决以下两个问题:
来自我们[前一课](../3-data/README.md)的简单 `account` 变量带来了三个重大问题,这些问题影响用户体验和代码可维护性
- 如何让应用程序中的数据流易于理解?
- 如何确保状态数据始终与用户界面保持同步(反之亦然)?
| 问题 | 技术原因 | 用户影响 |
|---------|--------|----------------|
| **会话丢失** | 页面刷新清空JavaScript变量 | 用户需频繁重新认证 |
| **更新分散** | 多个函数直接修改状态 | 调试越来越困难 |
| **清理不完全** | 登出未清除所有状态引用 | 可能存在安全和隐私风险 |
一旦解决了这些问题,你可能会发现其他问题要么已经解决,要么变得更容易解决。解决这些问题有许多可能的方法,但我们将采用一种常见的解决方案,即**集中管理数据及其更改方式**。数据流将如下图所示:
**架构挑战:**
![显示 HTML、用户操作和状态之间数据流的示意图](../../../../translated_images/data-flow.fa2354e0908fecc89b488010dedf4871418a992edffa17e73441d257add18da4.zh.png)
就像泰坦尼克号的隔舱设计看似坚固,但多舱同时进水时却失效一样,逐个修复这些问题无法解决根本的架构问题。我们需要一个全面的状态管理方案。
> 我们在这里不会讨论数据自动触发视图更新的部分,因为它涉及到更高级的[响应式编程](https://en.wikipedia.org/wiki/Reactive_programming)概念。如果你有兴趣深入研究,这是一个很好的后续主题。
> 💡 **我们真正想达成的是什么?**
✅ 市面上有许多不同方法的状态管理库,[Redux](https://redux.js.org) 是一个流行的选择。了解其使用的概念和模式通常是学习如何解决大型 Web 应用程序中潜在问题以及解决方法的好途径。
[状态管理](https://en.wikipedia.org/wiki/State_management)其实解决两个基本难题:
### 任务
1. **我的数据在哪?**:跟踪我们拥有什么信息及其来源
2. **所有人都保持同步吗?**:确保用户看到的内容和实际状态一致
**我们的行动计划:**
我们将创建一个**集中式状态管理**系统。把它想象成一个非常有条理的人掌管所有重要事务:
![显示HTML、用户操作与状态间数据流的架构图](../../../../translated_images/data-flow.fa2354e0908fecc8.zh.png)
```mermaid
flowchart TD
A[用户操作] --> B[事件处理器]
B --> C[更新状态函数]
C --> D{状态验证}
D -->|有效| E[创建新状态]
D -->|无效| F[错误处理]
E --> G[Object.freeze]
G --> H[更新 localStorage]
H --> I[触发 UI 更新]
I --> J[用户看到更改]
F --> K[用户看到错误]
subgraph "状态管理层"
C
E
G
end
subgraph "持久层"
H
L[localStorage]
H -.-> L
end
```
**理解这个数据流:**
- **集中**所有应用状态于一处
- **通过可控函数**路由所有状态更改
- **确保**用户界面与当前状态同步
- **提供**清晰、可预测的数据管理模式
我们将从一些代码重构开始。替换 `account` 声明:
> 💡 **专业洞见**:本课聚焦核心概念。对于复杂应用,像 [Redux](https://redux.js.org) 这样库提供更高级的状态管理功能。理解这些核心原则将助你掌握任何状态管理库。
> ⚠️ **高级主题**我们不会覆盖由状态变化自动触发UI更新的内容因为这涉及[响应式编程](https://en.wikipedia.org/wiki/Reactive_programming)概念。把它视为你学习旅程的优秀下一步!
### 任务:集中状态结构
让我们开始将分散的状态管理转变为集中管理。第一步为后续所有改进奠定基础。
**步骤1创建集中状态对象**
替换简单的 `account` 声明:
```js
let account = null;
```
为:
用结构化的状态对象
```js
let state = {
@ -75,31 +244,112 @@ let state = {
};
```
我们的想法是将应用程序的所有数据集中到一个单一的状态对象中。目前状态中只有 `account`,因此变化不大,但这为未来的扩展铺平了道路。
**此变更的重要性:**
- **集中**所有应用数据于一处
- **为后续添加更多状态属性**做准备
- **划定状态与其他变量的清晰边界**
- **建立**一个可扩展的模式,随着应用增长而增长
我们还需要更新使用它的函数。在 `register()``login()` 函数中,将 `account = ...` 替换为 `state.account = ...`;
**步骤2更新状态访问模式**
`updateDashboard()` 函数的顶部,添加以下代码:
更新函数以使用新的状态结构
**在 `register()``login()` 函数中**,替换:
```js
account = ...
```
为:
```js
state.account = ...
```
**在 `updateDashboard()` 函数中**,顶部添加这一行:
```js
const account = state.account;
```
这次重构本身并没有带来太多改进,但目的是为接下来的更改奠定基础。
**这些更新带来的效果:**
- **保持**现有功能,结构更清晰
- **为更复杂的状态管理做好准备**
- **创建**访问状态数据的一致性模式
- **为集中状态更新奠定基础**
> 💡 **注意**:这次重构并未立即解决我们的问题,但它为接下来的强大改进创造了必要的基础!
### 🎯 教学检查点:集中原则
**暂停并思考**:你刚实现了集中状态管理的基础。这是关键的架构决策。
**快速自测**
- 你能解释为什么将状态集中到一个对象优于分散变量吗?
- 如果忘记更新某个函数以使用 `state.account` 会怎样?
- 这种模式如何为更高级功能准备代码?
**现实联系**你学到的集中模式是现代框架如Redux、Vuex和React Context的基础。你正在构建同样的架构思维。
**挑战问题**:如果你需要将用户偏好(主题、语言)添加到应用状态,该放哪里?这如何扩展?
## 跟踪数据更改
## 实现受控状态更新
现在我们已经设置了用于存储数据的 `state` 对象,下一步是集中更新。目标是更容易跟踪任何更改及其发生的时间。
有了集中状态,下一步是建立受控的数据修改机制。此方法确保状态改变可预测,且调试更便捷
为了避免对 `state` 对象进行更改,考虑将其视为[*不可变对象*](https://en.wikipedia.org/wiki/Immutable_object)是一个好习惯,这意味着它完全不能被修改。这也意味着如果你想更改其中的任何内容,必须创建一个新的状态对象。通过这样做,你可以防止潜在的[副作用](https://en.wikipedia.org/wiki/Side_effect_(computer_science)),并为应用程序的新功能(如实现撤销/重做)打开可能性,同时也更容易调试。例如,你可以记录对状态所做的每次更改,并保留更改历史记录,以了解错误的来源。
该核心原则类似空中交通管制:不允许多个函数独立修改状态,而是将所有更改通过单个受控函数进行。此模式清晰掌控何时及如何发生数据变化
在 JavaScript 中,你可以使用 [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) 创建对象的不可变版本。如果尝试更改不可变对象,将会抛出异常。
**不可变状态管理:**
✅ 你知道[*浅不可变对象*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze)和[*深不可变对象*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze)之间的区别吗?你可以在[这里](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze)阅读相关内容。
我们将把 `state` 对象视作[*不可变*](https://en.wikipedia.org/wiki/Immutable_object)的——不直接修改它。每次更改创建一个包含更新数据的新状态对象
这种方式相比直接修改可能初看低效,但对调试、测试和维护应用可预测性极具优势。
**不可变状态管理优势:**
| 优势 | 描述 | 影响 |
|---------|-------------|--------|
| **可预测性** | 仅通过受控函数发生更改 | 更易调试和测试 |
| **记录历史** | 每次变更创建新对象 | 支持撤销/重做功能 |
| **副作用防范** | 无意外修改 | 防止神秘bug |
| **性能优化** | 易于检测状态变化 | 支持高效UI更新 |
**JavaScript中通过 `Object.freeze()` 实现不可变:**
JavaScript 提供 [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) 防止对象被修改:
```js
const immutableState = Object.freeze({ account: userData });
// 任何尝试修改 immutableState 的操作都会抛出错误
```
**这段代码效果分析:**
- **防止**直接属性赋值或删除
- **修改尝试**时抛出异常
- **确保**状态更改必须通过受控函数
- **创建**状态更新的清晰约定
> 💡 **深入理解**:阅读 [MDN文档](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze) 了解*浅冻结*与*深冻结*的区别,这对复杂状态结构至关重要。
```mermaid
stateDiagram-v2
[*] --> StateV1: 初始状态
StateV1 --> StateV2: updateState('account', newData)
StateV2 --> StateV3: updateState('account', anotherUpdate)
StateV3 --> StateV4: updateState('preferences', userSettings)
note right of StateV1
Object.freeze()
不可变
可调试
end note
note right of StateV2
创建新对象
保留先前状态
可预测的变化
end note
```
### 任务
让我们创建一个新的 `updateState()` 函数:
让我们创建新的 `updateState()` 函数:
```js
function updateState(property, newData) {
@ -110,9 +360,9 @@ function updateState(property, newData) {
}
```
在这个函数中,我们创建了一个新的状态对象,并使用[*扩展运算符 (`...`)*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals)从之前的状态中复制数据。然后我们使用[方括号表示法](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` 为赋值覆盖状态对象的特定属性。最后,我们使用 `Object.freeze()` 锁定对象以防止修改。目前状态中只存储了 `account` 属性,但通过这种方法,你可以在状态中添加任意多的属性。
此函数通过使用[*扩展运算符*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals)复制之前的状态数据,然后用[括号语法](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` 覆盖特定属性的值。最后用 `Object.freeze()` 锁定对象防止修改。目前状态中仅存储 `account` 属性,但你可以用此方法添加任意多个状态属性。
我们还需要更新 `state` 的初始化,以确保初始状态也是冻结的
同时我们更新 `state` 初始化,确保初始状态也被冻结
```js
let state = Object.freeze({
@ -120,21 +370,21 @@ let state = Object.freeze({
});
```
之后,更新 `register` 函数,`state.account = result;` 替换为:
之后,`register` 函数中`state.account = result;` 替换为:
```js
updateState('account', result);
```
`login` 函数进行相同的操作,`state.account = data;` 替换为:
同样,`login` 函数中`state.account = data;` 替换为:
```js
updateState('account', data);
```
我们现在可以顺便解决用户点击*注销*时账户数据未清除的问题。
我们借机修复点击*登出*时账户数据未清除的问题。
创建一个新的 `logout()` 函数
创建新函数 `logout()`
```js
function logout() {
@ -143,49 +393,123 @@ function logout() {
}
```
`updateDashboard()` 中,将重定向 `return navigate('/login');` 替换`return logout();`
`updateDashboard()` 中,将重定向 `return navigate('/login');` 替换`return logout()`
尝试注册一个新账户,注销并重新登录,检查是否一切正常。
尝试注册新账号,登出,再登录,检查功能是否正常。
> 提示:你可以通过在 `updateState()` 的底部添加 `console.log(state)` 并打开浏览器开发工具中的控制台来查看所有状态更改
> 贴士:你可以在 `updateState()` 底部添加 `console.log(state)` 查看所有状态变化,同时打开浏览器开发工具控制台查看
## 持久化状态
## 实现数据持久化
大多数 Web 应用程序需要持久化数据才能正常工作。所有关键数据通常存储在数据库中,并通过服务器 API 访问,例如我们的用户账户数据。但有时,为了更好的用户体验或提高加载性能,在运行于浏览器的客户端应用程序中持久化一些数据也是很有意义的
我们之前识别的会话丢失问题需要持久化解决方案,以保持用户状态穿越浏览器会话。这将应用从临时体验转变为可靠、专业工具
当你想在浏览器中持久化数据时,有几个重要问题需要问自己:
考虑原子钟即使断电也能保持精确时间,因为它在非易失性存储中保存关键状态。同理,网页应用需要持久存储机制保留关键用户数据,支持跨会话和页面刷新。
- *数据是否敏感?* 你应该避免在客户端存储任何敏感数据,例如用户密码。
- *你需要保存这些数据多久?* 你是只计划在当前会话中访问这些数据,还是希望永久保存?
**数据持久化策略性问题:**
根据你的需求,有多种方法可以在 Web 应用程序中存储信息。例如,你可以使用 URL 存储搜索查询,并使其在用户之间共享。你也可以使用 [HTTP cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies),如果数据需要与服务器共享,比如[身份验证](https://en.wikipedia.org/wiki/Authentication)信息。
在实现持久化前,考虑以下关键因素:
另一个选项是使用众多浏览器 API 中的一个来存储数据。其中两个特别有趣:
| 问题 | 银行应用场景 | 决策影响 |
|----------|-------------------|----------------|
| **数据敏感吗?** | 账户余额、交易历史 | 选择安全存储方法 |
| **它应持续多久?** | 登录状态与临时 UI 偏好 | 选择合适的存储时长 |
| **服务器是否需要它?** | 认证令牌与 UI 设置 | 判定共享需求 |
- [`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage):一个[键值存储](https://en.wikipedia.org/wiki/Key%E2%80%93value_database),允许跨不同会话持久化特定于当前网站的数据。存储在其中的数据永不过期。
- [`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage):它的工作方式与 `localStorage` 相同,但存储在其中的数据会在会话结束时(浏览器关闭时)被清除。
**浏览器存储选项:**
注意,这两个 API 仅允许存储[字符串](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)。如果你想存储复杂对象,需要使用 [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) 将其序列化为 [JSON](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON) 格式。
现代浏览器提供多种存储机制,每种机制设计用途不同:
✅ 如果你想创建一个不依赖服务器的 Web 应用程序,也可以使用 [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API) 在客户端创建数据库。这个 API 适用于高级用例或需要存储大量数据的情况,因为它使用起来更复杂。
**主要存储 API**
### 任务
1. **[`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage)**:持久的[键/值存储](https://en.wikipedia.org/wiki/Key%E2%80%93value_database)
- **跨浏览器会话永久存储**数据
- **浏览器重启和电脑重启后依然保存**
- **限定于特定网站域名**
- **非常适合保存用户偏好和登录状态**
2. **[`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage)**:临时会话存储
- **在活动会话期间行为与 localStorage 一致**
- **浏览器标签关闭时自动清除**
- **适合不应持久保存的临时数据**
3. **[HTTP Cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies)**:服务器共享存储
- **每次发起服务器请求时自动发送**
- **非常适合[身份验证](https://en.wikipedia.org/wiki/Authentication)令牌**
- **大小有限且可能影响性能**
**数据序列化需求:**
我们希望用户在明确点击*注销*按钮之前保持登录状态,因此我们将使用 `localStorage` 存储账户数据。首先,定义一个用于存储数据的键。
`localStorage``sessionStorage` 仅存储[字符串](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)
```js
// 将对象转换为 JSON 字符串以便存储
const accountData = { user: 'john', balance: 150 };
localStorage.setItem('account', JSON.stringify(accountData));
// 在检索时将 JSON 字符串解析回对象
const savedAccount = JSON.parse(localStorage.getItem('account'));
```
**理解序列化:**
- **使用 [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) 将 JavaScript 对象转换为 JSON 字符串**
- **使用 [`JSON.parse()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) 从 JSON 重构对象**
- **自动处理复杂的嵌套对象和数组**
- **对函数、undefined 值和循环引用会失败**
> 💡 **高级选项**:对于具有大量数据集的复杂离线应用,考虑使用[`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API)。它提供完整的客户端数据库支持,但实现更复杂。
```mermaid
quadrantChart
title 浏览器存储选项
x-axis 低复杂度 --> 高复杂度
y-axis 短期 --> 长期
quadrant-1 专业工具
quadrant-2 简单持久化
quadrant-3 临时存储
quadrant-4 高级系统
localStorage: [0.3, 0.8]
sessionStorage: [0.2, 0.2]
HTTP Cookies: [0.6, 0.7]
IndexedDB: [0.9, 0.9]
Memory Variables: [0.1, 0.1]
```
### 任务:实现 localStorage 持久化
让我们实现持久化存储,使用户登录状态在显式注销前保持有效。我们将使用 `localStorage` 在浏览器会话间存储账户数据。
**第 1 步:定义存储配置**
```js
const storageKey = 'savedAccount';
```
**此常量的作用:**
- **创建一个存储数据时的统一标识符**
- **避免存储键引用时的拼写错误**
- **便于日后修改存储键**
- **遵循可维护代码的最佳实践**
然后在 `updateState()` 函数的末尾添加以下代码:
**第 2 步:添加自动持久化**
`updateState()` 函数末尾添加这行代码:
```js
localStorage.setItem(storageKey, JSON.stringify(state.account));
```
**这里发生的操作解析:**
- **将账户对象转换为 JSON 字符串以便存储**
- **使用统一存储键保存数据**
- **每当状态更新时自动执行**
- **确保存储数据始终与当前状态同步**
> 💡 **架构优势**:由于我们通过 `updateState()` 集中管理所有状态更新,添加持久化仅需一行代码,体现了良好架构设计的威力!
通过这样做,用户账户数据将被持久化并始终保持最新状态,因为我们之前已经集中管理了所有状态更新。这是我们开始从之前的所有重构中受益的地方 🙂。
**第 3 步:应用加载时恢复状态**
由于数据已保存,我们还需要在应用程序加载时恢复它。由于我们将开始拥有更多的初始化代码,创建一个新的 `init` 函数可能是个好主意,同时包括之前在 `app.js` 底部的代码:
创建初始化函数以恢复已保存的数据
```js
function init() {
@ -194,25 +518,72 @@ function init() {
updateState('account', JSON.parse(savedAccount));
}
// Our previous initialization code
// 我们之前的初始化代码
window.onpopstate = () => updateRoute();
updateRoute();
}
init();
```
**理解初始化流程:**
- **从 localStorage 取回之前保存的账户数据**
- **将 JSON 字符串解析回 JavaScript 对象**
- **通过受控更新函数更新状态**
- **页面加载时自动恢复用户会话**
- **在路由更新之前执行,确保状态可用**
**第 4 步:优化默认路由**
更新默认路由以利用持久化:
`updateRoute()` 中替换:
```js
// 替换为return navigate('/login');
return navigate('/dashboard');
```
**此更改的合理性:**
- **有效利用新实现的持久化系统**
- **允许仪表板组件处理认证检查**
- **无已保存会话时自动重定向到登录页**
- **创建更无缝的用户体验**
**测试你的实现:**
在这里,我们检索保存的数据,如果有数据,我们会相应地更新状态。重要的是要在更新路由之前完成此操作,因为在页面更新期间可能会有代码依赖状态。
1. 登录你的银行应用
2. 刷新浏览器页面
3. 确认保持登录状态且停留在仪表板
4. 关闭并重新打开浏览器
5. 回到应用确认仍处于登录状态
我们还可以将*仪表板*页面设置为应用程序的默认页面,因为我们现在已经持久化了账户数据。如果没有找到数据,仪表板会负责重定向到*登录*页面。在 `updateRoute()` 中,将回退 `return navigate('/login');` 替换为 `return navigate('/dashboard');`
🎉 **成就解锁**:你已成功实现持久化状态管理!你的应用现在表现得像专业的网络应用
现在登录应用程序并尝试刷新页面。你应该停留在仪表板页面。通过这一更新,我们解决了所有初始问题……
### 🎯 教学检查点:持久化架构
## 刷新数据
**架构理解**:你已构建了一个平衡用户体验与数据管理复杂性的成熟持久化层。
……但我们可能也制造了一个新问题。糟糕!
**掌握的关键概念:**
- **JSON 序列化**:将复杂对象转存为字符串
- **自动同步**:状态变更触发持久化存储
- **会话恢复**:应用能断点续传用户上下文
- **集中持久化**:唯一更新函数处理所有存储
使用 `test` 账户进入仪表板页面,然后在终端中运行以下命令创建一个新交易:
**行业联系**此持久化模式是渐进式网页应用PWA、离线优先应用及现代移动网页体验的基础。你已构建出生产级能力。
**反思问题**:如何调整系统来支持同一设备上的多个用户账户?考虑隐私和安全的影响。
## 平衡持久化与数据新鲜度
我们的持久化系统成功维持了用户会话,但引入了一个新挑战:数据陈旧。当多个用户或应用修改相同的服务器数据时,本地缓存信息可能过时。
这种情况类似维京航海者同时依赖存储的星图和当前天文观测。星图提供一致性,但航海者需要最新观测来调整变化。类似地,我们的应用需要既有持久的用户状态,也有当前的服务器数据。
**🧪 发现数据新鲜度问题:**
1. 使用 `test` 账户登录仪表板
2. 在终端执行此命令模拟其他来源的事务:
```sh
curl --request POST \
@ -220,16 +591,48 @@ curl --request POST \
--data "{ \"date\": \"2020-07-24\", \"object\": \"Bought book\", \"amount\": -20 }" \
http://localhost:5000/api/accounts/test/transactions
```
现在尝试在浏览器中刷新仪表板页面。发生了什么?你看到新交易了吗?
状态通过 `localStorage` 无限期持久化,但这也意味着在你注销并重新登录之前,它永远不会更新!
一种可能的解决策略是每次加载仪表板时重新加载账户数据,以避免数据过时。
### 任务
创建一个新的 `updateAccountData` 函数:
3. 刷新浏览器中的仪表板页面
4. 观察是否显示了新交易
**此测试说明了:**
- **本地存储如何变得“陈旧”(过时)**
- **模拟真实场景中数据在应用外的变更**
- **揭示持久化与数据新鲜度之间的矛盾**
**数据陈旧挑战:**
| 问题 | 原因 | 用户影响 |
|---------|-------|-------------|
| **数据陈旧** | localStorage 不会自动过期 | 用户看到过时信息 |
| **服务器变更** | 其他应用/用户修改相同数据 | 不同平台数据不一致 |
| **缓存与现实差异** | 本地缓存不匹配服务器状态 | 用户体验差且造成困惑 |
**解决策略:**
我们将实现“加载时刷新”模式,兼顾持久化带来的体验流畅和数据准确性。
```mermaid
sequenceDiagram
participant U as 用户
participant A as 应用
participant L as 本地存储
participant S as 服务器
U->>A: 打开应用
A->>L: 加载保存的状态
L-->>A: 返回缓存数据
A->>U: 立即显示界面
A->>S: 获取最新数据
S-->>A: 返回当前数据
A->>L: 更新缓存
A->>U: 用最新数据更新界面
```
### 任务:实现数据刷新系统
构建一个系统,自动从服务器获取最新数据,同时保持持久状态管理带来的优势。
**第 1 步:创建账户数据更新函数**
```js
async function updateAccountData() {
@ -246,10 +649,16 @@ async function updateAccountData() {
updateState('account', data);
}
```
**函数逻辑解析:**
- **检查当前是否有用户登录state.account 是否存在)**
- **无效会话时重定向到注销流程**
- **使用已有的 `getAccount()` 函数从服务器获取最新账户数据**
- **优雅处理服务器错误,注销无效会话**
- **使用受控更新系统更新状态**
- **通过 `updateState()` 触发本地存储自动持久化**
此方法检查我们当前是否已登录,然后从服务器重新加载账户数据。
创建另一个名为 `refresh` 的函数:
**第 2 步:创建仪表板刷新处理函数**
```js
async function refresh() {
@ -257,8 +666,16 @@ async function refresh() {
updateDashboard();
}
```
**此刷新函数的作用:**
- **协调数据刷新和 UI 更新流程**
- **等待新数据加载后再更新显示**
- **确保仪表板显示最新信息**
- **保持数据管理与 UI 更新的清晰分离**
此函数更新账户数据,然后负责更新仪表板页面的 HTML。这是我们需要在加载仪表板路由时调用的内容。使用以下代码更新路由定义
**第 3 步:集成路由系统**
更新路由配置,实现自动刷新:
```js
const routes = {
@ -266,29 +683,127 @@ const routes = {
'/dashboard': { templateId: 'dashboard', init: refresh }
};
```
**集成工作原理:**
- **每次仪表板路由加载时执行刷新函数**
- **确保用户导航到仪表板时显示最新数据**
- **保持现有路由结构,添加数据新鲜性**
- **为路由特定初始化提供一致模式**
现在尝试重新加载仪表板,它应该显示更新后的账户数据。
**测试你的数据刷新系统:**
---
1. 登录银行应用
2. 执行前面用 curl 的命令创建新交易
3. 刷新仪表板页面或离开再返回
4. 确认新交易立即显示
🎉 **完美平衡达成**:你的应用结合了持久状态的流畅体验与服务器数据的准确性!
## 📈 你的状态管理掌握路线
```mermaid
timeline
title 专业状态管理之旅
section 问题识别
State Issues Diagnosis
: 识别会话丢失问题
: 理解分散更新问题
: 识别架构需求
section 架构基础
Centralized State Design
: 创建统一状态对象
: 实施受控更新模式
: 建立不可变原则
Predictable Updates
: 精通 Object.freeze() 用法
: 构建调试友好系统
: 创建可扩展模式
section 持久化掌握
localStorage Integration
: 处理 JSON 序列化
: 实现自动同步
: 创建会话连续性
Data Freshness Balance
: 解决数据陈旧挑战
: 构建刷新机制
: 优化性能与准确性
section 专业模式
Production-Ready Systems
: 实现错误处理
: 创建可维护架构
: 遵循行业最佳实践
Advanced Capabilities
: 准备框架集成
: 应对复杂状态需求
: 实时特性的基础
```
**🎓 毕业里程碑**:你已成功构建完整状态管理系统,借鉴 Redux、Vuex 等专业状态库的原理。此模式从简单应用到企业级应用均可扩展。
**🔄 下一步能力**
- 准备掌握状态管理框架Redux、Zustand、Pinia
- 准备实现基于 WebSockets 的实时功能
- 装备构建离线优先渐进式网页应用PWA
- 扎实基础以支持状态机和观察者等高级模式
## 🚀 挑战
## GitHub Copilot Agent 挑战 🚀
现在我们每次加载仪表板时都会重新加载账户数据,你认为我们是否仍然需要持久化*所有账户*数据?
使用 Agent 模式完成以下挑战:
尝试一起修改 `localStorage` 中保存和加载的内容,仅包括应用程序正常运行所绝对需要的数据。
**描述:** 实现一个包含撤销/重做功能的完整状态管理系统,用于银行应用。该挑战将帮助你实践高级状态管理概念,包括状态历史跟踪、不可变更新以及用户界面同步。
**提示:** 创建增强状态管理系统包括1) 用于追踪先前状态的状态历史数组2) 可回退和重做到之前状态的函数3) 仪表板上的撤销/重做 UI 按钮4) 最大历史容量为 10 条以防止内存问题5) 用户注销时妥善清理历史。确保撤销/重做功能在账户余额变动时有效,并支持浏览器刷新持久化。
了解更多[agent 模式](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode)。
## 🚀 挑战:存储优化
你的实现当前已有效处理用户会话、数据刷新和状态管理。但考虑我们的现有方案是否在存储效率和功能间达成最优平衡。
正如国际象棋高手区分关键棋子和可舍弃卒子,有效状态管理需识别哪些数据必须持久,哪些应始终从服务器获取最新。
**优化分析:**
评估当前 localStorage 实现,思考以下策略性问题:
- 维持用户认证所需的最少信息是什么?
- 哪些数据变更频繁,以至于本地缓存意义不大?
- 如何通过存储优化提升性能而不影响用户体验?
这种架构分析区分了考虑功能与效率的资深开发者。
**实施策略:**
- **确认必须持久化的关键数据(可能仅限用户身份信息)**
- **调整 localStorage 只保存关键会话数据**
- **确保每次访问仪表板都从服务器加载最新数据**
- **测试优化方案确保用户体验无差异**
**高级考虑:**
- **比较存储完整账户数据与仅存储认证令牌的利弊**
- **为团队成员文档化决策与理由**
此挑战助你成为兼顾用户体验和应用效率的专业开发者。请尽情试验不同方案!
## 课后测验
[课后测验](https://ff-quizzes.netlify.app/web/quiz/48)
## 作业
[实现“添加交易”对话框](assignment.md)
以下是完成作业后的示例结果:
完成作业后的示例效果:
![显示示例“添加交易”对话框的截图](../../../../translated_images/dialog.93bba104afeb79f12f65ebf8f521c5d64e179c40b791c49c242cf15f7e7fab15.zh.png)
![显示“添加交易”对话框示例的截图](../../../../translated_images/dialog.93bba104afeb79f1.zh.png)
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免责声明**
本文档使用AI翻译服务 [Co-op Translator](https://github.com/Azure/co-op-translator) 进行翻译。尽管我们尽力确保准确性,但请注意,自动翻译可能包含错误或不准确之处。应以原始语言的文档作为权威来源。对于关键信息,建议使用专业人工翻译。因使用本翻译而引起的任何误解或误读,我们概不负责。
本文件系使用 AI 翻译服务 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻译而成。虽然我们尽力确保翻译的准确性,但请注意,自动翻译可能存在错误或不准确之处。原始语言的文件应视为权威来源。对于重要信息,建议使用专业人工翻译。我们不对因使用本翻译而产生的任何误解或误释承担责任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,37 +1,164 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "f23a868536c07da991b1d4e773161e25",
"translation_date": "2025-08-24T00:14:55+00:00",
"original_hash": "50a7783473b39a2e0f133e271a102231",
"translation_date": "2026-01-06T11:46:54+00:00",
"source_file": "7-bank-project/4-state-management/assignment.md",
"language_code": "zh"
}
-->
# 实现“添加交易”对话框
## 说明
## 概述
我们的银行应用程序仍然缺少一个重要功能:输入新交易的功能。
使用您在前四节课中学到的所有内容,来实现一个“添加交易”对话框:
您的银行应用现在已经具备了稳健的状态管理和数据持久化功能,但缺少了一个真实银行应用所需的重要功能:允许用户添加自己的交易。在本作业中,您将实现一个完整的“添加交易”对话框,并与现有的状态管理系统无缝集成。
- 在仪表板页面添加一个“添加交易”按钮
- 可以创建一个带有 HTML 模板的新页面,或者使用 JavaScript 显示/隐藏对话框的 HTML而无需离开仪表板页面您可以使用 [`hidden`](https://developer.mozilla.org/docs/Web/HTML/Global_attributes/hidden) 属性或 CSS 类来实现)
- 确保处理好对话框的[键盘和屏幕阅读器的可访问性](https://developer.paciellogroup.com/blog/2018/06/the-current-state-of-modal-dialog-accessibility/)
- 实现一个 HTML 表单以接收输入数据
- 从表单数据创建 JSON 数据并发送到 API
- 使用新数据更新仪表板页面
本作业将汇集您在四个银行课程中所学的所有内容HTML 模板、表单处理、API 集成和状态管理。
查看 [服务器 API 规范](../api/README.md),了解需要调用的 API 以及预期的 JSON 格式。
## 学习目标
以下是完成任务后的示例结果:
完成本作业后,您将能够:
- **创建** 适合用户输入的友好对话框界面
- **实现** 具有键盘和屏幕阅读器支持的无障碍表单设计
- **集成** 新功能到现有的状态管理系统
- **练习** API 通信和错误处理
- **应用** 现代网页开发模式于真实功能
![显示“添加交易”对话框的示例截图](../../../../7-bank-project/4-state-management/images/dialog.png)
## 操作说明
## 评分标准
### 第一步:添加交易按钮
| 标准 | 卓越 | 合格 | 需要改进 |
| -------- | ------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ |
| | 完全按照课程中看到的所有最佳实践实现了添加交易功能。 | 实现了添加交易功能,但未完全遵循课程中看到的最佳实践,或者功能仅部分工作。 | 添加交易功能完全无法正常工作。 |
**创建** 一个“添加交易”按钮,放置在仪表盘页面,方便用户查找和访问。
**免责声明**
本文档使用AI翻译服务 [Co-op Translator](https://github.com/Azure/co-op-translator) 进行翻译。尽管我们努力确保翻译的准确性,但请注意,自动翻译可能包含错误或不准确之处。应以原文档的原始语言版本为权威来源。对于关键信息,建议使用专业人工翻译。我们对因使用此翻译而引起的任何误解或误读不承担责任。
**要求:**
- **将** 按钮放在仪表盘的合适位置
- **使用** 明确、面向动作的按钮文本
- **按照** 现有用户界面设计风格为按钮美化样式
- **确保** 按钮支持键盘访问
### 第二步:对话框实现
选择以下两种方法之一来实现您的对话框:
**方案 A独立页面**
- **创建** 一个新的 HTML 模板用于交易表单
- **在** 路由系统中添加新路由
- **实现** 往返表单页面的导航
**方案 B模态对话框推荐**
- **使用** JavaScript 在不离开仪表盘的情况下显示/隐藏对话框
- **采用** [`hidden` 属性](https://developer.mozilla.org/docs/Web/HTML/Global_attributes/hidden) 或 CSS 类实现显示控制
- **营造** 流畅的用户体验,并正确管理焦点
### 第三步:无障碍实现
**确保** 您的对话框符合[模态对话框的无障碍标准](https://developer.paciellogroup.com/blog/2018/06/the-current-state-of-modal-dialog-accessibility/)
**键盘导航:**
- **支持** 使用 Esc 键关闭对话框
- **限制** 在对话框打开时焦点在对话框内部循环
- **关闭后** 将焦点返回触发按钮
**屏幕阅读器支持:**
- **添加** 合适的 ARIA 标签和角色
- **向** 屏幕阅读器通报对话框的打开和关闭
- **提供** 清晰的表单字段标签和错误消息
### 第四步:表单创建
**设计** 一个 HTML 表单来收集交易数据:
**必填字段:**
- **日期**:交易发生时间
- **描述**:交易用途说明
- **金额**:交易金额(收入为正数,支出为负数)
**表单功能:**
- **提交前** 验证用户输入内容
- **为** 无效数据提供明确的错误提示
- **包含** 有用的占位符文本和标签
- **样式** 与现有设计保持一致
### 第五步API 集成
**将** 您的表单连接到后端 API
**实现步骤:**
- **查阅** [服务器 API 规范](../api/README.md) 以获取正确的端点和数据格式
- **将表单输入** 转换为 JSON 数据
- **使用** 适当的错误处理将数据发送到 API
- **向用户显示** 成功/失败消息
- **优雅处理** 网络错误
### 第六步:状态管理集成
**在仪表盘** 更新新添加的交易:
**集成要求:**
- **成功添加交易后** 刷新账户数据
- **更新** 仪表盘显示,无需页面刷新
- **确保** 新交易立即显示
- **保持** 整个过程中的状态一致性
## 技术规格
**API 端点详情:**
请参阅[服务器 API 文档](../api/README.md)了解:
- 交易数据所需的 JSON 格式
- HTTP 方法和端点 URL
- 期望的响应格式
- 错误响应处理
**预期效果:**
完成本作业后,您的银行应用应具备一个功能齐全的“添加交易”功能,外观与行为均专业:
![显示示例“添加交易”对话框的截图](../../../../translated_images/dialog.93bba104afeb79f1.zh.png)
## 测试您的实现
**功能测试:**
1. **确认** “添加交易”按钮清晰可见且可访问
2. **测试** 对话框能正常打开和关闭
3. **验证** 表单验证适用于所有必填字段
4. **检查** 成功的交易能立即显示在仪表盘
5. **确保** 无效数据和网络问题的错误处理有效
**无障碍测试:**
1. **使用** 仅键盘操作完成整个流程
2. **使用** 屏幕阅读器测试对话框开关及通知
3. **验证** 焦点管理是否正确
4. **检查** 所有表单控件是否具有适当标签
## 评估标准
| 标准 | 优秀 | 合格 | 需改进 |
| -------- | --------- | -------- | ----------------- |
| **功能性** | 添加交易功能完美运行,用户体验优秀,遵循课程所有最佳实践 | 添加交易功能正常工作,但可能未遵循部分最佳实践或存在轻微可用性问题 | 添加交易功能部分可用,存在较大可用性问题 |
| **代码质量** | 代码结构良好,遵循既定模式,包含适当错误处理,与现有状态管理无缝集成 | 代码功能正常,但结构存在些许问题,与现有代码库模式不够一致 | 代码存在重大结构性问题,或无法很好地与现有模式集成 |
| **无障碍性** | 完整支持键盘导航,兼容屏幕阅读器,遵循 WCAG 指南且焦点管理优秀 | 实现基本无障碍功能,但可能缺失某些键盘导航或屏幕阅读器支持 | 无或极少考虑无障碍需求 |
| **用户体验** | 界面直观且精良,反馈清晰,交互流畅,专业美观 | 用户体验良好,但反馈或视觉设计存在微小改进空间 | 用户体验差,界面混乱或缺乏有效用户反馈 |
## 其他挑战(可选)
完成基础要求后,您可以考虑以下增强功能:
**增强功能:**
- **添加** 交易类别(餐饮、交通、娱乐等)
- **实现** 实时反馈的输入验证
- **创建** 面向高级用户的键盘快捷键
- **添加** 交易编辑和删除功能
**高级集成:**
- **实现** 最近添加交易的撤销功能
- **添加** 从 CSV 文件批量导入交易
- **创建** 交易搜索和筛选功能
- **实现** 数据导出功能
这些可选功能将帮助您练习更高级的网页开发概念,打造更完整的银行应用!
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免责声明**
本文件使用AI翻译服务[Co-op Translator](https://github.com/Azure/co-op-translator)进行翻译。尽管我们努力确保准确性,但请注意,自动翻译可能包含错误或不准确之处。原始语言的文件应被视为权威版本。对于关键信息,建议采用专业人工翻译。因使用本翻译而产生的任何误解或误释,我们概不负责。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,192 +1,639 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "f8d4b0284f3fc1de7eb65073d8338cca",
"translation_date": "2025-10-03T08:48:42+00:00",
"original_hash": "a9a3bcc037a447e2d8994d99e871cd9f",
"translation_date": "2026-01-06T11:19:40+00:00",
"source_file": "8-code-editor/1-using-a-code-editor/README.md",
"language_code": "zh"
}
-->
***
# 使用代码编辑器:[VSCode.dev](https://vscode.dev) 高级指南
**欢迎!**
本课程将带你从基础到高级使用 [VSCode.dev](https://vscode.dev)——一个强大的基于网页的代码编辑器。你将学会如何自信地编辑代码、管理项目、跟踪更改、安装扩展以及像专业人士一样协作——这一切都可以直接在浏览器中完成,无需安装任何软件。
***
## 学习目标
完成本课程后,你将能够:
- 高效地在任何项目上使用代码编辑器,无论身处何地
- 使用内置版本控制无缝跟踪工作进度
- 通过编辑器自定义和扩展提升开发工作流程
***
## 前置条件
开始之前,请**注册一个免费的 [GitHub](https://github.com) 账号**,它可以帮助你管理代码库并与全球开发者协作。如果你还没有账号,[点击这里创建一个](https://github.com/)。
***
## 为什么选择基于网页的代码编辑器?
像 VSCode.dev 这样的**代码编辑器**是你编写、编辑和管理代码的指挥中心。它拥有直观的界面、丰富的功能,并且可以通过浏览器即时访问,你可以:
- 在任何设备上编辑项目
- 避免安装的麻烦
- 即时协作和贡献
一旦熟悉了 VSCode.dev你就可以随时随地处理编码任务。
***
## 开始使用 VSCode.dev
访问 **[VSCode.dev](https://vscode.dev)**——无需安装,无需下载。使用 GitHub 登录可以解锁完整功能,包括同步你的设置、扩展和代码库。如果系统提示,请连接你的 GitHub 账号。
加载后,你的工作区将如下所示:
![默认 VSCode.dev](../../../../8-code-editor/images/default-vscode-dev)
VSCode.dev 的界面分为三个核心部分,从左到右分别是:
- **活动栏:** 包括图标如 🔎(搜索)、⚙️(设置)、文件、源代码控制等。
- **侧边栏:** 根据活动栏中选择的图标改变上下文(默认显示 *资源管理器*,用于展示文件)。
- **编辑器/代码区域:** 最右侧的最大部分——你将在这里编辑和查看代码。
点击图标探索功能,但记得返回 _资源管理器_ 保持位置。
***
# 使用代码编辑器:掌握 VSCode.dev
还记得*黑客帝国*里尼奥需要连接到一个庞大的计算机终端以访问数字世界吗今天的网页开发工具则相反——功能强大且随时随地都能访问。VSCode.dev 是一个基于浏览器的代码编辑器,能为任何有网络连接的设备带来专业开发工具。
就像印刷术让图书不再是修道院中抄写员的专属VSCode.dev 让编码更民主化。你可以在图书馆的电脑、学校的实验室或任何有浏览器的地方工作。无需安装,也不必受限于“我需要特定的配置”。
完成本课后,你将了解如何操作 VSCode.dev直接在浏览器中打开 GitHub 仓库,并使用 Git 进行版本控制——这些都是专业开发者每天必用的技能。
## ⚡ 你在接下来的5分钟内能做什么
**面向忙碌开发者的快速入门路径**
```mermaid
flowchart LR
A[⚡ 5分钟] --> B[访问 vscode.dev]
B --> C[连接 GitHub 账户]
C --> D[打开任意仓库]
D --> E[立即开始编辑]
```
- **第1分钟**:访问 [vscode.dev](https://vscode.dev) — 无需安装
- **第2分钟**:使用 GitHub 登录以连接你的仓库
- **第3分钟**:尝试网址技巧:将任意仓库 URL 中的 `github.com` 改为 `vscode.dev/github`
- **第4分钟**:新建文件,自动体验语法高亮
- **第5分钟**:通过源代码控制面板提交修改
**快速测试 URL**
```
# Transform this:
github.com/microsoft/Web-Dev-For-Beginners
# Into this:
vscode.dev/github/microsoft/Web-Dev-For-Beginners
```
**为什么这很重要**5分钟内你将体验到能在任何地方使用专业工具自由编码的感觉。这代表了开发的未来——可访问强大并且即时。
## 🗺️ 你的云端开发学习之旅
```mermaid
journey
title 从本地设置到云开发大师之路
section 了解平台
发现基于网络的编辑: 4: You
连接到GitHub生态系统: 6: You
精通界面导航: 7: You
section 文件管理技能
创建和组织文件: 5: You
使用语法高亮编辑: 7: You
浏览项目结构: 8: You
section 版本控制精通
理解Git集成: 6: You
练习提交工作流程: 8: You
掌握协作模式: 9: You
section 专业定制
安装强大扩展: 7: You
配置开发环境: 8: You
构建个人工作流: 9: You
```
**你的旅途终点**:本课结束时,你将掌握一个能在任何设备上使用的专业云开发环境,让你能够使用大型科技公司开发者同样的工具编写代码。
## 你将学到什么
完成本次学习后,你将能够:
- 像在第二个家一样熟练操作 VSCode.dev —— 找到所需功能而不迷路
- 直接在浏览器打开任何 GitHub 仓库,立即开始编辑(这非常神奇!)
- 使用 Git 跟踪改动,像专业人士一样保存进度
- 用扩展插件增强编辑器,让编码更快更有趣
- 自信地创建和组织项目文件
## 你需要准备什么
要求很简单:
- 一个免费的 [GitHub 账号](https://github.com)(如果需要,我们会指导你创建)
- 具备基本的网页浏览器使用能力
- GitHub 基础课程提供有用背景知识,但不是必需
> 💡 **GitHub 新手提示**创建账号免费且只需几分钟。就像图书馆卡让你访问全球书籍GitHub 账号能让你进入互联网上的代码仓库大门。
## 🧠 云开发生态系统概览
```mermaid
mindmap
root((VSCode.dev 掌握))
Platform Benefits
Accessibility
设备独立性
无需安装
即时更新
通用访问
Integration
GitHub 连接
仓库同步
设置持久化
协作准备
Development Workflow
File Management
项目结构
语法高亮
多标签编辑
自动保存功能
Version Control
Git 集成
提交工作流程
分支管理
变更追踪
Customization Power
Extensions Ecosystem
生产力工具
语言支持
主题选项
自定义快捷键
Environment Setup
个人偏好
工作区配置
工具集成
工作流程优化
Professional Skills
Industry Standards
版本控制
代码质量
协作
文档编写
Career Readiness
远程工作
云端开发
团队项目
开源
```
**核心原则**:基于云的开发环境代表了编码的未来——为开发者提供专业级、可访问、协作和跨平台的工具。
## 为什么基于网页的代码编辑器重要
在互联网出现之前不同大学的科学家很难轻松分享研究。1960年代的 ARPANET 连接了远距离的计算机。网页代码编辑器也遵循这种原则——无论你身在何处、用何种设备,都能访问强大工具。
代码编辑器是你的开发工作空间,用于编写、编辑和组织代码文件。与简单文本编辑器不同,专业的代码编辑器提供语法高亮、错误检测和项目管理功能。
VSCode.dev 将这些能力带入浏览器:
**基于网页编辑的优势:**
| 功能 | 描述 | 实际好处 |
|--------------|---------------------------|------------------------------|
| **平台无关** | 在带浏览器的任何设备运行 | 无缝在不同计算机间切换工作 |
| **无需安装** | 通过网页 URL 访问 | 绕过软件安装限制 |
| **自动更新** | 始终运行最新版本 | 无需手动更新即可使用新功能 |
| **仓库集成** | 直接连接 GitHub | 无需本地管理文件即可编辑代码 |
**实际影响:**
- 在不同环境间工作无缝衔接
- 操作系统无关,界面一致
- 立即支持协作
- 减少对本地存储空间需求
## 探索 VSCode.dev
就像居里夫人的实验室在简洁空间中配备了先进设备VSCode.dev 将专业开发工具浓缩进浏览器界面。这一网页应用提供与桌面代码编辑器相同的核心功能。
从浏览器访问 [vscode.dev](https://vscode.dev) 开始。界面无需下载或系统安装——这是云计算原则的直接应用。
### 连接你的 GitHub 账号
就像贝尔电话连接了远方,链接你的 GitHub 账号让 VSCode.dev 与你的代码仓库相通。提示登录 GitHub 时,建议接受连接。
**GitHub 集成带来:**
- 在编辑器内直接访问仓库
- 跨设备同步设置和扩展
- 流畅的保存流程到 GitHub
- 个性化的开发环境
### 了解你的新工作空间
加载完成后,你会看到一个清爽的工作区,设计围绕代码展开,让你专注于核心任务!
![默认 VSCode.dev 界面](../../../../translated_images/default-vscode-dev.5d06881d65c1b323.zh.png)
**你的界面导览:**
- **活动栏**(左侧条带):主导航,含资源管理器 📁、搜索 🔍、源代码控制 🌿、扩展 🧩 及设置 ⚙️
- **侧边栏**(旁边面板):根据选择显示相关信息
- **编辑区域**(中间大块区域):魔法发生地——你的主要代码区域
**花点时间探索:**
- 点击活动栏图标,看看它们各自作用
- 注意侧边栏如何更新显示不同信息——很酷吧?
- 资源管理器视图(📁)你会用得最多,慢慢熟悉它吧
```mermaid
flowchart TB
subgraph "VSCode.dev 界面架构"
A[活动栏] --> B[资源管理器 📁]
A --> C[搜索 🔍]
A --> D[源码管理 🌿]
A --> E[扩展 🧩]
A --> F[设置 ⚙️]
B --> G[文件树]
C --> H[查找与替换]
D --> I[Git 状态]
E --> J[扩展市场]
F --> K[配置]
L[侧边栏] --> M[上下文面板]
N[编辑区] --> O[代码文件]
P[终端/输出] --> Q[命令行]
end
```
## 打开 GitHub 仓库
### 方法 1通过编辑器打开
1. 访问 [VSCode.dev](https://vscode.dev)。点击 **"Open Remote Repository"**。
过去研究人员需亲自去图书馆查阅资料。GitHub 仓库类似是远端保存的代码集合。VSCode.dev 省去了传统下载本地才编辑的步骤。
![打开远程仓库](../../../../8-code-editor/images/open-remote-repository)
这个功能让你能立即访问任何公共仓库,查看、编辑或贡献。打开仓库有两种方式:
2. 使用 _命令面板_Ctrl-Shift-P 或 Mac 上的 Cmd-Shift-P
### 方法1点点点击法
![命令面板菜单](../../../../translated_images/palette-menu.4946174e07f426226afcdad707d19b8d5150e41591c751c45b5dee213affef91.zh.png)
适合刚进入 VSCode.dev 想打开特定仓库的用户,简单易用,适合初学者:
- 选择“打开远程仓库”选项。
- 粘贴你的 GitHub 仓库 URL例如 `https://github.com/microsoft/Web-Dev-For-Beginners`),然后按 Enter。
**操作步骤:**
如果成功,你将看到整个项目加载完成并准备编辑!
1. 如果没在页面,先访问 [vscode.dev](https://vscode.dev)
2. 在欢迎界面点击“Open Remote Repository”按钮
***
![打开远程仓库](../../../../translated_images/open-remote-repository.bd9c2598b8949e7f.zh.png)
### 方法 2通过 URL 快速打开
3. 粘贴任意 GitHub 仓库 URL试试这个`https://github.com/microsoft/Web-Dev-For-Beginners`
4. 按回车,见证神奇!
将任何 GitHub 仓库 URL 转换为 VSCode.dev 直接打开的链接,只需将 `github.com` 替换为 `vscode.dev/github`
例如:
**专业技巧 - 命令面板快捷键:**
- GitHub: `https://github.com/microsoft/Web-Dev-For-Beginners`
- VSCode.dev: `https://vscode.dev/github/microsoft/Web-Dev-For-Beginners`
想更像编码巫师?试试 Ctrl+Shift+PMac 上为 Cmd+Shift+P打开命令面板
此功能让你可以快速访问任何项目。
![命令面板](../../../../translated_images/palette-menu.4946174e07f42622.zh.png)
***
**命令面板是你所有操作的搜索引擎:**
- 输入“open remote”即可找到打开远程仓库的功能
- 记忆你最近打开过的仓库(超级方便!)
- 熟练后感觉像闪电般快速编码
- 这就是 VSCode.dev 的“嘿 Siri”只不过是为编码设计的
## 编辑项目中的文件
### 方法2修改 URL 技巧
打开仓库后,你可以:
如同 HTTP 和 HTTPS 在同一域名下用了不同协议VSCode.dev 使用与 GitHub 地址体系类似的 URL 格式。任何 GitHub 仓库 URL 都可以修改成 VSCode.dev 直接打开。
### 1. **创建新文件**
- 在 *资源管理器* 侧边栏中,导航到目标文件夹或使用根目录。
- 点击 _新建文件..._ 图标。
- 命名文件,按 **Enter**,文件会立即出现。
**URL 变换规则:**
![创建新文件](../../../../8-code-editor/images/create-new-file)
| 仓库类型 | GitHub URL | VSCode.dev URL |
|--------------|----------------------------------------------|-------------------------------------------------|
| **公共仓库** | `github.com/microsoft/Web-Dev-For-Beginners` | `vscode.dev/github/microsoft/Web-Dev-For-Beginners` |
| **个人项目** | `github.com/your-username/my-project` | `vscode.dev/github/your-username/my-project` |
| **任何可访问仓库** | `github.com/their-username/awesome-repo` | `vscode.dev/github/their-username/awesome-repo` |
### 2. **编辑和保存文件**
- 点击 *资源管理器* 中的文件,将其打开到代码区域。
- 根据需要进行修改。
- VSCode.dev 会自动保存你的更改,但你也可以按 Ctrl+S 手动保存。
**实际操作:**
- 将 `github.com` 替换为 `vscode.dev/github`
- 保持其他 URL 组件不变
- 支持所有公共仓库
- 即刻进入编辑状态
![编辑文件](../../../../translated_images/edit-a-file.52c0ee665ef19f08119d62d63f395dfefddc0a4deb9268d73bfe791f52c5807a.zh.png)
> 💡 **改变生活的提示**:将你喜爱的仓库的 VSCode.dev 版本收藏为书签。我有“编辑我的作品集”和“修正文档”的书签,点击就直达编辑模式!
### 3. **使用版本控制跟踪和提交更改**
**选择使用哪种方法?**
- **界面打开法**:适合探索或不记得确切仓库名时用
- **URL 技巧法**:准确知道目标时极速访问
VSCode.dev 集成了 **Git** 版本控制!
### 🎯 教学检查点:云开发访问
- 点击 _'源代码控制'_ 图标查看所有更改。
- `Changes` 文件夹中的文件显示新增内容(绿色)和删除内容(红色)。
![查看更改](../../../../translated_images/working-tree.c58eec08e6335c79cc708c0c220c0b7fea61514bd3c7fb7471905a864aceac7c.zh.png)
**暂停思考**:你刚学会了两种通过浏览器访问代码仓库的方法。这标志着开发方式的根本转变。
- 点击文件旁边的 `+` 准备提交更改。
- 点击撤销图标 **丢弃** 不需要的更改。
- 输入清晰的提交信息,然后点击复选标记提交并推送。
**快速自测:**
- 你能解释基于网页编辑为何免去了传统“开发环境设置”吗?
- URL 修改技巧相比本地 git 克隆有哪些优势?
- 这种方式如何改变你参与开源项目的方式?
要返回 GitHub 仓库,请点击左上角的汉堡菜单。
**现实联系**GitHub、GitLab 与 Replit 等大公司都基于云优先原则构建开发平台。你正在学习的工作流程,正是全球专业开发团队在使用的
![暂存并提交更改](../../../../8-code-editor/images/edit-vscode.dev)
**思考问题**:云开发会如何影响学校的编程教学?考虑设备需求、软件管理与协作可能性。
***
## 使用文件和项目
## 使用扩展增强功能
既然打开了仓库开始构建吧VSCode.dev 为你提供创建、编辑和组织代码文件所需的一切。把它看作你的数字化车间——所有工具都唾手可得。
扩展可以为 VSCode.dev 添加语言支持、主题、调试器和生产力工具——让你的编码生活更轻松、更有趣。
下面介绍你日常编码工作中最常用的操作
### 浏览和管理扩展
### 创建新文件
- 点击活动栏中的 **扩展图标**。
- 在 _'搜索市场中的扩展'_ 框中搜索扩展。
就像建筑师办公室里有条理地保存蓝图VSCode.dev 的文件创建也遵循结构化方式。支持所有标准的网页开发文件类型。
![扩展详情](../../../../8-code-editor/images/extension-details)
**文件创建流程:**
- **已安装:** 所有你添加的扩展
- **热门:** 行业推荐
- **推荐:** 根据你的工作流程量身定制
1. 在资源管理器侧边栏导航到目标文件夹
2. 鼠标悬停在文件夹名称上,出现“新建文件”图标(📄+
3. 输入文件名并包含适当扩展名(如 `style.css``script.js``index.html`
4. 按回车创建文件
![查看扩展](
![创建新文件](../../../../translated_images/create-new-file.2814e609c2af9aeb.zh.png)
***
**命名规范:**
- 使用描述性名称,体现文件用途
- 加上文件扩展名以支持正确的语法高亮
- 贯穿项目应用一致的命名规则
- 使用小写字母和连字符,避免空格
### 1. **安装扩展**
### 编辑和保存文件
- 在搜索框中输入扩展名称,点击扩展并在编辑器中查看详情。
- 点击侧边栏或代码区域中的 **蓝色安装按钮**。
真正的乐趣从这里开始VSCode.dev 的编辑器装满实用功能,让编码感觉流畅自然。它就像一个超级聪明的写作助手,但专为代码设计。
![安装扩展](../../../../8-code-editor/images/install-extension)
**你的编辑工作流:**
### 2. **自定义扩展**
1. 点击资源管理器中的任意文件,在主区域打开
2. 开始输入,感受 VSCode.dev 用颜色、高亮和错误提示支持你的创作
3. 用 Ctrl+SWindows/Linux或 Cmd+SMac保存——其实它也会自动保存
- 找到已安装的扩展。
- 点击 **齿轮图标** → 选择 _扩展设置_,根据需要调整行为。
![编辑文件](../../../../translated_images/edit-a-file.52c0ee665ef19f08.zh.png)
![修改扩展设置](../../../../8-code-editor/images/extension-settings)
**编码时的酷炫特性:**
- 代码色彩丰富,易于阅读
- 输入时自动补全(像拼写纠正,但更智能)
- 保存前自动检测错误
- 可打开多个文件标签页,类似浏览器
- 背景自动保存,无需担心
### 3. **管理扩展**
你可以:
> ⚠️ **小提醒**:尽管有自动保存,养成按 Ctrl+S 或 Cmd+S 的习惯依然好。它能立即保存并触发额外的错误检查功能。
- **禁用:** 暂时关闭扩展但保留安装
- **卸载:** 如果不再需要,可以永久移除
### 使用 Git 进行版本控制
找到扩展,点击齿轮图标,选择“禁用”或“卸载”,或者使用代码区域中的蓝色按钮。
就像考古学家详细记录挖掘层次Git 跟踪代码随时间的变化。它保存项目历史便于回退到之前版本。VSCode.dev 集成了 Git 功能
***
**源代码控制界面:**
## 作业
1. 通过活动栏的 🌿 图标访问源代码控制面板
2. 已修改的文件会出现在“变更”区域
3. 用颜色区分变动类型:绿色表示新增,红色表示删除
![查看源代码变更](../../../../translated_images/working-tree.c58eec08e6335c79.zh.png)
**保存工作(提交工作流):**
```mermaid
flowchart TD
A[修改文件] --> B[在源代码管理中查看更改]
B --> C[点击 + 暂存更改]
C --> D[编写描述性提交信息]
D --> E[点击对勾提交]
E --> F[更改推送到 GitHub]
```
```mermaid
stateDiagram-v2
[*] --> Modified: 编辑文件
Modified --> Staged: 点击 + 以暂存
Staged --> Modified: 点击 - 以取消暂存
Staged --> Committed: 添加信息并提交
Committed --> [*]: 同步到 GitHub
state Committed {
[*] --> LocalCommit
LocalCommit --> RemotePush: 自动同步
}
```
**步骤说明:**
- 点击想保存文件旁的“+”图标,将其“暂存”
- 仔细检查你对所有暂存更改是否满意
- 写一段简短的说明解释你做了什么(这就是你的“提交信息”)
- 点击勾选按钮将所有内容保存到 GitHub
- 如果改变主意,撤销图标可以舍弃更改
**编写好的提交信息(其实比你想的容易!):**
- 只需描述你做了什么,比如“添加联系表单”或“修复导航故障”
- 保持简短明了——想象推文长度,而不是论文
- 以动作动词开头,如“添加”、“修复”、“更新”或“删除”
- **好的示例**:“添加响应式导航菜单”,“修复移动端布局问题”,“更新颜色以提升可访问性”
> 💡 **快速导航提示**:使用左上角的汉堡菜单(☰)跳回你的 GitHub 仓库,查看在线提交的更改。这就像编辑环境和项目的 GitHub 首页之间的传送门!
## 通过扩展增强功能
正如工匠的工作坊中包含专门工具处理不同任务VSCode.dev 也可以通过扩展定制以添加具体功能。这些社区开发的插件满足了常见开发需求,如代码格式化、实时预览和增强的 Git 集成。
扩展市场托管着开发者社区全球创作的成千上万免费工具。每个扩展解决特定的工作流难题,让你构建一个个性化的开发环境,适合你的特定需求和喜好。
```mermaid
mindmap
root((扩展生态系统))
Essential Categories
Productivity
Live Server
Auto Rename Tag
Bracket Pair Colorizer
GitLens
Code Quality
Prettier
ESLint
Spell Checker
Error Lens
Language Support
HTML CSS Support
JavaScript ES6
Python Extension
Markdown Preview
Themes & UI
Dark+ Modern
Material Icon Theme
Peacock
Rainbow Brackets
Discovery Methods
Popular Rankings
Download Counts
User Ratings
Recent Updates
Community Reviews
Recommendations
Workspace Suggestions
Language-based
Workflow-specific
Team Standards
```
### 寻找理想扩展
扩展市场结构非常清晰,所以你不会迷失在寻找所需工具中。它设计用来帮助你发现既具体又有趣的新工具,甚至是以前不知道的好东西!
**如何进入市场:**
1. 点击活动栏中的扩展图标(🧩)
2. 浏览或搜索特定内容
3. 点击感兴趣的扩展,了解更多信息
![扩展市场界面](../../../../translated_images/extensions.eca0e0c7f59a10b5.zh.png)
**你会看到内容:**
| 部分 | 包含内容 | 有何作用 |
|----------|---------|----------|
| **已安装** | 你已经添加的扩展 | 你的个人编码工具箱 |
| **流行** | 大家喜欢的扩展 | 大多数开发者推荐的 |
| **推荐** | 针对你的项目的智能建议 | VSCode.dev 的贴心推荐 |
**方便浏览的理由:**
- 每个扩展展示评分、下载次数和真实用户评论
- 有截图和清晰的描述说明功能
- 明确标注兼容性信息
- 推荐类似扩展,方便你做比较
### 安装扩展(超简单!)
给编辑器添加新功能,只需点击按钮。扩展几秒钟即可安装,立马生效——无需重启,无需等待。
**步骤非常简单:**
1. 搜索你想要的扩展试试搜索“live server”或“prettier”
2. 点击一个看起来不错的扩展,查看详情
3. 阅读功能描述和用户评分
4. 点击蓝色“安装”按钮,完成!
![安装扩展](../../../../8-code-editor/images/install-extension.gif)
**幕后发生了什么:**
- 扩展自动下载并设置好
- 新功能立即出现在界面中
- 立刻开始工作(真的,很快!)
- 如果你已登录,扩展会同步到所有设备
**推荐开始试用的几个扩展:**
- **Live Server**:编码时实时查看网站更新(超级神奇!)
- **Prettier**:自动让代码格式整洁专业
- **Auto Rename Tag**:修改一个 HTML 标签,配对标签同步更新
- **Bracket Pair Colorizer**:用颜色区分括号,避免迷失
- **GitLens**:让 Git 功能超强,信息丰富
### 自定义你的扩展
大多数扩展带有可调设置,让你调整到最适合你的使用习惯。就像调节汽车的座椅和后视镜一样,每个人都有偏好!
**调整扩展设置:**
1. 在扩展面板找到已安装扩展
2. 点击名称旁的小齿轮图标(⚙️)
3. 选择“扩展设置”
4. 进行调整,直到感觉顺手
![自定义扩展设置](../../../../translated_images/extension-settings.21c752ae4f4cdb78.zh.png)
**你可能想调整的常见项:**
- 代码格式化方式(制表符与空格,行宽等)
- 触发不同操作的快捷键
- 扩展适用的文件类型
- 开启或关闭特定功能,让界面更简洁
### 维护扩展的整洁
随着你发现越来越多好用的扩展想保持扩展集合整洁高效VSCode.dev 提供了简单易用的管理方式。
**你可以做的操作:**
| 操作 | 适用场景 | 专业提示 |
|--------|---------|----------|
| **禁用** | 测试某扩展是否导致问题 | 比卸载更灵活,想用时能恢复 |
| **卸载** | 完全移除不需要的扩展 | 保持环境清爽快速 |
| **更新** | 获取新功能和修复 | 通常自动进行,手动检查也好 |
**我的扩展管理习惯:**
- 每几个月审查一次安装的扩展,清理未用的
- 保持扩展更新,享受最新功能与安全修复
- 如果感觉系统变慢,暂时禁用扩展找原因
- 关注更新日志中的重大改进和新特性
> ⚠️ **性能提示**:扩展虽好,数量太多会拖慢速度。专注使用真正帮到你的,敢于卸载从未用过的扩展。
### 🎯 教学回顾:开发环境定制
**架构理解**:你学会了用社区扩展定制专业开发环境。这与企业开发团队构建标准化工具链如出一辙。
**掌握的关键概念**
- **扩展发现**:找到解决具体开发问题的工具
- **环境配置**:根据个人或团队偏好定制工具
- **性能优化**:功能与系统性能的平衡
- **社区协作**:利用全球开发者创建的工具资源
**行业关联**:扩展生态是 VS Code、Chrome DevTools 和现代 IDE 等主流开发平台的核心。了解扩展的评估、安装和配置是专业开发流程的基础。
**思考问题**:如果为 10 人开发团队搭建统一开发环境,你会如何确保一致性、性能和个性化需求的平衡?
## 📈 你的云端开发大师之路
```mermaid
timeline
title 专业云开发之旅
section 平台基础
Cloud Development Understanding
: 掌握基于网页的编辑概念
: 连接 GitHub 集成模式
: 浏览专业界面
section 工作流掌握
文件与项目管理
: 创建有组织的项目结构
: 掌握语法高亮的优势
: 处理多文件编辑工作流
版本控制集成
: 了解 Git 可视化
: 练习提交信息规范
: 掌握变更跟踪工作流
section 环境定制
扩展生态系统
: 发现生产力扩展
: 配置开发偏好
: 优化性能与功能的平衡
专业设置
: 构建一致的工作流
: 创建可复用配置
: 建立团队标准
section 行业准备
云优先开发
: 掌握远程开发实践
: 了解协作工作流
: 构建平台无关技能
专业实践
: 遵循行业标准
: 创建可维护的工作流
: 为团队环境做准备
```
**🎓 毕业里程碑**:你已成功掌握云端开发,使用与大型科技公司专业开发者相同的工具和工作流。这代表了软件开发的未来。
**🔄 下一阶段能力**
- 准备探索高级云端开发平台Codespaces、GitPod
- 准备参与分布式开发团队协作
- 装备好为全球开源项目贡献代码
- 打下现代 DevOps 与持续集成实践基础
## GitHub Copilot Agent 挑战 🚀
像 NASA 对航天任务的结构化方法,本挑战需要你系统应用 VSCode.dev 技能,完成完整的开发工作流。
**目标**:展示使用 VSCode.dev 建立完整 Web 开发工作流的熟练度。
**项目要求**:使用 Agent 模式协助,完成以下任务:
1. Fork 现有仓库或新建一个
2. 建立含 HTML、CSS 和 JavaScript 文件的功能性项目结构
3. 安装并配置三个提升开发效率的扩展
4. 使用描述性提交信息练习版本控制
5. 体验特性分支的创建和修改
6. 在 README.md 中记录过程和心得
此练习整合所有 VSCode.dev 概念,形成可用于未来开发项目的实用工作流。
在此了解更多有关 [agent 模式](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode)。
## 任务
是时候实战检验这些技能了!这里有一个动手项目,帮你实践所学: [使用 VSCode.dev 创建简历网站](./assignment.md)
该任务引导你用浏览器,全程构建一个专业简历网站。你将用到所有已学的 VSCode.dev 功能,最终不仅拥有漂亮网站,更具备扎实的工作流信心。
## 持续探索与提升技能
你已打下坚实基础,但还有更多精彩内容等你发掘!以下资源和建议帮你将 VSCode.dev 技能提升到新高度:
**官方文档推荐收藏:**
- [VSCode Web 文档](https://code.visualstudio.com/docs/editor/vscode-web?WT.mc_id=academic-0000-alfredodeza) 浏览器编辑完全指南
- [GitHub Codespaces](https://docs.github.com/en/codespaces) 想要云端更强大时必备
**值得尝试的新功能:**
- **键盘快捷键**:学会极速操作,像码农忍者一样!
- **工作区设置**:为不同项目配置不同环境
- **多根工作区**:同时操作多个仓库(超级方便!)
- **终端集成**:浏览器内直接用命令行工具
**实战练习灵感:**
- 参与开源项目贡献代码,用 VSCode.dev 回馈社区
- 尝试不同扩展,找到最适合自己的组合
- 创建常用网站类型的项目模板
- 练习 Git 工作流,如分支和合并,这些技能团队合作必备
测试你的技能:[使用 vscode.dev 创建一个简历网站](https://github.com/microsoft/Web-Dev-For-Beginners/blob/main/8-code-editor/1-using-a-code-editor/assignment.md)
***
## 深入探索和自学
---
- 通过 [VSCode 官方网页文档](https://code.visualstudio.com/docs/editor/vscode-web?WT.mc_id=academic-0000-alfredodeza) 深入了解。
- 探索高级工作区功能、快捷键和设置。
**你已掌握基于浏览器的开发!** 🎉 就像便携仪器让科学家能在偏远地区做研究一样VSCode.dev 让你能用任何联网设备进行专业编码。
***
这些技能符合当前行业习惯——许多专业开发者用云端开发环境,因为它灵活且易用。你学到的工作流可从个人项目扩展到大规模团队协作。
**现在你已经准备好随时随地使用 VSCode.dev 进行编码、创建和协作了!**
将这些技术应用到下一个开发项目吧! 🚀
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免责声明**
本文档使用AI翻译服务 [Co-op Translator](https://github.com/Azure/co-op-translator) 进行翻译。尽管我们努力确保翻译的准确性,但请注意,自动翻译可能包含错误或不准确之处。原始语言的文档应被视为权威来源。对于关键信息,建议使用专业人工翻译。我们不对因使用此翻译而产生的任何误解或误读承担责任。
本文档由 AI 翻译服务 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻译。虽然我们力求准确,但请注意,自动翻译可能存在错误或不准确之处。原始语言版本的文档应被视为权威来源。对于关键信息,建议使用专业人工翻译。因使用本翻译而产生的任何误解或误释,我们概不负责。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,247 +1,594 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "bd3aa6d2b879c30ea496c43aec1c49ed",
"translation_date": "2025-08-29T14:51:38+00:00",
"original_hash": "effe56ba51c38d7bdfad1ea38288666b",
"translation_date": "2026-01-06T11:21:50+00:00",
"source_file": "8-code-editor/1-using-a-code-editor/assignment.md",
"language_code": "zh"
}
-->
# 使用 vscode.dev 创建一个简历网站
# 使用 VSCode.dev 创建简历网站
_当招聘人员向你索要简历时你直接发给他们一个网址这是不是很酷_ 😎
通过构建一个专业的简历网站,展示您的技能和经验,以交互式、现代化的形式提升您的职业前景。想象一下,您不再发送传统的 PDF而是向招聘人员提供一个时尚、响应式的网站既展示您的资格也体现您的网页开发能力。
## 目标
此动手作业让您将所有 VSCode.dev 技能付诸实践,同时创建一个对您的职业生涯真正有用的东西。您将体验完整的网页开发工作流程——从仓库创建到部署——全部在浏览器内完成。
完成本任务后,你将学会:
完成本项目后,您将拥有一个专业的线上形象,能够轻松与潜在雇主分享,随着技能提升不断更新,并可根据个人品牌定制。这正是展示实际网页开发技能的实用项目。
- 创建一个网站来展示你的简历
## 学习目标
### 前置条件
完成此作业后,您将能够:
1. 一个 GitHub 账号。如果你还没有账号,请访问 [GitHub](https://github.com/) 并创建一个账号。
- **使用 VSCode.dev** 创建并管理完整的网页开发项目
- **使用语义 HTML 元素** 构建专业网站结构
- **利用现代 CSS 技术** 设计响应式布局
- **使用基础网页技术** 实现交互功能
- **部署** 可通过共享链接访问的实时网站
- **展示** 开发过程中的版本控制最佳实践
## 步骤
## 前提条件
**步骤 1** 创建一个新的 GitHub 仓库,并命名为 `my-resume`
开始本作业之前,请确保您具备:
**步骤 2** 在你的仓库中创建一个 `index.html` 文件。我们需要在 github.com 上至少添加一个文件,因为你无法在 vscode.dev 中打开一个空的仓库。
- 一个 GitHub 账号(如需请前往 [github.com](https://github.com/) 创建)
- 已完成 VSCode.dev 课程中关于界面导航和基本操作的学习
- 对 HTML 结构和 CSS 样式概念有基本理解
点击 `creating a new file` 链接,输入文件名 `index.html`,然后选择 `Commit new file` 按钮。
## 项目设置与仓库创建
![在 github.com 上创建新文件](../../../../translated_images/new-file-github.com.c886796d800e8056561829a181be1382c5303da9d902d8b2dd82b68a4806e21f.zh.png)
让我们从搭建项目基础开始。此过程模拟实际开发流程,项目起步即有合适的仓库初始化和结构规划。
**步骤 3** 打开 [VSCode.dev](https://vscode.dev) 并选择 `Open Remote Repository` 按钮。
### 第 1 步:创建您的 GitHub 仓库
复制你刚刚为简历网站创建的仓库的 URL并将其粘贴到输入框中
设置专用仓库,确保项目自始至终有良好组织和版本控制。
_将 `your-username` 替换为你的 GitHub 用户名_
1. **访问** [GitHub.com](https://github.com) 并登录您的账号
2. **点击** 右上角绿色的“New”按钮或“+”图标
3. **为仓库命名** 为 `my-resume`(或选择个性化名称如 `john-smith-resume`
4. **填写** 简短描述:“使用 HTML 和 CSS 构建的专业简历网站”
5. **选择** “Public”使您的简历对潜在雇主可见
6. **勾选** “Add a README file” 以创建初始项目描述
7. **点击** “Create repository” 完成设置
> 💡 **仓库命名建议**:使用描述性、专业的名称,明确表明项目用途,有助于与雇主分享或作品集展示。
### 第 2 步:初始化项目结构
由于 VSCode.dev 需要至少一个文件才能打开仓库,我们将在 GitHub 上直接创建主 HTML 文件,然后切换到网页编辑器。
1. **点击** “creating a new file” 链接
2. **输入** 文件名为 `index.html`
3. **添加** 以下初始 HTML 结构:
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Your Name - Professional Resume</title>
</head>
<body>
<h1>Your Name</h1>
<p>Professional Resume Website</p>
</body>
</html>
```
https://github.com/your-username/my-resume
```
✅ 如果成功,你将在浏览器中的文本编辑器中看到你的项目和 `index.html` 文件。
![在 vscode.dev 上打开项目](../../../../translated_images/project-on-vscode.dev.e79815a9a95ee7feac72ebe5c941c91279716be37c575dbdbf2f43bea2c7d8b6.zh.png)
4. **填写** 提交信息“Add initial HTML structure”
5. **点击** “Commit new file” 保存更改
![在 GitHub 上创建初始文件](../../../../translated_images/new-file-github.com.c886796d800e8056.zh.png)
**此初始设置功效:**
- **建立** 合适的 HTML5 文档结构及语义元素
- **包含** 适用于响应式设计的视口元标签
- **设置** 浏览器标签页上显示的描述性页面标题
- **搭建** 专业内容组织的基础
## 在 VSCode.dev 中工作
有了仓库基础后,让我们切换到 VSCode.dev 进行主要开发工作。这个基于网页的编辑器提供了专业网页开发所需的所有工具。
### 第 3 步:在 VSCode.dev 中打开项目
1. **在新标签页中访问** [vscode.dev](https://vscode.dev)
2. **点击** 欢迎屏幕上的“Open Remote Repository”
3. **复制** 您在 GitHub 上仓库的 URL 并粘贴到输入框中
格式示例:`https://github.com/your-username/my-resume`
*将 `your-username` 替换为您的实际 GitHub 用户名*
4. **按回车键** 加载项目
**成功标志**:您将在资源管理器侧边栏看到项目文件,并可在主编辑区打开 `index.html` 进行编辑。
![VSCode.dev 中的项目加载界面](../../../../translated_images/project-on-vscode.dev.e79815a9a95ee7fe.zh.png)
**步骤 4** 打开 `index.html` 文件,将以下代码粘贴到代码区域并保存。
**界面包含内容:**
- **资源管理器侧边栏**:显示仓库文件及目录结构
- **编辑区**:展示选中文件内容供编辑
- **活动栏**:访问版本控制、扩展等功能
- **状态栏**:显示连接状态和当前分支信息
### 第 4 步:构建简历内容
用完整的简历结构替换 `index.html` 中的占位内容。此 HTML 构成您资格专业展现的基础。
<details>
<summary><b>负责简历网站内容的 HTML 代码。</b></summary>
<summary><b>完整 HTML 简历结构</b></summary>
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="style.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<title>Your Name - Professional Resume</title>
</head>
<body>
<header id="header">
<h1>Your Full Name</h1>
<hr>
<p class="role">Your Professional Title</p>
<hr>
</header>
<html>
<head>
<link href="style.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<title>Your Name Goes Here!</title>
</head>
<body>
<header id="header">
<!-- 简历头部,包含你的名字和职位 -->
<h1>Your Name Goes Here!</h1>
<hr>
Your Role!
<hr>
</header>
<main>
<article id="mainLeft">
<section>
<h2>联系方式</h2>
<!-- 联系信息,包括社交媒体 -->
<p>
<i class="fa fa-envelope" aria-hidden="true"></i>
<a href="mailto:username@domain.top-level domain">在这里填写你的邮箱</a>
</p>
<p>
<i class="fab fa-github" aria-hidden="true"></i>
<a href="github.com/yourGitHubUsername">在这里填写你的 GitHub 用户名!</a>
</p>
<p>
<i class="fab fa-linkedin" aria-hidden="true"></i>
<a href="linkedin.com/yourLinkedInUsername">在这里填写你的 LinkedIn 用户名!</a>
</p>
</section>
<section>
<h2>技能</h2>
<!-- 你的技能 -->
<ul>
<li>技能 1</li>
<li>技能 2</li>
<li>技能 3</li>
<li>技能 4</li>
</ul>
</section>
<section>
<h2>教育背景</h2>
<!-- 你的教育背景 -->
<h3>在这里填写你的课程!</h3>
<p>
在这里填写你的学校!
</p>
<p>
开始日期 - 结束日期
</p>
</section>
</article>
<article id="mainRight">
<section>
<h2>关于我</h2>
<!-- 关于你 -->
<p>在这里写一段关于自己的介绍!</p>
</section>
<section>
<h2>工作经历</h2>
<!-- 你的工作经历 -->
<h3>职位名称</h3>
<p>
在这里填写组织名称 | 开始月份 结束月份
</p>
<ul>
<li>任务 1 - 描述你做了什么!</li>
<li>任务 2 - 描述你做了什么!</li>
<li>描述你的贡献成果/影响</li>
</ul>
<h3>职位名称 2</h3>
<p>
在这里填写组织名称 | 开始月份 结束月份
</p>
<ul>
<li>任务 1 - 描述你做了什么!</li>
<li>任务 2 - 描述你做了什么!</li>
<li>描述你的贡献成果/影响</li>
</ul>
</section>
</article>
</main>
</body>
</html>
<main>
<article id="mainLeft">
<section>
<h2>CONTACT</h2>
<p>
<i class="fa fa-envelope" aria-hidden="true"></i>
<a href="mailto:your.email@domain.com">your.email@domain.com</a>
</p>
<p>
<i class="fab fa-github" aria-hidden="true"></i>
<a href="https://github.com/your-username">github.com/your-username</a>
</p>
<p>
<i class="fab fa-linkedin" aria-hidden="true"></i>
<a href="https://linkedin.com/in/your-profile">linkedin.com/in/your-profile</a>
</p>
</section>
<section>
<h2>SKILLS</h2>
<ul>
<li>HTML5 & CSS3</li>
<li>JavaScript (ES6+)</li>
<li>Responsive Web Design</li>
<li>Version Control (Git)</li>
<li>Problem Solving</li>
</ul>
</section>
<section>
<h2>EDUCATION</h2>
<h3>Your Degree or Certification</h3>
<p>Institution Name</p>
<p>Start Date - End Date</p>
</section>
</article>
<article id="mainRight">
<section>
<h2>ABOUT</h2>
<p>Write a compelling summary that highlights your passion for web development, key achievements, and career goals. This section should give employers insight into your personality and professional approach.</p>
</section>
<section>
<h2>WORK EXPERIENCE</h2>
<div class="job">
<h3>Job Title</h3>
<p class="company">Company Name | Start Date End Date</p>
<ul>
<li>Describe a key accomplishment or responsibility</li>
<li>Highlight specific skills or technologies used</li>
<li>Quantify impact where possible (e.g., "Improved efficiency by 25%")</li>
</ul>
</div>
<div class="job">
<h3>Previous Job Title</h3>
<p class="company">Previous Company | Start Date End Date</p>
<ul>
<li>Focus on transferable skills and achievements</li>
<li>Demonstrate growth and learning progression</li>
<li>Include any leadership or collaboration experiences</li>
</ul>
</div>
</section>
<section>
<h2>PROJECTS</h2>
<div class="project">
<h3>Project Name</h3>
<p>Brief description of what the project accomplishes and technologies used.</p>
<a href="#" target="_blank">View Project</a>
</div>
</section>
</article>
</main>
</body>
</html>
```
</details>
将你的简历信息替换 HTML 代码中的 _占位文本_
**定制指南:**
- **将所有占位文本替换** 为您的真实信息
- **根据经验和职业重点** 调整各版块
- **按需添加或删除** 部分(如证书、志愿活动、语言能力)
- **包含** 指向您的实际简历资料和项目的链接
### 第 5 步:创建辅助文件
**步骤 5** 将鼠标悬停在 My-Resume 文件夹上,点击 `New File ...` 图标,在你的项目中创建两个新文件:`style.css` 和 `codeswing.json`
专业网站需要有条理的文件结构。创建 CSS 样式文件和配置文件,实现完整项目
**步骤 6** 打开 `style.css` 文件,将以下代码粘贴进去并保存。
1. **在资源管理器侧边栏** 将鼠标悬浮于项目文件夹名称上
2. **点击** 出现的“新建文件”图标 (📄+)
3. **依次创建** 以下文件:
- `style.css`(用于样式及布局)
- `codeswing.json`(预览扩展配置文件)
**创建 CSS 文件(`style.css`**
<details>
<summary><b>用于格式化网站布局的 CSS 代码。</b></summary>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
font-size: 16px;
max-width: 960px;
margin: auto;
}
h1 {
font-size: 3em;
letter-spacing: .6em;
padding-top: 1em;
padding-bottom: 1em;
}
h2 {
font-size: 1.5em;
padding-bottom: 1em;
}
h3 {
font-size: 1em;
padding-bottom: 1em;
}
main {
display: grid;
grid-template-columns: 40% 60%;
margin-top: 3em;
}
header {
text-align: center;
margin: auto 2em;
}
section {
margin: auto 1em 4em 2em;
}
i {
margin-right: .5em;
}
p {
margin: .2em auto
}
hr {
border: none;
background-color: lightgray;
height: 1px;
}
h1, h2, h3 {
font-weight: 100;
margin-bottom: 0;
}
#mainLeft {
border-right: 1px solid lightgray;
}
<summary><b>专业 CSS 样式</b></summary>
```css
/* Modern Resume Styling */
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
font-size: 16px;
line-height: 1.6;
max-width: 960px;
margin: 0 auto;
padding: 20px;
color: #333;
background-color: #f9f9f9;
}
/* Header Styling */
header {
text-align: center;
margin-bottom: 3em;
padding: 2em;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
h1 {
font-size: 3em;
letter-spacing: 0.1em;
margin-bottom: 0.2em;
font-weight: 300;
}
.role {
font-size: 1.3em;
font-weight: 300;
margin: 1em 0;
}
/* Main Content Layout */
main {
display: grid;
grid-template-columns: 35% 65%;
gap: 3em;
margin-top: 3em;
background: white;
padding: 2em;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
/* Typography */
h2 {
font-size: 1.4em;
font-weight: 600;
margin-bottom: 1em;
color: #667eea;
border-bottom: 2px solid #667eea;
padding-bottom: 0.3em;
}
h3 {
font-size: 1.1em;
font-weight: 600;
margin-bottom: 0.5em;
color: #444;
}
/* Section Styling */
section {
margin-bottom: 2.5em;
}
#mainLeft {
border-right: 1px solid #e0e0e0;
padding-right: 2em;
}
/* Contact Links */
section a {
color: #667eea;
text-decoration: none;
transition: color 0.3s ease;
}
section a:hover {
color: #764ba2;
text-decoration: underline;
}
/* Icons */
i {
margin-right: 0.8em;
width: 20px;
text-align: center;
color: #667eea;
}
/* Lists */
ul {
list-style: none;
padding-left: 0;
}
li {
margin: 0.5em 0;
padding: 0.3em 0;
position: relative;
}
li:before {
content: "▸";
color: #667eea;
margin-right: 0.5em;
}
/* Work Experience */
.job, .project {
margin-bottom: 2em;
padding-bottom: 1.5em;
border-bottom: 1px solid #f0f0f0;
}
.company {
font-style: italic;
color: #666;
margin-bottom: 0.5em;
}
/* Responsive Design */
@media (max-width: 768px) {
main {
grid-template-columns: 1fr;
gap: 2em;
}
#mainLeft {
border-right: none;
border-bottom: 1px solid #e0e0e0;
padding-right: 0;
padding-bottom: 2em;
}
h1 {
font-size: 2.2em;
}
body {
padding: 10px;
}
}
/* Print Styles */
@media print {
body {
background: white;
color: black;
font-size: 12pt;
}
header {
background: none;
color: black;
box-shadow: none;
}
main {
box-shadow: none;
}
}
```
</details>
**步骤 6** 打开 `codeswing.json` 文件,将以下代码粘贴进去并保存。
**创建配置文件(`codeswing.json`**
{
```json
{
"scripts": [],
"styles": []
}
}
```
**CSS 功能解析:**
- **使用** CSS Grid 实现响应式、专业布局结构
- **采用** 渐变头部等现代配色方案
- **加入** 悬停效果和平滑过渡增强交互性
- **提供** 适用于各种设备尺寸的响应式设计
- **支持** 打印友好样式,便于生成 PDF
### 第 6 步:安装和配置扩展
扩展提升开发体验提供实时预览和改进工作流的工具。CodeSwing 扩展对网页开发尤为有用。
**安装 CodeSwing 扩展:**
1. **点击** 活动栏的扩展图标 (🧩)
2. **在市场搜索框** 输入“CodeSwing”
3. **在搜索结果中** 选中 CodeSwing 扩展
4. **点击** 蓝色“安装”按钮
![安装 CodeSwing 扩展](../../../../8-code-editor/images/install-extension.gif)
**CodeSwing 提供的功能:**
- **允许** 编辑网页时实时预览
- **无需手动刷新** 实时展示更改
- **支持** HTML、CSS 和 JavaScript 等多种文件类型
- **提供** 集成开发环境体验
**安装后即时效果:**
安装完成后,编辑器内会显示简历网站的实时预览,方便您边编辑边查看网站实际效果。
![CodeSwing 扩展实时预览](../../../../translated_images/after-codeswing-extension-pb.0ebddddcf73b5509.zh.png)
**步骤 7** 安装 `Codeswing 扩展`,以在代码区域中可视化简历网站。
**增强界面解析:**
- **分屏视图**:左侧显示代码,右侧实时预览
- **实时更新**:敲击代码立即反映更改
- **交互式预览**:允许测试链接和交互行为
- **移动端模拟**:支持响应式设计测试
点击活动栏上的 _`Extensions`_ 图标,输入 Codeswing。点击扩展栏中显示的 _蓝色安装按钮_ 或代码区域中加载的安装按钮来安装扩展。安装完成后,观察代码区域,查看项目的变化 😃。
### 第 7 步:版本控制与发布
![安装扩展](../../../../8-code-editor/images/install-extension.gif)
简历网站完成后,使用 Git 保存工作并上线。
安装扩展后,你的屏幕上会显示以下内容。
**提交更改:**
![Codeswing 扩展效果](../../../../translated_images/after-codeswing-extension-pb.0ebddddcf73b550994947a9084e35e2836c713ae13839d49628e3c764c1cfe83.zh.png)
1. **点击** 活动栏中的版本控制图标 (🌿)
2. **在“Changes”区** 查看所有新建和更改文件
3. **点击** 每个文件旁的“+”图标进行暂存
4. **填写** 描述性提交信息,如:
- “添加完整响应式简历网站”
- “实现专业样式与内容结构”
5. **点击** 勾号(✓)提交并推送改动
如果你对所做的更改感到满意,将鼠标悬停在 `Changes` 文件夹上,点击 `+` 按钮以暂存更改。
**有效提交信息示例:**
- “添加专业简历内容及样式”
- “实现移动设备兼容的响应式设计”
- “更新联系方式和项目链接”
输入提交信息 _(对项目所做更改的描述)_,然后点击 `check` 按钮提交更改。完成项目后,选择左上角的汉堡菜单图标返回 GitHub 上的仓库。
> 💡 **职业建议**:规范的提交信息有助于跟踪项目演变,展现细致认真,是雇主看重的品质
恭喜 🎉 你已经通过几个简单的步骤使用 vscode.dev 创建了你的简历网站。
**访问已发布网站:**
提交完成后,您可通过左上角汉堡菜单 (☰) 返回 GitHub 仓库。您的简历网站已进入版本管理,随时可部署或分享。
## 🚀 挑战
## 成果与后续步骤
打开一个你有权限修改的远程仓库,更新一些文件。接下来,尝试创建一个包含更改的新分支并发起 Pull Request。
**恭喜!🎉** 您已成功使用 VSCode.dev 创建了专业简历网站。您的项目展示了:
**技术技能体现:**
- **仓库管理**:创建并组织完整项目结构
- **网页开发**:用现代 HTML5 和 CSS3 构建响应式网站
- **版本控制**:结合有意义的提交实现 Git 工作流
- **工具熟练度**:高效使用 VSCode.dev 界面及扩展系统
**职业成效达成:**
- **线上形象**:可分享链接,展示您的资历
- **现代形式**:互动式替代传统 PDF 简历
- **技能展示**:切实证明您的网页开发能力
- **轻松更新**:可持续完善和自定义的基础
### 部署选项
为让雇主访问您的简历,考虑这些托管方案:
**GitHub Pages (推荐)**
1. 进入 GitHub 仓库的 Settings
2. 滚动至 "Pages" 区域
3. 选择“从分支部署”选“main”
4. 访问网址为 `https://your-username.github.io/my-resume`
**其他平台:**
- **Netlify**:自动部署,支持自定义域名
- **Vercel**:快速部署,现代托管功能
- **GitHub Codespaces**:带内置预览的开发环境
### 增强建议
继续提升技能,添加以下功能:
**技术改进:**
- **JavaScript 交互**:添加平滑滚动或互动元素
- **暗/亮主题切换**:实现主题切换功能及平滑过渡
- **联系表单**:支持潜在雇主直接联系
- **SEO 优化**:加入元标签和结构化数据提升搜索可见度
**内容升级:**
- **项目作品集**:链接 GitHub 仓库和实时演示
- **技能可视化**:制作进度条或技能评分系统
- **推荐信部分**:展示同事或导师推荐
- **博客集成**:添加博客,记录学习历程
## GitHub Copilot Agent 挑战 🚀
使用 Agent 模式完成以下挑战:
**描述:** 通过高级功能增强您的简历网站,展示专业网页开发能力和现代设计理念。
**提示:** 在现有简历网站基础上,实现:
1. 添加暗/亮主题切换,支持平滑过渡
2. 创建带动画进度条的互动技能模块
3. 实现带表单验证的联系表单
4. 添加带悬停效果和模态弹窗的项目作品集
5. 包含至少 3 篇有关学习历程的博客文章
6. 进行 SEO 优化,包含正确的元标签、结构化数据及性能优化
7. 使用 GitHub Pages 或 Netlify 部署增强版网站
8. 在 README.md 中使用截图记录所有新功能
您增强的网站应展示现代网页开发实践精通包括响应式设计、JavaScript 交互和专业部署流程。
## 挑战扩展
准备好进一步提升技能?尝试这些高级挑战:
**📱 移动优先重构:** 使用 CSS Grid 和 Flexbox 完全重建网站,采用移动优先策略
**🔍 SEO 优化:** 实现全面 SEO包括元标签、结构化数据和性能优化
**🌐 多语言支持:** 添加国际化功能支持多语言
**📊 分析集成:** 集成 Google Analytics用于跟踪访客参与度并优化内容
**🚀 性能优化:** 在所有分类中达到完美 Lighthouse 分数
## 复习与自学
阅读更多关于 [VSCode.dev](https://code.visualstudio.com/docs/editor/vscode-web?WT.mc_id=academic-0000-alfredodeza) 及其其他功能的内容。
拓展知识,参考以下资源:
**高级 VSCode.dev 功能:**
- [VSCode.dev 文档](https://code.visualstudio.com/docs/editor/vscode-web?WT.mc_id=academic-0000-alfredodeza) - 网页编辑全指南
- [GitHub Codespaces](https://docs.github.com/en/codespaces) - 云开发环境
**网页开发最佳实践:**
- **响应式设计**:深入学习 CSS Grid 和 Flexbox 用于现代布局
- **无障碍性**学习符合WCAG指南的包容性网页设计
- **性能**探索如Lighthouse等优化工具
- **SEO**:了解搜索引擎优化基础
**职业发展:**
- **作品集建设**:创建更多项目以展示多样化技能
- **开源贡献**:参与现有项目以获得协作经验
- **社交网络**:在开发者社区分享你的简历网站以获得反馈
- **持续学习**:保持对网页开发趋势和技术的更新
---
**你的下一步:** 将你的简历网站分享给朋友、家人或导师,获取反馈。利用他们的建议不断迭代和改进你的设计。记住,这个项目不仅仅是简历——它展示了你作为网页开发者的成长!
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免责声明**
本文档使用AI翻译服务[Co-op Translator](https://github.com/Azure/co-op-translator)进行翻译。尽管我们努力确保准确性,但请注意,自动翻译可能包含错误或不准确之处。应以原始语言的文档作为权威来源。对于关键信息,建议使用专业人工翻译。因使用本翻译而导致的任何误解或误读,我们概不负责。
本文件使用人工智能翻译服务 [Co-op Translator](https://github.com/Azure/co-op-translator) 进行翻译。虽然我们力求准确,但请注意,自动翻译可能包含错误或不准确之处。原始文档的母语版本应被视为权威来源。对于重要信息,建议进行专业人工翻译。对于因使用本翻译而引起的任何误解或误译,我们概不负责。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

File diff suppressed because it is too large Load Diff

@ -1,8 +1,8 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "537f02a36d73db093cbb8b9b44867645",
"translation_date": "2025-09-01T15:46:09+00:00",
"original_hash": "0aaa930f076f2d83cc872ad157f8ffd3",
"translation_date": "2026-01-06T12:03:23+00:00",
"source_file": "9-chat-project/solution/backend/python/README.md",
"language_code": "zh"
}
@ -21,34 +21,44 @@ source ./venv/bin/activate
## 安装依赖
```sh
pip install openai flask flask-cors
pip install openai fastapi uvicorn python-dotenv
```
## 运行 API
```sh
# 方法1直接执行
python api.py
# 方法2使用uvicorn
uvicorn api:app --host 0.0.0.0 --port 5000 --reload
```
## 测试 API
访问交互式 API 文档:`http://localhost:5000/docs`
## 运行前端
确保你位于前端文件夹中
确保你处于 frontend 文件夹内
找到 *app.js*,将 `BASE_URL` 更改为你的后端 URL
运行前端
运行
```
npx http-server -p 8000
```
尝试在聊天框中输入消息,你应该会看到一个响应(前提是你在 Codespace 中运行或者已经设置了访问令牌)。
尝试在聊天中输入消息,你应该能看到响应(前提是在 Codespace 中运行或已设置访问令牌)。
## 设置访问令牌(如果你不是在 Codespace 中运行)
请参阅 [设置 PAT](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens)
参见 [设置 PAT](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens)
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免责声明**
本文档使用AI翻译服务 [Co-op Translator](https://github.com/Azure/co-op-translator) 进行翻译。尽管我们努力确保翻译的准确性,但请注意,自动翻译可能包含错误或不准确之处。原始语言的文档应被视为权威来源。对于关键信息,建议使用专业人工翻译。我们不对因使用此翻译而产生的任何误解或误读承担责任。
本文件使用AI翻译服务[Co-op Translator](https://github.com/Azure/co-op-translator)进行翻译。虽然我们力求准确,但请注意,自动翻译可能包含错误或不准确之处。原文的母语版本应被视为权威来源。对于重要信息,建议使用专业人工翻译。对于因使用本翻译而产生的任何误解或误释,我们概不负责。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,8 +1,8 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "caf2ca695e9d259153d24a5cf3e07ef5",
"translation_date": "2025-10-11T10:49:34+00:00",
"original_hash": "fea3a0fceb8ad86fd640c09cf63a2aac",
"translation_date": "2026-01-06T11:01:36+00:00",
"source_file": "README.md",
"language_code": "zh"
}
@ -17,220 +17,262 @@ CO_OP_TRANSLATOR_METADATA:
[![GitHub forks](https://img.shields.io/github/forks/microsoft/Web-Dev-For-Beginners.svg?style=social&label=Fork&maxAge=2592000)](https://GitHub.com/microsoft/Web-Dev-For-Beginners/network/)
[![GitHub stars](https://img.shields.io/github/stars/microsoft/Web-Dev-For-Beginners.svg?style=social&label=Star&maxAge=2592000)](https://GitHub.com/microsoft/Web-Dev-For-Beginners/stargazers/)
[![](https://dcbadge.vercel.app/api/server/ByRwuEEgH4)](https://discord.gg/zxKYvhSnVp?WT.mc_id=academic-000002-leestott)
[![Microsoft Foundry Discord](https://dcbadge.limes.pink/api/server/nTYy5BXMWG)](https://discord.gg/nTYy5BXMWG)
# 初学者的网页开发课程 - 一套完整的课程
# 面向初学者的网页开发课程
通过微软云倡导者提供的12周综合课程学习网页开发的基础知识。课程包含24节课涵盖JavaScript、CSS和HTML通过实践项目如生态瓶、浏览器扩展和太空游戏进行学习。参与测验、讨论和实践任务提升技能并优化知识保留效果。今天就开始你的编程之旅吧!
通过微软云倡导者为期12周的综合课程学习网页开发的基础知识。24节课中每节课都通过实践项目如生态瓶、浏览器扩展程序和太空游戏深入讲解JavaScript、CSS和HTML。参与测验、讨论和实操任务。借助我们高效的项目驱动教学法提高你的技能水平并优化知识掌握。马上开始你的编码之旅吧!
加入Azure AI Foundry Discord社区
加入 Azure AI Foundry Discord 社区
[![Microsoft Azure AI Foundry Discord](https://dcbadge.limes.pink/api/server/ByRwuEEgH4)](https://discord.com/invite/ByRwuEEgH4)
[![Microsoft Foundry Discord](https://dcbadge.limes.pink/api/server/nTYy5BXMWG)](https://discord.gg/nTYy5BXMWG)
按照以下步骤开始使用这些资源:
1. **Fork仓库**:点击 [![GitHub forks](https://img.shields.io/github/forks/microsoft/Web-Dev-For-beginners.svg?style=social&label=Fork)](https://GitHub.com/microsoft/Web-Dev-For-Beginners/fork)
2. **克隆仓库** `git clone https://github.com/microsoft/Web-Dev-For-Beginners.git`
3. [**加入Azure AI Foundry Discord与专家和其他开发者交流**](https://discord.com/invite/ByRwuEEgH4)
1. **Fork 仓库**:点击 [![GitHub forks](https://img.shields.io/github/forks/microsoft/Web-Dev-For-beginners.svg?style=social&label=Fork)](https://GitHub.com/microsoft/Web-Dev-For-Beginners/fork)
2. **克隆仓库**`git clone https://github.com/microsoft/Web-Dev-For-Beginners.git`
3. [**加入 Azure AI Foundry Discord与专家和开发者交流**](https://discord.com/invite/ByRwuEEgH4)
### 🌐 多语言支持
#### 通过GitHub Action支持自动且始终保持最新)
#### 通过 GitHub Actions 支持(自动且始终保持最新)
<!-- CO-OP TRANSLATOR LANGUAGES TABLE START -->
[阿拉伯语](../ar/README.md) | [孟加拉语](../bn/README.md) | [保加利亚语](../bg/README.md) | [缅甸语](../my/README.md) | [中文(简体)](./README.md) | [中文(繁体,香港)](../hk/README.md) | [中文(繁体,澳门)](../mo/README.md) | [中文(繁体,台湾)](../tw/README.md) | [克罗地亚语](../hr/README.md) | [捷克语](../cs/README.md) | [丹麦语](../da/README.md) | [荷兰语](../nl/README.md) | [爱沙尼亚语](../et/README.md) | [芬兰语](../fi/README.md) | [法语](../fr/README.md) | [德语](../de/README.md) | [希腊语](../el/README.md) | [希伯来语](../he/README.md) | [印地语](../hi/README.md) | [匈牙利语](../hu/README.md) | [印尼语](../id/README.md) | [意大利语](../it/README.md) | [日语](../ja/README.md) | [韩语](../ko/README.md) | [立陶宛语](../lt/README.md) | [马来语](../ms/README.md) | [马拉地语](../mr/README.md) | [尼泊尔语](../ne/README.md) | [挪威语](../no/README.md) | [波斯语](../fa/README.md) | [波兰语](../pl/README.md) | [葡萄牙语(巴西)](../br/README.md) | [葡萄牙语(葡萄牙)](../pt/README.md) | [旁遮普语](../pa/README.md) | [罗马尼亚语](../ro/README.md) | [俄语](../ru/README.md) | [塞尔维亚语(西里尔字母)](../sr/README.md) | [斯洛伐克语](../sk/README.md) | [斯洛文尼亚语](../sl/README.md) | [西班牙语](../es/README.md) | [斯瓦希里语](../sw/README.md) | [瑞典语](../sv/README.md) | [他加禄语](../tl/README.md) | [泰米尔语](../ta/README.md) | [泰语](../th/README.md) | [土耳其语](../tr/README.md) | [乌克兰语](../uk/README.md) | [乌尔都语](../ur/README.md) | [越南语](../vi/README.md)
[阿拉伯语](../ar/README.md) | [孟加拉语](../bn/README.md) | [保加利亚语](../bg/README.md) | [缅甸语 (Myanmar)](../my/README.md) | [简体中文](./README.md) | [繁体中文 (香港)](../hk/README.md) | [繁体中文 (澳门)](../mo/README.md) | [繁体中文 (台湾)](../tw/README.md) | [克罗地亚语](../hr/README.md) | [捷克语](../cs/README.md) | [丹麦语](../da/README.md) | [荷兰语](../nl/README.md) | [爱沙尼亚语](../et/README.md) | [芬兰语](../fi/README.md) | [法语](../fr/README.md) | [德语](../de/README.md) | [希腊语](../el/README.md) | [希伯来语](../he/README.md) | [印地语](../hi/README.md) | [匈牙利语](../hu/README.md) | [印尼语](../id/README.md) | [意大利语](../it/README.md) | [日语](../ja/README.md) | [卡纳达语](../kn/README.md) | [韩语](../ko/README.md) | [立陶宛语](../lt/README.md) | [马来语](../ms/README.md) | [马拉雅拉姆语](../ml/README.md) | [马拉地语](../mr/README.md) | [尼泊尔语](../ne/README.md) | [尼日利亚皮钦语](../pcm/README.md) | [挪威语](../no/README.md) | [波斯语 (法尔西语)](../fa/README.md) | [波兰语](../pl/README.md) | [巴西葡萄牙语](../br/README.md) | [葡萄牙语 (葡萄牙)](../pt/README.md) | [旁遮普语 (古鲁穆奇文)](../pa/README.md) | [罗马尼亚语](../ro/README.md) | [俄语](../ru/README.md) | [塞尔维亚语 (西里尔字母)](../sr/README.md) | [斯洛伐克语](../sk/README.md) | [斯洛文尼亚语](../sl/README.md) | [西班牙语](../es/README.md) | [斯瓦希里语](../sw/README.md) | [瑞典语](../sv/README.md) | [他加禄语 (菲律宾语)](../tl/README.md) | [泰米尔语](../ta/README.md) | [泰卢固语](../te/README.md) | [泰语](../th/README.md) | [土耳其语](../tr/README.md) | [乌克兰语](../uk/README.md) | [乌尔都语](../ur/README.md) | [越南语](../vi/README.md)
> **想本地克隆?**
> 本仓库包含50多种语言的翻译显著增加了下载大小。若想不带翻译克隆请使用稀疏检出
> ```bash
> git clone --filter=blob:none --sparse https://github.com/microsoft/Web-Dev-For-Beginners.git
> cd Web-Dev-For-Beginners
> git sparse-checkout set --no-cone '/*' '!translations' '!translated_images'
> ```
> 这样您就可以高速下载到完成课程所需的所有内容。
<!-- CO-OP TRANSLATOR LANGUAGES TABLE END -->
**如果希望支持更多语言,支持的语言列表请点击[这里](https://github.com/Azure/co-op-translator/blob/main/getting_started/supported-languages.md)**
**如果您希望支持其他翻译语言,列表见 [这里](https://github.com/Azure/co-op-translator/blob/main/getting_started/supported-languages.md)**
[![在Visual Studio Code中打开](https://img.shields.io/static/v1?logo=visualstudiocode&label=&message=Open%20in%20Visual%20Studio%20Code&labelColor=2c2c32&color=007acc&logoColor=007acc)](https://open.vscode.dev/microsoft/Web-Dev-For-Beginners)
[![用 Visual Studio Code 打开](https://img.shields.io/static/v1?logo=visualstudiocode&label=&message=Open%20in%20Visual%20Studio%20Code&labelColor=2c2c32&color=007acc&logoColor=007acc)](https://open.vscode.dev/microsoft/Web-Dev-For-Beginners)
#### 🧑‍🎓 _你是学生吗_
访问 [**学生中心页面**](https://docs.microsoft.com/learn/student-hub/?WT.mc_id=academic-77807-sagibbon),这里有初学者资源、学生礼包,甚至可以获得免费证书兑换券。这个页面值得收藏,并定期查看,因为我们每月都会更新内容。
访问 [**学生中心页面**](https://docs.microsoft.com/learn/student-hub/?WT.mc_id=academic-77807-sagibbon),这里有初学者资源、学生包,甚至获取免费证书凭证的方法。请收藏此页并定期查看,我们每月更换内容。
### 📣 通告 - 新增 GitHub Copilot Agent 模式挑战任务!
### 📣 公告 - _使用生成式AI构建新项目_
新增挑战,查看大多数章节中的 “GitHub Copilot Agent Challenge 🚀”。这是使用 GitHub Copilot 和 Agent 模式完成的新挑战。如果你以前没用过 Agent 模式,它不仅能生成文本,还能创建和编辑文件、运行命令等。
刚刚添加了新的AI助手项目快来看看 [项目](./09-chat-project/README.md)
### 📣 通告 - _新增使用生成式 AI 构建的项目_
### 📣 公告 - _新课程_ 关于JavaScript的生成式AI课程已发布
新增 AI 助手项目,快来看看 [项目](./9-chat-project/README.md)
不要错过我们的新生成式AI课程
### 📣 通告 - _面向 JavaScript 的生成式 AI 新课程发布_
不要错过我们的生成式 AI 新课程!
访问 [https://aka.ms/genai-js-course](https://aka.ms/genai-js-course) 开始学习!
![背景](../../translated_images/background.148a8d43afde57303419a663f50daf586681bc2fabf833f66ef6954073983c66.zh.png)
![背景](../../translated_images/background.148a8d43afde5730.zh.png)
- 课程内容涵盖从基础到RAG的所有内容
- 使用生成式AI和我们的配套应用与历史人物互动。
- 有趣且引人入胜的叙事,你将进行时间旅行
- 涵盖从基础到 RAG 的课程
- 使用生成式 AI 和我们的配套应用与历史人物互动。
- 趣味且引人入胜的叙事,你将体验时空穿越
![角色](../../translated_images/character.5c0dd8e067ffd693c16e2c5b7412ab075a2215ce31f998305639fa3a05e14fbe.zh.png)
![角色](../../translated_images/character.5c0dd8e067ffd693.zh.png)
每节课都包括一个任务、知识检查和挑战,帮助你学习以下主题:
- 提示提示工程
每节课均包含作业、知识检测和挑战,引导你学习以下主题:
- 提示词及提示工程
- 文本和图像应用生成
- 搜索应用
访问 [https://aka.ms/genai-js-course](https://aka.ms/genai-js-course) 开始学习!
访问 [https://aka.ms/genai-js-course](../../[https:/aka.ms/genai-js-course) 开始吧!
## 🌱 开始学习
> **教师们**,我们[提供了一些建议](for-teachers.md)供您使用这套课程。欢迎在我们的[讨论论坛](https://github.com/microsoft/Web-Dev-For-Beginners/discussions/categories/teacher-corner)中提供反馈!
## 🌱 开始入门
**[学习者](https://aka.ms/student-page/?WT.mc_id=academic-77807-sagibbon)**,每节课从课前测验开始,阅读课程材料,完成各种活动,并通过课后测验检查你的理解。
> **教师们**,我们提供了 [一些建议](for-teachers.md) 帮助您使用本课程。欢迎在我们的 [讨论区](https://github.com/microsoft/Web-Dev-For-Beginners/discussions/categories/teacher-corner) 反馈意见!
为了提升学习体验,与同伴一起完成项目!我们鼓励在[讨论论坛](https://github.com/microsoft/Web-Dev-For-Beginners/discussions)中进行讨论,我们的版主团队会回答你的问题
**[学习者](https://aka.ms/student-page/?WT.mc_id=academic-77807-sagibbon)**,每节课建议从课前测验开始,阅读讲义内容,完成各种活动,并通过课后测验检验理解
为了进一步学习,我们强烈推荐探索 [Microsoft Learn](https://learn.microsoft.com/users/wirelesslife/collections/p1ddcy5jwy0jkm?WT.mc_id=academic-77807-sagibbon) 以获取更多学习材料
为了提升学习体验,建议与你的同伴共同完成项目!欢迎在我们的[讨论区](https://github.com/microsoft/Web-Dev-For-Beginners/discussions)积极发言,我们的版主团队会解答你的问题
### 📋 设置你的开发环境
如果想深入学习,我们强烈推荐访问 [Microsoft Learn](https://learn.microsoft.com/users/wirelesslife/collections/p1ddcy5jwy0jkm?WT.mc_id=academic-77807-sagibbon) 获取更多学习资料。
这套课程已经准备好了开发环境!你可以选择在 [Codespace](https://github.com/features/codespaces/)(基于浏览器,无需安装的环境)中运行课程,或者在本地电脑上使用文本编辑器如 [Visual Studio Code](https://code.visualstudio.com/?WT.mc_id=academic-77807-sagibbon)。
### 📋 配置开发环境
本课程已准备好开发环境!开始时,你可选择在 [Codespace](https://github.com/features/codespaces/) 环境运行(基于浏览器,无需安装),或者本地使用文本编辑器如 [Visual Studio Code](https://code.visualstudio.com/?WT.mc_id=academic-77807-sagibbon) 编写代码。
#### 创建你的仓库
为了方便保存你的学习成果,建议你创建自己的仓库副本。点击页面顶部的 **Use this template** 按钮即可。这将在你的GitHub账户中创建一个包含课程副本的新仓库。
建议你创建本仓库的副本以便保存你的作业。点击页面顶部的 **Use this template** 按钮GitHub 会在你的账号中创建一个带有课程副本的新仓库。
按照以下步骤操作
1. **Fork仓库**:点击页面右上角的 "Fork" 按钮。
2. **克隆仓库** `git clone https://github.com/microsoft/Web-Dev-For-Beginners.git`
操作步骤
1. **Fork 仓库**:点击本页右上角的 “Fork” 按钮。
2. **克隆仓库**`git clone https://github.com/microsoft/Web-Dev-For-Beginners.git`
#### 在Codespace中运行课程
#### 在 Codespace 中运行课程
在你创建的仓库副本中,点击 **Code** 按钮并选择 **Open with Codespaces**。这将为你创建一个新的Codespace以供使用。
在你创建的仓库副本中,点击 **Code** 按钮,选择 **Open with Codespaces**。这将创建一个新的 Codespace 供你使用。
![Codespace](../../translated_images/createcodespace.0238bbf4d7a8d955fa8fa7f7b6602a3cb6499a24708fbee589f83211c5a613b7.zh.png)
![Codespace](../../translated_images/createcodespace.0238bbf4d7a8d955.zh.png)
#### 在本地电脑上运行课程
#### 在本地计算机上运行课程
要在本地电脑上运行课程,你需要一个文本编辑器、浏览器和命令行工具。我们的第一节课 [编程语言和工具介绍](../../1-getting-started-lessons/1-intro-to-programming-languages) 将指导你选择适合自己的工具
要在本地计算机上运行本课程,你需要文本编辑器、浏览器和命令行工具。首节课[编程语言与工具简介](../../1-getting-started-lessons/1-intro-to-programming-languages)会介绍这些工具的多种选择,帮助你选出最适合你的
我们推荐使用 [Visual Studio Code](https://code.visualstudio.com/?WT.mc_id=academic-77807-sagibbon) 作为编辑器,它还内置了 [终端](https://code.visualstudio.com/docs/terminal/basics/?WT.mc_id=academic-77807-sagibbon)。你可以在[这里](https://code.visualstudio.com/?WT.mc_id=academic-77807-sagibbon)下载Visual Studio Code
我们推荐使用 [Visual Studio Code](https://code.visualstudio.com/?WT.mc_id=academic-77807-sagibbon) 作为编辑器,它自带集成 [终端](https://code.visualstudio.com/docs/terminal/basics/?WT.mc_id=academic-77807-sagibbon)。你可以从这里下载 Visual Studio Code [链接](https://code.visualstudio.com/?WT.mc_id=academic-77807-sagibbon)。
1. 将你的仓库克隆到电脑上。点击 **Code** 按钮并复制URL
1. 将你的仓库克隆到本地。点击 **Code** 按钮,复制仓库 URL
[CodeSpace](./images/createcodespace.png)
然后,在 [Visual Studio Code](https://code.visualstudio.com/?WT.mc_id=academic-77807-sagibbon) 的 [终端](https://code.visualstudio.com/docs/terminal/basics/?WT.mc_id=academic-77807-sagibbon) 中运行以下命令,将 `<your-repository-url>` 替换为你刚刚复制的URL
然后,在 [Visual Studio Code](https://code.visualstudio.com/?WT.mc_id=academic-77807-sagibbon) 中打开 [Terminal](https://code.visualstudio.com/docs/terminal/basics/?WT.mc_id=academic-77807-sagibbon) 并运行以下命令,将 `<your-repository-url>` 替换为你刚才复制的 URL
```bash
git clone <your-repository-url>
```
2. 在Visual Studio Code中打开文件夹。点击 **File** > **Open Folder**,选择你刚刚克隆的文件夹。
2. 在 Visual Studio Code 中打开该文件夹。你可以通过点击 **文件** > **打开文件夹** 并选择你刚克隆的文件夹来完成此操作。
> 推荐的Visual Studio Code扩展
> 推荐的 Visual Studio Code 扩展:
>
> * [Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer&WT.mc_id=academic-77807-sagibbon) - 在Visual Studio Code中预览HTML页面
> * [Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer&WT.mc_id=academic-77807-sagibbon) - 用于 Visual Studio Code 中预览 HTML 页面
> * [Copilot](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot&WT.mc_id=academic-77807-sagibbon) - 帮助你更快地编写代码
## 📂 每节课包括:
## 📂 每节课内容包括:
- 可选的手绘笔记
- 可选的手绘草图笔记
- 可选的补充视频
- 课前热身测验
- 书面课程
- 针对项目型课程,提供逐步指导如何构建项目
- 知识检查
- 挑战任务
- 补充阅读
- 作业
- [课后测验](https://ff-quizzes.netlify.app/web/)
> **关于测验的说明**:所有测验都包含在 Quiz-app 文件夹中,共有 48 个测验,每个测验包含三个问题。测验可在 [此处](https://ff-quizzes.netlify.app/web/) 获取。测验应用可以在本地运行或部署到 Azure请按照 `quiz-app` 文件夹中的说明操作。
## 🗃️ 课程
| | 项目名称 | 教授的概念 | 学习目标 | 课程链接 | 作者 |
| :-: | :--------------------------------------------------: | :----------------------------------------------------------------: | ----------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------: | :-------------------: |
| 01 | 入门课程 | 编程简介及开发工具 | 学习大多数编程语言的基本原理以及帮助专业开发者完成工作的相关软件 | [编程语言简介及开发工具](./1-getting-started-lessons/1-intro-to-programming-languages/README.md) | Jasmine |
| 02 | 入门课程 | GitHub 基础知识,包括团队协作 | 学习如何在项目中使用 GitHub以及如何与他人协作开发代码库 | [GitHub 简介](./1-getting-started-lessons/2-github-basics/README.md) | Floor |
| 03 | 入门课程 | 无障碍访问 | 学习网页无障碍访问的基础知识 | [无障碍访问基础](./1-getting-started-lessons/3-accessibility/README.md) | Christopher |
| 04 | JS 基础 | JavaScript 数据类型 | JavaScript 数据类型的基础知识 | [数据类型](./2-js-basics/1-data-types/README.md) | Jasmine |
| 05 | JS 基础 | 函数和方法 | 学习如何使用函数和方法来管理应用程序的逻辑流 | [函数和方法](./2-js-basics/2-functions-methods/README.md) | Jasmine 和 Christopher |
| 06 | JS 基础 | 使用 JS 做决策 | 学习如何使用决策方法在代码中创建条件 | [做决策](./2-js-basics/3-making-decisions/README.md) | Jasmine |
| 07 | JS 基础 | 数组和循环 | 使用 JavaScript 中的数组和循环处理数据 | [数组和循环](./2-js-basics/4-arrays-loops/README.md) | Jasmine |
| 08 | [生态瓶](./3-terrarium/solution/README.md) | HTML 实践 | 构建 HTML 创建在线生态瓶,重点是布局构建 | [HTML 简介](./3-terrarium/1-intro-to-html/README.md) | Jen |
| 09 | [生态瓶](./3-terrarium/solution/README.md) | CSS 实践 | 构建 CSS 样式化在线生态瓶,重点是 CSS 基础知识,包括使页面响应式 | [CSS 简介](./3-terrarium/2-intro-to-css/README.md) | Jen |
| 10 | [生态瓶](./3-terrarium/solution/README.md) | JavaScript 闭包和 DOM 操作 | 构建 JavaScript 使生态瓶具备拖放界面功能,重点是闭包和 DOM 操作 | [JavaScript 闭包和 DOM 操作](./3-terrarium/3-intro-to-DOM-and-closures/README.md) | Jen |
| 11 | [打字游戏](./4-typing-game/solution/README.md) | 构建打字游戏 | 学习如何使用键盘事件驱动 JavaScript 应用的逻辑 | [事件驱动编程](./4-typing-game/typing-game/README.md) | Christopher |
| 12 | [绿色浏览器扩展](./5-browser-extension/solution/README.md) | 浏览器相关知识 | 学习浏览器的工作原理、历史,以及如何搭建浏览器扩展的初始元素 | [关于浏览器](./5-browser-extension/1-about-browsers/README.md) | Jen |
| 13 | [绿色浏览器扩展](./5-browser-extension/solution/README.md) | 构建表单、调用 API 并将变量存储在本地存储中 | 构建浏览器扩展的 JavaScript 元素,通过存储在本地存储中的变量调用 API | [API、表单和本地存储](./5-browser-extension/2-forms-browsers-local-storage/README.md) | Jen |
| 14 | [绿色浏览器扩展](./5-browser-extension/solution/README.md) | 浏览器后台进程和网页性能 | 使用浏览器的后台进程管理扩展图标;学习网页性能及一些优化方法 | [后台任务和性能](./5-browser-extension/3-background-tasks-and-performance/README.md) | Jen |
| 15 | [太空游戏](./6-space-game/solution/README.md) | 更高级的 JavaScript 游戏开发 | 学习使用类和组合进行继承以及发布/订阅模式,为构建游戏做准备 | [高级游戏开发简介](./6-space-game/1-introduction/README.md) | Chris |
| 16 | [太空游戏](./6-space-game/solution/README.md) | 绘制到画布 | 学习 Canvas API用于将元素绘制到屏幕 | [绘制到画布](./6-space-game/2-drawing-to-canvas/README.md) | Chris |
| 17 | [太空游戏](./6-space-game/solution/README.md) | 在屏幕上移动元素 | 探索如何使用笛卡尔坐标和 Canvas API 使元素获得运动 | [移动元素](./6-space-game/3-moving-elements-around/README.md) | Chris |
| 18 | [太空游戏](./6-space-game/solution/README.md) | 碰撞检测 | 使元素碰撞并对彼此做出反应,使用按键提供冷却功能以确保游戏性能 | [碰撞检测](./6-space-game/4-collision-detection/README.md) | Chris |
| 19 | [太空游戏](./6-space-game/solution/README.md) | 计分 | 根据游戏状态和表现进行数学计算 | [计分](./6-space-game/5-keeping-score/README.md) | Chris |
| 20 | [太空游戏](./6-space-game/solution/README.md) | 结束和重新开始游戏 | 学习如何结束和重新开始游戏,包括清理资源和重置变量值 | [结束条件](./6-space-game/6-end-condition/README.md) | Chris |
| 21 | [银行应用](./7-bank-project/solution/README.md) | HTML 模板和网页应用中的路由 | 学习如何使用路由和 HTML 模板创建多页面网站的架构 | [HTML 模板和路由](./7-bank-project/1-template-route/README.md) | Yohan |
| 22 | [银行应用](./7-bank-project/solution/README.md) | 构建登录和注册表单 | 学习如何构建表单并处理验证逻辑 | [表单](./7-bank-project/2-forms/README.md) | Yohan |
| 23 | [银行应用](./7-bank-project/solution/README.md) | 数据获取和使用方法 | 学习数据如何在应用中流动,如何获取、存储和处理数据 | [数据](./7-bank-project/3-data/README.md) | Yohan |
| 24 | [银行应用](./7-bank-project/solution/README.md) | 状态管理概念 | 学习应用如何保持状态以及如何通过编程管理状态 | [状态管理](./7-bank-project/4-state-management/README.md) | Yohan |
| 25 | [浏览器/VS Code 编辑器](../../8-code-editor) | 使用 VS Code | 学习如何使用代码编辑器 | [使用 VS Code 编辑器](./8-code-editor/1-using-a-code-editor/README.md) | Chris |
| 26 | [AI 助手](./9-chat-project/README.md) | 使用 AI | 学习如何构建自己的 AI 助手 | [AI 助手项目](./9-chat-project/README.md) | Chris |
## 🏫 教学法
我们的课程设计基于两个关键教学原则:
* 项目式学习
* 频繁测验
该课程教授 JavaScript、HTML 和 CSS 的基础知识,以及当今网页开发者使用的最新工具和技术。学生将通过构建打字游戏、虚拟生态瓶、环保浏览器扩展、太空入侵者风格游戏以及企业银行应用,获得实践经验。课程结束时,学生将对网页开发有一个扎实的理解。
> 🎓 您可以在 Microsoft Learn 上以 [学习路径](https://docs.microsoft.com/learn/paths/web-development-101/?WT.mc_id=academic-77807-sagibbon) 的形式学习本课程的前几节课!
通过确保内容与项目相结合,学习过程变得更加有趣,学生对概念的记忆也会得到增强。我们还编写了几节 JavaScript 基础课程来介绍概念,并配有 "[JavaScript 初学者系列](https://channel9.msdn.com/Series/Beginners-Series-to-JavaScript/?WT.mc_id=academic-77807-sagibbon)" 视频教程中的视频,其中一些作者也参与了本课程的编写。
此外,课前的低压力测验可以让学生专注于学习主题,而课后的第二次测验可以进一步巩固记忆。本课程设计灵活有趣,可以完整学习,也可以部分学习。项目从小开始,到 12 周周期结束时逐渐变得复杂。
虽然我们有意避免引入 JavaScript 框架,以专注于成为网页开发者所需的基本技能,但完成本课程后的一个良好下一步是通过另一系列视频学习 Node.js"[Node.js 初学者系列](https://channel9.msdn.com/Series/Beginners-Series-to-Nodejs/?WT.mc_id=academic-77807-sagibbon)"。
> 请访问我们的 [行为准则](CODE_OF_CONDUCT.md) 和 [贡献指南](CONTRIBUTING.md)。我们欢迎您的建设性反馈!
- 文字课程内容
- 针对项目课程,有逐步的项目构建指南
- 知识点检查
- 挑战题
- 补充阅读资料
- 任务
- [课后测验](https://ff-quizzes.netlify.app/web/)
> **关于测验的说明**:所有测验都包含在 Quiz-app 文件夹中,共有 48 个测验,每个测验包含三个问题。它们可从[此处](https://ff-quizzes.netlify.app/web/)访问,测验应用程序可以在本地运行或部署到 Azure请按照 `quiz-app` 文件夹中的说明操作。
## 🗃️ 课程列表
| | 项目名称 | 教授的概念 | 学习目标 | 关联课程 | 作者 |
| :-: | :--------------------------------------------------: | :----------------------------------------------------------------: | ------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------: | :-------------------: |
| 01 | 起步篇 | 编程介绍及开发工具基础 | 学习大多数编程语言的基本原理以及辅助专业开发者完成工作的软件工具 | [编程语言与开发工具介绍](./1-getting-started-lessons/1-intro-to-programming-languages/README.md) | Jasmine |
| 02 | 起步篇 | GitHub 基础,包括团队协作 | 学习如何在项目中使用 GitHub如何与他人协作管理代码库 | [GitHub 入门](./1-getting-started-lessons/2-github-basics/README.md) | Floor |
| 03 | 起步篇 | 无障碍访问 | 学习网页无障碍的基础知识 | [无障碍基础](./1-getting-started-lessons/3-accessibility/README.md) | Christopher |
| 04 | JS基础 | JavaScript 数据类型 | 掌握 JavaScript 数据类型的基础 | [数据类型](./2-js-basics/1-data-types/README.md) | Jasmine |
| 05 | JS基础 | 函数与方法 | 学习函数和方法,用于管理应用的逻辑流程 | [函数与方法](./2-js-basics/2-functions-methods/README.md) | Jasmine 和 Christopher |
| 06 | JS基础 | JavaScript 中的决策制作 | 学习如何使用决策结构为代码创建条件 | [制作决策](./2-js-basics/3-making-decisions/README.md) | Jasmine |
| 07 | JS基础 | 数组和循环 | 在 JavaScript 中使用数组和循环操作数据 | [数组和循环](./2-js-basics/4-arrays-loops/README.md) | Jasmine |
| 08 | [生态瓶](./3-terrarium/solution/README.md) | HTML 实践 | 构建用于在线生态瓶的 HTML重点是布局搭建 | [HTML 入门](./3-terrarium/1-intro-to-html/README.md) | Jen |
| 09 | [生态瓶](./3-terrarium/solution/README.md) | CSS 实践 | 构建用于在线生态瓶的 CSS 样式,聚焦 CSS 基础,包括让页面响应式 | [CSS 入门](./3-terrarium/2-intro-to-css/README.md) | Jen |
| 10 | [生态瓶](./3-terrarium/solution/README.md) | JavaScript 闭包与 DOM 操作 | 构建使生态瓶具备拖拽功能的 JavaScript重点讲解闭包和 DOM 操作 | [JavaScript 闭包与 DOM 操作](./3-terrarium/3-intro-to-DOM-and-closures/README.md) | Jen |
| 11 | [打字游戏](./4-typing-game/solution/README.md) | 构建打字游戏 | 学习如何使用键盘事件驱动 JavaScript 应用的逻辑 | [事件驱动编程](./4-typing-game/typing-game/README.md) | Christopher |
| 12 | [绿色浏览器扩展](./5-browser-extension/solution/README.md) | 浏览器相关知识 | 学习浏览器的工作原理、历史以及如何搭建第一个浏览器扩展界面 | [关于浏览器](./5-browser-extension/1-about-browsers/README.md) | Jen |
| 13 | [绿色浏览器扩展](./5-browser-extension/solution/README.md) | 构建表单、调用 API 及本地存储变量 | 构建浏览器扩展的 JavaScript 元素,使用本地存储中的变量调用 API | [API、表单与本地存储](./5-browser-extension/2-forms-browsers-local-storage/README.md) | Jen |
| 14 | [绿色浏览器扩展](./5-browser-extension/solution/README.md) | 浏览器中的后台进程与网页性能优化 | 利用浏览器后台进程管理扩展图标,了解网页性能以及提升性能的优化方法 | [后台任务与性能](./5-browser-extension/3-background-tasks-and-performance/README.md) | Jen |
| 15 | [太空游戏](./6-space-game/solution/README.md) | 更高级的 JavaScript 游戏开发 | 学习继承(包括类继承与组合)与发布/订阅模式,为构建游戏做准备 | [高级游戏开发介绍](./6-space-game/1-introduction/README.md) | Chris |
| 16 | [太空游戏](./6-space-game/solution/README.md) | 画布绘制 | 学习 Canvas API用于在屏幕上绘制元素 | [画布绘制](./6-space-game/2-drawing-to-canvas/README.md) | Chris |
| 17 | [太空游戏](./6-space-game/solution/README.md) | 在屏幕上移动元素 | 了解如何使用笛卡尔坐标和 Canvas API 为元素赋予移动效果 | [元素移动](./6-space-game/3-moving-elements-around/README.md) | Chris |
| 18 | [太空游戏](./6-space-game/solution/README.md) | 碰撞检测 | 使用按键驱动使元素碰撞并互相响应,提供冷却函数确保游戏性能 | [碰撞检测](./6-space-game/4-collision-detection/README.md) | Chris |
| 19 | [太空游戏](./6-space-game/solution/README.md) | 计分系统 | 根据游戏状态和表现执行数学计算 | [计分系统](./6-space-game/5-keeping-score/README.md) | Chris |
| 20 | [太空游戏](./6-space-game/solution/README.md) | 游戏结束与重启 | 学习游戏的结束和重启,包括资源清理和变量值重置 | [结束条件](./6-space-game/6-end-condition/README.md) | Chris |
| 21 | [银行应用](./7-bank-project/solution/README.md) | 网页应用中的 HTML 模板和路由 | 学习如何创建多页面网站架构的骨架,使用路由和 HTML 模板 | [HTML 模板与路由](./7-bank-project/1-template-route/README.md) | Yohan |
| 22 | [银行应用](./7-bank-project/solution/README.md) | 构建登录和注册表单 | 学习构建表单及处理验证流程 | [表单](./7-bank-project/2-forms/README.md) | Yohan |
| 23 | [银行应用](./7-bank-project/solution/README.md) | 获取和使用数据的方法 | 了解数据在应用中的流入与流出,如何获取、存储及处理数据 | [数据](./7-bank-project/3-data/README.md) | Yohan |
| 24 | [银行应用](./7-bank-project/solution/README.md) | 状态管理概念 | 学习应用如何保持状态以及如何以编程方式管理状态 | [状态管理](./7-bank-project/4-state-management/README.md) | Yohan |
| 25 | [浏览器/VScode 代码](../../8-code-editor) | 使用 VScode | 学习如何使用代码编辑器| [使用 VScode 代码编辑器](./8-code-editor/1-using-a-code-editor/README.md) | Chris |
| 26 | [AI 助手](./9-chat-project/README.md) | 与 AI 交互 | 学习如何构建自己的 AI 助手 | [AI 助手项目](./9-chat-project/README.md) | Chris |
## 🏫 教学理念
我们的课程设计基于两个核心教学原则:
* 项目驱动学习
* 频繁测验
本课程教授 JavaScript、HTML 和 CSS 的基础知识,以及当今网页开发者使用的最新工具与技术。 学生将有机会通过开发打字游戏、虚拟生态瓶、环保浏览器扩展、太空入侵者风格游戏和企业银行应用,积累实践经验。 在课程结束时,学生将对网页开发有扎实的理解。
> 🎓 你可以将本课程的前几节课作为 Microsoft Learn 上的[学习路径](https://docs.microsoft.com/learn/paths/web-development-101/?WT.mc_id=academic-77807-sagibbon)学习!
通过确保内容与项目相匹配,学习过程对学生来说更加有趣,知识点的记忆也会增强。我们还编写了几节 JavaScript 基础课程来介绍概念,搭配来自 “[Beginners Series to: JavaScript](https://channel9.msdn.com/Series/Beginners-Series-to-JavaScript/?WT.mc_id=academic-77807-sagibbon)" 视频教程系列的视频,其中一些作者也参与了本课程的编写。
此外课堂前的低风险测验能引导学生集中学习某课题课堂后的第二次测验则帮助进一步巩固记忆。本课程设计灵活且有趣可以全部学习也可选择部分学习。项目从小型开始12 周周期结束时逐渐变得复杂。
虽然我们有意避免引入 JavaScript 框架,以便专注于在学习框架之前网页开发者需掌握的基本技能,但完成本课程后学习 Node.js 是一个很好的下一步,可通过另一视频系列“[Beginner Series to: Node.js](https://channel9.msdn.com/Series/Beginners-Series-to-Nodejs/?WT.mc_id=academic-77807-sagibbon)”来实现。
> 请参阅我们的[行为准则](CODE_OF_CONDUCT.md)和[贡献指南](CONTRIBUTING.md)。我们欢迎你的建设性反馈!
## 🧭 离线访问
您可以使用 [Docsify](https://docsify.js.org/#/) 离线运行此文档。Fork 此仓库,在本地机器上 [安装 Docsify](https://docsify.js.org/#/quickstart),然后在此仓库的根文件夹中输入 `docsify serve`。网站将在本地的 3000 端口上运行`localhost:3000`。
你可以使用 [Docsify](https://docsify.js.org/#/) 离线运行本教程。Fork 本仓库,[在本机安装 Docsify](https://docsify.js.org/#/quickstart),然后在仓库根目录下输入 `docsify serve`。网站将在本地主机 3000 端口提供服务`localhost:3000`。
## 📘 PDF
所有课程的 PDF 可在 [此处](https://microsoft.github.io/Web-Dev-For-Beginners/pdf/readme.pdf) 找到。
## 🎒 其他课程
我们的团队还制作了其他课程!请查看:
- [MCP 初学者课程](https://aka.ms/mcp-for-beginners)
- [边缘 AI 初学者课程](https://aka.ms/edgeai-for-beginners)
- [AI 代理初学者课程](https://aka.ms/ai-agents-beginners)
- [生成式 AI 初学者课程 .NET](https://github.com/microsoft/Generative-AI-for-beginners-dotnet)
- [使用 JavaScript 的生成式 AI](https://github.com/microsoft/generative-ai-with-javascript)
- [使用 Java 的生成式 AI](https://github.com/microsoft/Generative-AI-for-beginners-java)
- [AI 初学者课程](https://aka.ms/ai-beginners)
- [数据科学初学者课程](https://aka.ms/datascience-beginners)
- [机器学习初学者课程](https://aka.ms/ml-beginners)
- [网络安全初学者课程](https://github.com/microsoft/Security-101)
- [网页开发初学者课程](https://aka.ms/webdev-beginners)
- [物联网初学者课程](https://aka.ms/iot-beginners)
- [初学者的XR开发](https://github.com/microsoft/xr-development-for-beginners)
- [掌握GitHub Copilot的智能使用](https://github.com/microsoft/Mastering-GitHub-Copilot-for-Paired-Programming)
- [为C#/.NET开发者掌握GitHub Copilot](https://github.com/microsoft/mastering-github-copilot-for-dotnet-csharp-developers)
- [选择你的Copilot冒险之旅](https://github.com/microsoft/CopilotAdventures)
所有课程的 PDF 可在[此处](https://microsoft.github.io/Web-Dev-For-Beginners/pdf/readme.pdf)找到。
## 🎒 其它课程
我们的团队制作了其他课程!赶快看看:
<!-- CO-OP TRANSLATOR OTHER COURSES START -->
### LangChain
[![LangChain4j for Beginners](https://img.shields.io/badge/LangChain4j%20for%20Beginners-22C55E?style=for-the-badge&&labelColor=E5E7EB&color=0553D6)](https://aka.ms/langchain4j-for-beginners)
[![LangChain.js for Beginners](https://img.shields.io/badge/LangChain.js%20for%20Beginners-22C55E?style=for-the-badge&labelColor=E5E7EB&color=0553D6)](https://aka.ms/langchainjs-for-beginners?WT.mc_id=m365-94501-dwahlin)
---
### Azure / Edge / MCP / Agents
[![AZD for Beginners](https://img.shields.io/badge/AZD%20for%20Beginners-0078D4?style=for-the-badge&labelColor=E5E7EB&color=0078D4)](https://github.com/microsoft/AZD-for-beginners?WT.mc_id=academic-105485-koreyst)
[![Edge AI for Beginners](https://img.shields.io/badge/Edge%20AI%20for%20Beginners-00B8E4?style=for-the-badge&labelColor=E5E7EB&color=00B8E4)](https://github.com/microsoft/edgeai-for-beginners?WT.mc_id=academic-105485-koreyst)
[![MCP for Beginners](https://img.shields.io/badge/MCP%20for%20Beginners-009688?style=for-the-badge&labelColor=E5E7EB&color=009688)](https://github.com/microsoft/mcp-for-beginners?WT.mc_id=academic-105485-koreyst)
[![AI Agents for Beginners](https://img.shields.io/badge/AI%20Agents%20for%20Beginners-00C49A?style=for-the-badge&labelColor=E5E7EB&color=00C49A)](https://github.com/microsoft/ai-agents-for-beginners?WT.mc_id=academic-105485-koreyst)
---
### 生成式 AI 系列
[![Generative AI for Beginners](https://img.shields.io/badge/Generative%20AI%20for%20Beginners-8B5CF6?style=for-the-badge&labelColor=E5E7EB&color=8B5CF6)](https://github.com/microsoft/generative-ai-for-beginners?WT.mc_id=academic-105485-koreyst)
[![Generative AI (.NET)](https://img.shields.io/badge/Generative%20AI%20(.NET)-9333EA?style=for-the-badge&labelColor=E5E7EB&color=9333EA)](https://github.com/microsoft/Generative-AI-for-beginners-dotnet?WT.mc_id=academic-105485-koreyst)
[![Generative AI (Java)](https://img.shields.io/badge/Generative%20AI%20(Java)-C084FC?style=for-the-badge&labelColor=E5E7EB&color=C084FC)](https://github.com/microsoft/generative-ai-for-beginners-java?WT.mc_id=academic-105485-koreyst)
[![Generative AI (JavaScript)](https://img.shields.io/badge/Generative%20AI%20(JavaScript)-E879F9?style=for-the-badge&labelColor=E5E7EB&color=E879F9)](https://github.com/microsoft/generative-ai-with-javascript?WT.mc_id=academic-105485-koreyst)
---
### 核心学习
[![ML for Beginners](https://img.shields.io/badge/ML%20for%20Beginners-22C55E?style=for-the-badge&labelColor=E5E7EB&color=22C55E)](https://aka.ms/ml-beginners?WT.mc_id=academic-105485-koreyst)
[![Data Science for Beginners](https://img.shields.io/badge/Data%20Science%20for%20Beginners-84CC16?style=for-the-badge&labelColor=E5E7EB&color=84CC16)](https://aka.ms/datascience-beginners?WT.mc_id=academic-105485-koreyst)
[![AI for Beginners](https://img.shields.io/badge/AI%20for%20Beginners-A3E635?style=for-the-badge&labelColor=E5E7EB&color=A3E635)](https://aka.ms/ai-beginners?WT.mc_id=academic-105485-koreyst)
[![Cybersecurity for Beginners](https://img.shields.io/badge/Cybersecurity%20for%20Beginners-F97316?style=for-the-badge&labelColor=E5E7EB&color=F97316)](https://github.com/microsoft/Security-101?WT.mc_id=academic-96948-sayoung)
[![Web Dev for Beginners](https://img.shields.io/badge/Web%20Dev%20for%20Beginners-EC4899?style=for-the-badge&labelColor=E5E7EB&color=EC4899)](https://aka.ms/webdev-beginners?WT.mc_id=academic-105485-koreyst)
[![IoT for Beginners](https://img.shields.io/badge/IoT%20for%20Beginners-14B8A6?style=for-the-badge&labelColor=E5E7EB&color=14B8A6)](https://aka.ms/iot-beginners?WT.mc_id=academic-105485-koreyst)
[![XR Development for Beginners](https://img.shields.io/badge/XR%20Development%20for%20Beginners-38BDF8?style=for-the-badge&labelColor=E5E7EB&color=38BDF8)](https://github.com/microsoft/xr-development-for-beginners?WT.mc_id=academic-105485-koreyst)
---
### Copilot 系列
[![Copilot for AI Paired Programming](https://img.shields.io/badge/Copilot%20for%20AI%20Paired%20Programming-FACC15?style=for-the-badge&labelColor=E5E7EB&color=FACC15)](https://aka.ms/GitHubCopilotAI?WT.mc_id=academic-105485-koreyst)
[![Copilot for C#/.NET](https://img.shields.io/badge/Copilot%20for%20C%23/.NET-FBBF24?style=for-the-badge&labelColor=E5E7EB&color=FBBF24)](https://github.com/microsoft/mastering-github-copilot-for-dotnet-csharp-developers?WT.mc_id=academic-105485-koreyst)
[![Copilot Adventure](https://img.shields.io/badge/Copilot%20Adventure-FDE68A?style=for-the-badge&labelColor=E5E7EB&color=FDE68A)](https://github.com/microsoft/CopilotAdventures?WT.mc_id=academic-105485-koreyst)
<!-- CO-OP TRANSLATOR OTHER COURSES END -->
## 获取帮助
如果在构建AI应用时遇到困难或有任何问题请加入
如果你遇到困难或对构建 AI 应用有任何疑问,加入其他学习者和经验丰富开发者的 MCP 讨论。这里是一个支持性的社区,欢迎提问并自由分享知识。
[![Azure AI Foundry Discord](https://img.shields.io/badge/Discord-Azure_AI_Foundry_Community_Discord-blue?style=for-the-badge&logo=discord&color=5865f2&logoColor=fff)](https://aka.ms/foundry/discord)
[![Microsoft Foundry Discord](https://dcbadge.limes.pink/api/server/nTYy5BXMWG)](https://discord.gg/nTYy5BXMWG)
如果您有产品反馈或在构建过程中遇到错误,请访问:
如果你在构建过程中有产品反馈或遇到错误,请访问:
[![Azure AI Foundry Developer Forum](https://img.shields.io/badge/GitHub-Azure_AI_Foundry_Developer_Forum-blue?style=for-the-badge&logo=github&color=000000&logoColor=fff)](https://aka.ms/foundry/forum)
[![Microsoft Foundry Developer Forum](https://img.shields.io/badge/GitHub-Microsoft_Foundry_Developer_Forum-blue?style=for-the-badge&logo=github&color=000000&logoColor=fff)](https://aka.ms/foundry/forum)
## 许可证
此存储库遵循MIT许可证。有关更多信息请参阅[LICENSE](../../LICENSE)文件。
仓库使用 MIT 许可证。详情请参阅 [LICENSE](../../LICENSE) 文件。
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免责声明**
本文档使用AI翻译服务 [Co-op Translator](https://github.com/Azure/co-op-translator) 进行翻译。尽管我们努力确保翻译的准确性,但请注意,自动翻译可能包含错误或不准确之处。原始语言的文档应被视为权威来源。对于关键信息,建议使用专业人工翻译。我们不对因使用此翻译而产生的任何误解或误读承担责任。
本文件通过 AI 翻译服务 [Co-op Translator](https://github.com/Azure/co-op-translator) 进行翻译。虽然我们力求准确,但请注意自动翻译可能存在错误或不准确之处。原始的本地语言版本应视为权威来源。对于重要信息,建议使用专业人工翻译。因使用本翻译而引起的任何误解或曲解,我们不承担任何责任。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -42,10 +42,10 @@ CO_OP_TRANSLATOR_METADATA:
- Moodle Cloud 对 Common Cartridge 的支持有限。建议优先使用上述 Moodle 文件,该文件也可以上传到 Canvas。
- 导入后,请根据学期安排检查模块、截止日期和测验设置。
![Moodle](../../translated_images/moodle.94eb93d714a50cb2c97435b408017dee224348b61bc86203ffd43a4f4e57b95f.zh.png)
![Moodle](../../translated_images/moodle.94eb93d714a50cb2.zh.png)
> Moodle 教室中的课程内容
![Canvas](../../translated_images/canvas.fbd605ff8e5b8aff567d398528ce113db304446b90b9cad55c654de3fdfcda34.zh.png)
![Canvas](../../translated_images/canvas.fbd605ff8e5b8aff.zh.png)
> Canvas 教室中的课程内容
### 直接使用代码库(不使用 Classroom

Loading…
Cancel
Save