+
{{ showMessage }}
-
-
-
+
+
+
-
-
const name = "张三"
-let age = 25
-const isStudent = true
-
-{{ executeCode.join('\n') }}
+
+ const name = "{{ name }}"
+ let age = {{ age }}
@@ -92,42 +87,59 @@ const isStudent = true
.variable-box-demo {
border: 1px solid var(--vp-c-border);
border-radius: 12px;
- padding: 24px;
- margin: 24px 0;
+ padding: 20px;
+ margin: 16px 0;
background: var(--vp-c-bg);
}
-h3 {
- margin: 0 0 16px 0;
- font-size: 18px;
+.demo-header {
+ margin-bottom: 16px;
+}
+
+.title {
+ font-size: 16px;
font-weight: 600;
color: var(--vp-c-text-1);
}
-.boxes-container {
+.boxes-row {
display: flex;
gap: 16px;
justify-content: center;
- margin-bottom: 20px;
+ margin-bottom: 16px;
flex-wrap: wrap;
}
-.variable-box {
- position: relative;
- width: 120px;
- height: 120px;
+.var-box {
+ width: 100px;
+ height: 100px;
border: 2px solid var(--vp-c-border);
- border-radius: 12px;
+ border-radius: 10px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
+ position: relative;
+ background: var(--vp-c-bg);
transition: all 0.3s ease;
}
-.variable-box.success {
- border-color: #3eaf7c;
- animation: pulse 0.5s ease;
+.var-box.error {
+ border-color: #ef4444;
+ background: #fef2f2;
+ animation: shake 0.4s ease;
+}
+
+.var-box.success {
+ border-color: #10b981;
+ background: #ecfdf5;
+ animation: pulse 0.4s ease;
+}
+
+@keyframes shake {
+ 0%, 100% { transform: translateX(0); }
+ 25% { transform: translateX(-4px); }
+ 75% { transform: translateX(4px); }
}
@keyframes pulse {
@@ -135,144 +147,107 @@ h3 {
50% { transform: scale(1.05); }
}
-.box-label {
+.box-tag {
position: absolute;
- top: -12px;
- left: 50%;
- transform: translateX(-50%);
- background: var(--vp-c-brand-1);
+ top: -10px;
+ padding: 2px 8px;
+ border-radius: 10px;
+ font-size: 10px;
+ font-weight: 600;
color: white;
- padding: 4px 12px;
- border-radius: 12px;
- font-size: 12px;
- font-weight: 500;
- white-space: nowrap;
}
-.let-box .box-label {
- background: #42b983;
+.box-tag.const {
+ background: #3b82f6;
+}
+
+.box-tag.let {
+ background: #10b981;
+}
+
+.box-name {
+ font-size: 13px;
+ color: var(--vp-c-text-2);
+ margin-bottom: 4px;
}
.box-value {
- font-size: 24px;
+ font-size: 20px;
font-weight: 600;
- font-family: 'Courier New', monospace;
+ font-family: monospace;
color: var(--vp-c-text-1);
- margin-bottom: 8px;
}
-.box-icon {
- font-size: 16px;
+.box-lock {
+ position: absolute;
+ bottom: 8px;
+ font-size: 12px;
}
-.message-bubble {
+.message {
text-align: center;
- padding: 12px;
- border-radius: 8px;
- margin-bottom: 20px;
- font-size: 14px;
+ padding: 10px;
+ border-radius: 6px;
+ margin-bottom: 12px;
+ font-size: 13px;
font-weight: 500;
- animation: fadeIn 0.3s ease;
}
-@keyframes fadeIn {
- from { opacity: 0; transform: translateY(-10px); }
- to { opacity: 1; transform: translateY(0); }
+.message.error {
+ background: #fef2f2;
+ color: #dc2626;
}
-.message-bubble.error {
- background: #fee;
- color: #c00;
-}
-
-.message-bubble.success {
- background: #e8f5e9;
- color: #2e7d32;
+.message.success {
+ background: #ecfdf5;
+ color: #059669;
}
.controls {
display: flex;
- gap: 12px;
+ gap: 8px;
justify-content: center;
- flex-wrap: wrap;
- margin-bottom: 20px;
+ margin-bottom: 12px;
}
-button {
- padding: 8px 16px;
+.btn {
+ padding: 8px 14px;
border: none;
border-radius: 6px;
- font-size: 14px;
+ font-size: 13px;
font-weight: 500;
cursor: pointer;
- transition: all 0.2s ease;
-}
-
-button:active {
- transform: scale(0.95);
+ transition: all 0.2s;
}
.btn-primary {
- background: var(--vp-c-brand-1);
+ background: #3b82f6;
color: white;
}
-.btn-primary:hover {
- background: var(--vp-c-brand-2);
-}
-
.btn-danger {
- background: #f56565;
+ background: #ef4444;
color: white;
}
-.btn-danger:hover {
- background: #e53e3e;
-}
-
.btn-secondary {
background: var(--vp-c-bg-soft);
color: var(--vp-c-text-1);
+ border: 1px solid var(--vp-c-border);
}
-.btn-secondary:hover {
- background: var(--vp-c-bg-soft-hover);
-}
-
-.code-display {
+.code-snippet {
background: #1e1e1e;
- border-radius: 8px;
- padding: 16px;
- overflow-x: auto;
+ border-radius: 6px;
+ padding: 10px 14px;
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
}
-.code-display pre {
- margin: 0;
-}
-
-.code-display code {
- font-family: 'Courier New', monospace;
- font-size: 13px;
- line-height: 1.6;
+.code-snippet code {
+ font-family: monospace;
+ font-size: 12px;
color: #d4d4d4;
}
-
-@media (max-width: 640px) {
- .boxes-container {
- flex-direction: column;
- align-items: center;
- }
-
- .variable-box {
- width: 200px;
- }
-
- .controls {
- flex-direction: column;
- }
-
- button {
- width: 100%;
- }
-}
diff --git a/docs/.vitepress/theme/components/appendix/js-runtime/CallStackDemo.vue b/docs/.vitepress/theme/components/appendix/js-runtime/CallStackDemo.vue
new file mode 100644
index 0000000..cfe08b4
--- /dev/null
+++ b/docs/.vitepress/theme/components/appendix/js-runtime/CallStackDemo.vue
@@ -0,0 +1,542 @@
+
+
+
+
+
调用栈:函数执行的足迹
+
+
+
+
+
代码
+
+
+ {{ index + 1 }}
+ {{ step.code }}
+
+
+
+
+
+
+
调用栈
+
+
+
+
+
+
+
{{ frame.function }}()
+
{{ frame.code }}
+
+
+
+
+ 栈为空
+
+
+
+
+
+
+
+
当前状态:
+
+ {{ codeSteps[currentStep]?.description }}
+
+
+ 执行完成
+
+
+
+
+
+
+
+
输出
+
+
+ 等待输出...
+
+
+
+ {{ log }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
调用栈工作原理:
+
+ - 每次调用函数,就会在栈上"压入"一个新的"栈帧"
+ - 栈帧记录了函数的执行状态、局部变量等信息
+ - 函数执行完毕,栈帧就会从栈上"弹出"
+ - 栈是"后进先出"(LIFO)的数据结构
+ - 如果递归太深,会导致"栈溢出"错误
+
+
+ 调用栈就像一摞盘子:最后放上去的盘子最先被取走。每个函数就是一个盘子,执行完就取走,然后继续执行下面的函数。
+
+
+
+
+
+
diff --git a/docs/.vitepress/theme/components/appendix/js-runtime/GarbageCollectionDemo.vue b/docs/.vitepress/theme/components/appendix/js-runtime/GarbageCollectionDemo.vue
new file mode 100644
index 0000000..9e45a53
--- /dev/null
+++ b/docs/.vitepress/theme/components/appendix/js-runtime/GarbageCollectionDemo.vue
@@ -0,0 +1,752 @@
+
+
+
+
+
垃圾回收机制
+
+
+
+
+
+ {{ p.label }}
+ {{ p.description }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ obj.collected ? '💀' : '📦' }}
+
+
{{ obj.name }}
+
✓ 可达
+
✗ 回收
+
+
+
+
+
+
+
+
+
+
+
+
+ 当前操作:
+
+ {{ steps[currentStep].description }}
+
+
+ 垃圾回收完成
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
标记-清除算法 (Mark-and-Sweep)
+
+
+
1
+
+
标记阶段
+
从根对象(Root)开始,遍历所有可达对象,标记为"活动对象"
+
+
+
+
2
+
+
清除阶段
+
遍历整个堆内存,回收所有未被标记的对象
+
+
+
+
3
+
+
重置标记
+
清除所有标记位,为下一次垃圾回收做准备
+
+
+
+
+
+
核心要点
+
+ - 根对象(Root): 全局变量、栈上的变量等,总是被认为是可达的
+ - 可达对象: 从根对象出发,通过引用链能访问到的对象
+ - 垃圾对象: 无法从根对象访问到的对象,会被回收
+ - 循环引用: 如果两个对象互相引用但都不可达,仍会被回收
+
+
+
+
+
+
+
实际应用技巧
+
+
+
💡
+
+
及时解除引用
+
对象不再使用时,将其设为 null
+
+
+
+
🔒
+
+
避免意外的全局变量
+
使用 const/let 代替 var
+
+
+
+
🧹
+
+
清理事件监听
+
组件销毁时移除所有监听器
+
+
+
+
📊
+
+
定期检查内存
+
用 DevTools Memory 面板监控
+
+
+
+
+
+
+
+
diff --git a/docs/.vitepress/theme/components/appendix/js-runtime/MemoryLeakDemo.vue b/docs/.vitepress/theme/components/appendix/js-runtime/MemoryLeakDemo.vue
new file mode 100644
index 0000000..6a8dc4e
--- /dev/null
+++ b/docs/.vitepress/theme/components/appendix/js-runtime/MemoryLeakDemo.vue
@@ -0,0 +1,682 @@
+
+
+
+
+
内存泄漏演示
+
+
+
+
+
+
+
+
+
+
+
+ {{ memoryUsage }}%
+
+
+
+ ⚠️ 内存占用过高!可能导致页面卡顿或崩溃
+
+
+
+
+
+
+
+
全局变量泄漏
+
+
+
问题:全局变量不会被垃圾回收,会一直占用内存
+
示例:不断往全局数组添加数据,从不清理
+
+
+
+
+
+
+
+
+
+
+
+ ID: {{ item.id }}
+ {{ item.timestamp }}
+ {{ item.data.length }} 项数据
+
+
+ 暂无全局变量
+
+
+ ... 还有 {{ globalMemory.length - 5 }} 项
+
+
+
+
+
+
❌ 错误做法
+
// 全局变量不会被回收
+globalCache = []
+function addItem() {
+ globalCache.push(largeData)
+}
+
+
+
+
+
+
事件监听器泄漏
+
+
+
问题:事件监听器没有被移除,持续占用内存
+
示例:动态创建元素并添加监听,但从不移除
+
+
+
+
+
+
+
+
+
+
+
+
🎯
+
+ 监听器 #{{ listener.id }}
+ 活跃中
+
+
+
+ 暂无事件监听器
+
+
+ ... 还有 {{ eventListeners.length - 5 }} 个监听器
+
+
+
+
+
+
❌ 错误做法
+
// 监听器没有被移除
+button.addEventListener('click', handler)
+// 元素删除时监听器还在!
+
+
✅ 正确做法
+
// 保存监听器引用
+const handler = () => { ... }
+button.addEventListener('click', handler)
+
+// 不需要时移除
+button.removeEventListener('click', handler)
+
+
+
+
+
+
闭包引用泄漏
+
+
+
问题:闭包持有大对象引用,导致对象无法被回收
+
示例:闭包函数一直引用大数组
+
+
+
+
+
+
+
+
+
+
+
+
🔒
+
+ 闭包 #{{ item.id }}
+ {{ item.timestamp }}
+ 持有 {{ item.data.length }} 项数据
+
+
+
+ 暂无闭包
+
+
+ ... 还有 {{ closureItems.length - 5 }} 个闭包
+
+
+
+
+
+
❌ 错误做法
+
// 闭包持有大对象引用
+function createHandler() {
+ const largeData = new Array(1000000)
+ return function() {
+ // largeData 一直被引用,不会被回收
+ console.log('处理中')
+ }
+}
+const handler = createHandler()
+
+
✅ 正确做法
+
// 使用后释放引用
+let handler = createHandler()
+handler() // 使用
+handler = null // 释放引用
+
+
+
+
+
+
+
+
+
+
+
+
如何避免内存泄漏
+
+ - 避免全局变量: 使用 const/let 代替 var,尽量使用局部变量
+ - 及时清理监听器: 组件销毁时移除所有事件监听
+ - 释放闭包引用: 不需要时将闭包变量设为 null
+ - 使用 WeakMap/WeakSet: 自动清理不再被引用的对象
+ - 定期检查: 用 DevTools Memory 面板检查内存泄漏
+
+
+
+
+
+
diff --git a/docs/.vitepress/theme/components/appendix/js-runtime/RuntimeEnvironmentDemo.vue b/docs/.vitepress/theme/components/appendix/js-runtime/RuntimeEnvironmentDemo.vue
new file mode 100644
index 0000000..d4f8afa
--- /dev/null
+++ b/docs/.vitepress/theme/components/appendix/js-runtime/RuntimeEnvironmentDemo.vue
@@ -0,0 +1,487 @@
+
+
+
+
+
运行时环境对比
+
+
+
+
+
+
+
+
+
+
浏览器环境
+
+
+
+
{{ api.name }}
+
{{ api.description }}
+
{{ api.example }}
+
+
+
+
+
特点:
+
+ - ✅ 有 DOM 和 BOM API,可以操作网页
+ - ✅ 有 Web Storage (localStorage, sessionStorage)
+ - ✅ 有 fetch 和 XMLHttpRequest 进行网络请求
+ - ❌ 没有文件系统访问权限
+ - ❌ 不能直接创建 HTTP 服务器
+
+
+
+
+
+
+
Node.js 环境
+
+
+
+
{{ api.name }}
+
{{ api.description }}
+
{{ api.example }}
+
+
+
+
+
特点:
+
+ - ✅ 有文件系统访问权限
+ - ✅ 可以创建 HTTP 服务器
+ - ✅ 可以操作进程和系统资源
+ - ❌ 没有 DOM 和 BOM
+ - ❌ 不能直接操作网页元素
+
+
+
+
+
+
+
+
+
代码演示:不同环境的差异
+
+
+
+
+
+
+
+
+
+
+ {{ browserResult || '点击"在浏览器运行"查看结果' }}
+
+
+
+
+
+
+
+ {{ nodeResult || '需要在 Node.js 环境中运行' }}
+
+
+
+
+
+
+
+
+
+
+
核心区别:
+
浏览器运行时专注于用户界面和网页交互,提供 DOM、BOM、fetch 等前端专用 API。
+
Node.js 运行时专注于服务器端开发,提供文件系统、HTTP 服务器、进程管理等后端专用 API。
+
同样的 JavaScript 语法,但能用的 API 完全不同——这就是"环境判断"的重要性。
+
+
+
+
+
diff --git a/docs/.vitepress/theme/components/appendix/js-runtime/TaskQueueDemo.vue b/docs/.vitepress/theme/components/appendix/js-runtime/TaskQueueDemo.vue
new file mode 100644
index 0000000..7a8ec1a
--- /dev/null
+++ b/docs/.vitepress/theme/components/appendix/js-runtime/TaskQueueDemo.vue
@@ -0,0 +1,652 @@
+
+
+
+
+
任务队列:宏任务 vs 微任务
+
+
+
+
代码示例
+
+
+ {{ item.id }}
+ {{ item.code }}
+ 同步
+ 微任务
+ 宏任务
+
+
+
+
+
+
+
+
+
调用栈 (正在执行)
+
+
+ {{ executionSteps[currentStep]?.description }}
+
+
+ 执行完成
+
+
+
+
+
+
+
+ 微任务队列
+ Microtask
+
+
+
+
+
{{ task.code }}
+
✅ 就绪
+
⏳ 等待
+
+
+
+ 队列为空
+
+
+
+
+
+
+
+ 宏任务队列
+ Macrotask
+
+
+
+
+
{{ task.code }}
+
✅ 就绪
+
⏳ 等待
+
+
+
+ 队列为空
+
+
+
+
+
+
+
+
输出日志 (执行顺序)
+
+
+ 等待输出...
+
+
+
+ {{ log.output }}
+ ({{ log.source }})
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
执行顺序规则
+
+
+ 1
+ 执行所有同步代码
+
+
+ 2
+ 执行微任务队列中的所有任务
+
+
+ 3
+ 执行一个宏任务
+
+
+ 4
+ 重复步骤 2-3
+
+
+
+ 核心要点: 微任务优先级高于宏任务。每次执行完一个宏任务后,都会检查并执行所有微任务,然后再执行下一个宏任务。
+
+
+
+
+
+
diff --git a/docs/.vitepress/theme/components/appendix/typescript-intro/GenericDemo.vue b/docs/.vitepress/theme/components/appendix/typescript-intro/GenericDemo.vue
new file mode 100644
index 0000000..6daba65
--- /dev/null
+++ b/docs/.vitepress/theme/components/appendix/typescript-intro/GenericDemo.vue
@@ -0,0 +1,556 @@
+
+
+
+
+
🔄 泛型 (Generics) 演示
+
+
+
+
+
💡
+
+ 泛型就像"通用模板" - 可以处理不同类型的数据,同时保持类型安全
+
+
+
+
+
+
+
// T 是类型变量,使用时才会确定具体类型
+function identity<T>(arg: T): T {
+ return arg
+}
+
+// 泛型数组反转
+function reverseArray<T>(arr: T[]): T[] {
+ return [...arr].reverse()
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
输入类型:
+
{{ result.type }}
+
+
+
+
输入数组:
+
+ [{{ result.input.join(', ') }}]
+
+
+
+
+
输出数组:
+
+ [{{ result.output.join(', ') }}]
+
+
+
+
+
✅
+
类型安全:输入 {{ result.type }},输出 {{ result.type }}
+
+
+
+
+ {{ result?.error || result }}
+
+
+
+
+
+
+
📝 泛型使用示例
+
+
+
数字数组
+
const nums = [1, 2, 3, 4, 5]
+const reversed = reverseArray<number>(nums)
+// 结果: [5, 4, 3, 2, 1]
+// 类型: number[]
+
+
+
+
字符串数组
+
const strs = ["a", "b", "c"]
+const reversed = reverseArray<string>(strs)
+// 结果: ["c", "b", "a"]
+// 类型: string[]
+
+
+
+
+
+
+
+
diff --git a/docs/.vitepress/theme/components/appendix/typescript-intro/InterfaceDemo.vue b/docs/.vitepress/theme/components/appendix/typescript-intro/InterfaceDemo.vue
new file mode 100644
index 0000000..a0227dd
--- /dev/null
+++ b/docs/.vitepress/theme/components/appendix/typescript-intro/InterfaceDemo.vue
@@ -0,0 +1,445 @@
+
+
+
+
+
🎯 Interface 接口演示
+
+
+
+
+
+
interface User {
+ id: number
+ name: string
+ email: string
+ age: number
+}
+
+
+
+
+
+
+
+
+ ID:
+ {{ user.id }}
+ number
+
+
+ 年龄:
+ {{ user.age }}
+ number
+
+
+
+
+
+
+
+ {{ errorMessage }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
const user: User = {
+ id: 1,
+ name: "张三",
+ email: "zhangsan@example.com",
+ age: 25
+} // ✅ 类型完全匹配
+
+
+
+
+
const user: User = {
+ id: 1,
+ name: "张三",
+ email: "zhangsan@example.com",
+ age: "25" // ❌ 错误:age 应该是 number,不是 string
+}
+
+
+
+
+
+
+
diff --git a/docs/.vitepress/theme/components/appendix/typescript-intro/TypeAnnotationDemo.vue b/docs/.vitepress/theme/components/appendix/typescript-intro/TypeAnnotationDemo.vue
new file mode 100644
index 0000000..537fb1b
--- /dev/null
+++ b/docs/.vitepress/theme/components/appendix/typescript-intro/TypeAnnotationDemo.vue
@@ -0,0 +1,402 @@
+
+
+
+
+
📝 TypeScript 类型注解演示
+
+
+
+
+
+
+
{{ name }}
+
+ const name: string = "{{ name }}"
+
+
+
+
+
+
+
{{ age }}
+
+ const age: number = {{ age }}
+
+
+
+
+
+
+
+
+ {{ isActive ? 'true' : 'false' }}
+
+
+ const isActive: boolean = {{ isActive }}
+
+
+
+
+
+
+ {{ errorMessage }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
let name = "张三"
+name = 123 // ✅ 运行时才会报错(可能很晚才发现)
+
+
+
+
let name: string = "张三"
+name = 123 // ❌ 编译时立即报错(写代码时就发现)
+
+
+
+
+
+
+
diff --git a/docs/.vitepress/theme/components/appendix/typescript-intro/TypeInferenceDemo.vue b/docs/.vitepress/theme/components/appendix/typescript-intro/TypeInferenceDemo.vue
new file mode 100644
index 0000000..c805bd4
--- /dev/null
+++ b/docs/.vitepress/theme/components/appendix/typescript-intro/TypeInferenceDemo.vue
@@ -0,0 +1,618 @@
+
+
+
+
+
🔮 类型推断演示
+
+
+
+
+
+
🧠
+
+
什么是类型推断?
+
TypeScript 很聪明,它能根据你写的代码自动推断出变量的类型,不需要每次都手动标注。
+
+
+
+
+
+
+
选择一个示例看看类型推断是如何工作的:
+
+
+
{{ example.code }}
+
→ {{ example.inferredType }}
+
+
+
+
+
+
+
+
+
+
{{ currentExample.code }}
+
+
+
→
+
+
+
+
{{ currentExample.inferredType }}
+
+
+
+
+
💡
+
{{ currentExample.explanation }}
+
+
+
+
+
+ {{ errorMessage }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
🔄 类型推断 vs 显式注解
+
+
{{ comparison.scenario }}
+
+
+
使用推断
+
{{ comparison.withInference }}
+
+
+
显式注解
+
{{ comparison.withAnnotation }}
+
+
+
+ ✅
+ {{ comparison.recommendation }}
+
+
+
+
+
+
+
+
diff --git a/docs/.vitepress/theme/index.js b/docs/.vitepress/theme/index.js
index e89ff8e..5b46221 100644
--- a/docs/.vitepress/theme/index.js
+++ b/docs/.vitepress/theme/index.js
@@ -494,6 +494,19 @@ import ThisContextDemo from './components/appendix/javascript-intro/ThisContextD
import PrototypeDemo from './components/appendix/javascript-intro/PrototypeDemo.vue'
import AsyncDemo from './components/appendix/javascript-intro/AsyncDemo.vue'
+// JavaScript Runtime Components
+import RuntimeEnvironmentDemo from './components/appendix/js-runtime/RuntimeEnvironmentDemo.vue'
+import CallStackDemo from './components/appendix/js-runtime/CallStackDemo.vue'
+import TaskQueueDemo from './components/appendix/js-runtime/TaskQueueDemo.vue'
+import MemoryLeakDemo from './components/appendix/js-runtime/MemoryLeakDemo.vue'
+import GarbageCollectionDemo from './components/appendix/js-runtime/GarbageCollectionDemo.vue'
+
+// TypeScript Intro Components
+import TypeAnnotationDemo from './components/appendix/typescript-intro/TypeAnnotationDemo.vue'
+import InterfaceDemo from './components/appendix/typescript-intro/InterfaceDemo.vue'
+import GenericDemo from './components/appendix/typescript-intro/GenericDemo.vue'
+import TypeInferenceDemo from './components/appendix/typescript-intro/TypeInferenceDemo.vue'
+
export default {
extends: DefaultTheme,
Layout,
@@ -935,6 +948,7 @@ export default {
app.component('MacroMicroTaskDemo', MacroMicroTaskDemo)
app.component('RenderingPerformanceDemo', RenderingPerformanceDemo)
app.component('RenderingPipelineDemo', RenderingPipelineDemo)
+ app.component('EventLoopDemo', JSEventLoopDemo) // Alias for browser rendering context
// Cache Design Extra Components Registration
app.component('CacheArchitectureOverview', CacheArchitectureOverview)
@@ -986,6 +1000,19 @@ export default {
app.component('DOMTreeDemo', DOMTreeDemo)
app.component('AsyncRestaurantDemo', AsyncRestaurantDemo)
app.component('JSEventLoopDemo', JSEventLoopDemo)
+
+ // JavaScript Runtime Components Registration
+ app.component('RuntimeEnvironmentDemo', RuntimeEnvironmentDemo)
+ app.component('CallStackDemo', CallStackDemo)
+ app.component('TaskQueueDemo', TaskQueueDemo)
+ app.component('MemoryLeakDemo', MemoryLeakDemo)
+ app.component('GarbageCollectionDemo', GarbageCollectionDemo)
+
+ // TypeScript Intro Components Registration
+ app.component('TypeAnnotationDemo', TypeAnnotationDemo)
+ app.component('InterfaceDemo', InterfaceDemo)
+ app.component('GenericDemo', GenericDemo)
+ app.component('TypeInferenceDemo', TypeInferenceDemo)
},
setup() {
const route = useRoute()
diff --git a/docs/zh-cn/appendix/3-browser-and-frontend/browser-as-os-rendering.md b/docs/zh-cn/appendix/3-browser-and-frontend/browser-as-os-rendering.md
index 52bb97b..d3250af 100644
--- a/docs/zh-cn/appendix/3-browser-and-frontend/browser-as-os-rendering.md
+++ b/docs/zh-cn/appendix/3-browser-and-frontend/browser-as-os-rendering.md
@@ -3,6 +3,22 @@
**为什么有些网页流畅如丝,有些却卡成PPT?** 浏览器是怎么把一堆HTML、CSS、JavaScript代码变成你眼前看到的网页的?本章将带你深入浏览器的"车间",理解它的工作流程,从而写出性能更好的网页。
:::
+**这篇文章会带你学什么?**
+
+| 章节 | 内容 | 学完能干嘛 |
+|-----|------|-----------|
+| **第 1 章** | 为什么要理解渲染管线 | 理解性能优化的必要性 |
+| **第 2 章** | 渲染管线的五个阶段 | 掌握浏览器渲染的基本流程 |
+| **第 3 章** | 构建DOM树和CSSOM树 | 理解HTML和CSS如何被解析 |
+| **第 4 章** | 构建渲染树 | 知道哪些元素会被渲染 |
+| **第 5 章** | 布局与重排 | 避免触发昂贵的布局计算 |
+| **第 6 章** | 绘制与重绘 | 减少不必要的绘制操作 |
+| **第 7 章** | 合成与GPU加速 | 利用GPU提升动画性能 |
+| **第 8 章** | 事件循环 | 理解JavaScript的执行机制 |
+| **第 9 章** | 性能优化实战 | 掌握常用的性能优化技巧 |
+
+每一章都从"理解原理"开始,不需要你会手写优化代码。遇到性能问题时,随时回来查就行。
+
---
## 1. 为什么要理解"渲染管线"?
@@ -933,7 +949,41 @@ lazyImages.forEach(img => imageObserver.observe(img))
---
-## 10. 总结:渲染管线优化的本质
+## 10. 你现在应该能识别的性能问题
+
+理解了浏览器的渲染管线后,你应该能识别以下常见的性能问题:
+
+| 问题代码 | 问题所在 | 如何描述给AI |
+|---------|---------|-------------|
+| `element.style.width = ...` | 在循环中频繁修改宽度 | "这里会触发多次重排,请改用transform或者批量处理" |
+| `height = element.offsetHeight` | 在写入后立即读取布局属性 | "这是强制同步布局,请分离读写操作" |
+| `element.className = ...` | 频繁修改class触发样式重新计算 | "用classList.add/remove代替,减少样式计算" |
+| 动画用`width`/`left` | 触发重排和重绘,性能差 | "改用transform和opacity做动画" |
+| 给所有元素加`translateZ(0)` | 滥用GPU加速导致内存爆炸 | "只给需要动画的元素开启GPU加速" |
+| 列表项10000个全渲染 | DOM节点过多导致卡顿 | "实现虚拟滚动,只渲染可见区域" |
+| scroll事件里直接操作DOM | 触发频率太高导致卡顿 | "用requestAnimationFrame或节流优化" |
+| `box-shadow`做hover动画 | 复杂的阴影计算很慢 | "改用transform或伪元素,避免动画阴影" |
+
+**如果你认真读了每一章的"踩坑实录",你还掌握了这些核心概念:**
+
+- **渲染管线五阶段**:DOM/CSSOM → 渲染树 → 布局 → 绘制 → 合成
+- **重排 vs 重绘**:重排最昂贵(几何变化),重绘次之(外观变化)
+- **强制同步布局**:读写交替会导致布局抖动,必须分离
+- **GPU加速**:transform和opacity由GPU处理,性能最佳
+- **事件循环**:JavaScript是单线程的,通过任务队列实现异步
+
+这些概念会帮你快速定位性能瓶颈。
+
+::: info 💡 遇到性能问题时这样跟AI说
+- "动画卡顿,检查是否触发了重排或重绘"
+- "滚动性能差,可能需要节流或requestAnimationFrame"
+- "列表数据量大时卡顿,需要虚拟滚动"
+- "频繁修改样式导致性能问题,请用transform优化"
+:::
+
+---
+
+## 11. 总结:渲染管线优化的本质
通过本文的学习,我们可以得出以下核心结论:
@@ -948,7 +998,7 @@ lazyImages.forEach(img => imageObserver.observe(img))
---
-## 11. 名词对照表
+## 12. 名词对照表
| 英文术语 | 中文对照 | 解释 |
| :--- | :--- | :--- |
diff --git a/docs/zh-cn/appendix/3-browser-and-frontend/browser-as-os.md b/docs/zh-cn/appendix/3-browser-and-frontend/browser-as-os.md
index e26d015..1e4d112 100644
--- a/docs/zh-cn/appendix/3-browser-and-frontend/browser-as-os.md
+++ b/docs/zh-cn/appendix/3-browser-and-frontend/browser-as-os.md
@@ -1,18 +1,77 @@
# 浏览器是一个操作系统
-> **学习指南**:本章节无需编程基础。我们将用**"网购"**的生活化比喻,配合**真实的技术过程**,带你一步步理解浏览器如何将一行网址变成丰富多彩的页面。
+::: tip 前言
+你每天都在用浏览器——看视频、刷新闻、在线办公。但你有没有想过:**当你在地址栏输入一个网址并按下回车,背后发生了什么?**
+
+这篇文章会用**"网购"**的生活化比喻,配合**真实的技术过程**,带你一步步理解浏览器如何将一行网址变成丰富多彩的页面。
+
+读完这篇,你就能:
+- 理解从输入网址到显示页面的完整流程
+- 掌握 URL、DNS、TCP、HTTP 等核心概念
+- 了解浏览器如何渲染页面
+- 知道静态网站和动态网站的区别
+
+**无需编程基础**,只需要你平时网购的经验即可。
+:::
+
+**这篇文章会带你学什么?**
+
+| 章节 | 内容 | 核心概念 |
+|-----|------|---------|
+| **第 1 章** | URL 解析 | 网址的结构和作用 |
+| **第 2 章** | DNS 查询 | 域名如何转换成 IP 地址 |
+| **第 3 章** | TCP 握手 | 如何建立可靠的连接 |
+| **第 4 章** | HTTP 通信 | 浏览器和服务器如何对话 |
+| **第 5 章** | 浏览器渲染 | 代码如何变成画面 |
+| **第 6 章** | 静态 vs 动态 | 网页内容的生成方式 |
---
## 0. 引言:当你按下回车键的那一刻
-想象你正在进行一次**网购**。你需要:
+::: tip 🤔 核心问题
+**当你在浏览器输入网址并按下回车,后台发生了什么?** 为什么有的网页打开很快,有的很慢?为什么有时候会出现"找不到服务器"的错误?
+:::
-1. **填写订单**(选好商品,确认收货地址)
-2. **系统查找仓库**(根据店铺名找到具体的发货仓库)
-3. **建立物流通道**(确保仓库正常营业且能发货)
-4. **仓库发货**(快递员把包裹送上门)
-5. **拆箱体验**(打开包裹,看到心仪的商品)
+### 生活比喻:一次网购之旅
+
+想象你正在进行一次**网购**。整个过程可以分为 5 个步骤:
+
+
+
+
+**🛒 第 1 步:填写订单**
+选好商品,确认收货地址
+
+
+
+
+**🗺️ 第 2 步:查找仓库**
+系统找到具体的发货仓库
+
+
+
+
+**📞 第 3 步:建立通道**
+确认仓库营业且能发货
+
+
+
+
+
+
+
+**🚚 第 4 步:仓库发货**
+快递员把包裹送上门
+
+
+
+
+**🎁 第 5 步:拆箱体验**
+打开包裹,看到心仪的商品
+
+
+
**访问网页的过程和网购惊人地相似!**
@@ -20,10 +79,18 @@
+::: info 💡 核心启示
+理解浏览器工作原理的关键是:**把复杂的技术过程映射到熟悉的生活场景**。网购的 5 个步骤完美对应了浏览器访问网页的 5 个技术阶段。
+:::
+
---
## 1. 第一步:填写"订单" —— URL 解析
+::: tip 🤔 核心问题
+**为什么网址要写成这样?** `https://www.example.com:8080/path/page.html?id=123#section` — 这串字符到底有什么含义?
+:::
+
### 生活比喻:填写购物单
假设你只在订单上写"买鞋子",仓库肯定不知道发哪双。你需要写清楚:
@@ -49,12 +116,18 @@
-> **关键理解**:URL 的存在是为了让**人类**能记住和输入。计算机最终需要的是 **IP 地址**(就像快递员最终需要的是具体的仓库地址,而不是"Nike 官方店"这个名字)。
+::: info 💡 关键理解
+URL 的存在是为了让**人类**能记住和输入。计算机最终需要的是 **IP 地址**(就像快递员最终需要的是具体的仓库地址,而不是"Nike 官方店"这个名字)。
+:::
---
## 2. 第二步:查"地址簿" —— DNS 查询
+::: tip 🤔 核心问题
+**为什么浏览器能找到网站?** 你输入的是人类可读的域名(如 `baidu.com`),但计算机真正需要的是数字地址(IP)。这中间发生了什么?
+:::
+
### 生活比喻:查仓库地址
你下单写的是"Nike 官方店",但物流系统不知道仓库在哪。它需要查地址簿:
@@ -90,12 +163,20 @@
-> **为什么需要这么多层?** 想象一下如果全世界只有一个地址簿,几十亿人同时查,早就崩溃了。分层设计让每个层级只管理自己的"辖区",既高效又可靠。
+::: info 💡 为什么需要这么多层?
+想象一下如果全世界只有一个地址簿,几十亿人同时查,早就崩溃了。分层设计让每个层级只管理自己的"辖区",既高效又可靠。
+
+这就是互联网设计的核心思想:**分布式系统**。
+:::
---
## 3. 第三步:打电话确认 —— TCP 三次握手
+::: tip 🤔 核心问题
+**为什么需要"三次握手"?** 找到服务器地址后,为什么不能直接发送数据?为什么要先进行三次通信?
+:::
+
### 生活比喻:建立物流通道
假设物流车直接开到仓库,结果:
@@ -145,6 +226,10 @@
## 4. 第四步:"买家"和"商家"的对话 —— HTTP 请求与响应
+::: tip 🤔 核心问题
+**浏览器和服务器在说什么?** 建立连接后,浏览器如何"告诉"服务器它想要什么?服务器又如何"回应"?
+:::
+
### 生活比喻:仓库发货
物流车到达仓库:"这是订单(HTTP请求),**我要取回商品(网页 HTML 源代码)!**"
@@ -225,6 +310,10 @@ Set-Cookie: user_id=xyz789 ← 设置 Cookie
## 5. 第五步:拆开"包裹" —— 浏览器渲染
+::: tip 🤔 核心问题
+**代码怎么变成画面?** 服务器发来的是枯燥的 HTML/CSS/JavaScript 代码,浏览器如何把它们变成丰富多彩的网页?
+:::
+
### 生活比喻:拆箱与组装
你终于收到了快递包裹(HTTP 响应),但打开一看,里面不是现成的家具,而是一堆**零件**(HTML)和一本**组装说明书**(CSS)。作为"买家"(浏览器),你需要亲自动手组装:
@@ -298,14 +387,18 @@ DOM 树 + CSSOM 树 = **渲染树 (Render Tree)**。
-> **💡 你知道吗?**
->
-> **布局和绘制**是浏览器最忙碌的时候。网页里的元素越多、结构越复杂,浏览器就需要花更多时间来计算位置和上色。这就是为什么有的复杂网页打开会卡顿的原因。
+::: info 💡 你知道吗?
+**布局和绘制**是浏览器最忙碌的时候。网页里的元素越多、结构越复杂,浏览器就需要花更多时间来计算位置和上色。这就是为什么有的复杂网页打开会卡顿的原因。
+:::
---
## 5.5 网页是怎么"生成"的?静态网站 vs 动态网站
+::: tip 🤔 核心问题
+**网页内容从哪里来?** 前面我们讲了浏览器如何渲染页面,但服务器上的 HTML 文件是怎么来的?是提前做好还是现做?
+:::
+
前面我们讲的都是浏览器如何"拆开包裹"——把服务器发来的 HTML/CSS/JS 渲染成页面。但你有没有想过一个问题:**服务器上那个 HTML 文件是怎么来的?**
答案是:**有两种方式**,这就是静态网站和动态网站的区别。
@@ -381,6 +474,14 @@ DOM 树 + CSSOM 树 = **渲染树 (Render Tree)**。
## 6. 总结:一次完整的"网购"之旅
+::: tip 🎉 学完本章,你应该能
+- 解释从输入网址到显示页面的完整流程
+- 理解 URL、DNS、TCP、HTTP 的作用和关系
+- 知道浏览器如何渲染页面
+- 区分静态网站和动态网站
+- 用生活化比喻向他人解释浏览器工作原理
+:::
+
让我们回顾整个旅程:
| 阶段 | 技术术语 | 网购类比 | 核心任务 | 关键技术 |
@@ -403,6 +504,13 @@ DOM 树 + CSSOM 树 = **渲染树 (Render Tree)**。
这就是互联网的魅力:**复杂的技术,简单的体验**。
+::: info 💡 进阶学习
+如果你想深入了解某个环节,可以参考:
+- **API 开发**:[API 简介](./api-intro.md) - 学习如何设计和使用 API
+- **前端性能**:[前端性能优化](./frontend-performance.md) - 学习如何优化网页加载速度
+- **浏览器渲染**:[浏览器渲染管道](./browser-rendering-pipeline.md) - 深入了解渲染细节
+:::
+
---
## 7. 名词速查表 (Glossary)
@@ -424,4 +532,13 @@ DOM 树 + CSSOM 树 = **渲染树 (Render Tree)**。
---
-> **恭喜!** 现在当你再次在地址栏输入网址时,你已经能看到屏幕背后的那个忙碌而精彩的数字世界了。
+::: tip 🎓 恭喜
+现在当你再次在地址栏输入网址并按下回车时,你已经能看到屏幕背后的那个忙碌而精彩的数字世界了。
+
+你理解了:
+- 为什么有时候网页打不开(DNS 解析失败、服务器宕机)
+- 为什么有的网页快、有的慢(网络延迟、服务器性能、页面复杂度)
+- 浏览器是如何把代码变成画面的(渲染管道)
+
+**这就是理解技术原理的价值** — 遇到问题时,你能知道从哪里找原因,而不是束手无策。
+:::
diff --git a/docs/zh-cn/appendix/3-browser-and-frontend/frontend-frameworks.md b/docs/zh-cn/appendix/3-browser-and-frontend/frontend-frameworks.md
index ce4e881..2a2cfc9 100644
--- a/docs/zh-cn/appendix/3-browser-and-frontend/frontend-frameworks.md
+++ b/docs/zh-cn/appendix/3-browser-and-frontend/frontend-frameworks.md
@@ -1,69 +1,101 @@
-# 前端框架对比(React / Vue / Svelte / Angular)
-::: tip 🎯 核心问题
-**为什么网页越来越复杂?前端技术为什么要不断演进?** 这个问题会带你理解从简单网页到现代 Web 应用的技术演变之路。
+# 前端框架深度指南
+
+::: tip 前言
+你已经学会了 HTML、CSS 和 JavaScript 基础,能做出简单的网页了。但随着网页功能越来越复杂,你可能会发现:用原生 JavaScript 写代码变得很难维护,改一处要动很多地方,多人协作时经常冲突。
+
+这就是我们需要前端框架的原因——它让代码更有条理、更易维护、更高效开发。在 vibecoding 里,AI 会帮你写大部分代码。但你至少得能看懂不同框架的代码风格,知道它们的优缺点,这样 AI 才能帮你选择最合适的技术栈。
+
+读完这篇,你就能:
+- 理解前端技术为什么要不断演进
+- 知道 Vue、React、Svelte、Angular 各有什么特点
+- 懂得"数据驱动"、"组件化"这些核心概念
+- 能根据项目选择合适的框架
:::
+**这篇文章会带你学什么?**
+
+| 章节 | 内容 | 学完能干嘛 |
+|-----|------|-----------|
+| **第 1 章** | 为什么要关注前端演进 | 明白技术演进是为了解决什么问题 |
+| **第 2 章** | 静态网页时代 | 了解最早期的网页开发方式 |
+| **第 3 章** | jQuery 时代 | 理解"命令式"编程的痛点 |
+| **第 4 章** | Vue/React 时代 | 掌握"声明式"和"数据驱动"思想 |
+| **第 5 章** | 渲染策略 | 知道 CSR、SSR、SSG 的区别和适用场景 |
+| **第 6 章** | 工程化工具 | 理解 Webpack、Vite 等构建工具的作用 |
+
+每一章都从"为什么需要这个技术"开始,让你理解技术演进背后的逻辑。
+
---
## 1. 为什么要关注前端演进史?
+::: tip 🤔 核心问题
+**为什么网页越来越复杂?前端技术为什么要不断演进?** 这个问题会带你理解从简单网页到现代 Web 应用的技术演变之路。
+:::
+
### 1.1 从"电子海报"到"桌面应用"
-想象一下你在街上看到的**海报**:
+想象一下你在街上看到的**海报**:
-- ✅ 有内容(文字、图片)
-- ✅ 有设计(颜色、排版)
-- ❌ 但你跟它说话,它不会回应
-- ❌ 你点击某个地方,不会发生什么
+- ✅ 有内容(文字、图片)
+- ✅ 有设计(颜色、排版)
+- ❌ 但你跟它说话,它不会回应
+- ❌ 你点击某个地方,不会发生什么
-**最早的网页**就是这样的"电子海报":只能看、不能改、内容固定。
+**最早的网页**就是这样的"电子海报":只能看、不能改、内容固定。
-**现代网页**完全不同了。它们像**桌面应用**(VS Code、Figma):
+**现代网页**完全不同了。它们像**桌面应用**(VS Code、Figma):
- ✅ 可以编辑文档、画图、玩游戏
- ✅ 实时响应你的每个操作
- ✅ 甚至可以离线工作
-**这种转变的核心原因: 网页的功能越来越复杂,需要更高效的技术和开发方式。**
+**这种转变的核心原因:网页的功能越来越复杂,需要更高效的技术和开发方式。**
-### 1.2 一个生活的比喻:盖房子
+### 1.2 一个生活的比喻:盖房子
-前端技术的演进,就像盖房子方式的进化:
+前端技术的演进,就像盖房子方式的进化:
-| 时代 | 🏠 盖房比喻 | 实际特点 | 优缺点 |
-| --------- | ------------------ | ---------------------------- | --------------------------- |
-| **2000s** | **贴海报** | 静态网页,写好 HTML 就行 | ✅ 简单 ❌ 不能互动 |
-| **2010s** | **请工人手动装修** | jQuery 时代,手动操作每个元素 | ✅ 能互动 ❌ 代码乱、难维护 |
-| **2020s** | **用乐高搭房子** | Vue/React 时代,组件化开发 | ✅ 高效、可维护 ❌ 学习曲线 |
+| 时代 | 🏠 盖房比喻 | 实际特点 | 优缺点 |
+|------|-----------|---------|--------|
+| **2000s** | **贴海报** | 静态网页,写好 HTML 就行 | ✅ 简单 ❌ 不能互动 |
+| **2010s** | **请工人手动装修** | jQuery 时代,手动操作每个元素 | ✅ 能互动 ❌ 代码乱、难维护 |
+| **2020s** | **用乐高搭房子** | Vue/React 时代,组件化开发 | ✅ 高效、可维护 ❌ 学习曲线 |
-::: tip 💡 从表格中你能看到什么?
+::: tip 💡 从表格中你能看到什么?
-**阶段一 → 阶段二**: 从"不能动"到"能动"。这是质的飞跃——网页开始有交互,但代价是代码变得混乱。
+**阶段一 → 阶段二**:从"不能动"到"能动"。这是质的飞跃——网页开始有交互,但代价是代码变得混乱。
-**阶段二 → 阶段三**: 从"能用"到"好用"。组件化让代码像积木一样可复用,大幅提升开发效率。
+**阶段二 → 阶段三**:从"能用"到"好用"。组件化让代码像积木一样可复用,大幅提升开发效率。
-**核心思想**: 技术演进不是"为了新而新",而是为了解决上一个阶段的痛点。
+**核心思想**:技术演进不是"为了新而新",而是为了解决上一个阶段的痛点。
:::
---
-## 2. 第一阶段:静态网页与"切图"(2000s)
+---
+
+## 2. 第一阶段:静态网页与"切图"(2000s)
+
+::: tip 🤔 核心问题
+**最早的网页是什么样的?为什么那时候不需要框架?** 理解这个阶段的局限性,才能明白后来技术演进的必要性。
+:::
-### 2.1 这个时代是什么样的?
+### 2.1 这个时代是什么样的?
-**开发方式**:
+**开发方式**:
- 写几个 HTML 文件
- 内嵌一些 CSS 和 JavaScript
- 直接把文件拖到浏览器就能看效果
- 上传文件夹到服务器就完成部署
-**特点**:
+**特点**:
-- ✅ **优点**: 简单直接,没有学习成本,写完就能跑
-- ❌ **缺点**: 无法实现复杂交互,代码一多就乱
+- ✅ **优点**:简单直接,没有学习成本,写完就能跑
+- ❌ **缺点**:无法实现复杂交互,代码一多就乱
::: details 查看当时的项目结构
@@ -80,63 +112,69 @@ project/
└── images/
```
-**遇到的问题**:
+**遇到的问题**:
-1. **全局变量污染**: 所有变量都在全局命名空间,容易互相覆盖
-2. **依赖管理混乱**: 必须按正确顺序加载 JS 文件,否则会报错
-3. **代码难以复用**: 想复用某个功能,只能复制粘贴
- :::
+1. **全局变量污染**:所有变量都在全局命名空间,容易互相覆盖
+2. **依赖管理混乱**:必须按正确顺序加载 JS 文件,否则会报错
+3. **代码难以复用**:想复用某个功能,只能复制粘贴
+:::
-### 2.2 "切图"是什么?
+### 2.2 "切图"是什么?
-
+你可能听说过"切图"这个词。它是早期前端的主要工作:
-你可能听说过"切图"这个词。它是早期前端的主要工作:
-
-**什么是切图?**
+**什么是切图?**
设计师用 Photoshop 设计好页面 → 前端把设计切成小图片 → 用 HTML 把图片拼成页面
-**为什么这么慢?**
+**为什么这么慢?**
-网页上的每张小图片,浏览器都要发一次**网络请求**。请求越多,加载越慢。
+网页上的每张小图片,浏览器都要发一次**网络请求**。请求越多,加载越慢。
-::: tip 💡 雪碧图(Sprite)
+👇 **动手试试看**:观察图片请求对加载性能的影响
-为了减少请求数,出现了"雪碧图"技术:把很多小图合成一张大图。
+
-优点是请求数变少,缺点是制作和维护都很麻烦。
+::: tip 💡 雪碧图(Sprite)
-这个阶段的教训:**请求太多是性能大敌**。
+为了减少请求数,出现了"雪碧图"技术:把很多小图合成一张大图。
+
+优点是请求数变少,缺点是制作和维护都很麻烦。
+
+这个阶段的教训:**请求太多是性能大敌**。
:::
---
-## 3. 第二阶段:jQuery 时代 - "手动搬砖"(2010s)
+---
-### 3.1 为什么需要 jQuery?
+## 3. 第二阶段:jQuery 时代 - "手动搬砖"(2010s)
-随着网页变复杂,原生 JavaScript 的问题暴露出来:
+::: tip 🤔 核心问题
+**为什么需要 jQuery?它解决了什么问题,又带来了什么新问题?** 理解 jQuery 的局限性,才能明白 Vue/React 的价值。
+:::
-- ❌ **API 繁琐**: 简单的操作也要写很多代码
-- ❌ **浏览器兼容**: 不同浏览器的 API 不一样,要写很多兼容代码
-- ❌ **选择器弱**: 找元素很麻烦
+### 3.1 为什么需要 jQuery?
-**jQuery** 诞生了。它让 JavaScript 变得简单:
+随着网页变复杂,原生 JavaScript 的问题暴露出来:
+
+- ❌ **API 繁琐**:简单的操作也要写很多代码
+- ❌ **浏览器兼容**:不同浏览器的 API 不一样,要写很多兼容代码
+- ❌ **选择器弱**:找元素很麻烦
+
+**jQuery** 诞生了。它让 JavaScript 变得简单:
```javascript
-// 原生 JavaScript (繁琐)
+// 原生 JavaScript(繁琐)
const element = document.getElementById('title')
-// jQuery (简洁)
+// jQuery(简洁)
const element = $('#title')
```
-### 3.2 jQuery 的思路:亲手改页面
+### 3.2 jQuery 的思路:亲手改页面
-
-
-jQuery 的核心思路是**命令式**: 你告诉浏览器"怎么做"。
+jQuery 的核心思路是**命令式**:你告诉浏览器"怎么做"。
```javascript
// 找到标题元素
@@ -149,11 +187,15 @@ $('#submit-btn').attr('disabled', true)
$('ul').append('
新项目')
```
-**问题**: 你需要记住页面上有哪些元素,每次数据变化都要手动更新所有相关元素。
+**问题**:你需要记住页面上有哪些元素,每次数据变化都要手动更新所有相关元素。
+
+👇 **动手试试看**:对比 jQuery 和数据驱动的方式
+
+
::: warning ⚠️ jQuery 的痛点
-想象你在做一个购物车:
+想象你在做一个购物车:
```javascript
// 用户点击"添加到购物车"
@@ -165,32 +207,30 @@ function addToCart() {
$('#cart-page-count').text(cartCount) // 购物车页面
$('#checkout-price').text(calculatePrice()) // 结算按钮
- // 如果漏了一个地方,页面就不一致了!
+ // 如果漏了一个地方,页面就不一致了!
}
```
-**这就是"手动搬砖"的代价**: 容易出错,难以维护。
+**这就是"手动搬砖"的代价**:容易出错,难以维护。
:::
-### 3.3 移动端普及:响应式设计的出现
+### 3.3 移动端普及:响应式设计的出现
-这个阶段还有一个重要变化:**手机和平板开始流行**。
+这个阶段还有一个重要变化:**手机和平板开始流行**。
-
+网页必须适配不同屏幕。这需要**响应式布局**:同一套 HTML/CSS,自动根据屏幕宽度变换布局。
-网页必须适配不同屏幕。这需要**响应式布局**: 同一套 HTML/CSS,自动根据屏幕宽度变换布局。
-
-**响应式布局的核心: 媒体查询 (Media Query)**
+**响应式布局的核心:媒体查询(Media Query)**
```css
-/* 电脑屏幕(大于 640px) */
+/* 电脑屏幕(大于 640px) */
@media (min-width: 640px) {
.container {
display: flex;
}
}
-/* 手机屏幕(小于 640px) */
+/* 手机屏幕(小于 640px) */
@media (max-width: 640px) {
.container {
display: block;
@@ -198,35 +238,43 @@ function addToCart() {
}
```
+👇 **动手试试看**:调整浏览器宽度,观察响应式布局的效果
+
+
+
::: tip 💡 响应式就像"智能相框"
-想象你在不同房间看同一张照片:
+想象你在不同房间看同一张照片:
-- 在**大客厅**(电脑屏幕),照片可以摆大一些,旁边还能放其他装饰品
-- 在**小卧室**(手机屏幕),照片需要缩小,其他装饰品要收起来
+- 在**大客厅**(电脑屏幕),照片可以摆大一些,旁边还能放其他装饰品
+- 在**小卧室**(手机屏幕),照片需要缩小,其他装饰品要收起来
-**响应式布局**就是"智能相框",它会自动根据房间大小调整展示方式。
+**响应式布局**就是"智能相框",它会自动根据房间大小调整展示方式。
:::
---
-## 4. 第三阶段:从"手动搬砖"到"数据驱动"(Vue/React)
+---
-### 4.1 为什么需要新框架?
+## 4. 第三阶段:从"手动搬砖"到"数据驱动"(Vue/React)
-jQuery 时代的问题积累到一定程度:
+::: tip 🤔 核心问题
+**为什么需要 Vue/React?它们和 jQuery 的本质区别是什么?** 理解"声明式"和"数据驱动",是掌握现代前端框架的关键。
+:::
-- **代码一多就乱**: 到处都是 DOM 操作,难以维护
-- **容易出 bug**: 漏更新一个地方,页面就不一致
-- **协作困难**: 多人修改同一个文件,容易冲突
+### 4.1 为什么需要新框架?
-**Vue / React** 的核心思路:**只改数据,页面自动更新**。
+jQuery 时代的问题积累到一定程度:
-### 4.2 Vue/React 的思路:声明式 UI
+- **代码一多就乱**:到处都是 DOM 操作,难以维护
+- **容易出 bug**:漏更新一个地方,页面就不一致
+- **协作困难**:多人修改同一个文件,容易冲突
-
+**Vue / React** 的核心思路:**只改数据,页面自动更新**。
-**jQuery (命令式)**:
+### 4.2 Vue/React 的思路:声明式 UI
+
+**jQuery(命令式)**:
```javascript
// 你要告诉浏览器每一步怎么做
@@ -235,7 +283,7 @@ $('#title').css('color', 'red')
$('#title').show()
```
-**Vue (声明式)**:
+**Vue(声明式)**:
```javascript
// 你只需告诉浏览器"要显示什么"
@@ -248,213 +296,409 @@ data() {
}
```
+👇 **动手试试看**:对比命令式和声明式的区别
+
+
+
::: tip 💡 命令式 vs 声明式
-就像画一幅画:
+就像画一幅画:
-- **命令式**: 你告诉画家"拿起笔,蘸红颜料,在坐标(10,10)画一个圈"
-- **声明式**: 你直接给画家一张照片,"给我画成这样"
+- **命令式**:你告诉画家"拿起笔,蘸红颜料,在坐标(10,10)画一个圈"
+- **声明式**:你直接给画家一张照片,"给我画成这样"
-Vue/React 就是"声明式": 你描述"页面长什么样",框架负责"怎么把它画出来"。
+Vue/React 就是"声明式":你描述"页面长什么样",框架负责"怎么把它画出来"。
:::
-### 4.3 组件化:像搭乐高一样写页面
+### 4.3 组件化:像搭乐高一样写页面
-**Vue / React** 最强大的特性是**组件化**: 把页面拆成一个个独立的"积木"。
+**Vue / React** 最强大的特性是**组件化**:把页面拆成一个个独立的"积木"。
-想象一下你在搭乐高:
+想象一下你在搭乐高:
-- 你不需要"从头开始雕刻每一块积木"(从头写 HTML/CSS)
-- 你只需要"按说明书把积木拼在一起"(把组件组合起来)
-- 每个积木都是**独立的**,你可以在不同的套装里**重复使用**
+- 你不需要"从头开始雕刻每一块积木"(从头写 HTML/CSS)
+- 你只需要"按说明书把积木拼在一起"(把组件组合起来)
+- 每个积木都是**独立的**,你可以在不同的套装里**重复使用**
-**组件的好处**:
+**组件的好处**:
-- **复用**: 写一个"商品卡片"组件,可以用 100 次
-- **封装**: 组件内部的状态不影响别人
-- **维护**: 修改一个组件,所有用到它的地方都会更新
+- **复用**:写一个"商品卡片"组件,可以用 100 次
+- **封装**:组件内部的状态不影响别人
+- **维护**:修改一个组件,所有用到它的地方都会更新
-### 4.4 SPA:单页应用的诞生
+::: info 💡 识别技巧
+- 看到 `
` → 这是一个组件
+- 看到 `import xxx from './xxx.vue'` → 在导入一个组件
+- 看到 `props: {...}` → 组件接收的参数
+- 看到 `emit('xxx')` → 组件向父组件发送事件
+:::
+
+### 4.4 SPA:单页应用的诞生
+
+**Vue / React** 时代还有一个重要变化:**从 MPA 到 SPA**。
+
+**MPA(Multi-Page Application)**:
+
+- 点一个链接 → 整页刷新 → 显示新页面
+- 就像**翻书**:每翻一页都要把旧书合上、去书架拿新书
+
+**SPA(Single-Page Application)**:
+
+- 点一个链接 → 只刷新内容区域 → 页面不刷新
+- 就像**同一本书里换章节**:只擦掉旧内容、写上新内容
+
+👇 **动手试试看**:体验 MPA 和 SPA 的区别
-**Vue / React** 时代还有一个重要变化:**从 MPA 到 SPA**。
+**SPA 的优点**:
-**MPA (Multi-Page Application)**:
-
-- 点一个链接 → 整页刷新 → 显示新页面
-- 就像**翻书**: 每翻一页都要把旧书合上、去书架拿新书
-
-**SPA (Single-Page Application)**:
-
-- 点一个链接 → 只刷新内容区域 → 页面不刷新
-- 就像**同一本书里换章节**: 只擦掉旧内容、写上新内容
-
-**SPA 的优点**:
-
-- ✅ **体验丝滑**: 页面切换快
-- ✅ **状态好管理**: 输入的内容、滚动位置都在
-- ❌ **首屏可能慢**: 需要先下载 JavaScript
-- ❌ **SEO 要额外处理**: 搜索引擎可能抓不到内容(需要 SSR/SSG)
+- ✅ **体验丝滑**:页面切换快
+- ✅ **状态好管理**:输入的内容、滚动位置都在
+- ❌ **首屏可能慢**:需要先下载 JavaScript
+- ❌ **SEO 要额外处理**:搜索引擎可能抓不到内容(需要 SSR/SSG)
---
-## 5. 渲染策略:从 CSR 到 SSR/SSG
+---
+
+## 5. 渲染策略:从 CSR 到 SSR/SSG
+
+::: tip 🤔 核心问题
+**页面是在服务器生成,还是在浏览器生成?** 不同渲染策略各有优劣,选择合适的策略对性能和 SEO 至关重要。
+:::
+
+**CSR(Client-Side Rendering)客户端渲染**:
+
+- 浏览器下载 JavaScript → 执行代码 → 生成页面
+- 优点:交互流畅,服务器压力小
+- 缺点:首屏慢,不利于 SEO
+
+**SSR(Server-Side Rendering)服务端渲染**:
+
+- 服务器生成 HTML → 发给浏览器 → 浏览器直接显示
+- 优点:首屏快,利于 SEO
+- 缺点:服务器压力大,实现复杂
+
+**SSG(Static Site Generation)静态站点生成**:
+
+- 构建时生成所有页面的 HTML
+- 优点:极快,完全静态,CDN 友好
+- 缺点:不适合动态内容
+
+👇 **动手试试看**:对比不同渲染策略的特点
-## 6. 第四阶段:工程化与构建工具(2015s-2020s)
-
-### 6.1 为什么需要"工程化"?
-
-前端项目越来越大,不能再靠"手动引入脚本"。
-
-**工程化**就是用工具和规范,让开发更高效、代码更可靠、协作更顺畅。
-
-::: tip 💡 工程化 = 从"手工作坊"到"现代化工厂"
-
-想象一下你在家做饭 vs 开餐厅:
-
-- **在家做饭**: 想吃什么就做什么,很自由
-- **开餐厅**: 需要标准化的菜谱、规范的操作流程、统一的原材料采购
-
-前端开发也一样:
-
-- **小项目**: 怎么写都行
-- **大项目**: 需要统一的代码规范、自动化工具、标准化流程
- :::
-
-### 6.2 构建工具:Webpack → Vite
-
-**Webpack** (传统):
-
-- 工作方式:**先打包,后服务**
-- 启动时: 打包所有代码 → 启动服务器
-- 问题:**慢**。项目越大,启动越慢(可能要等 30 秒)
-
-**Vite** (现代):
-
-- 工作方式:**按需编译**
-- 启动时: 不打包,直接启动服务器
-- 浏览器请求哪个文件,就实时编译哪个
-- 优势:**快**。通常 1 秒内启动
-
-| 对比项 | Webpack | Vite | 提升 |
-| -------- | ------- | ------ | ------------ |
-| 冷启动 | 30s+ | <1s | **快 30 倍** |
-| 热更新 | 3-5s | <100ms | **快 30 倍** |
-| 配置文件 | 几百行 | 几十行 | **大幅简化** |
-
-::: tip 💡 为什么 Vite 这么快?
-
-**Webpack** 就像**整备家当搬家**:先把所有东西打包,再出门。
-
-**Vite** 就像**轻装旅行**:只带必需品,用到什么再买什么。
-
-在开发环境,大多数时候你只需要修改几个文件,Vite 只编译这几个文件,当然快。
+::: info 💡 如何选择?
+- **内容网站**(博客、文档):优先 SSG
+- **需要 SEO 的动态网站**(电商、新闻):使用 SSR
+- **后台管理系统**:使用 CSR
+- **混合需求**:考虑 Nuxt/Next.js 的混合渲染
:::
---
-## 7. 总结:演进的本质
+## 6. 第四阶段:工程化与构建工具(2015s-2020s)
-前端技术的演进,本质上是在解决两个问题:
+::: tip 🤔 核心问题
+**为什么前端需要"工程化"?构建工具到底在做什么?** 理解工程化,才能看懂现代前端项目的工作流程。
+:::
-### 7.1 效率:从手动到自动
+### 6.1 为什么需要"工程化"?
-| 时代 | 开发方式 | 效率 |
-| --------- | ------------------------ | ---------- |
-| **2000s** | 手写 HTML/CSS/JS | ⭐ |
-| **2010s** | jQuery + 手动 DOM 操作 | ⭐⭐ |
-| **2020s** | Vue/React + 数据驱动 | ⭐⭐⭐ |
-| **现在** | 组件化 + 工程化 + 自动化 | ⭐⭐⭐⭐⭐ |
+前端项目越来越大,不能再靠"手动引入脚本"。
-### 7.2 规模:从个人到团队
+**工程化**就是用工具和规范,让开发更高效、代码更可靠、协作更顺畅。
-| 时代 | 项目规模 | 协作方式 |
-| --------- | ---------- | ----------------------- |
-| **2000s** | 几个文件 | 单人就能维护 |
-| **2010s** | 几十个文件 | 小团队,容易冲突 |
-| **2020s** | 几百个文件 | 中团队,需要规范 |
-| **现在** | 几千个文件 | 大团队,需要完整工程体系 |
+::: tip 💡 工程化 = 从"手工作坊"到"现代化工厂"
+
+想象一下你在家做饭 vs 开餐厅:
+
+- **在家做饭**:想吃什么就做什么,很自由
+- **开餐厅**:需要标准化的菜谱、规范的操作流程、统一的原材料采购
+
+前端开发也一样:
+
+- **小项目**:怎么写都行
+- **大项目**:需要统一的代码规范、自动化工具、标准化流程
+:::
+
+### 6.2 构建工具:Webpack → Vite
+
+**Webpack**(传统):
+
+- 工作方式:**先打包,后服务**
+- 启动时:打包所有代码 → 启动服务器
+- 问题:**慢**。项目越大,启动越慢(可能要等 30 秒)
+
+**Vite**(现代):
+
+- 工作方式:**按需编译**
+- 启动时:不打包,直接启动服务器
+- 浏览器请求哪个文件,就实时编译哪个
+- 优势:**快**。通常 1 秒内启动
+
+| 对比项 | Webpack | Vite | 提升 |
+|--------|---------|------|------|
+| 冷启动 | 30s+ | <1s | **快 30 倍** |
+| 热更新 | 3-5s | <100ms | **快 30 倍** |
+| 配置文件 | 几百行 | 几十行 | **大幅简化** |
+
+::: tip 💡 为什么 Vite 这么快?
+
+**Webpack** 就像**整备家当搬家**:先把所有东西打包,再出门。
+
+**Vite** 就像**轻装旅行**:只带必需品,用到什么再买什么。
+
+在开发环境,大多数时候你只需要修改几个文件,Vite 只编译这几个文件,当然快。
+:::
---
-## 8. 学习路线图
+---
-### 8.1 如果你是零基础
+## 7. 主流框架对比
-**第 1 步: HTML/CSS/JavaScript 基础**
+::: tip 🤔 核心问题
+**Vue、React、Svelte、Angular 各有什么特点?如何选择适合自己的框架?** 了解它们的设计理念和使用场景,才能做出明智的选择。
+:::
+
+### 7.1 四大框架对比
+
+| 特性 | Vue | React | Svelte | Angular |
+|------|-----|-------|--------|---------|
+| **设计理念** | 渐进式框架 | UI 库 | 编译时框架 | 完整平台 |
+| **学习曲线** | ⭐⭐ 简单 | ⭐⭐⭐ 中等 | ⭐⭐ 简单 | ⭐⭐⭐⭐ 陡峭 |
+| **性能** | 快 | 快 | **极快** | 快 |
+| **生态系统** | 完善 | **最完善** | 成长中 | 完善 |
+| **包大小** | 小 | 中等 | **最小** | 大 |
+| **适合场景** | 中小型项目 | 大型项目 | 性能要求高 | 企业级应用 |
+| **公司支持** | 尤雨溪(独立) | Meta | 社区 | Google |
+
+### 7.2 Vue:渐进式框架
+
+**核心理念**:渐进式采用,可以只用一部分,也可以用全家桶
+
+```vue
+
+ {{ message }}
+
+
+
+```
+
+**优点**:
+- ✅ 学习曲线平缓,中文文档完善
+- ✅ 模板语法直观,易于理解
+- ✅ 单文件组件(.vue)结构清晰
+- ✅ 适合快速开发
+
+**缺点**:
+- ❌ 大型项目的状态管理需要额外学习 Vuex/Pinia
+- ❌ 灵活性略逊于 React
+
+**适用场景**:
+- 中小型 Web 应用
+- 快速原型开发
+- 中文团队(文档友好)
+
+### 7.3 React:UI 库
+
+**核心理念**:只负责视图层,其他问题交给社区
+
+```jsx
+function App() {
+ const [message, setMessage] = useState('Hello React')
+ return
{message}
+}
+```
+
+**优点**:
+- ✅ 生态系统最完善,组件库丰富
+- ✅ JSX 语法灵活,表达能力强大
+- ✅ 虚拟 DOM 性能优秀
+- ✅ 适合大型项目
+
+**缺点**:
+- ❌ 学习曲线较陡,需要掌握额外概念
+- ❌ 需要自己选择和搭配各种库
+- ❌ JSX 需要编译,不能直接在浏览器运行
+
+**适用场景**:
+- 大型复杂应用
+- 需要丰富生态的项目
+- 跨平台开发(React Native)
+
+### 7.4 Svelte:编译时框架
+
+**核心理念**:没有虚拟 DOM,编译时将组件转换为高效的原生代码
+
+```svelte
+
+
+
{message}
+```
+
+**优点**:
+- ✅ **性能最优**(无虚拟 DOM 运行时开销)
+- ✅ 包体积最小
+- ✅ 语法简单直观
+- ✅ 响应式系统天然支持
+
+**缺点**:
+- ❌ 生态相对较小
+- ❌ 社区规模不如 Vue/React
+- ❌ 第三方库较少
+
+**适用场景**:
+- 性能要求极高的应用
+- 包体积敏感的项目
+- 愿意尝试新技术的团队
+
+### 7.5 Angular:完整平台
+
+**核心理念**:提供完整的解决方案,开箱即用
+
+```typescript
+@Component({
+ selector: 'app-root',
+ template: '
{{ message }}
'
+})
+export class AppComponent {
+ message = 'Hello Angular'
+}
+```
+
+**优点**:
+- ✅ 功能完整,路由、HTTP、表单全都有
+- ✅ TypeScript 原生支持
+- ✅ 适合大型团队和项目
+- ✅ 代码规范统一
+
+**缺点**:
+- ❌ 学习曲线陡峭
+- ❌ 概念多,复杂度高
+- ❌ 包体积大
+- ❌ 不适合小型项目
+
+**适用场景**:
+- 大型企业级应用
+- 需要严格规范的团队
+- 已有 TypeScript 技术栈的项目
+
+---
+
+## 8. 总结:演进的本质
+
+前端技术的演进,本质上是在解决两个问题:
+
+### 8.1 效率:从手动到自动
+
+| 时代 | 开发方式 | 效率 |
+|------|---------|------|
+| **2000s** | 手写 HTML/CSS/JS | ⭐ |
+| **2010s** | jQuery + 手动 DOM 操作 | ⭐⭐ |
+| **2020s** | Vue/React + 数据驱动 | ⭐⭐⭐ |
+| **现在** | 组件化 + 工程化 + 自动化 | ⭐⭐⭐⭐⭐ |
+
+### 8.2 规模:从个人到团队
+
+| 时代 | 项目规模 | 协作方式 |
+|------|---------|---------|
+| **2000s** | 几个文件 | 单人就能维护 |
+| **2010s** | 几十个文件 | 小团队,容易冲突 |
+| **2020s** | 几百个文件 | 中团队,需要规范 |
+| **现在** | 几千个文件 | 大团队,需要完整工程体系 |
+
+---
+
+---
+
+## 9. 学习路线图
+
+### 9.1 如果你是零基础
+
+**第 1 步:HTML/CSS/JavaScript 基础**
- 理解网页的三大基石
- 能写出简单的静态页面
-**第 2 步: 学习一个框架(Vue 推荐)**
+**第 2 步:学习一个框架(Vue 推荐)**
- 理解"数据驱动"的思想
- 掌握组件化开发
-**第 3 步: 实战项目**
+**第 3 步:实战项目**
- 做一个完整的单页应用
- 熟悉路由、状态管理、API 调用
-### 8.2 如果你有基础
+### 9.2 如果你有基础
-**进阶方向**:
+**进阶方向**:
-- **工程化**: 学习 Vite/Webpack,理解构建流程
-- **性能优化**: 学习懒加载、代码分割、缓存策略
-- **TypeScript**: 为代码加上类型,提升可靠性
-- **服务端渲染**: 学习 Nuxt/Next.js,解决 SEO 和首屏问题
+- **工程化**:学习 Vite/Webpack,理解构建流程
+- **性能优化**:学习懒加载、代码分割、缓存策略
+- **TypeScript**:为代码加上类型,提升可靠性
+- **服务端渲染**:学习 Nuxt/Next.js,解决 SEO 和首屏问题
---
-## 9. 名词速查表 (Glossary)
+## 10. 你现在应该能识别的代码
-| 名词 | 英文 | 用人话解释 |
-| ---------------- | ----------------------- | --------------------------------------------- |
-| **DOM** | Document Object Model | 文档对象模型。用对象树表示页面,可被 JS 读写。 |
-| **jQuery** | - | 早期流行的 JS 库,简化了 DOM 操作。 |
-| **Vue/React** | - | 现代前端框架,采用数据驱动和组件化开发。 |
-| **组件** | Component | 可复用的 UI 单元,如按钮、卡片、导航栏。 |
-| **MPA** | Multi-Page Application | 多页应用。每次跳转都重新加载整个页面。 |
-| **SPA** | Single-Page Application | 单页应用。只加载一次,后续切换不刷新页面。 |
-| **路由** | Routing | 管理页面之间切换的规则和过程。 |
-| **SSR** | Server-Side Rendering | 服务端渲染。服务器生成 HTML 后发给浏览器。 |
-| **SSG** | Static Site Generation | 静态站点生成。构建时预渲染页面为静态 HTML。 |
-| **Webpack** | - | 传统打包工具,先打包后服务。 |
-| **Vite** | - | 现代构建工具,按需编译,速度极快。 |
-| **响应式** | Responsive Design | 页面自动适配不同屏幕尺寸的设计。 |
-| **媒体查询** | Media Query | CSS 的条件判断,根据屏幕宽度应用不同样式。 |
-| **命令式** | Imperative | 告诉程序"怎么做"。 |
-| **声明式** | Declarative | 告诉程序"要什么"。 |
-| **数据驱动** | Data-Driven | 只修改数据,界面自动更新。 |
-| **Tree Shaking** | - | 摇树优化。自动移除未使用的代码,减小包体积。 |
-| **代码分割** | Code Splitting | 把代码分成多个小块,按需加载。 |
+通过阅读本章,你应该能够:
+
+- ✅ 理解前端技术演进的脉络和原因
+- ✅ 区分 Vue、React、Svelte、Angular 的特点
+- ✅ 理解"命令式"和"声明式"的区别
+- ✅ 掌握"数据驱动"的核心思想
+- ✅ 知道组件化开发的价值
+- ✅ 了解 CSR、SSR、SSG 的适用场景
+- ✅ 理解构建工具(Webpack、Vite)的作用
+- ✅ 能根据项目选择合适的框架和技术栈
+
+::: info 💡 实际应用
+当你用 AI 做项目时,你可以这样告诉它:
+
+- "这是一个需要 SEO 的博客网站,用 Nuxt(Vue 的 SSR 框架)"
+- "这是一个后台管理系统,用 Vue + Element Plus,不需要 SSR"
+- "这是一个性能要求高的 Web 应用,考虑使用 Svelte"
+- "项目已经用 React 了,继续用 React 生态的库"
+:::
---
-## 总结
+## 名词速查表
-前端技术的演进,本质上是**从"手工"到"工业化"的进化**:
-
-- **2000s**: 手工时代,简单直接
-- **2010s**: 工具化时代,开始有框架
-- **2020s**: 工业化时代,组件化 + 工程化
-- **现在**: 智能化时代,AI 辅助开发
-
-理解这个演进,你就能:
-
-- 知道为什么要有 Vue/React
-- 理解"数据驱动"的价值
-- 明白工程化的必要性
-- 快速上手新技术
-
-**下一步建议**:
-
-- 如果你想快速上手,学习 **Vue 3** (推荐) 或 **React**
-- 如果你想深入理解,学习 **Vite** 构建流程
-- 如果你想提升代码质量,学习 **TypeScript**
-
-祝你学习愉快!
+| 名词 | 英文 | 用人话解释 |
+|------|------|-----------|
+| **DOM** | Document Object Model | 文档对象模型。用对象树表示页面,可被 JS 读写。 |
+| **jQuery** | - | 早期流行的 JS 库,简化了 DOM 操作。 |
+| **Vue/React** | - | 现代前端框架,采用数据驱动和组件化开发。 |
+| **组件** | Component | 可复用的 UI 单元,如按钮、卡片、导航栏。 |
+| **MPA** | Multi-Page Application | 多页应用。每次跳转都重新加载整个页面。 |
+| **SPA** | Single-Page Application | 单页应用。只加载一次,后续切换不刷新页面。 |
+| **路由** | Routing | 管理页面之间切换的规则和过程。 |
+| **SSR** | Server-Side Rendering | 服务端渲染。服务器生成 HTML 后发给浏览器。 |
+| **SSG** | Static Site Generation | 静态站点生成。构建时预渲染页面为静态 HTML。 |
+| **CSR** | Client-Side Rendering | 客户端渲染。浏览器通过 JS 生成页面。 |
+| **Webpack** | - | 传统打包工具,先打包后服务。 |
+| **Vite** | - | 现代构建工具,按需编译,速度极快。 |
+| **响应式** | Responsive Design | 页面自动适配不同屏幕尺寸的设计。 |
+| **媒体查询** | Media Query | CSS 的条件判断,根据屏幕宽度应用不同样式。 |
+| **命令式** | Imperative | 告诉程序"怎么做"。 |
+| **声明式** | Declarative | 告诉程序"要什么"。 |
+| **数据驱动** | Data-Driven | 只修改数据,界面自动更新。 |
+| **Tree Shaking** | - | 摇树优化。自动移除未使用的代码,减小包体积。 |
+| **代码分割** | Code Splitting | 把代码分成多个小块,按需加载。 |
diff --git a/docs/zh-cn/appendix/3-browser-and-frontend/javascript-deep-dive.md b/docs/zh-cn/appendix/3-browser-and-frontend/javascript-deep-dive.md
index 38596ac..2069a76 100644
--- a/docs/zh-cn/appendix/3-browser-and-frontend/javascript-deep-dive.md
+++ b/docs/zh-cn/appendix/3-browser-and-frontend/javascript-deep-dive.md
@@ -1,206 +1,234 @@
# JavaScript 深度指南
::: tip 前言
-你已经学了 HTML(网页的骨架)和 CSS(网页的皮肤)。
-但光有骨架和皮肤,网页只能"看",不能"用"——
-点一个按钮什么都不会发生,填一个表单提交不了。
+你已经学会了 HTML 和 CSS,能做出好看的网页了。但你可能会发现:点击按钮没反应,填了表单提交不了,网页就像一张"静态"的图片。
-**JavaScript 就是让网页能响应你操作的语言。**
-点击按钮弹出菜单、输入搜索词显示建议、
-下拉页面加载更多内容——这些全靠 JavaScript。
+这就是我们需要 JavaScript 的原因——它让网页"活"起来。点击按钮能弹出菜单,输入文字能实时搜索,滚动页面能加载更多内容……这些交互效果都靠 JavaScript。
-在 vibecoding 的工作流里,AI 会帮你写大部分 JS 代码。
-你的任务不是从零手写,而是:
-1. **读懂** AI 写了什么
-2. **判断** 它写得对不对
-3. **精准描述** 需要修改的地方
+在 vibecoding 里,AI 会帮你写大部分代码。但你至少得能看懂代码在做什么,否则 AI 写错了你也发现不了。读完这篇,你就能:
-本章从最基础的概念讲起,逐步深入到专业开发者的思维方式。
-读完后,你不只是能"用" JavaScript,而是能"理解"它——
-这会让你在 vibecoding 中如虎添翼。
+- 读懂 AI 写的代码在做什么
+- 看出代码哪里有问题
+- 用清晰的话告诉 AI 怎么改
:::
+**这篇文章会带你学什么?**
+
+| 章节 | 内容 | 学完能干嘛 |
+|-----|------|-----------|
+| **第 1 章** | JavaScript 是什么 | 明白它在网页里扮演什么角色 |
+| **第 2 章** | 数据与变量 | 知道程序怎么存东西、怎么用东西 |
+| **第 3 章** | 函数与逻辑 | 看懂代码的判断、循环和复用逻辑 |
+| **第 4 章** | DOM 与事件 | 知道代码怎么控制网页、怎么响应用户操作 |
+| **第 5 章** | 实战技巧 | 拿到 AI 代码怎么读、遇到报错怎么说 |
+
+每一章都从"能识别代码"开始,不需要你会手写。遇到不懂的代码,随时回来查就行。
+
---
## 1. JavaScript 是什么
-### 1.1 从"只能看"到"能交互"
+::: tip 🤔 核心问题
+**为什么网页需要 JavaScript?** HTML 和 CSS 已经能让网页有内容、有样式了,为什么还要学一门新语言?
+:::
-早期的网页就像一本**电子杂志**——你只能看,不能改。内容是固定的,你点击什么都不会改变。
+### 1.1 从"静态网页"到"动态应用"
-但现代网页完全不同了。它们更像**桌面软件**:
-- 在线文档可以像 Word 一样编辑
-- 地图网站可以像 GPS 一样导航
-- 聊天应用可以像微信一样实时收发消息
+
+
-**这种转变的核心技术就是 JavaScript**——它让网页从"展示信息"变成了"可以交互的工具"。
+**📄 没有 JavaScript 的网页**
+- 内容固定,无法交互
+- 点击按钮没反应
+- 填写表单提交不了
+- 页面不会自动更新
-用一句话定位:
-- **HTML** 是网页的骨架(结构)
-- **CSS** 是网页的皮肤(样式)
-- **JavaScript** 是网页的肌肉和神经系统(行为)
+*就像一张纸质海报,只能看*
-### 1.2 Vibecoding 中的 JavaScript
+
+
-::: warning 💡 从踩坑到顿悟
-小李用 AI 做了一个待办事项应用。AI 生成的代码能添加待办、能标记完成,看起来一切正常。
+**🚀 有 JavaScript 的网页**
+- 点击按钮弹出菜单
+- 输入文字实时搜索
+- 滚动自动加载内容
+- 数据实时更新显示
-但当他想加"删除"功能时,对 AI 说:"加一个删除功能。" AI 加了,可每次点删除,删掉的都不是他点的那一项,而是列表最后一项。
+*就像一个真正的应用程序*
-小李完全看不懂代码,只能反复说"删除有 bug",AI 改了好几版都不对。
+
+
-最后他花了 10 分钟学了"数组"和"索引"的概念,看懂了代码里的 `splice(index, 1)`,然后对 AI 说:"删除时不要用数组索引来定位,改成用每个事项的唯一 id 来匹配删除。"
+**用一句话理解三者的关系:**
+
+| 技术 | 比喻 | 作用 |
+|------|------|------|
+| **HTML** | 骨架 | 定义网页的结构和内容 |
+| **CSS** | 皮肤 | 定义网页的外观和样式 |
+| **JavaScript** | 肌肉和神经系统 | 让网页能响应、能交互、能思考 |
+
+### 1.2 为什么 vibecoding 也需要懂 JavaScript?
+
+::: warning 刚学 JS 的开发者踩坑记
+一位刚学 JavaScript 的开发者用 AI 做了一个"计数器"应用:点击按钮,数字加 1。AI 生成的代码能正常工作。
+
+但他想改成"点击加 2",对 AI 说:"让每次点击加 2。" AI 改了代码,可数字还是只加 1。
+
+他问 AI 为啥没效果,AI 解释了一通,但他看不懂代码里的 `count = count + 1` 是什么意思,也不知道 AI 改的是不是这个地方。只能反复说"加 2 没效果",AI 又改了好几版,有的把初始值改成 2,有的在完全不相关的地方加了 2。
+
+最后他看了第 2 章"变量"的概念,明白了 `count = count + 1` 是在把 count 的值加 1 再存回去。然后他对 AI 说:"把 `count + 1` 改成 `count + 2`。"
一次就改对了。
-**这就是为什么 vibecoding 也需要读懂代码——不是为了手写,而是为了在 AI 出错时能一句话说到点子上。**
+**这就是为什么要懂 JavaScript——不是为了手写代码,而是为了在 AI 没改对时,你能一眼看出问题在哪,一句话说到点子上。**
:::
-**你的定位不是从零手写代码**,而是:
-- 能看懂 AI 生成的代码在做什么
-- 能判断它写得对不对
-- 能用精准的语言告诉 AI 需要怎么改
+### 1.3 先睹为快:一段真实的 AI 代码
-### 1.3 从一段真实代码开始
+在深入学习之前,让我们先看一段 AI 生成的真实代码。不要担心看不懂,只要有个印象,后面我们会逐一讲解每个部分。
-让我们先看一段 AI 生成的真实代码。不要担心看不懂,我们会在后面的章节逐一讲解每个部分。
-
-**场景**:让 AI 做一个"点击按钮切换背景颜色"的网页
+**场景**:做一个"点击按钮切换背景颜色"的功能
```javascript
-// 场景:点击按钮切换背景颜色
-const colors = ['#ff6b6b', '#4ecdc4', '#45b7d1', '#96ceb4'] // ← 数组(第2章 2.2节)
-let currentIndex = 0 // ← 变量(第2章 2.1节)
+// 定义一组颜色
+const colors = ['#ff6b6b', '#4ecdc4', '#45b7d1', '#96ceb4']
+let currentIndex = 0
-const button = document.querySelector('#changeBtn') // ← DOM 查找(第4章 4.2节)
+// 找到页面上的按钮
+const button = document.querySelector('#changeBtn')
-button.addEventListener('click', () => { // ← 事件监听 + 箭头函数(第3章 3.2节 + 第4章 4.3节)
- currentIndex = (currentIndex + 1) % colors.length // ← 运算(第2章)
- document.body.style.backgroundColor = colors[currentIndex] // ← 修改样式(第4章 4.2节)
+// 给按钮添加点击事件
+button.addEventListener('click', () => {
+ currentIndex = (currentIndex + 1) % colors.length
+ document.body.style.backgroundColor = colors[currentIndex]
})
```
**这段代码在做什么?**
-- 定义了一组颜色(数组)
-- 记录当前用到了第几个颜色(变量)
-- 找到页面上的按钮(DOM 查找)
-- 给按钮添加点击事件:每次点击就换一个背景色(事件监听)
-现在你不需要理解每一行,只要有个印象即可。接下来我们会按顺序学习每个概念。
+| 代码 | 作用 | 对应章节 |
+|------|------|----------|
+| `const colors = [...]` | 定义一组颜色数据 | 第 2 章:数组 |
+| `let currentIndex = 0` | 记录当前显示第几个颜色 | 第 2 章:变量 |
+| `document.querySelector(...)` | 找到页面上的按钮 | 第 4 章:DOM 查找 |
+| `button.addEventListener(...)` | 给按钮添加点击事件 | 第 4 章:事件监听 |
+| `() => {...}` | 定义点击后要执行的代码 | 第 3 章:箭头函数 |
-::: tip 🤖 Vibecoding 备忘
-**AI 代码里你会看到:**
-- `const` / `let` → 变量声明(第2章)
-- `{}` / `[]` → 对象和数组(第2章)
-- `function` / `=>` → 函数定义(第3章)
-- `document.querySelector` → 查找网页元素(第4章)
-- `addEventListener` → 监听用户操作(第4章)
-- `async` / `await` → 等待耗时操作(第4章)
-
-**遇到问题时这样跟 AI 说:**
-- ✅ "第 X 行是什么意思?"
-- ✅ "这个代码的执行流程是什么?"
-- ✅ "我想让它在点击时做 XXX,该怎么改?"
+::: info 💡 核心启示
+你不需要现在就理解每一行代码。只要记住:**JavaScript 代码就是一系列指令,告诉浏览器"当用户做某事时,应该发生什么"。**
:::
---
## 2. 数据篇:变量与数据类型
-### 2.1 变量:给数据贴标签
+::: tip 🤔 核心问题
+**程序是怎么"记住"东西的?** 用户输入的内容、从服务器获取的数据、计算过程中的中间结果——这些信息都存在哪里?
+:::
-**变量就像一个带名字的盒子**——你可以把数据放进去,需要时再取出来。
+### 2.1 变量:给数据起个名字
+
+**变量就像一个有标签的盒子**——你可以把数据放进去,以后通过标签来取用。
```javascript
const name = "张三" // 名字不会变,用 const
let age = 25 // 年龄可能会变,用 let
```
-**两种声明方式:**
+**为什么要区分 const 和 let?**
-| 关键字 | 能否重新赋值 | 使用场景 |
-|--------|-------------|---------|
-| `const` | ❌ 不能 | 默认首选,值不会变的情况 |
-| `let` | ✅ 能 | 需要重新赋值的情况 |
-| `var` | (老语法) | 遇到了知道是变量就行,不要用 |
+想象一下:你的身份证号码(const)这辈子都不会变,但你的年龄(let)每年都会变。JavaScript 让你用不同的关键字来表达这种"变与不变"的意图。
-**Vibecoding 提示:**
-- 看到 `const` → 这个值后面不会变
-- 看到 `let` → 这个值后面会变
+| 关键字 | 能否修改 | 使用场景 | 示例 |
+|--------|---------|----------|------|
+| `const` | ❌ 不能 | 值不会变的数据 | 身份证号、配置项、颜色列表 |
+| `let` | ✅ 能 | 值会变化的数据 | 计数器、当前选中的选项、用户输入 |
+::: details 🔍 看一个具体的例子
```javascript
-const score = 0
-score = 10 // ❌ 报错!const 不能重新赋值
+// 用 const:这些值不会变
+const PI = 3.14159
+const MAX_USERS = 100
+const APP_NAME = "TodoList"
-let points = 0
-points = 10 // ✅ 正确,let 可以重新赋值
+// 用 let:这些值会变化
+let count = 0
+count = 1 // ✅ 可以修改
+
+count = count + 1 // ✅ 可以基于原值计算
+
+// 如果用 const 会怎样?
+const fixedCount = 0
+fixedCount = 1 // ❌ 报错!const 不能重新赋值
```
+:::
-👇 **动手试试看**:
+👇 **动手试试看**:修改下面的代码,看看 const 和 let 的区别
-### 2.2 数据类型:JS 世界里的几种"东西"
+### 2.2 数据类型:JavaScript 里的几种"东西"
-JavaScript 有几种基本的数据类型,最常用的是这三个:
+JavaScript 把数据分成几种类型,最常用的有三种:
-**基本类型:**
+| 类型 | 说明 | 示例 | 实际场景 |
+|------|------|------|----------|
+| `string`(字符串)| 文本内容 | `"hello"`, `'你好'` | 用户名、商品描述、提示信息 |
+| `number`(数字)| 数值 | `42`, `3.14` | 价格、数量、评分 |
+| `boolean`(布尔值)| 是/否 | `true`, `false` | 是否登录、是否完成、是否可见 |
-| 类型 | 说明 | 示例 |
-|------|------|------|
-| `string` | 文本 | `"hello"`, `'你好'` |
-| `number` | 数字 | `42`, `3.14`, `NaN` |
-| `boolean` | 布尔值(真/假) | `true`, `false` |
+**还有两个特殊值需要知道:**
-**两个特殊的值:**
-- `undefined` → 还没给值
-- `null` → 故意设为空
+- `undefined` → 变量声明了,但还没给值
+- `null` → 故意设为空(表示"这里没有值")
-**模板字符串(反引号):**
-
-AI 代码里你经常会看到这种写法:
+::: details 🔍 模板字符串:更方便地拼接文本
+在 AI 代码里,你经常会看到用反引号(`` ` ``)包裹的字符串,里面还有 `${...}`:
```javascript
const name = "张三"
const age = 25
-// 用反引号(键盘左上角那个键)和 ${}
+// 传统写法(麻烦)
+const message = "我叫" + name + ",今年" + age + "岁"
+
+// 模板字符串(简洁)
const message = `我叫${name},今年${age}岁`
-// message = "我叫张三,今年25岁"
+// 结果:"我叫张三,今年25岁"
```
-**Vibecoding 提示:**
-- 看到反引号 `` ` `` → 这是模板字符串,里面可以用 `${变量}` 插入值
+**识别要点**:看到反引号和 `${}`,就知道是在把变量插入到文本中。
+:::
-### 2.3 对象与数组:把数据组织起来
+### 2.3 对象和数组:把数据组织起来
-**对象 = 一组有名字的属性**(像身份证/个人资料卡)
+**对象 = 一组有名字的属性**(像一张个人信息表)
```javascript
-const person = {
+const user = {
name: "张三",
age: 25,
- isStudent: true
+ isVIP: true
}
-// 访问属性
-console.log(person.name) // "张三"
-console.log(person.age) // 25
+// 使用点号访问属性
+console.log(user.name) // "张三"
+console.log(user.age) // 25
```
-**数组 = 一组有顺序的数据**(像排队/列表)
+**数组 = 一组有顺序的数据**(像一个列表)
```javascript
const colors = ['红色', '绿色', '蓝色']
-// 访问元素(索引从 0 开始)
+// 用索引访问(从 0 开始)
console.log(colors[0]) // "红色"
console.log(colors[1]) // "绿色"
```
-**嵌套结构:**
+**嵌套结构:对象里套数组、数组里套对象**
-在 AI 生成的代码里,你经常会看到对象里套数组、数组里套对象:
+这是 AI 代码中最常见的数据结构:
```javascript
const todos = [
@@ -209,21 +237,21 @@ const todos = [
{ id: 3, text: "写文档", done: false }
]
-// 访问:先找数组索引,再找对象属性
+// 访问:先取数组的第 0 项,再取它的 text 属性
console.log(todos[0].text) // "学习 JavaScript"
-console.log(todos[1].done) // true
```
-**Vibecoding 提示:**
-- 看到 `{}` → 对象(一组有名字的数据)
-- 看到 `[]` → 数组(一组有顺序的数据)
-- 看到 `data[0].name` → 先取数组的第 0 项,再取它的 name 属性
+::: info 💡 识别技巧
+- 看到 `{}` → 这是一个对象,里面是一组 `名字: 值`
+- 看到 `[]` → 这是一个数组,里面是一组按顺序排列的值
+- 看到 `data[0].name` → 先取数组第 0 项,再取它的 name 属性
+:::
-### 2.4 值与引用:为什么改了 B,A 也变了?
+### 2.4 值与引用:一个容易踩的坑
-这是新手最容易踩的坑!
+这是新手最常遇到的问题之一!
-**基本类型(string、number、boolean)赋值 = 复制一份副本:**
+**基本类型(string、number、boolean)赋值 = 复制一份全新的数据:**
```javascript
let a = 10
@@ -232,102 +260,80 @@ b = 20
console.log(a) // 10(a 不受影响)
```
-**对象和数组赋值 = 复制的是"地址":**
+**对象和数组赋值 = 复制的是"地址"(指向同一个东西):**
```javascript
-let obj1 = { name: "张三" }
-let obj2 = obj1 // obj2 指向同一个对象
-obj2.name = "李四" // 修改 obj2 会影响 obj1
-console.log(obj1.name) // "李四"(obj1 也变了!)
+let user1 = { name: "张三" }
+let user2 = user1 // user2 指向同一个对象
+user2.name = "李四" // 修改 user2 会影响 user1
+console.log(user1.name) // "李四"(user1 也变了!)
```
-这就是为什么 AI 代码里经常看到 `[...array]` 或 `{...obj}`——它在"创建副本",避免互相影响。
+**为什么要创建副本?**
+
+在 React/Vue 中,直接修改数据会导致界面不更新。所以 AI 代码里经常看到 `[...array]` 或 `{...obj}`——它在创建副本,避免互相影响。
```javascript
// 用展开运算符创建副本
const arr1 = [1, 2, 3]
-const arr2 = [...arr1] // 创建新数组,不是复制地址
+const arr2 = [...arr1] // 创建新数组
arr2.push(4)
console.log(arr1) // [1, 2, 3](不受影响)
console.log(arr2) // [1, 2, 3, 4]
```
-**Vibecoding 场景:**
-
-如果你发现修改了一条数据,别的地方也莫名其妙变了,十有八九是引用问题。
-
-告诉 AI:**"这里需要深拷贝,不要直接修改原数据"**
-
-👇 **动手试试看**:
+👇 **动手试试看**:观察修改副本时原数据的变化
-### 2.5 解构与展开:现代 JS 的快捷写法
+### 2.5 解构与展开:现代 JavaScript 的快捷写法
-这两个语法在 AI 生成的代码里到处都是,不认识就读不懂代码。
+这两个语法在 AI 代码里到处都是,不认识就读不懂代码。
-**解构赋值:从对象或数组里把数据拿出来**
+**解构赋值:从对象或数组里快速提取数据**
```javascript
-const person = { name: "张三", age: 25, city: "北京" }
+const user = { name: "张三", age: 25, city: "北京" }
-// 不用解构(传统写法)
-const name = person.name
-const age = person.age
+// 传统写法(麻烦)
+const name = user.name
+const age = user.age
-// 用解构(现代写法)
-const { name, age } = person
-
-// 数组解构
-const colors = ['红色', '绿色', '蓝色']
-const [first, second] = colors
-// first = '红色', second = '绿色'
+// 解构写法(简洁)
+const { name, age } = user
+// 效果一样,但一行搞定
```
-**展开运算符:把数组或对象"展开铺平"**
+**展开运算符:复制并扩展数据**
```javascript
-// 数组展开
+// 复制数组并添加新元素
const arr1 = [1, 2, 3]
const arr2 = [...arr1, 4, 5] // [1, 2, 3, 4, 5]
-// 对象展开
-const obj1 = { name: "张三", age: 25 }
-const obj2 = { ...obj1, city: "北京" }
+// 复制对象并添加新属性
+const user1 = { name: "张三", age: 25 }
+const user2 = { ...user1, city: "北京" }
// { name: "张三", age: 25, city: "北京" }
-
-// 合并对象
-const baseConfig = { url: "/api", timeout: 5000 }
-const userConfig = { timeout: 10000 }
-const finalConfig = { ...baseConfig, ...userConfig }
-// { url: "/api", timeout: 10000 }(userConfig 会覆盖 baseConfig 的同名属性)
```
-**Vibecoding 提示:**
-- 看到 `const { name, age } = person` → 从 person 对象里把 name 和 age 拿出来
+::: info 💡 识别技巧
+- 看到 `const { name, age } = person` → 从 person 对象里提取 name 和 age
- 看到 `...array` 或 `...obj` → 把数组或对象展开铺平
- 你不需要能手写,但必须能读懂
-
-::: tip 🤖 Vibecoding 备忘
-**AI 代码里你会看到:**
-- `const { data } = response` → 从 response 里提取 data 字段
-- `const [first, ...rest] = array` → 取第一个,剩下的放 rest 里
-- `{ ...obj, newProp: value }` → 复制 obj 并添加新属性
-- `[...arr, newItem]` → 复制数组并添加新元素
-
-**遇到问题时这样跟 AI 说:**
-- "这个解构是什么意思?"
-- "我想从对象里提取 XXX 字段"
-- "这里需要创建副本,不要修改原数据"
:::
---
## 3. 逻辑篇:函数与流程控制
-### 3.1 条件判断:if/else 和三元运算符
+::: tip 🤔 核心问题
+**代码是怎么"做决定"和"重复做事"的?** 程序需要根据条件执行不同的操作,也需要重复执行某些任务——这些逻辑怎么表达?
+:::
-**if/else:如果...就...否则...**
+### 3.1 条件判断:如果...就...否则...
+
+**if/else:最基本的条件判断**
```javascript
const age = 18
@@ -339,10 +345,10 @@ if (age >= 18) {
}
```
-**三元运算符:简写的条件判断**
+**三元运算符:简写的 if/else**
```javascript
-// 完整写法
+// 完整写法(4 行)
let message
if (age >= 18) {
message = "成年人"
@@ -350,9 +356,9 @@ if (age >= 18) {
message = "未成年"
}
-// 三元运算符(一行搞定)
+// 三元运算符(1 行)
const message = age >= 18 ? "成年人" : "未成年"
-// 格式:条件 ? 真的值 : 假的值
+// 格式:条件 ? 条件为真时的值 : 条件为假时的值
```
**&& 短路写法:React 代码里常见**
@@ -367,11 +373,12 @@ if (isLoggedIn) {
}
```
-**Vibecoding 提示:**
+::: info 💡 识别技巧
- 看到 `? :` → 这是三元运算符,简写的 if/else
- 看到 `&&` → 前面为 true 才执行后面
+:::
-### 3.2 函数:可以反复调用的操作
+### 3.2 函数:把操作打包起来
**函数 = 一道菜的配方**
@@ -391,50 +398,36 @@ console.log(greet("张三")) // "Hello 张三"
console.log(greet("李四")) // "Hello 李四"
```
-**三种写法一眼识别:**
+**三种写法,一眼识别:**
```javascript
-// 1. function 声明
+// 1. function 声明(传统写法)
function greet(name) {
return "Hello " + name
}
-// 2. 函数表达式
-const greet = function(name) {
- return "Hello " + name
-}
-
-// 3. 箭头函数(AI 代码里用得最多)
+// 2. 箭头函数(AI 代码里用得最多)
const greet = (name) => {
return "Hello " + name
}
-// 箭头函数简写(只有一行时可以省略 {} 和 return)
+// 3. 箭头函数简写(只有一行时)
const greet = (name) => "Hello " + name
```
-**重点:** 能认出来就行,不需要纠结什么时候用哪种。箭头函数最简洁,AI 代码里用得最多。
-
-👇 **动手试试看**:
+👇 **动手试试看**:输入不同的名字,看看函数怎么工作
-**Vibecoding 提示:**
+::: info 💡 识别技巧
- 看到 `function` 或 `=>` → 这是一个函数
- 看到 `fn()` → 在调用这个函数
- 看到 `() => {}` → 箭头函数,现代 JS 的主流写法
+:::
-### 3.3 循环与数组方法
+### 3.3 数组方法:处理列表的利器
-**for 循环:基本认识即可**
-
-```javascript
-for (let i = 0; i < 5; i++) {
- console.log(i) // 输出 0, 1, 2, 3, 4
-}
-```
-
-**数组方法:React/Vue 代码里几乎每个列表渲染都用 map**
+在 React/Vue 里,几乎每个列表渲染都会用到这些方法。
```javascript
const todos = [
@@ -442,9 +435,9 @@ const todos = [
{ id: 2, text: "工作", done: true }
]
-// .map():把数组的每一项变成另一个东西(返回新数组)
-const todoItems = todos.map(todo => `
${todo.text}`)
-// ["
学习", "
工作"]
+// .map():把数组的每一项变成另一个东西
+const texts = todos.map(todo => todo.text)
+// ["学习", "工作"]
// .filter():筛选出符合条件的项
const unfinished = todos.filter(todo => !todo.done)
@@ -455,10 +448,11 @@ const found = todos.find(todo => todo.id === 1)
// { id: 1, text: "学习", done: false }
```
-**Vibecoding 提示:**
+::: info 💡 识别技巧
- 看到 `.map()` → 对数组做变换,返回新数组
- 看到 `.filter()` → 筛选数组
-- 看到 `items.map(item =>
{item.name})` → 把每个数据项变成一个列表标签
+- 看到 `items.map(item =>
{item.name})` → 把每个数据项变成列表标签
+:::
### 3.4 作用域:变量的"可见范围"
@@ -478,102 +472,55 @@ function room() {
console.log(local) // ❌ 报错!外面看不到房间里的东西
```
-**三种作用域:**
-
-```javascript
-// 全局作用域(走廊)
-const appName = "Todo"
-
-function outer() {
- // 函数作用域(房间)
- const message = "你好"
-
- if (true) {
- // 块级作用域(小房间)
- const greeting = message + appName
- console.log(greeting) // ✅ 能看到外层的
- }
-
- console.log(greeting) // ❌ 报错!外层看不到内层
-}
-```
-
**核心直觉:** 代码写在哪里,决定了它能看到什么变量。
-👇 **动手试试看**:
+👇 **动手试试看**:点击不同的作用域,看看能访问哪些变量
### 3.5 闭包:函数"记住"了它诞生时的环境
-**不要把闭包当成独立的难点概念来讲**,从一个具体场景引入:
-
-**问题:为什么点击事件的回调函数能使用外面定义的变量?**
+**不要把它当成独立的概念,从一个具体场景理解:**
```javascript
-function setupButtons() {
- let count = 0
-
- button.addEventListener('click', () => {
- count++ // 为什么这里的 count 能记住上次的值?
- console.log(count)
- })
-}
-```
-
-**核心直觉:** 函数在被创建时,会"记住"它周围的变量,即使外层函数已经执行完了。
-
-**实际场景:计数器**
-
-```javascript
-function createCounter() {
- let count = 0 // 私有变量
+function setupCounter() {
+ let count = 0 // 这个变量在函数内部
return {
add: () => { count++; return count },
- subtract: () => { count--; return count },
getCount: () => count
}
}
-const counter1 = createCounter()
-console.log(counter1.add()) // 1
-console.log(counter1.add()) // 2
-console.log(counter1.getCount()) // 2
-
-const counter2 = createCounter()
-console.log(counter2.add()) // 1(每个计数器独立)
+const counter = setupCounter()
+console.log(counter.add()) // 1
+console.log(counter.add()) // 2
+console.log(counter.getCount()) // 2
```
-👇 **动手试试看**:
+**核心直觉:** 函数在被创建时,会"记住"它周围的变量,即使外层函数已经执行完了。
+
+👇 **动手试试看**:观察闭包如何让函数"记住"状态
-**Vibecoding 场景:**
+### 3.6 this:函数被谁调用
-如果 AI 代码里一个内部函数用了外部变量,这就是闭包在工作。一般不需要你干预。
+**不讲复杂的绑定规则,只讲最常见的场景:**
-但如果循环里创建函数导致所有函数共享同一个变量值,告诉 AI:**"闭包捕获了循环变量的引用,需要修复"**
-
-### 3.6 this:谁在调用我?
-
-**不讲四种绑定规则**,只讲两个最常见的场景:
-
-**场景 1:在 class 的方法里,this 指向这个 class 的实例**
+**场景 1:在对象的方法里,this 指向这个对象**
```javascript
-class Counter {
- constructor() {
- this.count = 0
- }
-
- increment() {
- this.count++ // this 指向 Counter 的实例
+const user = {
+ name: "张三",
+ sayHi() {
+ console.log("你好,我是" + this.name) // this 指向 user
}
}
+user.sayHi() // "你好,我是张三"
```
-**场景 2:在事件监听回调里,this 指向触发事件的 DOM 元素**
+**场景 2:在事件监听里,this 指向触发事件的元素**
```javascript
button.addEventListener('click', function() {
@@ -586,37 +533,21 @@ button.addEventListener('click', () => {
})
```
-**核心直觉:** this 不是固定的,取决于函数怎么被调用。
-
-**Vibecoding 场景:**
-
-如果 AI 代码里出现 this 相关的 bug(比如 `Cannot read property of undefined`),通常是因为函数的 this 指向丢了。
-
-告诉 AI:**"这个方法里的 this 指向不对,改成箭头函数或者用 bind"**
-
-::: tip 🤖 Vibecoding 备忘
-**AI 代码里你会看到:**
-- `if/else` → 条件判断
-- `condition ? a : b` → 三元运算符,简写 if/else
-- `fn()` → 调用函数
-- `() => {}` → 箭头函数
-- `.map()` / `.filter()` / `.find()` → 数组方法
-- `this` → 取决于函数怎么被调用
-
-**遇到问题时这样跟 AI 说:**
-- "这个判断条件是什么意思?"
-- "这个函数的返回值是什么?"
-- "这个 this 指向哪里?"
-- "这个闭包为什么会共享变量?"
+::: info 💡 遇到问题怎么办?
+如果 AI 代码里出现 this 相关的 bug(比如 `Cannot read property of undefined`),告诉 AI:"这个方法里的 this 指向不对,改成箭头函数或者用 bind"
:::
---
## 4. 交互篇:DOM、事件与异步
-### 4.1 DOM:JavaScript 看到的网页长什么样
+::: tip 🤔 核心问题
+**JavaScript 怎么跟网页"互动"?** 怎么找到页面上的元素?怎么响应用户的点击、输入?怎么从服务器获取数据?
+:::
-网页在 JS 眼里是一棵"树",每个 HTML 标签是树上的一个"节点"。
+### 4.1 DOM:JavaScript 看到的网页
+
+网页在 JavaScript 眼里是一棵"树",每个 HTML 标签都是树上的一个"节点"。
```html
@@ -631,9 +562,9 @@ button.addEventListener('click', () => {
```
-**JS 操控网页 = 找到节点、修改节点、创建/删除节点**
+**JS 操控网页 = 找到节点 + 修改节点 + 创建/删除节点**
-👇 **动手试试看**:
+👇 **动手试试看**:点击节点,看看 DOM 树是怎么组织的
@@ -643,12 +574,9 @@ button.addEventListener('click', () => {
```javascript
// 根据 CSS 选择器查找(最常用)
-const title = document.querySelector('h1')
-const button = document.querySelector('#submitBtn')
-const items = document.querySelectorAll('.item')
-
-// 根据 ID 查找
-const button = document.getElementById('submitBtn')
+const title = document.querySelector('h1') // 找第一个 h1
+const button = document.querySelector('#btn') // 找 id="btn" 的元素
+const items = document.querySelectorAll('.item') // 找所有 class="item" 的元素
```
**修改元素:**
@@ -656,28 +584,23 @@ const button = document.getElementById('submitBtn')
```javascript
// 改文字
title.textContent = "新标题"
-title.innerHTML = "
粗体标题"
// 改样式
element.style.color = "red"
element.style.fontSize = "20px"
// 改 CSS 类
-element.classList.add('active')
-element.classList.remove('hidden')
-element.classList.toggle('open')
-
-// 创建新元素
-const newItem = document.createElement('li')
-newItem.textContent = "新项目"
-document.querySelector('ul').appendChild(newItem)
+element.classList.add('active') // 添加类
+element.classList.remove('hidden') // 移除类
+element.classList.toggle('open') // 切换类(有就移除,没有就添加)
```
-**Vibecoding 提示:**
+::: info 💡 识别技巧
- 看到 `document.querySelector` → 在查找网页元素
-- 看到 `.textContent` / `.innerHTML` → 改文字
+- 看到 `.textContent` → 改文字
- 看到 `.style.xxx` → 改样式
- 看到 `.classList.add/remove/toggle` → 改 CSS 类
+:::
### 4.3 事件:当用户做了某个操作时...
@@ -691,26 +614,25 @@ button.addEventListener('click', () => {
**常见事件:**
-| 事件 | 触发时机 |
-|------|---------|
-| `click` | 点击 |
-| `input` | 输入框内容变化 |
-| `submit` | 表单提交 |
-| `scroll` | 滚动页面 |
-| `keydown` | 按下键盘 |
+| 事件 | 触发时机 | 实际场景 |
+|------|---------|----------|
+| `click` | 点击 | 按钮点击、链接跳转 |
+| `input` | 输入框内容变化 | 实时搜索、表单验证 |
+| `submit` | 表单提交 | 登录、注册、提交数据 |
+| `scroll` | 滚动页面 | 懒加载、回到顶部 |
-**事件对象:**
+**事件对象:获取更多信息**
```javascript
input.addEventListener('input', (e) => {
console.log(e.target.value) // 获取输入框的值
- e.preventDefault() // 阻止默认行为
+ e.preventDefault() // 阻止默认行为(比如表单提交后刷新页面)
})
```
-**Vibecoding 场景:**
-
-当你想给按钮加一个功能,本质上就是在告诉 AI:**"给这个按钮添加一个点击事件,点击后执行某某操作"**
+::: info 💡 实际应用
+当你想给按钮加一个功能,本质上就是在告诉 AI:"给这个按钮添加一个点击事件,点击后执行某某操作"
+:::
### 4.4 异步:为什么有些操作不是立刻完成的
@@ -718,14 +640,13 @@ input.addEventListener('input', (e) => {
点菜后不用站在厨房门口等,可以先做别的事,菜好了服务员会端过来。
-**最常见场景:从服务器获取数据(fetch / API 调用)**
+**最常见场景:从服务器获取数据**
```javascript
-// 同步写法(会卡住页面)
-const data = fetch('/api/data') // ❌ 别这样写
-console.log(data)
+// 同步写法(会卡住页面,不要用)
+const data = fetch('/api/data') // ❌ 这样写会卡住
-// 异步写法(不卡住)
+// 异步写法(正确)
async function loadData() {
try {
const response = await fetch('/api/data')
@@ -743,28 +664,23 @@ async function loadData() {
- `await` → 等待这个操作完成(但不会卡住页面)
- `try/catch` → 处理可能出现的错误
-**只讲 async/await**。回调和 Promise.then 链各用一句话提("这是旧的写法,认识就行")。
-
-👇 **动手试试看**:
+👇 **动手试试看**:观察异步操作的执行顺序
-**Vibecoding 提示:**
+::: info 💡 识别技巧
- 看到 `async/await` → 在等待耗时操作
- 看到 `fetch()` → 在从服务器获取数据
- 看到 `try/catch` → 在处理可能的错误
+:::
### 4.5 事件循环:JavaScript 到底怎么工作的
-**不用术语"微任务/宏任务"**,用可视化演示:
+**不用术语"微任务/宏任务",用一个简单的模型理解:**
**JS 是一个"单人工位"**,同时只做一件事,但有一个"待办便签栏"(任务队列)。
-当遇到要等待的操作(网络请求、定时器),JS 不是傻等,而是把"等好了之后做什么"贴到便签栏,自己继续往下执行。
-
-等当前事情做完了,才去看便签栏上有没有该做的事。
-
-**这个心智模型解释了**为什么 `console.log` 的打印顺序有时候跟代码顺序不一样。
+当遇到要等待的操作(网络请求、定时器),JS 不是傻等,而是把"等好了之后做什么"贴到便签栏,自己继续往下执行。等当前事情做完了,才去看便签栏。
```javascript
console.log("1")
@@ -776,41 +692,22 @@ console.log("3")
// 输出:1, 3, 2(不是 1, 2, 3!)
```
-**执行流程:**
+**为什么?**
1. 执行 `console.log("1")` → 输出 1
2. 遇到 `setTimeout` → 把回调贴到便签栏,继续往下
3. 执行 `console.log("3")` → 输出 3
4. 当前代码执行完了,去看便签栏
5. 执行 `setTimeout` 的回调 → 输出 2
-👇 **动手试试看**:
+👇 **动手试试看**:观察代码的执行顺序
-**Vibecoding 场景:**
-
-如果 AI 代码里数据还没获取到页面就渲染了,这是异步时序问题。
-
-告诉 AI:**"数据还没加载完就开始渲染了,需要添加 loading 状态,等数据到了再渲染"**
-
-::: tip 🤖 Vibecoding 备忘
-**AI 代码里你会看到:**
-- `document.querySelector()` → 查找元素
-- `.addEventListener()` → 监听事件
-- `async/await` → 异步操作
-- `fetch()` → 网络请求
-
-**遇到问题时这样跟 AI 说:**
-- "我想给按钮添加点击事件"
-- "数据加载完了但没有显示"
-- "页面在数据加载前就渲染了,需要加 loading"
+::: info 💡 遇到问题怎么办?
+如果 AI 代码里数据还没获取到页面就渲染了,告诉 AI:"数据还没加载完就开始渲染了,需要添加 loading 状态,等数据到了再渲染"
:::
----
-
-## 5. 实战篇:像老手一样读懂和调试代码
-
-### 5.1 模块:import 和 export
+### 4.6 模块:import 和 export
AI 生成的 React/Vue 代码第一行几乎都是 `import`。
@@ -839,65 +736,39 @@ export default function formatDate(date) {
}
```
-**npm 包 = 别人写好的工具,你可以直接安装使用**
+**npm 包 = 别人写好的工具,安装后就能用**
```javascript
-// 安装包
-// npm install lodash
-
+// 安装包:npm install lodash
// 使用包
import _ from 'lodash'
```
-这一节放在实战篇而不是语法篇,因为读者在前 4 章建立了足够基础后,这里只是"识别"。
-
-**Vibecoding 提示:**
+::: info 💡 识别技巧
- 看到 `import` → 从别的文件引入功能
- 看到 `export` → 把功能暴露给别人用
- 看到 `from 'react'` → 从 React 包引入
- 看到 `from './utils'` → 从本地文件引入
+:::
-### 5.2 拿到 AI 代码后的阅读策略
+---
-**第一步:看整体结构**
+## 5. 实战篇:读懂代码、看懂报错、精准描述
-有几个函数?分别叫什么名字?大致做什么?
+::: tip 🤔 核心问题
+**前面学了这么多语法,实际拿到 AI 代码时怎么用?** 怎么快速读懂代码?遇到报错怎么办?怎么让 AI 准确地帮你改代码?
+:::
-```javascript
-// 一眼看出:三个函数
-function loadData() { } // 加载数据
-function renderList() { } // 渲染列表
-function handleClick() { } // 处理点击
-```
+### 5.1 拿到 AI 代码后怎么读
-**第二步:找入口**
+**四步法:**
-哪里是程序开始执行的地方?事件监听绑在了哪些元素上?
-
-```javascript
-// 入口点
-document.addEventListener('DOMContentLoaded', () => {
- loadData() // 程序从这里开始
-})
-
-button.addEventListener('click', handleClick)
-```
-
-**第三步:追踪数据流**
-
-数据从哪里来?经过了什么变换?最终渲染到了哪里?
-
-```javascript
-async function loadData() {
- const data = await fetch('/api/todos') // 数据从服务器来
- const todos = await data.json() // 解析成 JSON
- renderList(todos) // 渲染到页面
-}
-```
-
-**第四步:看细节逻辑**
-
-某个具体函数里面是怎么处理的?
+| 步骤 | 看什么 | 示例 |
+|------|--------|------|
+| **第一步:看整体结构** | 有几个函数?分别做什么? | `loadData()` 加载数据,`renderList()` 渲染列表 |
+| **第二步:找入口** | 程序从哪里开始执行? | `addEventListener('click', ...)` 点击时开始 |
+| **第三步:追踪数据流** | 数据从哪里来?到哪里去? | 从 API 获取 → 解析 → 渲染到页面 |
+| **第四步:看细节逻辑** | 具体函数里怎么处理的? | 循环、判断、计算 |
**用第 1 章的代码示例做一次完整的"阅读演示":**
@@ -918,23 +789,20 @@ async function loadData() {
// 这个公式的意思:每次 +1,但不超过数组长度(循环)
```
-### 5.3 常见报错速查与应对
+### 5.2 常见报错速查
-| 报错 | 大白话翻译 | 怎么跟 AI 说 |
-|------|-----------|------------|
+| 报错 | 大白话解释 | 怎么跟 AI 说 |
+|------|-----------|-------------|
| `TypeError: Cannot read properties of undefined` | 你想从一个不存在的东西上取值 | "第 X 行报错,某某变量是 undefined,检查它的赋值逻辑" |
| `ReferenceError: xxx is not defined` | 用了一个没有声明过的变量名 | "变量 xxx 没有定义,是不是拼写错了或者忘了导入" |
| `TypeError: xxx is not a function` | 把一个不是函数的东西当函数调用了 | "xxx 不是函数,检查一下它的类型和来源" |
| `SyntaxError: Unexpected token` | 语法写错了(括号不匹配、少了逗号等) | "第 X 行语法错误,检查括号和标点" |
| `CORS error` | 浏览器阻止了跨域请求 | "遇到 CORS 错误,需要配置跨域资源共享" |
| `404 Not Found` | 请求的资源不存在 | "API 返回 404,检查接口地址是否正确" |
-| `500 Internal Server Error` | 服务器出错了 | "服务器返回 500,需要检查后端代码" |
-### 5.4 如何用精准的语言让 AI 改代码
+### 5.3 如何精准描述问题
-这是"3-5 年经验 sense"的核心体现:**描述问题的精准度**。
-
-**6-8 组对比示例:**
+新手和熟练开发者的差距,往往就体现在**描述问题的精准度**上。
| ❌ 差的描述 | ✅ 好的描述 |
|-----------|-----------|
@@ -943,9 +811,6 @@ async function loadData() {
| "数据显示不出来" | "fetch 请求返回了数据(控制台能看到),但页面没有重新渲染" |
| "加一个功能" | "在用户列表页面添加一个搜索框,输入时实时过滤列表,按 name 字段模糊匹配" |
| "点击没反应" | "点击按钮时控制台报错 'Cannot read property of undefined',错误在第 X 行" |
-| "布局乱了" | "在小屏幕上,导航栏和内容区域重叠了,需要调整响应式布局" |
-| "太慢了" | "加载 100 条数据时页面卡顿 2 秒,需要做虚拟滚动或分页" |
-| "我想做个登录功能" | "实现一个登录表单,包含邮箱和密码输入框,点击登录后调用 /api/login 接口,成功后保存 token 并跳转到首页" |
**一个实战练习:**
@@ -962,66 +827,31 @@ function deleteTodo(index) {
**✅ 好的描述:** "点击删除按钮时,删除的不是当前项而是最后一项。代码里用了 splice(index, 1),但 index 可能不正确。需要改成用每个事项的唯一 id 来匹配删除。"
-### 5.5 你的下一步:概念地图
+### 5.4 你现在应该能识别的代码
-**你现在应该能做到:**
+- 看到 `const/let` → 知道变量能不能重新赋值
+- 看到 `{}` → 对象 / 看到 `[]` → 数组
+- 看到 `{...obj}` 或 `[...arr]` → 在创建副本
+- 看到 `function` 或 `=>` → 定义了一段可重复执行的操作
+- 看到 `if/else` 或 `? :` → 代码在做判断
+- 看到 `.map()` / `.filter()` → 在变换或筛选数组
+- 看到 `document.querySelector` → 在查找网页元素
+- 看到 `addEventListener` → 在监听用户操作
+- 看到 `async/await` → 在等待耗时操作
+- 看到 `import/export` → 在引入或导出模块
+- 遇到报错 → 能读懂大意并精准描述给 AI
-✅ 看到 `const/let` → 知道变量能不能重新赋值
-✅ 看到 `{}` → 对象 / 看到 `[]` → 数组
-✅ 看到 `{...obj}` 或 `[...arr]` → 在创建副本
-✅ 看到 `function` 或 `=>` → 定义了一段可重复执行的操作
-✅ 看到 `if/else` 或 `? :` → 代码在做判断
-✅ 看到 `.map()` / `.filter()` → 在变换或筛选数组
-✅ 看到 `document.querySelector` → 在查找网页元素
-✅ 看到 `addEventListener` → 在监听用户操作
-✅ 看到 `async/await` → 在等待耗时操作
-✅ 看到 `import/export` → 在引入或导出模块
-✅ 遇到报错 → 能读懂大意并精准描述给 AI
-
-**更深层的理解:**
-
-如果你读完了每章的"深入"部分,你还建立了这些心智模型:
+**如果你认真读了每章的"深入"部分,你还掌握了这些核心概念:**
- **值 vs 引用**:基本类型复制值,对象/数组复制的是地址
- **作用域与闭包**:函数能"记住"它诞生时周围的变量
- **this 的本质**:取决于函数被谁调用,而不是写在哪里
- **事件循环**:JS 是单线程的,靠任务队列实现"不阻塞"
-这些是区分"能用"和"真懂"的分水岭。
+这些概念会帮你更快定位问题。
-在你的 vibecoding 旅程中,它们会一次又一次帮你快速定位问题。
-
-**进阶概念地图:**
-
-- **TypeScript** → 给 JavaScript 加类型检查
-- **React 状态管理** → useState、useReducer、Zustand
-- **Vue 响应式系统** → ref、reactive、computed
-- **API 设计** → REST、GraphQL
-- **构建工具** → Vite、Webpack
-- **性能优化** → 防抖节流、虚拟滚动、懒加载
-- **测试** → 单元测试、集成测试、E2E 测试
-
-现在你已经有了坚实的基础,这些概念学起来会更轻松。
-
-::: tip 🤖 Vibecoding 备忘
-**AI 代码里你会看到:**
-- `import` / `export` → 模块导入导出
-- `try/catch` → 错误处理
-- `.then()` / `.catch()` → Promise 链式调用(旧写法)
-
-**遇到问题时这样跟 AI 说:**
+::: info 💡 遇到问题时这样跟 AI 说
- "第 X 行报错 XXX,帮我看看是什么问题"
- "这个函数的逻辑是 XXX,但结果不对,应该是 XXX"
- "我想修改 XXX 功能,具体要求是 XXX"
-- "这段代码有性能问题,需要优化 XXX"
:::
-
----
-
-**写在最后:**
-
-JavaScript 是一门看似简单、实则精妙的语言。
-
-通过本章的学习,你已经建立了对这门语言的系统性认识。**深入理解这些概念,你不仅能更好地与 AI 协作,还能更快地学习新技术。**
-
-记住:**不必一次全学会,循序渐进、持续实践,你终将掌握这门语言的精髓。**
diff --git a/docs/zh-cn/appendix/3-browser-and-frontend/javascript-runtime.md b/docs/zh-cn/appendix/3-browser-and-frontend/javascript-runtime.md
index c301c92..a32a886 100644
--- a/docs/zh-cn/appendix/3-browser-and-frontend/javascript-runtime.md
+++ b/docs/zh-cn/appendix/3-browser-and-frontend/javascript-runtime.md
@@ -1,3 +1,599 @@
-# JavaScript 运行时
+# JavaScript 运行时深度指南
-> 待实现
+::: tip 前言
+你已经学会了 JavaScript 的基本语法,但你是否想过:
+- 代码到底在哪里运行?
+- 为什么同样的代码在浏览器和 Node.js 中行为不一样?
+- 为什么有时代码会"卡住",有时却能"并行"执行?
+
+这篇文章会带你深入了解 JavaScript 的运行时环境,包括事件循环、调用栈、内存管理等。读完这篇,你就能理解代码为什么按某个顺序执行,快速定位异步相关的 bug,优化代码性能并避免内存泄漏。
+:::
+
+**这篇文章会带你学什么?**
+
+| 章节 | 内容 | 学完能干嘛 |
+|-----|------|-----------|
+| **第 1 章** | 运行时概述 | 理解 JavaScript 代码在哪里运行 |
+| **第 2 章** | 浏览器运行时 | 知道浏览器提供了哪些 Web API |
+| **第 3 章** | Node.js 运行时 | 了解服务器端的 JavaScript 环境 |
+| **第 4 章** | 事件循环深入 | 掌握宏任务和微任务的执行顺序 |
+| **第 5 章** | 调用栈与内存 | 理解代码执行过程和内存管理 |
+| **第 6 章** | 实战技巧 | 优化性能、调试内存泄漏 |
+
+---
+
+## 1. 运行时概述
+
+::: tip 🤔 核心问题
+**什么是"运行时"?** JavaScript 只是一门语言,为什么同样的代码在不同环境中会有不同的行为?
+:::
+
+### 1.1 运行时是什么
+
+**运行时 = JavaScript 引擎 + 环境提供的 API**
+
+如果把 JavaScript 比作"编程语言",那么运行时就是"操作系统"——它决定了你的代码能做什么、不能做什么。
+
+```
+┌─────────────────────────────────────┐
+│ JavaScript 代码 │
+├─────────────────────────────────────┤
+│ JavaScript 引擎 (V8) │ ← 负责解析和执行代码
+├─────────────────────────────────────┤
+│ 运行时环境 (浏览器/Node.js) │ ← 提供额外能力
+└─────────────────────────────────────┘
+```
+
+**一个比喻:JavaScript 是"普通话",运行时是"城市"**
+
+- JavaScript 语法(普通话)哪里都一样
+- 但不同城市提供的设施不一样:
+ - 浏览器 = 有 DOM、window、fetch(就像城市有商场、图书馆)
+ - Node.js = 有 fs、http、path(就像城市有工厂、高速公路)
+
+### 1.2 两大主流运行时
+
+| 特性 | 浏览器 | Node.js |
+|------|--------|---------|
+| **主要用途** | 网页交互、用户界面 | 服务器端应用、命令行工具 |
+| **全局对象** | `window` | `global` |
+| **DOM API** | ✅ 支持 | ❌ 不支持 |
+| **文件系统** | ❌ 受限 | ✅ 完整支持 |
+| **模块系统** | ES Modules | CommonJS + ES Modules |
+| **定时器** | `setTimeout`, `setInterval` | `setTimeout`, `setInterval` |
+| **网络请求** | `fetch`, `XMLHttpRequest` | `http`, `https` 模块 |
+
+👇 **动手试试看**:对比浏览器和 Node.js 的环境差异
+
+
+
+::: info 💡 核心启示
+运行时决定了你能用什么 API。在浏览器能用的 DOM API,在 Node.js 里用不了;在 Node.js 能用的文件 API,在浏览器里也用不了。这就是为什么有些代码需要"环境判断"。
+:::
+
+---
+
+## 2. 浏览器运行时
+
+::: tip 🤔 核心问题
+**浏览器提供了哪些能力让 JavaScript 操作网页?**
+:::
+
+### 2.1 浏览器运行时的组成
+
+```
+┌─────────────────────────────────────────────┐
+│ JavaScript 引擎 │
+│ (V8 / SpiderMonkey) │
+└─────────────────────────────────────────────┘
+ ↓
+┌─────────────────────────────────────────────┐
+│ Web APIs │
+│ ┌─────────┐ ┌──────────┐ ┌──────────┐ │
+│ │ DOM │ │ BOM │ │ Network │ │
+│ │ 操作网页 │ │ 操作浏览器 │ │ 网络请求 │ │
+│ └─────────┘ └──────────┘ └──────────┘ │
+└─────────────────────────────────────────────┘
+ ↓
+┌─────────────────────────────────────────────┐
+│ 事件循环 (Event Loop) │
+│ 负责协调代码执行、事件处理、任务调度 │
+└─────────────────────────────────────────────┘
+```
+
+### 2.2 Web APIs 的三大类
+
+**1. DOM API - 操作网页内容**
+
+```javascript
+// 查找元素
+const title = document.querySelector('h1')
+
+// 修改内容
+title.textContent = '新标题'
+
+// 添加样式
+title.style.color = 'red'
+```
+
+**2. BOM API - 操作浏览器**
+
+```javascript
+// 页面跳转
+window.location.href = 'https://example.com'
+
+// 浏览器存储
+localStorage.setItem('key', 'value')
+
+// 浏览器历史
+history.back()
+```
+
+**3. Network API - 网络请求**
+
+```javascript
+// 发送 HTTP 请求
+fetch('/api/data')
+ .then(response => response.json())
+ .then(data => console.log(data))
+```
+
+### 2.3 浏览器特有的事件机制
+
+浏览器运行时最强大的功能之一是"事件驱动"——代码不需要一直运行,而是等用户操作时才执行。
+
+```javascript
+button.addEventListener('click', () => {
+ console.log('按钮被点击了')
+})
+```
+
+**常见事件类型:**
+
+| 事件类型 | 触发时机 | 实际场景 |
+|---------|---------|---------|
+| `click` | 鼠标点击 | 按钮交互 |
+| `input` | 输入框内容变化 | 实时搜索 |
+| `scroll` | 页面滚动 | 懒加载 |
+| `load` | 资源加载完成 | 初始化数据 |
+| `error` | 发生错误 | 错误处理 |
+
+---
+
+## 3. Node.js 运行时
+
+::: tip 🤔 核心问题
+**JavaScript 能在服务器端运行,靠的是什么?**
+:::
+
+### 3.1 Node.js 的组成
+
+```
+┌─────────────────────────────────────────────┐
+│ JavaScript 引擎 │
+│ (V8) │
+└─────────────────────────────────────────────┘
+ ↓
+┌─────────────────────────────────────────────┐
+│ Node.js 内置模块 │
+│ ┌─────────┐ ┌──────────┐ ┌──────────┐ │
+│ │ fs │ │ http │ │ path │ │
+│ │ 文件操作 │ │ 网络服务器 │ │ 路径处理 │ │
+│ └─────────┘ └──────────┘ └──────────┘ │
+└─────────────────────────────────────────────┘
+ ↓
+┌─────────────────────────────────────────────┐
+│ libuv 事件循环库 │
+│ 跨平台的异步 I/O 支持 │
+└─────────────────────────────────────────────┘
+```
+
+### 3.2 Node.js 特有能力
+
+**1. 文件系统操作**
+
+```javascript
+const fs = require('fs')
+
+// 读取文件
+fs.readFile('./data.txt', 'utf8', (err, data) => {
+ if (err) throw err
+ console.log(data)
+})
+
+// 写入文件
+fs.writeFile('./output.txt', 'Hello', (err) => {
+ if (err) throw err
+ console.log('写入成功')
+})
+```
+
+**2. HTTP 服务器**
+
+```javascript
+const http = require('http')
+
+const server = http.createServer((req, res) => {
+ res.writeHead(200, { 'Content-Type': 'text/html' })
+ res.end('
Hello World
')
+})
+
+server.listen(3000)
+```
+
+**3. 模块系统**
+
+```javascript
+// CommonJS (Node.js 默认)
+const fs = require('fs')
+module.exports = { myFunction }
+
+// ES Modules (现代方式)
+import fs from 'fs'
+export { myFunction }
+```
+
+### 3.3 浏览器 vs Node.js 对比
+
+| 特性 | 浏览器 | Node.js |
+|------|--------|---------|
+| **入口文件** | HTML 文件 | JavaScript 文件 |
+| **全局对象** | `window`, `document` | `global`, `process` |
+| **模块加载** | `