feat: add AI and Backend evolution history with interactive demos, and refine Frontend evolution demo

This commit is contained in:
sanbuphy
2026-01-18 10:24:35 +08:00
parent 82be39a9ac
commit 26ed39e1eb
44 changed files with 9868 additions and 2633 deletions
+83
View File
@@ -0,0 +1,83 @@
# 人工智能进化史:从 "逻辑" 到 "直觉" (Interactive Intro)
> 💡 **学习指南**:本章节通过交互式演示,带你梳理人工智能 70 年的发展脉络。从最早的下棋程序,到今天能写诗作画的 ChatGPT。
<AiEvolutionDemo />
## 0. 引言:机器能思考吗?
图灵在 1950 年提出了这个问题。
为了回答它,人类进行了长达半个多世纪的探索。
我们走过弯路(试图穷举规则),也经历过寒冬(算力不足),最终在模仿人脑(神经网络)的道路上取得了突破。
---
## 1. 符号主义:教机器"守规矩" (1950s - 1980s)
早期的 AI 科学家认为:智慧就是**逻辑推理**。
只要我们把世界上的所有知识都写成 `If...Then...` 的规则,机器就能像人一样聪明。
这被称为**专家系统 (Expert Systems)**。
### 1.1 什么是"基于规则"
就像教小孩:
* 如果看到红灯,就停下。
* 如果下雨,就带伞。
### 1.2 交互演示:规则 vs 学习
下方的演示展示了两种方式的区别。
* **左边 (规则)**:你必须显式地写代码 `if (size > 6)`。如果世界变了(比如苹果变小了),你的代码就失效了。
* **右边 (学习)**:你不需要写规则。你只需要给机器看一堆苹果和樱桃的数据,点击 **Train**,它自己会"悟"出一个分界线。
<RuleBasedVsLearningDemo />
**局限性**:你能写出"识别猫"的规则吗?
"有胡须"?老鼠也有。"有尖耳朵"?狗也有。
现实世界太复杂,规则写不完。这就是符号主义 AI 衰落的原因。
---
## 2. 连接主义:教机器"像人脑一样思考" (2010s+)
既然规则写不完,不如让机器自己学?
科学家开始模仿人脑的结构——**神经元**。
### 2.1 感知机 (Perceptron)
这是最简单的神经元模型。它接收多个输入,根据**权重 (Weight)** 加权求和,如果超过某个**阈值 (Bias)**,就激活。
$$ Output = \begin{cases} 1 & \text{if } \sum (w_i \cdot x_i) + b > 0 \\ 0 & \text{otherwise} \end{cases} $$
听起来很复杂?动手试一下!
### 2.2 交互演示:玩转神经元
调整下方的 **Weights (权重)****Bias (偏置)**,看看能否控制神经元的输出。
* **Weights ($w$)**:代表输入的"重要性"。$w$ 越大,这个输入对结果影响越大。
* **Bias ($b$)**:代表神经元的"门槛"。$b$ 越小,神经元越容易兴奋(输出 1)。
<PerceptronDemo />
当几十亿个这样的神经元连接在一起,奇迹就发生了——这就是**深度学习 (Deep Learning)**。
---
## 3. 生成式 AI:机器有了"创造力" (2020s+)
以前的 AI 主要是**判别式**(这是猫还是狗?)。
现在的 AI 是**生成式**(画一只猫!)。
这一切的背后,是 **Transformer** 架构的诞生。它让 AI 学会了理解上下文,学会了"举一反三"。
> 关于大语言模型 (LLM) 的详细原理,请移步下一章:[大语言模型入门](./llm-intro.md)
---
## 4. 总结
| 时代 | 核心理念 | 代表产物 | 局限 |
| :--- | :--- | :--- | :--- |
| **符号主义** | 智慧 = 规则 | 深蓝 (下棋), 医疗诊断系统 | 无法处理模糊、复杂的现实世界 |
| **连接主义** | 智慧 = 神经网络 | AlphaGo, 人脸识别 | 需要海量数据,是个"黑盒" |
| **生成式 AI** | 智慧 = 通用理解 | ChatGPT, Midjourney | 幻觉 (一本正经胡说八道) |
AI 的进化,就是从"人工设定规则"到"机器自动学习数据"的过程。
+89
View File
@@ -0,0 +1,89 @@
# 后端进化史:从 "单体" 到 "无服务" (Interactive Intro)
> 💡 **学习指南**:本章节无需编程基础,通过交互式演示带你回顾后端架构的 30 年变迁。我们将从最早的物理服务器讲起,一直到现代的 Serverless 云计算。
<BackendEvolutionDemo />
## 0. 引言:看不见的"大后方"
你点的外卖、刷的视频、发的微信,背后都有一个庞大的系统在支撑。
这个系统就是**后端 (Backend)**。
如果前端是"餐厅的服务员",那后端就是"后厨"。
为了服务越来越多的客人(用户),后厨经历了一次次痛苦的扩建和重组。
核心变化只有一点:**如何以最低的成本,支撑最大规模的用户?**
---
## 1. 简单粗暴:单体架构 (Monolith)
在 2010 年以前,绝大多数应用都是**单体**的。
就像一个小餐馆,洗菜、切菜、炒菜都在一个大厨房里完成。
* **优点**:开发简单,部署方便(把一个大包扔到服务器上就行)。
* **缺点**:牵一发而动全身。
### 1.1 "雪崩"效应
想象一下,如果"切菜"的师傅不小心切到了手(代码出了 Bug),整个后厨都要停下来处理伤口,导致所有客人都吃不上饭。
这就是单体架构最大的风险:**隔离性差**。
### 1.2 交互演示:单体 vs 微服务
下方的演示展示了两种架构在面对故障时的不同表现。
* 点击 **"Simulate Crash"** 按钮,模拟订单模块 (Order Service) 崩溃。
* **左边 (单体)**:订单模块崩溃导致内存溢出,**整个系统**(包括用户、支付)全部瘫痪。
* **右边 (微服务)**:订单模块挂了,但用户还能登录,还能查看历史账单。只有"下单"功能暂时不可用。
<MonolithVsMicroserviceDemo />
**关键点**:微服务架构的核心价值,在于**故障隔离**和**独立扩展**。
---
## 2. 蚂蚁雄兵:微服务 (Microservices)
为了解决单体的问题,我们把大厨房拆成了很多个小厨房(服务)。
* 专门负责用户的服务
* 专门负责订单的服务
* 专门负责支付的服务
### 2.1 容器化革命 (Docker)
怎么管理这么多小厨房?
Docker 就像是**集装箱**。它把每个小服务连同它的锅碗瓢盆(依赖库)一起打包。
无论运到哪里(哪台服务器),打开集装箱就能直接开工,不用再重新安装环境。
---
## 3. 云端进化:无服务 (Serverless)
微服务虽然好,但维护几十个小厨房还是很累。你需要担心:
* 厨房够不够大?(服务器扩容)
* 停电了怎么办?(高可用)
### 3.1 什么是 Serverless
Serverless 并不是"没有服务器",而是**"你不需要管理服务器"**。
就像你现在不想自己做饭,也不想开饭馆,而是直接叫**外卖**。
* 你只需要写代码(下单)。
* 云厂商(美团)负责准备机器、运行代码、自动扩容。
* **按次付费**:代码跑了 100 毫秒,就收 100 毫秒的钱。没人访问就不收钱。
### 3.2 适用场景
Serverless 特别适合:
* **潮汐流量**:比如外卖软件,中午流量大,半夜没人。Serverless 会自动在中午为你分配 1000 台机器,半夜缩减到 0 台。
* **事件驱动**:比如"用户上传图片后,自动压缩图片"。
---
## 4. 总结
后端架构的演进,本质上是在做**加法**和**减法**:
| 时代 | 架构 | 开发者要做的事 | 运维要做的事 |
| :--- | :--- | :--- | :--- |
| **单体时代** | 一整块 | 写所有业务逻辑 | 维护一台大服务器 |
| **微服务时代** | 拆分 | 关注单一业务 | 维护 K8s 集群 (很累!) |
| **Serverless** | 函数 | 只写核心函数 | 喝茶 (云厂商全包了) |
未来的后端开发,将越来越像"搭积木"——你只需要关注**业务逻辑**,底层的脏活累活,全部交给云。
@@ -0,0 +1,119 @@
# 浏览器调试器 (DevTools) 指南
::: tip 💡 核心作用
浏览器开发者工具(DevTools)是前端开发的“X光机”和“手术台”。它能让你看穿网页的骨架(HTML)、皮肤(CSS)和神经系统(JavaScript),并且允许你实时地修改和调试它们。
:::
## 1. 什么是 DevTools
**DevTools** 是现代浏览器(Chrome, Edge, Firefox, Safari 等)内置的一套 Web 开发和调试工具。对于开发者来说,它比代码编辑器更接近“真相”,因为**它展示的是代码在浏览器中实际运行的样子**。
**如何打开 DevTools**
- **快捷键**`F12``Ctrl + Shift + I` (Mac: `Cmd + Option + I`)
- **鼠标**:在网页任意元素上**右键点击**,选择 **“检查 (Inspect)”**。
- **菜单**:浏览器右上角菜单 -> 更多工具 -> 开发者工具。
---
## 2. 交互式演示:DevTools 模拟器
为了让你快速上手,我们制作了一个模拟的 DevTools 面板,复刻了 Chrome 浏览器的调试界面。
**请尝试点击下方的“▶ 开始自动导览”按钮,跟随光标了解各个区域的功能。**
<ClientOnly>
<BrowserDevToolsDemo />
</ClientOnly>
### 2.1 进阶演示:实时修改网页 (Live Edit)
DevTools 最强大的功能之一就是**实时修改**。下方的演示包含了一个“虚拟网页”(上方)和一个“DevTools”(下方)。
**请尝试:**
1. 在下方的 Elements 面板中,点击 DOM 树中的 `h1``button` 元素。
2. 在右侧的 Styles 面板中,修改 `element.style` 中的属性值(例如将 `color` 改为 `red`)。
3. 观察上方的虚拟网页如何**实时发生变化**。
<ClientOnly>
<BrowserDevToolsLiveDemo />
</ClientOnly>
### 2.2 实战挑战:修改真实网页文字
既然你已经掌握了修改样式的技巧,现在让我们来点更刺激的——**直接修改你当前看到的网页!**
1. **打开真实的 DevTools**:按下 `F12`(或右键点击本行文字 -> 选择“检查”)。
2. **定位元素**:在 Elements 面板中,你会看到一行被高亮选中的代码,那正是你刚刚点击的文字。
3. **修改内容**:**双击** 这行代码中的黑色文字部分,将其修改为“**我是黑客!**”,然后按下回车。
4. **见证奇迹**:看!网页上的文字是不是变了?
::: info 🤔 为什么刷新后就没了?
你可能会发现,当你刷新页面后,所有的修改都消失了,网页又变回了原来的样子。
这是因为 DevTools 的修改仅仅发生在**你的浏览器本地内存**中。
- 当你访问网页时,浏览器从**远程服务器**下载了 HTML 代码并在本地渲染出来。
- 你修改的只是**本地的副本**,并没有权限去修改服务器上的**源代码**。
- 所以每次刷新,浏览器都会重新去服务器拉取最新的(未被修改的)代码,一切就复原了。
:::
---
## 3. 核心面板详解
### 3.1 Elements (元素面板)
**作用**:查看和实时编辑页面的 HTML 和 CSS。
- **左侧 (DOM 树)**:显示网页的 HTML 结构。你可以双击标签或文本进行修改,甚至拖拽节点改变位置。
- **右侧 (Styles)**:显示选中元素的 CSS 样式。你可以勾选/取消样式查看变化,或者直接修改数值(如颜色、边距)。
- **应用场景**
- "为什么这个按钮没有对齐?" -> 检查 CSS 样式。
- "我想试试这个标题变成红色好看吗?" -> 直接在 Styles 里修改 `color: red`
### 3.2 Console (控制台面板)
**作用**:查看日志信息,运行 JavaScript 代码。
- **日志输出**:网页运行时的 `console.log()` 信息、警告(黄色)和报错(红色)都会显示在这里。
- **交互环境**:你可以在这里输入任意 JS 代码并立即执行。例如输入 `alert('Hello')` 会弹窗,输入 `document.body.style.background = 'red'` 会把背景变红。
- **应用场景**
- "为什么点击按钮没反应?" -> 查看是否有红色报错信息。
- "验证一个 JS 函数的返回值。" -> 直接在控制台运行测试。
### 3.3 Network (网络面板)
**作用**:监控所有网络请求。
- **列表视图**:显示加载的所有资源(HTML, CSS, JS, 图片, 接口请求)。
- **交互详情**:点击任意请求行,右侧会滑出详情面板:
- **Headers (标头)**:查看请求头、响应头(如 `Content-Type`)。
- **Response (响应)**:查看服务器返回的原始数据(JSON、HTML 代码等)。
- **Preview (预览)**:以更易读的格式预览响应内容。
- **关键指标**
- **Status**:状态码(200 成功,404 找不到,500 服务器错误)。
- **Type**:资源类型(fetch/xhr 代表接口请求)。
- **Time**:加载耗时。
- **应用场景**
- "接口是不是挂了?" -> 看接口请求是不是红色的 500。
- "页面加载为什么这么慢?" -> 找哪个图片或文件加载时间最长。
### 3.4 Sources (源代码面板)
**作用**:查看源代码,调试 JavaScript。
- **断点调试**:点击行号可以设置“断点 (Breakpoint)”。当代码执行到这一行时会**暂停**,让你有机会查看当前的变量值,并单步执行代码。
- **应用场景**
- "代码逻辑哪里出错了?" -> 打断点,一步步看着代码跑,看变量值是否符合预期。
### 3.5 Application (应用面板)
**作用**:查看和管理浏览器存储。
- **Storage**
- **Local Storage**:持久化存储的数据。
- **Session Storage**:会话级存储(关闭标签页消失)。
- **Cookies**:用于身份验证等的小型文本数据。
- **应用场景**
- "清除登录状态" -> 删除 Cookies 或 Local Storage 中的 token。
- "查看缓存的数据" -> 检查 Local Storage 里存了什么。
---
## 4. 实战小技巧
1. **手机模式调试**:点击 DevTools 左上角的“手机图标” 📱,可以模拟不同型号的手机(iPhone, Pixel 等)屏幕尺寸,测试网页的响应式效果。
2. **强制状态**:在 Elements 面板,右键点击一个元素,选择 `Force state` -> `:hover`,可以强制让元素保持悬停状态,方便调试鼠标悬停时的样式。
3. **截图节点**:在 Elements 面板选中一个节点,按下 `Ctrl + Shift + P` (Mac: `Cmd + Shift + P`) 打开命令菜单,输入 `screenshot`,选择 `Capture node screenshot`,可以直接把这个 DOM 节点截图保存为图片。
::: warning ⚠️ 注意
DevTools 中的所有修改(修改 HTML、CSS、JS)都是**临时的**,仅在当前浏览器页面生效。一旦刷新页面,所有修改都会丢失。如果想永久生效,必须修改你的源代码文件。
:::
+66
View File
@@ -0,0 +1,66 @@
# 前端进化史:从 "切图" 到 "工程化" (Interactive Intro)
> 💡 **学习指南**:本章节无需编程基础,通过交互式演示带你回顾前端开发的 20 年变迁。我们将从最基础的 HTML 讲起,一直到现代的 Vue/React 组件化开发。
<FrontendEvolutionDemo />
## 0. 引言:网页的"身份危机"
最早的网页,只是**电子海报**。
现在的网页,是**桌面级应用** (如 VS Code, Figma)。
为了支撑这种转变,前端技术经历了一场从 "手工作坊" 到 "工业化生产" 的革命。
核心变化只有一点:**如何更高效地管理日益复杂的页面状态?**
---
## 1. 痛苦的根源:命令式操作 (Imperative)
在 jQuery 时代(2005+),我们操作网页就像是在**发号施令**。
"嘿,浏览器!找到那个 ID 是 `msg` 的 div,把它隐藏起来!然后把那个按钮变红!"
这种方式直观,但随着页面变复杂,命令会打结。
### 1.1 什么是"命令式"
这就好比你要画一幅画:
* **命令式**:你告诉画家“拿起笔,蘸红颜料,在坐标(10,10)画一个圈”。
* **声明式**:你直接给画家一张照片,“给我画成这样”。
### 1.2 交互演示:命令式 vs 声明式
下方的演示展示了两种思维的巨大差异。
* **左边 (jQuery)**:你需要手动关注每一步 DOM 操作。忘了更新 DOM?界面就不对了。
* **右边 (Vue)**:你只管修改数据 `count`,界面自动变。
<ImperativeVsDeclarativeDemo />
**关键点**:现代框架(Vue/React)的核心价值,就是把我们从繁琐的 DOM 操作中解放出来,专注于**数据(State)**。
---
## 2. 工业化革命:组件化 (Component)
解决了"怎么更新页面"的问题,接下来要解决"怎么组织代码"的问题。
以前写网页,是一个巨大的 HTML 文件,混杂着几千行 JS 代码。改一个按钮,可能弄坏整个页面。
### 2.1 乐高积木思维
现代前端把页面拆成了**组件**。
一个按钮、一个导航栏、一个商品卡片,都是独立的积木。
### 2.2 组件的复用
定义好一个"商品卡片"组件后,你可以由它生成 100 个实例。每个实例都有自己独立的状态(比如点赞状态),互不干扰。
<ComponentReusabilityDemo />
**思考**:上面的演示中,点击生成的每一个 "Counter" 或 "Card",它们的数据是独立的吗?
是的。这就是组件化的魔力:**封装**与**复用**。
---
## 3. 总结
前端技术的进化,本质上是在解决两个问题:
1. **效率**:从 手动操作 DOM -> 数据驱动 (MVVM)。
2. **规模**:从 巨型面条代码 -> 组件化架构。
现在的你,正站在巨人的肩膀上,使用着最先进的生产力工具。
+52 -526
View File
@@ -1,565 +1,91 @@
# Git 版本控制入门
# Git 版本控制:时间的后悔药
> 💡 **学习指南**:本章节通过**8个交互式演示**带你从零掌握 Git。无需编程基础,我们将从日常文件备份的痛点出发,通过可视化操作一步步探索 Git 的核心概念和工作原理
> 💡 **一句话解释**:Git 就是代码世界的**游戏存档管理器**。它能让你随时“读档”重来,也能让你和队友在各自的“平行宇宙”里互不干扰地开发
## 0. 引言:从"手动备份"到"智能版本管理"
## 1. 为什么我们需要它?
### 0.1 你是否遇到过这样的困境?
你是否经历过这种绝望?
想象一下,你在写一份重要的论文:
```
论文_最终版.docx
论文_最终版_v2.docx
论文_最终版_真的最终版.docx
论文_最终版_打死不改版.docx
论文_最终版_最后一次修改.docx
论文_最终版_老板说要改版.docx
```text
论文_初稿.doc
论文_修改版.doc
论文_最终版.doc
论文_最终版_打死不改版.doc
论文_绝对是最后一次修改版.doc
```
**核心问题**:
1. **混乱的版本号**:你根本分不清哪个是最新的版本
2. **无法回退**:不小心删了一段重要文字,想找回三天前的版本,但发现覆盖了
3. **协作灾难**:和同学一起写报告,各自修改后合并成噩梦
4. **不知道改了什么**:打开两个版本的文件对比,眼睛都看花了
### 0.2 Git 的解决方案
Git 的核心任务只有一个:**记录每一次改动,让你随时回到任何版本**。
本教程将通过**8个交互式演示**,让你直观理解 Git 的核心机制:
1. **Git工作流** - 完整的提交流程
2. **三个区域** - 工作区、暂存区、仓库
3. **存储机制** - 增量存储 vs 完整备份
4. **命令操作** - 交互式命令行
5. **分支管理** - 并行开发的魔法
6. **冲突解决** - 协作中的挑战
7. **Stash工作流** - 任务切换利器
8. **远程协作** - 团队协作基础
**Git 完美解决了三个问题**
1. **版本混乱**:不需要复制副本,一个文件夹搞定所有历史版本。
2. **无法后悔**:删错了代码?一秒钟找回三天前的状态。
3. **协作冲突**:你改了 A 文件,我改了 B 文件,Git 帮我们自动合并。
---
## 1. 快速体验:Git 是如何工作的?
## 2. 核心概念:三个箱子
让我们通过第一个交互式演示,直观感受 Git 的核心工作流程:
<GitWorkflowDemo />
**动手试试**:
1. 点击"初始化仓库",创建你的版本库
2. 点击"提交",记录当前的代码状态
3. 点击"创建分支",开发新功能而不影响主线
4. 点击"合并分支",将新功能整合到一起
> 💡 **观察**:每个提交都像给代码拍了一张"照片",Git 记住每一次变化,让你随时回退。
---
## 2. 核心概念:Git 的三个区域
### 2.1 为什么需要三个区域?
传统的备份工具只有一个区域(你的文件夹),你修改文件后就直接覆盖原文件。但这样有个大问题:**你无法区分"已保存的版本"和"正在修改的版本"**。
Git 的聪明之处在于:它引入了**三个区域**来管理文件:
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 工作区 │ ──▶ │ 暂存区 │ ──▶ │ 仓库 │
│ (Working) │ add │ (Staging) │ commit│ (Repository)│
│ │ │ │ │ │
│ 你实际看到的 │ │ 准备提交的 │ │ 永久保存的 │
│ 文件 │ │ 文件清单 │ │ 历史版本 │
└─────────────┘ └─────────────┘ └─────────────┘
```
**类比理解**:
- **工作区** = 你的办公桌,你可以随意摆放、修改文件
- **暂存区** = 准备归档的文件筐,你把要保存的文件放进去
- **仓库** = 档案馆,永久保存所有历史版本
### 2.2 文件的生命周期
```
未跟踪 (Untracked) → 已修改 (Modified) → 已暂存 (Staged) → 已提交 (Committed)
↑ ↓ ↓ ↓
新建文件 文件内容改变 添加到暂存区 保存到仓库
```
### 2.3 交互式演示:三个区域的文件流动
Git 的设计哲学其实很像**寄快递**。
<GitThreeAreasDemo />
**动手试试**:
* **工作区 (Working Dir)**:你的**书桌**。你正在这里写代码,想怎么乱改都行。
* **暂存区 (Staging Area)**:**快递盒**。你把写好的文件放进去(`git add`),准备打包。
* **仓库 (Repository)****快递柜**。一旦你封箱寄出(`git commit`),这个版本就被永久记录下来了。
1. 点击"新建文件"创建一个文件到工作区
2. 选择文件并点击"添加到暂存区"(git add)
3. 点击"提交"将暂存区的文件保存到仓库(git commit)
4. 观察文件状态的变化和提交历史的生成
> 💡 **观察**:暂存区就像一个"中间站",让你可以选择性地提交某些文件,而不是全部。这给了你更精细的控制权。
> 🔑 **关键点**:只有提交(Commit)到仓库的内容,才是安全的。工作区里没提交的内容,丢了就真丢了。
---
## 3. 为什么不用简单的"复制粘贴"?
## 3. 基础工作流:存档三步走
### 3.1 传统备份方案的问题
日常开发中,你 90% 的时间都在重复这三个动作。
**问题 1:空间浪费**
<GitWorkflowDemo />
```
版本 1: 一个 100MB 的项目文件
版本 2: 只改了 1 行代码,又要保存 100MB
版本 3: 又改了 2 行代码,再保存 100MB
...
结果:10 个版本 = 1GB 空间,但实际改动只有几行代码
```
**问题 2:无法快速对比**
你有 10 个版本的文件,想知道版本 5 和版本 8 之间改了什么:
❌ 只能打开两个文件,肉眼逐行对比
❌ 容易遗漏,耗时耗力
### 3.2 Git 的解决方案:增量存储 + 快照
<GitStorageDemo />
**动手试试**:
1. 切换到"完整备份"模式,观察存储空间的增长
2. 切换到"Git 增量存储"模式,对比存储效率
3. 点击"添加版本",模拟多次文件修改
4. 观察存储统计和空间节省
> 💡 **观察**:Git 通过增量存储和内容寻址,只保存文件的变更部分。随着版本增加,空间节省越明显。
1. **修改代码**:在工作区写写画画。
2. **`git add`**:挑选你要保存的文件,放入暂存区。
3. **`git commit`**:给这次修改起个名字(比如“修复了登录 Bug”),永久存档。
---
## 4. 第一次提交:Git 基础命令
## 4. 平行宇宙:分支 (Branch)
### 4.1 初始化仓库
这是 Git 最强大的功能。想象你在玩游戏,前面有个大 Boss(上线新功能),你怕打不过导致游戏结束(系统崩溃)。
```bash
# 在当前目录创建 Git 仓库
git init
# 输出:
# Initialized empty Git repository in /your/project/.git/
```
**发生了什么?**
Git 在你的项目目录下创建了一个隐藏的 `.git` 文件夹:
```
your-project/
├── .git/ # Git 的所有数据都在这里
│ ├── objects/ # 存储所有文件对象
│ ├── refs/ # 存储分支和标签引用
│ ├── HEAD # 指向当前分支
│ └── config # 仓库配置
└── your-files/ # 你的项目文件
```
### 4.2 查看状态
```bash
# 查看当前仓库状态
git status
```
### 4.3 添加文件到暂存区
```bash
# 添加单个文件
git add hello.txt
# 添加所有文件
git add .
```
### 4.4 提交到仓库
```bash
# 提交并添加说明
git commit -m "第一次提交:添加问候文件"
```
### 4.5 交互式演示:命令行实战
<GitCommandDemo />
**动手试试**:
1. 查看当前状态:`git status`
2. 添加文件到暂存区:`git add hello.txt`
3. 提交更改:`git commit -m "添加问候文件"`
4. 查看提交历史:`git log --oneline`
> 💡 **观察**:通过真实的命令行操作,理解 Git 的工作流程。记住:工作区 → add → 暂存区 → commit → 仓库
---
## 5. 分支管理:并行开发的魔法
### 5.1 什么是分支?
**传统开发的问题**:
你想开发一个新功能,但不想影响当前稳定的代码,怎么办?
**传统方案**:
```
❌ 复制整个项目文件夹
project/ # 当前版本
project-feature/ # 开发新功能
```
**问题**:
- 浪费空间(两个完整副本)
- 两个版本容易混淆
- 合并时困难
**Git 的解决方案:分支**
Git 的分支**不是复制整个项目**,而是:
- 只保存当前版本的"指针"
- 分支创建是瞬间完成的
- 分支间切换极其快速
**类比**:
> 📚 **分支就像书的"草稿"**:
>
> > - 正式内容在 main 分支(可以出版)
> > - 新想法在 feature 分支(自由修改)
> > - 修改完成后,合并到 main 分支
### 5.2 分支的基本操作
**创建和切换分支**:
```bash
# 查看所有分支
git branch
# 创建新分支
git branch feature-login
# 切换到新分支
git checkout feature-login
# 创建并切换(一步完成)
git checkout -b feature-login
# 或使用新的 switch 命令(推荐)
git switch -c feature-login
```
### 5.3 合并分支:整合你的工作
这时候,你可以开一个**分支 (Branch)**,相当于**复制了一个平行世界**。
<GitBranchMergeDemo />
**动手试试**:
* **主分支 (Main/Master)**:稳定的线上版本,只有测试通过的代码才能进来。
* **开发分支 (Feature)**:你的试验田。你在这里炸了地球也没关系,不会影响主分支。
* **合并 (Merge)**:你在试验田里测试成功了,就把改动“合并”回主分支。
1. 点击"初始化仓库"创建一个 Git 仓库
2. 在 main 分支上提交几次
3. 点击"创建分支"开发新功能
4. 在 feature 分支上提交几次
5. 点击"切换分支"回到 main 分支
6. 点击"合并分支"将 feature 合并到 main
---
> 💡 **观察**:注意观察分支历史图,每个分支都有独立的提交线,合并时会创建一个新的合并提交。
## 5. 常用命令速查
### 5.4 冲突解决:协作的挑战
| 命令 | 作用 | 人话解释 |
| :--- | :--- | :--- |
| `git init` | 初始化 | "我要在这里建个新仓库" |
| `git status` | 查看状态 | "现在书桌上乱不乱?有没有东西没装箱?" |
| `git add .` | 添加所有 | "把桌上所有文件都扔进快递盒" |
| `git commit -m "..."` | 提交 | "封箱!贴上标签,写上这次改了啥" |
| `git log` | 查看历史 | "翻翻以前的日记" |
| `git checkout -b dev` |以此创建新分支 | "我要去平行宇宙 dev 探险了" |
| `git checkout main` | 切换分支 | "回地球(主分支)看看" |
| `git merge dev` | 合并分支 | "把平行宇宙的成果带回地球" |
**什么是冲突?**
---
当两个分支修改了同一文件的同一行,Git 无法自动决定保留哪个版本。
## 6. 进阶:解决冲突与远程协作
当你和队友同时修改了同一个文件的同一行代码,Git 就会懵逼:“我该听谁的?” 这就是**冲突 (Conflict)**。
<GitConflictDemo />
**动手试试**:
此时你需要手动打开文件,保留需要的代码,删除 Git 自动生成的 `<<<<<<<` 标记,然后重新提交。
1. 查看"冲突场景",理解冲突产生的原因
2. 点击"模拟产生冲突",查看冲突提示
3. 在"冲突解决编辑器"中选择解决方案
4. 应用解决方案,完成合并
> 💡 **观察**:冲突并不可怕,它是协作的正常现象。关键是要仔细查看冲突内容,与团队沟通后决定保留哪个版本。
---
## 6. 实用技巧:Git 的高级用法
### 6.1 撤销操作:时间旅行
```bash
# 恢复文件到最近一次提交的状态
git restore file.txt
# 撤销最后一次提交(保留修改)
git reset --soft HEAD~1
# 回到某个历史版本
git reset --hard <commit-hash>
```
### 6.2 暂存工作:切换任务的利器
**场景**:你正在开发功能 A,突然需要紧急修复 bug B,但功能 A 还没完成。
**解决方案**:使用 `git stash`
<GitStashDemo />
**动手试试**:
1. 在功能分支上做些修改(点击"做些修改")
2. 突然需要修复bug(场景会自动提示)
3. 点击"保存工作现场"(git stash)
4. 切换分支修复bug并提交
5. 切回功能分支,点击"恢复工作现场"
> 💡 **观察**:stash 让你在不同任务间快速切换,而不需要提交未完成的工作。它使用栈结构,后进先出。
### 6.3 查看差异:代码审计
```bash
# 查看工作区修改(未暂存)
git diff
# 查看暂存区修改(已暂存)
git diff --staged
# 查看两次提交之间的差异
git diff <commit1> <commit2>
```
---
## 7. 远程仓库:团队协作的基础
### 7.1 什么是远程仓库?
**本地仓库** = 你电脑上的 `.git` 文件夹
**远程仓库** = 存储在服务器上的仓库副本(如 GitHub、GitLab)
**为什么需要远程仓库?**
- 🔄 **备份**:代码保存在云端,不怕硬盘损坏
- 👥 **协作**:团队成员可以共享代码
- 🏠 **部署**:自动部署到生产环境
### 7.2 推送到远程
```bash
# 首次推送(设置上游分支)
git push -u origin main
# 后续推送
git push
```
### 7.3 从远程拉取
```bash
# 拉取远程更新并合并
git pull origin main
# 等价于:
git fetch origin main # 从远程获取更新
git merge origin/main # 合并到本地
```
### 7.4 交互式演示:远程仓库协作
至于**远程仓库 (Remote)**(比如 GitHub/GitLab),它就是云端的备份中心。
* `git push`:把本地存档上传到云端。
* `git pull`:把云端最新的存档拉取到本地。
<GitRemoteDemo />
**动手试试**:
1. 点击"本地提交",在本地创建一些提交
2. 点击"推送到远程",将本地提交同步到远程
3. 点击"模拟团队更新",让团队成员推送代码
4. 点击"拉取远程更新",获取团队的新代码
> 💡 **观察**:远程协作的核心是"先拉取,再推送",确保你的代码基于最新版本,避免冲突。
---
## 8. 常见问题与解决方案
### 8.1 忘记推送某文件
**问题**:提交后发现忘了添加某个文件。
```bash
# 添加遗漏的文件
git add forgotten-file.txt
# 修改最后一次提交
git commit --amend
# 如果已推送,需要强制推送(谨慎!)
git push -f origin branch
```
### 8.2 提交信息写错了
```bash
# 修改最后一次提交信息
git commit --amend -m "正确的提交信息"
```
### 8.3 推送被拒绝
**问题**:本地和远程历史不一致。
```bash
# 方法 1:先拉取再推送(推荐)
git pull --rebase origin main
git push origin main
# 方法 2:强制推送(会覆盖远程,危险!)
git push -f origin main
```
---
## 9. Git 配置与最佳实践
### 9.1 基本配置
```bash
# 设置用户名(必填)
git config --global user.name "Your Name"
# 设置邮箱(必填)
git config --global user.email "your.email@example.com"
# 设置默认分支名
git config --global init.defaultBranch main
```
### 9.2 忽略文件
创建 `.gitignore` 文件:
```bash
# 忽略文件
*.log
*.tmp
.env
.DS_Store
# 忽略文件夹
node_modules/
dist/
.cache/
```
### 9.3 提交规范
**好的提交信息**:
```bash
# 格式:
<type>(<scope>): <subject>
# 示例:
feat(auth): 添加用户登录功能
fix(login): 修复密码验证错误
docs(readme): 更新安装说明
```
**类型(type)**:
- `feat`:新功能
- `fix`:修复 bug
- `docs`:文档更新
- `style`:格式调整
- `refactor`:重构
- `test`:添加测试
- `chore`:构建/工具
---
## 10. 学习资源与工具
### 10.1 可视化工具
**图形化界面(GUI)**:
- **GitHub Desktop**:免费,简洁易用
- **SourceTree**:免费,功能强大
- **GitKraken**:免费,跨平台
- **TortoiseGit**:Windows 集成
**在线学习**:
- **Learn Git Branching**:https://learngitbranching.js.org/(强烈推荐!)
- **Git Immersion**:http://gitimmersion.com/
### 10.2 高级主题(进阶学习)
- **Git 内部原理**:对象模型、引用、包文件
- **Rebase 交互**:整理提交历史
- **Git Hooks**:自动化工作流
- **Submodule**:子模块管理
---
## 11. 总结
### Git 核心要点
通过**8个交互式演示**,我们学习了:
1.**三个区域**:工作区 → 暂存区 → 仓库
2.**存储机制**:增量存储节省空间
3.**命令操作**:add、commit、log
4.**分支管理**:并行开发,互不干扰
5.**冲突解决**:协作中的挑战与应对
6.**Stash工作流**:任务切换利器
7.**远程协作**:push、pull、团队同步
8.**工作流程**:完整的 Git 使用流程
### 学习建议
-**多动手实践**:通过交互式演示熟悉操作
-**理解原理**:Git 的三个区域、快照机制
-**查看历史**:`git log` 了解项目演进
-**不要害怕冲突**:冲突是协作的常态
-**使用工具**:GUI 工具可以降低学习曲线
### 进阶路径
```
初级: add、commit、pull、push
中级:分支、合并、冲突解决、stash
高级:rebase、交互式 rebase、cherry-pick
专家:Git 内部原理、自定义脚本、性能优化
```
---
**下一步行动**:
1. ✅ 通过本章节的**8个交互式演示**熟悉 Git 操作
2. ✅ 创建一个 Git 仓库:`git init`
3. ✅ 练习基础命令:add、commit、log
4. ✅ 尝试分支管理:创建、切换、合并
5. ✅ 注册 GitHub 账号,推送你的第一个远程仓库
掌握 Git,你就掌握了软件开发的基础设施。现在就开始使用 Git 管理你的代码吧!💪
+84 -40
View File
@@ -1,6 +1,24 @@
# 集成开发环境 (IDE) 基础
> 💡 **学习指南**:本章节将带你深入了解程序员的核心生产力工具——**集成开发环境 (IDE)**。我们将从 IDE 的设计理念出发,逐一解析其核心组件,并通过虚拟 IDE 演示其工作原理。
::: tip 💡 学习指南
本章节将带你深入了解程序员的核心生产力工具——**集成开发环境 (IDE)**。我们将从 IDE 的设计理念出发,逐一解析其核心组件,并通过虚拟 IDE 演示其工作原理。
:::
## 遇到不懂的怎么办?(How to solve problems)
在学习和使用 IDE 的过程中,你可能会遇到各种看不懂的按钮、菜单或者代码报错。这时候,**不要慌张,利用 AI 助手是最高效的解决办法**。
**推荐做法:截图问 AI**
现在的 AI(如 ChatGPT、Claude、DeepSeek 等)都具备强大的识图能力。当你遇到不认识的界面元素或复杂的代码片段时:
1. **截图**:截取你不懂的那一部分(比如某个奇怪的图标,或者一段复杂的配置代码)。
2. **提问**:把图片发给 AI,并问它:“这个是什么?有什么用?”或者“这段代码里的 xxx 是干嘛的?”。
3. **追问**:如果 AI 的回答太专业看不懂,继续问:“请用大白话解释一下,最好举个生活中的例子。”
<AiHelpDemo />
---
## 0. 引言:为什么需要 IDE?
@@ -121,43 +139,46 @@ Python 解释器(或编译器)执行完代码,将结果(或错误信息
---
<script setup>
import { onMounted } from 'vue'
onMounted(() => {
const openTarget = () => {
const hash = window.location.hash
if (hash) {
try {
// Handle encoded Chinese characters in hash
const target = document.querySelector(decodeURIComponent(hash))
// If the target is a details element, open it
if (target && target.tagName === 'DETAILS') {
target.setAttribute('open', '')
}
// If the target is inside a details element, open the parent details
const parentDetails = target?.closest('details')
if (parentDetails) {
parentDetails.setAttribute('open', '')
}
} catch (e) {
console.error(e)
}
}
}
openTarget()
window.addEventListener('hashchange', openTarget)
})
</script>
# 附录: Visual Studio Code 菜单栏解析
<el-card id="appendix-2-map" shadow="hover" style="margin-top: 40px; margin-bottom: 20px; border-left: 5px solid #67C23A;">
<div style="font-weight: bold; margin-bottom: 10px;">🧭 界面导航:VS Code 核心区域</div>
<div style="display: flex; flex-wrap: wrap; gap: 10px;">
<a href="#vscode-title-bar" style="text-decoration: none;"><el-tag effect="plain" style="cursor: pointer;">Title Bar (标题栏)</el-tag></a>
<a href="#vscode-activity-bar" style="text-decoration: none;"><el-tag effect="plain" style="cursor: pointer;">Activity Bar (活动栏)</el-tag></a>
<a href="#vscode-side-bar" style="text-decoration: none;"><el-tag effect="plain" style="cursor: pointer;">Side Bar (侧边栏)</el-tag></a>
<a href="#vscode-editor" style="text-decoration: none;"><el-tag effect="plain" style="cursor: pointer;">Editor (编辑区)</el-tag></a>
<a href="#vscode-panel" style="text-decoration: none;"><el-tag effect="plain" style="cursor: pointer;">Panel (底部面板)</el-tag></a>
<a href="#vscode-status-bar" style="text-decoration: none;"><el-tag effect="plain" style="cursor: pointer;">Status Bar (状态栏)</el-tag></a>
</div>
</el-card>
## <span id="vscode-title-bar">[Title Bar(标题栏):窗口信息与全局入口](#appendix-2-map)</span>
标题栏位于窗口最上方,主要用于展示当前窗口信息并提供窗口级控制。常见细节包括:
- 应用与窗口信息
通常显示应用名称(如 Visual Studio Code)以及当前工作区(workspace)或当前打开文件的名称,便于在多窗口并行时识别不同项目。
- 菜单入口(部分系统/布局可见)
在 Windows/Linux 的常见布局中,`File / Edit / Selection / View / Go / Run / Terminal / Help` 等菜单可能与标题栏同一行或紧邻显示,是功能的传统入口。
- 窗口控制按钮
最小化、最大化/还原、关闭按钮属于操作系统窗口控件,用于调整窗口显示方式或关闭窗口。
- AI 侧边栏开启按钮
一般而言,在右上角可以控制是否开启 AI 侧边栏或者其他侧边栏。我们默认右侧的侧边栏为 AI 侧边栏。
- 环境与状态提示或系统更新提示(可能会出现提醒你重启更新的提示,建议每次看到主动进行点击更新)
部分情况下会显示远程连接状态(SSH/容器/WSL)、信任提示(Workspace Trust)等,具体取决于当前环境与设置。
为了方便大家理解每个选项的含义,在这里我们对菜单栏进行深入解析:
![](images/index-2026-01-09-11-35-55.png)
### File(文件):项目与文件的打开/保存/工作区管理
![](images/index-2026-01-09-11-36-23.png)
<details class="custom-block details" id="vscode-file-menu">
<summary>File(文件):项目与文件的打开/保存/工作区管理</summary>
本菜单主要负责:**创建/打开文件**、**打开项目文件夹(Folder**、**管理工作区(Workspace**、**保存与关闭**。
@@ -185,7 +206,10 @@ Python 解释器(或编译器)执行完代码,将结果(或错误信息
- **Close Folder(关闭文件夹)**:关闭当前项目文件夹(工作区变为空)。
- **Close Window(关闭窗口)**:关闭当前 VS Code 窗口。
### Edit(编辑):基础编辑、查找替换、注释与快速编辑动作
</details>
<details class="custom-block details" id="vscode-edit-menu">
<summary>Edit(编辑):基础编辑、查找替换、注释与快速编辑动作</summary>
本菜单主要负责:**撤销/重做**、**剪切复制粘贴**、**查找替换**、**注释与编辑器动作**(提升编辑效率)。
@@ -197,7 +221,10 @@ Python 解释器(或编译器)执行完代码,将结果(或错误信息
- **Toggle Block Comment(切换块注释)**`Shift + Alt + A`,快速注释/取消注释选区。
- **Emmet: Expand AbbreviationEmmet 展开)**HTML/CSS 开发神器,输入简写按 Tab 展开代码。
### Selection(选择):多光标与智能选区
</details>
<details class="custom-block details" id="vscode-selection-menu">
<summary>Selection(选择):多光标与智能选区</summary>
本菜单主要负责:**光标控制**、**多行编辑**、**扩大/缩小选区**。这是 VS Code 提升效率的杀手锏。
@@ -208,7 +235,10 @@ Python 解释器(或编译器)执行完代码,将结果(或错误信息
- **Add Cursor Above / Below(在上方 / 下方添加光标)**:`Ctrl + Alt + ↑ / ↓`,开启多光标模式,同时编辑多行。
- **Add Cursor to Line Ends(在行尾添加光标)**:选中多行文本后,在每一行末尾添加光标。
### View(查看):界面布局与面板控制
</details>
<details class="custom-block details" id="vscode-view-menu">
<summary>View(查看):界面布局与面板控制</summary>
本菜单主要负责:**开关侧边栏/面板**、**调整布局**、**命令面板**、**输出与调试控制台**。
@@ -220,7 +250,10 @@ Python 解释器(或编译器)执行完代码,将结果(或错误信息
- **Problems / Output / Debug Console / Terminal**:直接控制底部面板(Panel)的显示内容。
- **Word Wrap(自动换行)**`Alt + Z`,控制长行代码是否自动换行显示(不影响实际文件内容)。
### Go(转到):代码导航与跳转
</details>
<details class="custom-block details" id="vscode-go-menu">
<summary>Go(转到):代码导航与跳转</summary>
本菜单主要负责:**在文件间跳转**、**在符号(函数/变量)间跳转**。
@@ -232,7 +265,10 @@ Python 解释器(或编译器)执行完代码,将结果(或错误信息
- **Go to References(转到引用)**`Shift + F12`,查看该变量或函数在哪些地方被使用了。
- **Go to Line/Column…(转到行/列…)**`Ctrl + G`,跳转到指定行号。
### Run(运行):调试与执行
</details>
<details class="custom-block details" id="vscode-run-menu">
<summary>Run(运行):调试与执行</summary>
本菜单主要负责:**启动调试**、**断点管理**。
@@ -243,7 +279,10 @@ Python 解释器(或编译器)执行完代码,将结果(或错误信息
- **Toggle Breakpoint(切换断点)**`F9`,在当前行打上或取消红点(断点)。
- **New Breakpoint(新建断点)**:支持条件断点、日志断点等高级功能。
### Terminal(终端):集成命令行
</details>
<details class="custom-block details" id="vscode-terminal-menu">
<summary>Terminal(终端):集成命令行</summary>
本菜单主要负责:**新建终端**、**管理终端窗口**。
@@ -251,7 +290,10 @@ Python 解释器(或编译器)执行完代码,将结果(或错误信息
- **Split Terminal(拆分终端)**:在同一个终端面板中左右/上下拆分,同时运行多个命令。
- **Run Task…(运行任务…)**:运行 `tasks.json` 中定义的构建/测试任务。
### Help(帮助):文档与反馈
</details>
<details class="custom-block details" id="vscode-help-menu">
<summary>Help(帮助):文档与反馈</summary>
- **Welcome(欢迎)**:打开欢迎页(包含入门引导、最近项目)。
- **Show All Commands(显示所有命令)**:同命令面板。
@@ -259,3 +301,5 @@ Python 解释器(或编译器)执行完代码,将结果(或错误信息
- **Editor Playground(编辑器演练场)**:交互式教程,学习编辑技巧。
- **Check for Updates…(检查更新…)**:手动检查更新。
- **About(关于)**:查看版本号、构建时间、Electron/Node 版本信息。
</details>
+56 -73
View File
@@ -1,20 +1,14 @@
# 从 URL 输入到浏览器显示 (From URL to Browser)
> 💡 **学习指南**:本章节无需网络工程基础,通过交互式演示带你深入了解浏览器如何将一行网址变成丰富多彩的页面。我们将从输入 URL 开始,一步步拆解背后的网络请求、连接建立和页面渲染过程。
> 💡 **学习指南**:本章节通过交互式演示带你深入了解浏览器如何将一行网址变成丰富多彩的页面。我们将从输入 URL 开始,一步步拆解背后的网络请求、连接建立和页面渲染过程。
## 0. 引言:一次神奇的旅行
## 0. 全景图:一次神奇的旅行
当你在浏览器地址栏输入一个网址并按下回车,短短几秒钟内,背后发生了一系列复杂而精妙的过程。这就像是一次跨越万水千山的旅行。
它的核心任务只有一个:**将你想要的资源(网页)准确无误地从世界的另一端搬运到你的屏幕上**。
为了实现这个目标,我们需要解决五个核心挑战
1. **寻址 (Addressing)**:如何告诉浏览器我要去哪里?(URL)
2. **定位 (Discovery)**:服务器的真实地址是什么?(DNS)
3. **连接 (Connection)**:如何建立一条可靠的通路?(TCP/TLS)
4. **交流 (Exchange)**:如何用对方听得懂的语言沟通?(HTTP)
5. **展示 (Rendering)**:如何将枯燥的代码变成漂亮的画面?(Rendering)
我们可以把这个过程分为五个关键阶段,点击下方的步骤来预览整个流程
<UrlToBrowserDemo />
@@ -22,75 +16,63 @@
## 1. 第一步:寻址 (URL Parsing)
### 1.1 计算机不知道"去哪里"
### 1.1 计算机的"导航地址"
如果你告诉出租车司机"去那个有很多好吃的商场",他可能会一头雾水。计算机也是一样,它需要一个精确的地址格式
计算机需要一个精确的地址格式才能找到资源。这就是 **URL (统一资源定位符)** 的作用。它不仅告诉浏览器**去哪里**(域名),还告诉它**怎么去**(协议),以及**找什么**(路径)
### 1.2 解决方案:URL (统一资源定位符)
**URL (Uniform Resource Locator)** 是互联网上的标准地址格式。它不仅告诉浏览器**去哪里**(域名),还告诉它**怎么去**(协议),以及**找什么**(路径)。
**URL 的解剖学**
试着在下方的模拟地址栏中输入不同的 URL,看看它是如何被拆解的:
<UrlParserDemo />
**关键部分解析**
- **协议 (Protocol)**`https``http`。就像告诉司机是"坐飞机"还是"坐火车"。
- **域名 (Host)**`www.example.com`。目的地的名字,方便人类记忆。
- **路径 (Path)**`/path/to/page`。资源在服务器上的具体位置,就像"3楼302室"
- **参数 (Query)**`?q=vue`。给服务器的附加指令,就像"我要微辣"
- **Protocol (协议)**:通常是 `https` (安全)`http`。就像告诉司机是"坐飞机"还是"坐火车"。
- **Host (域名)**`www.example.com`。目的地的名字,方便人类记忆。
- **Port (端口)**:服务器的"门牌号"。Web 服务默认是 80 (HTTP) 或 443 (HTTPS),通常省略不写
- **Path (路径)**`/path/to/page`。资源在服务器文件系统中的具体位置
- **Query (参数)**`?q=vue`。给服务器的附加指令,就像点餐时的备注"不要香菜"。
---
## 2. 第二步:定位 (DNS Lookup)
### 2.1 为什么不用 IP 地址?
### 2.1 互联网的"电话簿"
计算机之间通信真正识别的是 **IP 地址**(如 `142.250.185.238`,而不是域名(如 `google.com`
虽然我们记住了 `google.com` 这样的域名,但计算机之间通信真正识别的是 **IP 地址**(如 `142.250.185.238`)。
- **缺点1**:IP 地址是一串枯燥的数字,人类很难记忆
- **缺点2**:服务器搬家后 IP 会变,但我们希望域名保持不变。
**DNS (Domain Name System)** 就是互联网的"电话簿"或"导航系统"。当你输入域名时,浏览器需要先通过 DNS 找到它对应的 IP 地址
### 2.2 解决方案:DNS (域名系统)
**DNS (Domain Name System)** 就像互联网的"电话簿"。当你呼叫 `google.com` 时,DNS 会帮你查找它对应的电话号码(IP 地址)。
这个查找过程是**分级**且**递归**的,就像你问村长,村长问镇长,镇长问市长...
点击下方的 **"Go"** 按钮,观察 DNS 的**递归查询**过程:
<DnsLookupDemo />
**查询流程**
**查询流程解析**
1. **浏览器缓存**:先看看口袋里有没有小纸条(最快)。
2. **系统缓存**:看看操作系统的记事本 (`/etc/hosts`)
3. **递归查询**:如果都没有,就让 DNS 服务器去问根域名服务器、顶级域名服务器,直到找到答案
1. **浏览器缓存/Hosts**:先看看自己是否最近去过(缓存),或者本地小本本上有没有记(Hosts 文件)。
2. **递归解析器 (Recursive Resolver)**:通常由你的 ISP (运营商) 提供。它像个尽职的跑腿员,负责帮你跑完剩下的路
3. **根域名服务器 (Root Server)**:它是 DNS 层级的顶端(`.`)。它不知道具体地址,但知道谁管 `.com`
4. **顶级域名服务器 (TLD Server)**:管理 `.com``.org` 等后缀的服务器。它会告诉你 `google.com` 归谁管。
5. **权威域名服务器 (Authoritative Server)**:最终的管理者,它知道 `www.google.com` 的确切 IP 地址。
---
## 3. 第三步:连接 (TCP Connection)
## 3. 第三步:连接 (TCP Handshake)
### 3.1 为什么不能直接发数据?
### 3.1 建立可靠的通路
互联网是一个充满不确定性的环境。如果你直接扔出一个数据包:
拿到 IP 地址后,浏览器找到了服务器。但在传输数据之前,它们必须建立一条可靠的连接,确保双方都能"听得到"且"说得出"。
- ❌ 它可能在路上丢了
- ❌ 它可能顺序乱了。
- ❌ 它可能坏了。
这就是著名的 **TCP 三次握手 (Three-Way Handshake)**
### 3.2 解决方案:TCP 三次握手
**TCP (Transmission Control Protocol)** 是一种**可靠**的传输协议。在传输数据之前,通信双方必须先建立连接,确认彼此都能"听得到"且"说得出"。
这被称为**三次握手 (Three-Way Handshake)**
点击 **"Connect"** 亲自完成这次握手
<TcpHandshakeDemo />
**握手三部曲**
1. **SYN**:客户端说"你好,我想和你建立连接"SYN
2. **SYN-ACK**:服务器说"收到,我也想和你建立连接"SYN-ACK
3. **ACK**:客户端说"好的,那我们开始吧"ACK)
1. **SYN** (Synchronize):客户端发送一个包,说"你好,我想和你建立连接,我的序列号是 X"。
2. **SYN-ACK** (Synchronize-Acknowledge):服务器收到后回复,"好的,收到了 X。我也想和你建立连接,我的序列号是 Y"
3. **ACK** (Acknowledge):客户端最后回复,"好的,收到了 Y。那我们开始传输数据吧"
> 🔒 **关于 HTTPS (TLS)**
> 如果使用 HTTPS,在 TCP 握手之后,还会进行 **TLS 握手**。双方会协商加密算法并交换证书,确保后续传输的数据像装在保险箱里一样安全。
@@ -99,27 +81,27 @@
## 4. 第四步:交流 (HTTP Exchange)
### 4.1 如何索取资源?
### 4.1 索取与交付
连接建立好了,浏览器需要告诉服务器它想要什么。这就像在餐厅点餐,你需要一种服务员能听懂的格式
连接建立好了,浏览器终于可以发出它的请求了:"请给我首页的 HTML 代码"。这就像在餐厅点餐
### 4.2 解决方案:HTTP 请求与响应
**HTTP (HyperText Transfer Protocol)** 定义了这种对话的格式。
**HTTP (HyperText Transfer Protocol)** 定义了客户端和服务器之间的对话格式。
在下方的模拟器中尝试发送不同的请求(GET/POST),观察网络日志:
<HttpExchangeDemo />
**对话过程**
1. **请求 (Request)**浏览器发送一个请求报文。
- **Method**`GET`(获取)、`POST`(提交)等。
1. **请求 (Request)**
- **Method**`GET`(获取)、`POST`(提交数据)等。
- **Path**:我要什么资源。
- **Headers**:我是谁(User-Agent)、我想要什么格式(Accept)。
- **Headers**:我是谁(User-Agent)、我想要什么格式(Accept)等元数据
2. **响应 (Response)**服务器返回一个响应报文。
- **Status Code**`200 OK`(成功)、`404 Not Found`(没找到)。
- **Headers**:内容类型(Content-Type)、服务器信息。
- **Body**:具体的 HTML 代码、图片数据等
2. **响应 (Response)**
- **Status Code**`200 OK`(成功)、`404 Not Found`(没找到)`500 Error`(服务器出错了)
- **Headers**:内容类型(Content-Type)、服务器信息
- **Body**:具体的 HTML 代码、JSON 数据或图片二进制流
---
@@ -127,33 +109,34 @@
### 5.1 代码如何变成画面?
浏览器收到的是一堆枯燥的 HTML 代码,它是如何变成我们在屏幕上看到的精美网页的呢?
浏览器收到的是一堆枯燥的 HTML 代码,它是如何变成我们在屏幕上看到的精美网页的呢?这个过程叫做**渲染 (Rendering)**。
### 5.2 解决方案:渲染流水线 (Rendering Pipeline)
浏览器就像一个精密的工厂,将原材料(HTML/CSS)加工成最终产品(屏幕上的像素)。
浏览器像一个精密的工厂,将原材料(HTML/CSS)加工成产品(像素)。
点击下方的步骤,查看渲染流水线的每个阶段:
<BrowserRenderingDemo />
**流水线步骤**
**关键渲染路径 (Critical Rendering Path)**
1. **构建 DOM 树**:解析 HTML,建立文档结构树(就像房屋的框架)。
2. **构建渲染树 (Render Tree)**:结合 CSS 样式,确定哪些节点需要显示以及长什么样(就像装修设计图)
3. **布局 (Layout)**:计算每个元素在屏幕上的确切坐标和大小(就像丈量尺寸)。
4. **绘制 (Paint)**将元素画到屏幕的像素点上(就像刷漆)
2. **构建渲染树 (Render Tree)**:结合 CSS 样式,计算出所有**可见**元素的样式规则
3. **布局 (Layout/Reflow)**:计算每个元素在屏幕上的确切坐标和大小(就像丈量尺寸)。
4. **绘制 (Paint)**填充像素,包括颜色、图片、边框等
5. **合成 (Composite)**:将不同的图层(Layer)在 GPU 中合成,最终显示在屏幕上。
---
## 6. 总结
从 URL 输入到页面显示,这短短的几秒钟内,凝聚了计算机网络几十年的智慧结晶
从 URL 输入到页面显示,这短短的几秒钟内,凝聚了计算机网络几十年的智慧结晶
| 阶段 | 核心任务 | 关键技术 | 类比 |
| :---------- | :------- | :-------- | :------------- |
| **1. 寻址** | 解析目标 | URL | 确定目的地地址 |
| **2. 定位** | 查找 IP | DNS | 查电话簿 |
| **3. 连接** | 建立通路 | TCP/TLS | 打电话确认通畅 |
| **4. 交流** | 交换数据 | HTTP | 点餐对话 |
| **5. 展示** | 绘制页面 | Rendering | 装修房子 |
| 阶段 | 核心任务 | 关键技术 | 类比 |
| :--- | :--- | :--- | :--- |
| **1. 寻址** | 解析目标 | URL | 确定目的地地址 |
| **2. 定位** | 查找 IP | DNS | 查电话簿 |
| **3. 连接** | 建立通路 | TCP/TLS | 打电话确认通畅 |
| **4. 交流** | 交换数据 | HTTP | 点餐对话 |
| **5. 展示** | 绘制页面 | Rendering | 装修房子 |
现在,当你再次按下回车键时,你已经看到了屏幕背后的那个忙碌而精彩的数字世界。
+111 -164
View File
@@ -4,243 +4,190 @@
<VlmQuickStartDemo />
## 0. 引言:从“读万卷书”到“行万里路”
## 0. 引言:给大脑装上眼睛
在 [大语言模型入门](./llm-intro) 章节中,我们学习了计算机如何通过 Tokenization(分词)和 Transformer 理解文字
但真实世界不仅有文字,还有图像、视频和声音。
在 [大语言模型入门](./llm-intro) 中,我们知道 LLM 本质上是一个被关在黑盒子里、只能通过**文字**来了解世界的“大脑”
**多模态大模型 (VLM, Vision-Language Model)** 的核心任务,就是打破感官的界限,让 AI 不仅能“读”,还能“看”
**多模态大模型 (VLM)** 的出现,相当于给这个大脑装上了一双**眼睛**
它的本质工作可以总结为一句话:**把图像信号“翻译”成大模型能听懂的语言信号。**
但这并不容易。因为:
- **大脑 (LLM)** 只懂**文字**(准确说是 Token ID)。
- **眼睛 (摄像头)** 看到的是**像素**(RGB 颜色数值)。
VLM 的核心任务,就是**把“像素信号”翻译成“文字信号”**,让 LLM 觉得看图就像读文章一样简单。
---
## 1. 第一步:视觉翻译 (Visual Tokenization)
## 1. 第一步:把图片变成“单词” (Visual Tokenization)
大模型(LLM)本质上是一个“文字接龙”机器,它只认识数字(Token ID)。要让它看懂图片,我们必须把图片也变成它能理解的数字序列
想象一下,你正在电话里给朋友描述一副拼图。你不可能一口气说完,你得一块一块地描述
计算机看图也是一样的道理。
这个过程主要由 **Vision Transformer (ViT)** 完成。**请注意:ViT 本身就是一个独立的、强大的深度学习模型**,你可以把它想象成 AI 的“视网膜”。
### 1.1 切块 (Patchify) —— 制作视觉单词
### 1.1 为什么是 Transformer(ViT 详解)
LLM 习惯读单词。为了配合它,我们得把一张完整的图片切成一个个小方块(Patch)。
在 ViT 出现之前,计算机看图主要靠 **CNN (卷积神经网络)**,它像一个放大镜,一点一点地扫描图片提取特征。但 CNN 有个局限:它很难理解“全局关系”(比如左上角的鸟和右下角的树有什么关系)。
- **图片** = 一篇文章
- **方块 (Patch)** = 一个单词
**Transformer** 的核心优势在于**全局注意力 (Global Attention)**。它能同时看到整张图,并理解各个部分之间的关联
通常,我们会把图片切成 $16 \times 16$ 像素的小方块。一张 $224 \times 224$ 的图片就会变成 $14 \times 14 = 196$ 个方块
但 Transformer 本来是处理文本(一维序列)的,怎么处理图片(二维矩阵)呢?
> 🕹️ **交互演示**:点击下方按钮,看图片是如何被“切”成单词的。
| NLP (文本处理) | CV (图像处理) |
| :--------------------- | :--------------------------- |
| **句子** (Sentence) | **图片** (Image) |
| **单词** (Word) | **图片块** (Patch) |
| **词向量** (Embedding) | **特征向量** (Patch Feature) |
<PatchifyDemo />
**ViT 的核心思想**:把一张图切成很多小块,然后把这些小块当成一个个单词,排成一句话喂给 Transformer。
### 1.2 序列化 (Flatten) —— 排成一句话
#### 核心步骤拆解:
切完后,我们得到的是一个 $14 \times 14$ 的方阵。但 LLM 只能读**一行字**(序列)。
所以,我们必须把这个方阵**拍扁**,变成长长的一串。
1. **切块 (Patchify)**
就像把一张完整的拼图拆散。假设输入图片是 `224x224` 像素,我们设定每个 Patch 大小为 `16x16`
那么这张图就被切成了 (224/16) × (224/16) = 14 × 14 = 196 个小块。
每个小块就是一个基础的视觉单词。
- **原本**:二维矩阵(有行有列)。
- **现在**:一维长条(只有前后)
<PatchifyDemo />
这样,图片就变成了一串“视觉单词序列”。
2. **拉平与映射 (Linear Projection)**
每个 `16x16` 的彩色小块包含 16 × 16 × 3 (RGB) = 768 个像素点。
我们把这 768 个点拉成一条直线(向量),然后通过一个线性层(矩阵乘法)把它压缩成固定长度的特征向量(比如 768 维或 1024 维)。
_现在的状态:196 个向量。_
下面的演示展示了:**一个 Patch** 是如何被拍扁,并变成一个**向量**(计算机能读懂的数字列表)的。
<LinearProjectionDemo />
3. **加上位置编码 (Positional Embedding)**
Transformer 是“无序”的。如果你把拼图打乱,它就不知道哪块是头,哪块是脚。
所以,我们必须给每个向量“贴上号码牌”:这是第1行第1列,那是第3行第5列。
这样模型就能记住图片的空间结构。
<PositionalEmbeddingDemo />
4. **自注意力机制 (Self-Attention)**
这是最神奇的一步。这 196 个 Patch 开始“开会”互相交流。
- **Patch A (猫耳朵)** 问:我是毛茸茸的三角形,谁跟我有关?
- **Patch B (猫脸)** 回答:我是圆圆的脸,我们可以拼成一只猫头!
- **Patch C (背景树)** 回答:我是绿色的,跟你们关系不大。
通过层层计算,模型不仅识别出了孤立的特征,还理解了物体之间的**语义关系**。
<AttentionDemo />
5. **输出 (Output)**
最终,ViT 输出的是一串**富含语义的特征向量序列**。这串向量就是 LLM 后续要阅读的“图像文章”。
<ViTOutputDemo />
<LinearProjectionDemo />
---
## 2. 核心难题:跨界沟通 (Projection)
## 2. 第二步:跨物种翻译 (Projection)
ViT 输出的向量虽然包含了图像信息,但它说的是“视觉方言”,LLM 的大脑只能听懂“文本方言”
**Projector (投射器)** 就是这个翻译官,负责对齐这两种语言的维度和语义
现在我们有了一串“视觉单词”,但 LLM 还是读不懂
因为这些“视觉单词”是**像素特征**(比如“这里是红色”、“那里有条线”),而 LLM 懂的是**语义特征**(比如“这是猫”、“那是树”)
### 架构对比:三种流派
这就需要一个翻译官:**Projector (投射器)**。
### 2.1 翻译官的作用
Projector 的工作就是把**视觉特征向量**(ViT 的输出)转换成**文本特征向量**(LLM 的输入)。
你可以把它理解为**外语翻译器**
- **输入**:视觉语言(ViT output
- **处理**:翻译(矩阵变换)
- **输出**LLM 语言(LLM embedding
<ProjectorDemo />
#### 1. 简单粗暴派:Linear Projector (如 LLaVA)
### 2.2 不同的翻译流派
- **结构**:一个简单的全连接层 (MLP)。
- **数学原理**$Y = WX + b$。其中 $X$ 是视觉向量,$W$ 是训练好的权重矩阵。
- **比喻**:**直译**。把视觉向量强行“拉伸”或“压缩”到和文本向量一样的维度。
- **优点**:保留了最多的原始视觉信息,几乎没有信息损失。
- **缺点**:Token 数量多。一张图可能产生 576 个 Token,LLM 处理起来比较累。
为了翻译得更好,科学家们发明了不同的翻译工具:
#### 2. 精细提取派:Q-Former (如 BLIP-2)
1. **直译派 (Linear)**
- 做法:简单粗暴,通过一个矩阵乘法直接转换。
- 特点:**保留原汁原味**,但废话多(Token 数量多)。
- 代表:LLaVA。
- **结构**:一个小型的 Transformer,带有两组输入:一组是固定的“查询向量 (Queries)”,一组是图片特征。
- **原理**
- 预设 32 个 Query(就像 32 个带着问题的记者)
- 这些记者进入图片特征的海洋里,寻找自己感兴趣的信息
- 最后只输出这 32 个记者采集到的精华摘要。
- **比喻**:**意译/摘要**。不管原图多复杂,我都只给你总结出 32 句话。
- **优点**Token 数量极少(32个),LLM 跑得飞快。
- **缺点**:信息压缩太狠,容易丢失细节(比如图片角落里的小字)。
2. **意译派 (Q-Former/Resampler)**
- 做法:用一个小模型先读一遍图片,总结出几十个核心要点。
- 特点:**精简**,Token 少,但可能会漏掉细节
- 代表:BLIP-2, Gemini
#### 3. 注意力压缩派:C-Abstractor (如 Qwen-VL)
- **结构**:在 Linear 和 Q-Former 之间取平衡。利用卷积或注意力机制将相邻的 Patch 合并
- **原理**:比如把 $2\times2$ 的 4 个 Patch 合并成 1 个。
- **优点**:既减少了 Token 数量(降低计算量),又保留了足够的空间细节。
3. **折中派 (C-Abstractor)**
- 做法:把相邻的几个方块合并成一个,既压缩了长度,又保留了空间感。
- 代表:Qwen-VL
---
## 3. 进化之路:ViT + LLM
## 3. 第三步:合体 (The Architecture)
现在的多模态大模型(M-LLM)本质上就是:**给 LLM 装了一副眼镜**
现在,零件都准备好了,我们把它们组装起来,就成了一个标准的 VLM
### 模型架构对比
让我们直观地对比一下传统 LLM 和 VLM 在架构上的区别。
### 3.1 VLM 的身体结构
<ModelArchitectureComparisonDemo />
### 模型解剖
一个典型的 VLM(如 LLaVA)由三个部分组成:
一个标准的 LLaVA 架构模型由三部分物理连接而成
1. **眼睛 (Vision Encoder)**
- 负责看图。
- 通常直接借用现成的、训练好的视觉模型(如 CLIP, SigLIP)。
- *它就像视网膜,负责感光。*
1. **Vision Encoder (ViT)**
- _来源_:通常借用已经训练好的模型(如 CLIP-ViT-L/14, SigLIP
- _状态_:在训练初期通常是**冻结 (Frozen)** 的,因为它们已经很会看图了
2. **Projector (Adapter)**
- _来源_:从零初始化。
- _状态_:**全程训练**。它是连接视觉和语言的关键枢纽。
3. **LLM (Backbone)**
- _来源_:开源大模型(如 Vicuna, Qwen, Llama-3)。
- _状态_:在预训练阶段冻结,在微调阶段解冻。
2. **视神经 (Projector)**
- 负责传输和翻译信号
- 这是 VLM 训练的重点
- *它连接眼睛和大脑。*
### 视频也能看吗?
是的。对于模型来说,视频就是**一连串连续的图片**
- **抽帧**:每秒抽取 1 帧或 2 帧。
- **堆叠**:把这 10 张图片的 Token 串起来,告诉 LLM:“这是第一帧,这是第二帧...”。
- **时间编码**:有些高级模型会加上“时间戳 Token”,让 LLM 理解动作的先后顺序。
3. **大脑 (LLM)**
- 负责思考和回答。
- 借用现成的强大 LLM(如 Vicuna, Qwen
- *它负责理解看到了什么,并组织语言回答。*
---
## 4. 训练揭秘:从对齐到对话 (Training Pipeline)
## 4. 它是怎么学会看图的?(Training)
要把这三个零件(ViT, Projector, LLM)磨合好,通常需要两阶段训练
刚组装好的 VLM 其实是“瞎”的,因为视神经(Projector)还没连通。我们需要分两步教它
### 阶段一:特征对齐 (Feature Alignment / Pre-training)
### 阶段一:认物 (Feature Alignment)
- **目标**:让 Projector 学会翻译。此时 LLM 还不参与学习,只是充当裁判
- **做法**
- **冻结**ViT 和 LLM
- **只训练**Projector
- **数据**558K 对简单的 `<图片, 标题>` 数据 (CC3M, LAION)
- **过程**
输入一张“猫”的图 -> ViT -> Projector -> 得到向量 V。
输入文字“一只猫” -> LLM -> 得到向量 T。
**Loss**:强迫向量 V 和向量 T 尽可能相似。
- **结果**Projector 能够把图像特征转换成 LLM 能够理解的 Embedding 空间。
这一阶段就像教婴儿认卡片
- **给它看**:一张“猫”的照片
- **告诉它**:这是“猫”
- **目标**让 Projector 学会把“猫的照片特征”翻译成“猫这个字的向量”
- **状态**冻结眼睛和大脑,**只训练视神经 (Projector)**。
<FeatureAlignmentDemo />
### 阶段二:视觉指令微调 (Visual Instruction Tuning / SFT)
### 阶段二:对话 (Visual Instruction Tuning)
- **目标**:让模型学会听指令,进行复杂对话
- **做法**
- **冻结**:ViT (通常保持冻结,有些激进的训练会解冻)。
- **全量微调**Projector + LLM
- **数据**150K+ 高质量的对话数据 (LLaVA-Instruct)
- _User_: `<image>` 图中的男人穿什么颜色的衣服?
- _Assistant_: 他穿着一件蓝色的衬衫。
- **结果**:LLM 学会了结合图片信息来回答用户的问题,而不仅仅是补全文字。
这一阶段是教它根据图片回答复杂问题
- **用户问**`<图片>` 图里的猫在干什么?
- **教它答**:它在睡觉
- **目标**让大脑 (LLM) 学会处理视觉信息,并结合常识进行推理
- **状态**:通常会同时微调 **Projector****LLM**
<VLMInferenceDemo />
---
## 5. 进阶:新模型的视觉 Trick (Advanced Tricks)
## 5. 进阶:看得更清 (Advanced Tricks)
### 5.1 Qwen-VL 的创新:像人眼一样看 (Naive Dynamic Resolution)
基础的 VLM 有个大问题:**视力不好**。
传统的 ViT 只能看 $224 \times 224$ 或 $336 \times 336$ 分辨率的图。这就像透过一个低清摄像头看世界,小字根本看不清。
传统的 ViT (如 CLIP) 有个大毛病:**强制缩放**。
不管你给它一张长长的手机截图,还是一张扁扁的全景照,它都会粗暴地把图片拉伸成 `224x224` 的正方形。
现在的模型(如 Qwen-VL, LLaVA-NeXT)用了一些聪明的方法来解决这个问题:
- **后果**:文字变形看不清,小物体丢失。
### 5.1 动态分辨率 (Dynamic Resolution)
**Qwen-VL** 引入了 **Naive Dynamic Resolution(动态分辨率)** 机制:
简单说,就是**“拼图法”**。
1. **保持原比例**:图片是长条的,就按长条的切。
2. **智能分块**:将大图切成多个 `224x224` 的子图(就像用手机拍全景时移动镜头)
3. **全局视角**:除了看局部细节,还会生成一张缩略图看整体布局
这就好比人眼看东西:既能眯着眼看全貌,也能凑近了看细节,保证了高清图像的信息不丢失
如果图片很大(比如 $1000 \times 1000$),模型不会强行把它缩小,而是:
1. 把它切成好几张 $336 \times 336$ 的小图
2. 分别看这些小图(看细节)
3. 再把全图缩小看一遍(看全貌)
4. 最后把所有信息拼起来。
### 5.2 LLaVA-NeXT (LLaVA-1.6): AnyRes 技术
这就好比你用手机拍全景照片,分段扫描,最后合成一张高清大图。
**LLaVA-NeXT** 采用了 **AnyRes (Any Resolution)** 技术,这是一种灵活的分辨率处理策略。
### 5.2 换个大眼睛
- **网格切分**:它构建了一个包含不同长宽比的网格配置集合(如 1:1, 1:2, 2:1 等)。给定一张输入图像,模型会从集合中选择最匹配的网格配置
- **避免变形**:通过这种方式,尽可能减少因缩放导致的图像变形
- **全局与局部结合**:它也会同时输入一张调整大小后的全图(用于看整体)和切分后的局部图块(用于看细节),让 LLM 综合判断
### 5.3 InternVL: 让眼睛变大 (Scaling Vision Encoder)
传统的 VLM 往往使用 CLIP-ViT-Large (约 300M 参数) 作为视觉编码器。
**InternVL (书生·万象)** 的思路很直接:**如果视力不好,那就换个更大的眼睛!**
- 它使用了一个高达 **60亿参数 (6B)** 的超大视觉编码器 (InternViT-6B)。
- 这使得模型在无需任何微调的情况下,光靠“眼睛”就能看懂非常复杂的视觉细节,甚至能做语义分割。
### 5.4 DeepSeek-VL & MiniCPM-V: 细节为王 (High-Res Tiling)
对于需要看清密集文字(OCR)或微小物体(如仪表盘读数)的场景,**DeepSeek-VL** 和 **MiniCPM-V** 采用了更激进的高清切片策略。
- **混合视觉编码**DeepSeek-VL 混合使用了负责语义理解的 SigLIP 和负责细节捕捉的 SAM (Segment Anything Model) 编码器,兼顾了“看得懂”和“看得清”。
- **自适应切片**:MiniCPM-V 针对端侧设备优化,能够智能地将高清大图切分为多个小图输入,即使是 800万像素的图片也能在手机上被精准识别。
还有一种暴力美学:直接换一个更强的视觉模型
比如 **InternVL**,直接用了一个 60 亿参数的超大视觉模型(InternViT-6B
这相当于从“手机摄像头”升级到了“哈勃望远镜”,不用切图也能看得一清二楚
---
## 6. 总结
VLM 的奇迹在于它证明了**语义的统一性**。无论是像素(图像)还是字符(文本),在深度神经网络的高维空间里,最终都可以汇聚为统一的数学表示。
多模态大模型 (VLM) 并没有什么魔法。它只是做了一件事:
当你给 AI 发一张照片时,你其实是在发送一串它能“读懂”的数字诗篇。
**把“图像”这种外语,翻译成了“文本”这种母语,然后喂给了 LLM。**
只要理解了这一点,你就理解了 VLM 的一切。
---
## 7. 名词速查表 (Glossary)
| 名词 | 全称 | 解释 |
| :---------------------------- | :---------------------------- | :--------------------------------------------------------------------------------------------- |
| **VLM** | Vision-Language Model | 多模态大模型。既能理解文本,又能理解图像(甚至视频)的 AI 模型。 |
| **ViT** | Vision Transformer | 视觉 Transformer。将图像切分为 Patch 并通过 Self-Attention 提取特征的模型,是 VLM 的“眼睛”。 |
| **Patch** | - | **图像块**。ViT 将图像切分成的固定大小的小方块(如 16x16 像素),相当于文本中的单词。 |
| **Projector** | - | **投射器/对齐层**。连接 ViT 和 LLM 的桥梁,负责将视觉特征向量转换为 LLM 能理解的文本向量维度。 |
| **Linear Projection** | - | **线性映射**最简单的 Projector,通过一个矩阵乘法改变向量维度。 |
| **Q-Former** | Querying Transformer | 一种复杂的 Projector,使用可学习的 Query 向量从图像特征中提取关键信息。 |
| **Feature Alignment** | - | **特征对齐**。VLM 训练的第一阶段,目的是让 Projector 学会将图像特征映射到文本空间。 |
| **Visual Instruction Tuning** | - | **视觉指令微调**。VLM 训练的第二阶段,使用多模态对话数据让模型学会根据图像回答问题。 |
| **Resolution** | - | **分辨率**。图像的像素尺寸(如 224x224)。分辨率越高,看得越清,但计算量越大。 |
| **AnyRes** | Any Resolution | **任意分辨率**。一种能够灵活处理不同尺寸和长宽比图像的技术,避免图像变形。 |
| **OCR** | Optical Character Recognition | **光学字符识别**。从图像中提取文字的技术。现代 VLM 通常具备强大的 OCR 能力。 |
| 名词 | 全称 | 解释 |
| :--- | :--- | :--- |
| **VLM** | Vision-Language Model | **多模态大模型**。能看懂图的 GPT。 |
| **ViT** | Vision Transformer | **视觉模型**。VLM 的“眼睛”,负责把像素变成向量。 |
| **Patch** | - | **图像块**。图片被切成的小方块,相当于“视觉单词”。 |
| **Projector** | - | **投射器/翻译官**。连接眼睛和大脑的桥梁。 |
| **Alignment** | - | **对齐**让图像特征和文本特征在同一个空间里“互相听得懂”。 |
+155 -6
View File
@@ -98,7 +98,21 @@ h1 {
<link rel="stylesheet" href="styles.css" />
```
### 2.3 盒模型:为什么宽度算不准
### 2.3 核心机制:CSS 怎么找到 HTML
新手最容易晕的就是:CSS 里写的 `p``.card``#btn` 到底是怎么跟 HTML 对应上的?
这就好比老师在班级里点名,有三种点法:
1. **喊“所有人” (标签选择器)**:喊 "男生",所有男生都要站起来。
2. **喊“小组名” (类选择器)**:喊 "英语课代表",可能有好几个。
3. **喊“学号” (ID 选择器)**:喊 "2024001",全班只有一个。
**互动演示:把鼠标移到左边的 CSS 规则上,看看右边谁会亮起来。**
<CssSelectorsDemo />
### 2.4 盒模型:为什么宽度算不准?
每个元素都是一个盒子,由 **内容 → 内边距 → 边框 → 外边距** 组成。
@@ -106,9 +120,81 @@ h1 {
记忆公式:**总宽度 = margin + border + padding + width + padding + border + margin**。
### 2.4 Flexbox:为什么对齐和分布这么简单
### 2.5 怎么知道有哪些 CSS 属性
<CssFlexbox />
新手常问:“我怎么知道要改颜色是 `color` 还是 `font-color`?” CSS 属性多到记不住,是因为网页要面对的情况太复杂了(各种屏幕尺寸、各种设计需求)。
**但好消息是:日常开发中,90% 的时间你只需要用到下面这 20% 的核心属性。**
<CssCommonProperties />
#### 遇到不认识的属性怎么办?
**在 AI 原生时代,解决这个问题有更聪明的方法:**
1. **直接问 AI (首选方案)**
* 现在的 AI 编程助手(如 Cursor、Trae、GitHub Copilot)已经非常强大。
* 你根本不需要背诵属性,直接用自然语言描述你的需求。
* **例子**:你对 AI 说 "我要一个带有阴影的蓝色圆形按钮",它会直接给你写出包含 `background-color`, `border-radius`, `box-shadow` 的完整代码。
* **为什么这是首选?** 因为它不仅告诉你“属性名”,还帮你把“值”都调好了。
2. **查文档 (MDN)**
* MDN Web Docs 是 Web 开发的权威字典。
* 搜 "MDN CSS text color",它会告诉你正确属性是 `color`
* 搜 "MDN CSS background",它会列出 `background-color`, `background-image` 等家族成员。
3. **用浏览器“偷看” (DevTools)**
* 在喜欢的网页上**右键 -> 检查 (Inspect)**。
***Styles** 面板里,你可以看到人家用了什么属性。
* 你甚至可以直接在那里面试着改改数值,实时看效果(刷新就没了,很安全)。
4. **CSS 游乐场**
* 下面的演示列出了一些最常用的“装修参数”。
* 试着拖动滑块、修改颜色,看看它们分别控制什么。
<CssPlaygroundDemo />
### 2.6 现代 CSS 开发:Tailwind CSS 简介
以前写 CSS,我们要给每个东西起个名字(比如 `.my-button`, `.header-title`),然后在 CSS 文件里写一堆属性。这叫“语义化 CSS”。
现在流行一种**原子化 CSS (Utility-first CSS)**,代表作是 **Tailwind CSS**
**它的核心思想**
不要写 CSS 代码,直接在 HTML 标签上写“代号”。
- **传统写法**
```html
<button class="btn-primary">按钮</button>
<style>
.btn-primary {
background-color: #3b82f6;
color: white;
padding: 0.5rem 1rem;
border-radius: 0.25rem;
}
</style>
```
- **Tailwind 写法**
```html
<!-- bg-blue-500: 蓝色背景 -->
<!-- text-white: 白字 -->
<!-- px-4 py-2: 左右间距4,上下间距2 -->
<!-- rounded: 圆角 -->
<button class="bg-blue-500 text-white px-4 py-2 rounded">按钮</button>
```
**为什么它这么火?**
1. **不用起名**:最头疼的“起类名”环节没了。
2. **不切文件**:不用在 HTML 和 CSS 文件之间切来切去。
3. **不怕删**:删掉 HTML 标签时,样式自动就没了,不会留下堆积如山的无用 CSS 代码。
> 💡 **提示**:现在的 AI 编程工具(如 Cursor, v0)非常擅长写 Tailwind。你只要说“给我一个蓝色的圆角按钮”,它大概率直接给你生成带 Tailwind 类的代码。
### 2.7 Flexbox:为什么对齐和分布这么简单?
<CssLayoutDemo />
核心属性速记:
@@ -174,9 +260,72 @@ document.getElementById('title').textContent = '新标题'
---
## 4. 协作实战:三者如何“分工又配合”?
## 4. 深入理解 DOM:网页的“族谱”
### 4.1 分工对比表
你可能经常听到 **DOM (Document Object Model)** 这个词。别被这个专业术语吓到,它其实就是一张**网页的族谱**。
### 4.1 什么是 DOM 树?
浏览器读取 HTML 代码后,不会把它们当成一堆字符串,而是会在内存里把它们画成一棵树。
* `<html>` 是祖先。
* `<body>` 是 `<html>` 的孩子。
* `div`、`p`、`button` 又是 `<body>` 的孩子。
这棵树就叫 **DOM 树**。
```mermaid
graph TD
Document[Document] --> HTML[html]
HTML --> Head[head]
HTML --> Body[body]
Head --> Title[title: "我的网页"]
Body --> H1[h1: "欢迎"]
Body --> Div[div.card]
Div --> Img[img]
Div --> P[p: "一段文字"]
Div --> Button[button: "点击"]
```
### 4.2 为什么叫“对象模型” (Object Model)
因为在 JS 眼里,HTML 标签不仅仅是标签,而是**对象 (Object)**。它们有属性,有方法。
* **属性 (Property)**
* `img.src` = "photo.jpg"
* `div.className` = "box"
* `input.value` = "123"
* **方法 (Method)**
* `button.click()` (假装被点了一下)
* `div.remove()` (自杀)
* `body.appendChild(newDiv)` (生个孩子)
### 4.3 怎么找节点?(CRUD)
就像在族谱里找人一样,JS 提供了很多方法:
1. **按身份证找 (ID)**
* `document.getElementById('header')` —— 全局唯一,最快。
2. **按特征找 (Selector)**
* `document.querySelector('.card h2')` —— 就像写 CSS 一样找,很灵活。
3. **按关系找**
* `element.parentNode` (找爸爸)
* `element.children` (找孩子)
### 4.4 性能警告:不要频繁“拆家”
操作 DOM 是很贵的。每次你修改 DOM(比如改大小、改位置),浏览器都要重新计算排版(**Reflow**)和重新绘制(**Repaint**)。
* ❌ **低效**:循环 1000 次,每次往 `body` 里插入一个 `div`。
* ✅ **高效**:先把 1000 个 `div` 拼好(DocumentFragment),一次性塞进 `body` 里。
这也正是 **Vue / React** 诞生的原因:它们在内存里玩“虚拟 DOM”,计算好最小修改量,最后才去动真正的 DOM,从而保护了性能。
---
## 5. 协作实战:三者如何“分工又配合”?
### 5.1 分工对比表
| 角色 | 负责什么 | 不做什么 | 典型示例 |
| :------------- | :--------------- | :-------------- | :--------------------------------- |
@@ -184,7 +333,7 @@ document.getElementById('title').textContent = '新标题'
| **CSS** | 定义表现与布局 | 不存放业务逻辑 | `.card { border-radius: 8px; }` |
| **JavaScript** | 定义行为与数据流 | 不承担视觉表现 | `button.onclick = changeTitle` |
### 4.2 组合示例:点击改变标题
### 5.2 组合示例:点击改变标题
```html
<!DOCTYPE html>
+23
View File
@@ -69,6 +69,29 @@ const base = site.value.base
</div>
<style>
/* LOGO 容器:负责上下浮动动画 */
.VPHomeHero .image {
animation: float 6s ease-in-out infinite;
}
/* 鼠标悬停时暂停浮动 */
.VPHomeHero .image:hover {
animation-play-state: paused;
}
@media (min-width: 960px) {
/* 给 LOGO 容器设置不可侵犯的左侧边界 */
.VPHomeHero .image {
margin-left: 80px !important;
flex-shrink: 0; /* 保证图片不被挤压 */
}
.VPHomeHero .tagline {
max-width: 450px;
white-space: pre-wrap;
}
}
.VPHomeHero .text {
font-size: 1.8rem;
}
@@ -261,7 +261,7 @@ Trae 分为国际版和中国版。国际版需要能够访问海外网络,但
![](images/index-2026-01-09-10-26-33.png)
如图所示,这里我们正在创建模板,但不知道如何操作,我们可以截图该部分对大模型进行新闻
如图所示,这里我们正在创建模板,但不知道如何操作,我们可以截图该部分对大模型进行询问
![](images/index-2026-01-09-10-29-12.png)