From 6b1a9cf056871c85447af4c4aa552de42ea3690d Mon Sep 17 00:00:00 2001 From: sanbuphy Date: Sun, 22 Feb 2026 01:21:39 +0800 Subject: [PATCH] feat(docs): add Netlify deployment guide and data encoding demos - Add Netlify deployment section with form handling and functions examples - Replace old Git demos with new interactive components - Add comprehensive data encoding visualization demos - Update comparison table with Netlify information --- .../data-encoding/AudioEncodingDemo.vue | 264 +++++++ .../CharacterEncodingExplorer.vue | 277 +++++++ .../data-encoding/DataTransmissionDemo.vue | 370 +++++++++ .../data-encoding/GarbledTextDemo.vue | 193 +++++ .../data-encoding/ImageEncodingDemo.vue | 244 ++++++ .../data-encoding/PhotoUploadJourneyDemo.vue | 365 +++++++++ .../data-encoding/StoragePyramidDemo.vue | 285 +++++++ .../appendix/git-intro/GitBranchMergeDemo.vue | 207 ----- .../appendix/git-intro/GitBranchVisual.vue | 410 ++++++++++ .../git-intro/GitCommandCheatsheet.vue | 192 +++++ .../appendix/git-intro/GitCommandDemo.vue | 455 ----------- .../appendix/git-intro/GitCommitFlow.vue | 529 +++++++++++++ .../appendix/git-intro/GitConflictDemo.vue | 132 ---- .../appendix/git-intro/GitRemoteDemo.vue | 264 ------- .../appendix/git-intro/GitScenariosDemo.vue | 561 ------------- .../appendix/git-intro/GitStashDemo.vue | 194 ----- .../appendix/git-intro/GitStorageDemo.vue | 161 ---- .../appendix/git-intro/GitSyncDemo.vue | 349 ++++++++ .../appendix/git-intro/GitThreeAreasDemo.vue | 743 ------------------ .../appendix/git-intro/GitWorkflowDemo.vue | 486 ------------ docs/.vitepress/theme/index.js | 47 +- .../data-encoding-storage.md | 339 ++++---- .../git-version-control.md | 678 ++++++++++------ .../graphics-animation.md | 571 ++------------ .../2.5-zeabur-deployment/extra6/index.md | 130 ++- 25 files changed, 4326 insertions(+), 4120 deletions(-) create mode 100644 docs/.vitepress/theme/components/appendix/data-encoding/AudioEncodingDemo.vue create mode 100644 docs/.vitepress/theme/components/appendix/data-encoding/CharacterEncodingExplorer.vue create mode 100644 docs/.vitepress/theme/components/appendix/data-encoding/DataTransmissionDemo.vue create mode 100644 docs/.vitepress/theme/components/appendix/data-encoding/GarbledTextDemo.vue create mode 100644 docs/.vitepress/theme/components/appendix/data-encoding/ImageEncodingDemo.vue create mode 100644 docs/.vitepress/theme/components/appendix/data-encoding/PhotoUploadJourneyDemo.vue create mode 100644 docs/.vitepress/theme/components/appendix/data-encoding/StoragePyramidDemo.vue delete mode 100644 docs/.vitepress/theme/components/appendix/git-intro/GitBranchMergeDemo.vue create mode 100644 docs/.vitepress/theme/components/appendix/git-intro/GitBranchVisual.vue create mode 100644 docs/.vitepress/theme/components/appendix/git-intro/GitCommandCheatsheet.vue delete mode 100644 docs/.vitepress/theme/components/appendix/git-intro/GitCommandDemo.vue create mode 100644 docs/.vitepress/theme/components/appendix/git-intro/GitCommitFlow.vue delete mode 100644 docs/.vitepress/theme/components/appendix/git-intro/GitConflictDemo.vue delete mode 100644 docs/.vitepress/theme/components/appendix/git-intro/GitRemoteDemo.vue delete mode 100644 docs/.vitepress/theme/components/appendix/git-intro/GitScenariosDemo.vue delete mode 100644 docs/.vitepress/theme/components/appendix/git-intro/GitStashDemo.vue delete mode 100644 docs/.vitepress/theme/components/appendix/git-intro/GitStorageDemo.vue create mode 100644 docs/.vitepress/theme/components/appendix/git-intro/GitSyncDemo.vue delete mode 100644 docs/.vitepress/theme/components/appendix/git-intro/GitThreeAreasDemo.vue delete mode 100644 docs/.vitepress/theme/components/appendix/git-intro/GitWorkflowDemo.vue diff --git a/docs/.vitepress/theme/components/appendix/data-encoding/AudioEncodingDemo.vue b/docs/.vitepress/theme/components/appendix/data-encoding/AudioEncodingDemo.vue new file mode 100644 index 0000000..a6e75f4 --- /dev/null +++ b/docs/.vitepress/theme/components/appendix/data-encoding/AudioEncodingDemo.vue @@ -0,0 +1,264 @@ + + + + + diff --git a/docs/.vitepress/theme/components/appendix/data-encoding/CharacterEncodingExplorer.vue b/docs/.vitepress/theme/components/appendix/data-encoding/CharacterEncodingExplorer.vue new file mode 100644 index 0000000..e96e399 --- /dev/null +++ b/docs/.vitepress/theme/components/appendix/data-encoding/CharacterEncodingExplorer.vue @@ -0,0 +1,277 @@ + + + + + diff --git a/docs/.vitepress/theme/components/appendix/data-encoding/DataTransmissionDemo.vue b/docs/.vitepress/theme/components/appendix/data-encoding/DataTransmissionDemo.vue new file mode 100644 index 0000000..5757d30 --- /dev/null +++ b/docs/.vitepress/theme/components/appendix/data-encoding/DataTransmissionDemo.vue @@ -0,0 +1,370 @@ + + + + + diff --git a/docs/.vitepress/theme/components/appendix/data-encoding/GarbledTextDemo.vue b/docs/.vitepress/theme/components/appendix/data-encoding/GarbledTextDemo.vue new file mode 100644 index 0000000..0a5e87f --- /dev/null +++ b/docs/.vitepress/theme/components/appendix/data-encoding/GarbledTextDemo.vue @@ -0,0 +1,193 @@ + + + + + diff --git a/docs/.vitepress/theme/components/appendix/data-encoding/ImageEncodingDemo.vue b/docs/.vitepress/theme/components/appendix/data-encoding/ImageEncodingDemo.vue new file mode 100644 index 0000000..61052f7 --- /dev/null +++ b/docs/.vitepress/theme/components/appendix/data-encoding/ImageEncodingDemo.vue @@ -0,0 +1,244 @@ + + + + + diff --git a/docs/.vitepress/theme/components/appendix/data-encoding/PhotoUploadJourneyDemo.vue b/docs/.vitepress/theme/components/appendix/data-encoding/PhotoUploadJourneyDemo.vue new file mode 100644 index 0000000..2c802b8 --- /dev/null +++ b/docs/.vitepress/theme/components/appendix/data-encoding/PhotoUploadJourneyDemo.vue @@ -0,0 +1,365 @@ + + + + + diff --git a/docs/.vitepress/theme/components/appendix/data-encoding/StoragePyramidDemo.vue b/docs/.vitepress/theme/components/appendix/data-encoding/StoragePyramidDemo.vue new file mode 100644 index 0000000..9b7dc57 --- /dev/null +++ b/docs/.vitepress/theme/components/appendix/data-encoding/StoragePyramidDemo.vue @@ -0,0 +1,285 @@ + + + + + diff --git a/docs/.vitepress/theme/components/appendix/git-intro/GitBranchMergeDemo.vue b/docs/.vitepress/theme/components/appendix/git-intro/GitBranchMergeDemo.vue deleted file mode 100644 index 1da4aec..0000000 --- a/docs/.vitepress/theme/components/appendix/git-intro/GitBranchMergeDemo.vue +++ /dev/null @@ -1,207 +0,0 @@ - - - - - diff --git a/docs/.vitepress/theme/components/appendix/git-intro/GitBranchVisual.vue b/docs/.vitepress/theme/components/appendix/git-intro/GitBranchVisual.vue new file mode 100644 index 0000000..c94fba4 --- /dev/null +++ b/docs/.vitepress/theme/components/appendix/git-intro/GitBranchVisual.vue @@ -0,0 +1,410 @@ + + + + + diff --git a/docs/.vitepress/theme/components/appendix/git-intro/GitCommandCheatsheet.vue b/docs/.vitepress/theme/components/appendix/git-intro/GitCommandCheatsheet.vue new file mode 100644 index 0000000..036c685 --- /dev/null +++ b/docs/.vitepress/theme/components/appendix/git-intro/GitCommandCheatsheet.vue @@ -0,0 +1,192 @@ + + + + + diff --git a/docs/.vitepress/theme/components/appendix/git-intro/GitCommandDemo.vue b/docs/.vitepress/theme/components/appendix/git-intro/GitCommandDemo.vue deleted file mode 100644 index 82d3a92..0000000 --- a/docs/.vitepress/theme/components/appendix/git-intro/GitCommandDemo.vue +++ /dev/null @@ -1,455 +0,0 @@ - - - - - diff --git a/docs/.vitepress/theme/components/appendix/git-intro/GitCommitFlow.vue b/docs/.vitepress/theme/components/appendix/git-intro/GitCommitFlow.vue new file mode 100644 index 0000000..1e46fb5 --- /dev/null +++ b/docs/.vitepress/theme/components/appendix/git-intro/GitCommitFlow.vue @@ -0,0 +1,529 @@ + + + + + diff --git a/docs/.vitepress/theme/components/appendix/git-intro/GitConflictDemo.vue b/docs/.vitepress/theme/components/appendix/git-intro/GitConflictDemo.vue deleted file mode 100644 index 7984327..0000000 --- a/docs/.vitepress/theme/components/appendix/git-intro/GitConflictDemo.vue +++ /dev/null @@ -1,132 +0,0 @@ - - - - - diff --git a/docs/.vitepress/theme/components/appendix/git-intro/GitRemoteDemo.vue b/docs/.vitepress/theme/components/appendix/git-intro/GitRemoteDemo.vue deleted file mode 100644 index 46adf66..0000000 --- a/docs/.vitepress/theme/components/appendix/git-intro/GitRemoteDemo.vue +++ /dev/null @@ -1,264 +0,0 @@ - - - - - diff --git a/docs/.vitepress/theme/components/appendix/git-intro/GitScenariosDemo.vue b/docs/.vitepress/theme/components/appendix/git-intro/GitScenariosDemo.vue deleted file mode 100644 index c610f6b..0000000 --- a/docs/.vitepress/theme/components/appendix/git-intro/GitScenariosDemo.vue +++ /dev/null @@ -1,561 +0,0 @@ - - - - - diff --git a/docs/.vitepress/theme/components/appendix/git-intro/GitStashDemo.vue b/docs/.vitepress/theme/components/appendix/git-intro/GitStashDemo.vue deleted file mode 100644 index d11a34b..0000000 --- a/docs/.vitepress/theme/components/appendix/git-intro/GitStashDemo.vue +++ /dev/null @@ -1,194 +0,0 @@ - - - - - diff --git a/docs/.vitepress/theme/components/appendix/git-intro/GitStorageDemo.vue b/docs/.vitepress/theme/components/appendix/git-intro/GitStorageDemo.vue deleted file mode 100644 index da6e272..0000000 --- a/docs/.vitepress/theme/components/appendix/git-intro/GitStorageDemo.vue +++ /dev/null @@ -1,161 +0,0 @@ - - - - - diff --git a/docs/.vitepress/theme/components/appendix/git-intro/GitSyncDemo.vue b/docs/.vitepress/theme/components/appendix/git-intro/GitSyncDemo.vue new file mode 100644 index 0000000..3087016 --- /dev/null +++ b/docs/.vitepress/theme/components/appendix/git-intro/GitSyncDemo.vue @@ -0,0 +1,349 @@ + + + + + diff --git a/docs/.vitepress/theme/components/appendix/git-intro/GitThreeAreasDemo.vue b/docs/.vitepress/theme/components/appendix/git-intro/GitThreeAreasDemo.vue deleted file mode 100644 index bd3f3ba..0000000 --- a/docs/.vitepress/theme/components/appendix/git-intro/GitThreeAreasDemo.vue +++ /dev/null @@ -1,743 +0,0 @@ - - - - - diff --git a/docs/.vitepress/theme/components/appendix/git-intro/GitWorkflowDemo.vue b/docs/.vitepress/theme/components/appendix/git-intro/GitWorkflowDemo.vue deleted file mode 100644 index aaa1153..0000000 --- a/docs/.vitepress/theme/components/appendix/git-intro/GitWorkflowDemo.vue +++ /dev/null @@ -1,486 +0,0 @@ - - - - - - diff --git a/docs/.vitepress/theme/index.js b/docs/.vitepress/theme/index.js index e88e09b..c4da2e9 100644 --- a/docs/.vitepress/theme/index.js +++ b/docs/.vitepress/theme/index.js @@ -92,15 +92,11 @@ import EmotionControlDemo from './components/appendix/audio-intro/EmotionControl import WebTechTriad from './components/appendix/web-basics/WebTechTriad.vue' import UrlToBrowserDemo from './components/appendix/web-basics/UrlToBrowserDemo.vue' // Git Intro Components -import GitWorkflowDemo from './components/appendix/git-intro/GitWorkflowDemo.vue' -import GitThreeAreasDemo from './components/appendix/git-intro/GitThreeAreasDemo.vue' -import GitStorageDemo from './components/appendix/git-intro/GitStorageDemo.vue' -import GitCommandDemo from './components/appendix/git-intro/GitCommandDemo.vue' -import GitBranchMergeDemo from './components/appendix/git-intro/GitBranchMergeDemo.vue' -import GitConflictDemo from './components/appendix/git-intro/GitConflictDemo.vue' -import GitStashDemo from './components/appendix/git-intro/GitStashDemo.vue' -import GitRemoteDemo from './components/appendix/git-intro/GitRemoteDemo.vue' -import GitScenariosDemo from './components/appendix/git-intro/GitScenariosDemo.vue' +import GitCommitFlow from './components/appendix/git-intro/GitCommitFlow.vue' +import GitBranchVisual from './components/appendix/git-intro/GitBranchVisual.vue' +import GitSyncDemo from './components/appendix/git-intro/GitSyncDemo.vue' +import GitCommandCheatsheet from './components/appendix/git-intro/GitCommandCheatsheet.vue' + // (保留网络相关,未修改) import NetworkLayers from './components/appendix/web-basics/NetworkLayers.vue' import TcpUdpComparison from './components/appendix/web-basics/TcpUdpComparison.vue' @@ -162,6 +158,15 @@ import GraphStructureDemo from './components/appendix/computer-fundamentals/Grap import LanguageTypeModelDemo from './components/appendix/computer-fundamentals/LanguageTypeModelDemo.vue' import CompilationPracticeDemo from './components/appendix/computer-fundamentals/CompilationPracticeDemo.vue' +// Data Encoding Components +import GarbledTextDemo from './components/appendix/data-encoding/GarbledTextDemo.vue' +import CharacterEncodingExplorer from './components/appendix/data-encoding/CharacterEncodingExplorer.vue' +import StoragePyramidDemo from './components/appendix/data-encoding/StoragePyramidDemo.vue' +import DataTransmissionDemo from './components/appendix/data-encoding/DataTransmissionDemo.vue' +import PhotoUploadJourneyDemo from './components/appendix/data-encoding/PhotoUploadJourneyDemo.vue' +import ImageEncodingDemo from './components/appendix/data-encoding/ImageEncodingDemo.vue' +import AudioEncodingDemo from './components/appendix/data-encoding/AudioEncodingDemo.vue' + // Deployment appendix components import DeploymentOverviewDemo from './components/appendix/deployment/DeploymentOverviewDemo.vue' import DeploymentBuildDemo from './components/appendix/deployment/DeploymentBuildDemo.vue' @@ -686,15 +691,12 @@ export default { app.component('WebTechTriad', WebTechTriad) app.component('UrlToBrowserDemo', UrlToBrowserDemo) app.component('UrlToBrowserQuickStart', UrlToBrowserQuickStart) - app.component('GitWorkflowDemo', GitWorkflowDemo) - app.component('GitThreeAreasDemo', GitThreeAreasDemo) - app.component('GitStorageDemo', GitStorageDemo) - app.component('GitCommandDemo', GitCommandDemo) - app.component('GitBranchMergeDemo', GitBranchMergeDemo) - app.component('GitConflictDemo', GitConflictDemo) - app.component('GitStashDemo', GitStashDemo) - app.component('GitRemoteDemo', GitRemoteDemo) - app.component('GitScenariosDemo', GitScenariosDemo) + + app.component('GitCommitFlow', GitCommitFlow) + app.component('GitBranchVisual', GitBranchVisual) + app.component('GitSyncDemo', GitSyncDemo) + app.component('GitCommandCheatsheet', GitCommandCheatsheet) + app.component('NetworkLayers', NetworkLayers) app.component('TcpUdpComparison', TcpUdpComparison) app.component('SubnetCalculator', SubnetCalculator) @@ -761,6 +763,15 @@ export default { app.component('LanguageTypeModelDemo', LanguageTypeModelDemo) app.component('CompilationPracticeDemo', CompilationPracticeDemo) + // Data Encoding Components Registration + app.component('GarbledTextDemo', GarbledTextDemo) + app.component('CharacterEncodingExplorer', CharacterEncodingExplorer) + app.component('StoragePyramidDemo', StoragePyramidDemo) + app.component('DataTransmissionDemo', DataTransmissionDemo) + app.component('PhotoUploadJourneyDemo', PhotoUploadJourneyDemo) + app.component('ImageEncodingDemo', ImageEncodingDemo) + app.component('AudioEncodingDemo', AudioEncodingDemo) + // Deployment appendix app.component('DeploymentOverviewDemo', DeploymentOverviewDemo) app.component('DeploymentBuildDemo', DeploymentBuildDemo) diff --git a/docs/zh-cn/appendix/1-computer-fundamentals/data-encoding-storage.md b/docs/zh-cn/appendix/1-computer-fundamentals/data-encoding-storage.md index d13c289..276278f 100644 --- a/docs/zh-cn/appendix/1-computer-fundamentals/data-encoding-storage.md +++ b/docs/zh-cn/appendix/1-computer-fundamentals/data-encoding-storage.md @@ -1,244 +1,237 @@ -# 数据的编码、存储与传输 +# 什么是数据的编码与传输? -::: tip 🎯 核心问题 -**计算机如何表示和存储各种数据?** 文字、图片、视频、声音...这些在现实世界中形态各异的信息,是如何变成 0 和 1 的?又是如何存储和传输的?本章带你理解数据的编码、存储和传输原理。 -::: +> 💡 **学习指南**:当你给朋友发一张照片、发一条微信,或者下载一个几 GB 的游戏时,这些信息是怎么穿过大半个地球、完好无损地出现在你的屏幕上的? +> +> 本章节会围绕一个经常困扰新手的问题展开:**为什么我收到的文件变成了乱码?** +> +> 顺着这个问题,我们将彻底揭开计算机底层最核心的三大基石:**编码、存储与传输**。 + +在开始之前,我们需要先明确一个经常被新手忽略的物理事实: + +计算机其实极其“死板”。它不认识汉字,不认得色彩,也听不懂周杰伦的歌。 + +它的底层全是由无数个微小的半导体开关组成的,**它只能一次又一次地判断“通电(1)”或“断电(0)”**。 + +既然计算机只认识 0 和 1,那我们怎么让它显示五颜六色的图片和复杂的文字呢? + +答案就是:**规定一本“密码本”**。 + +我们和计算机约定好:如果底层发来 `01000001` 这串微小的电信号,它在屏幕上就专门画出英文字母 `A`;如果发来另外一串信号,就专门显示红色。 + +这个**制定并使用密码本进行来回翻译的过程,就叫做“编码(Encoding)”**。 + +明白了“计算机里的一切本质上都是密码”这个逻辑起点,你就能瞬间明白日常最容易碰到的一个见鬼现象——乱码,到底是怎么产生的了。 --- -## 0. 全景图:数据的生命周期 +## 0. 引言:为什么文件会变成“天书”? -想象你要寄一封信给朋友: +想象一下,你收到一份重要的同事发来的文件,双击打开一看,里面全是类似“浣犲ソ”或“ä½ å¥½”这种奇怪的文字。 -1. **编码**:把想法变成文字(信息编码) -2. **存储**:写在纸上(数据存储) -3. **传输**:通过邮局寄出(数据传输) +直觉上,你肯定觉得:是不是文件在发送的过程中损坏了?是不是丢包了? -计算机处理数据也是类似的过程: +但实际上,绝大多数所谓的“文件损坏”,真相只有一个——**你的电脑“没找对阅读规则”**。 -| 阶段 | 做什么 | 核心问题 | 类比 | -|------|--------|---------|------| -| **编码** | 把信息变成 0 和 1 | 如何用二进制表示各种数据? | 把想法变成文字 | -| **存储** | 把数据保存起来 | 数据存在哪里?怎么组织? | 写在纸上 | -| **传输** | 把数据送到别处 | 如何可靠、高效地传输? | 邮局寄信 | +👇 **动手点点看**: -::: tip 📊 逐行解读这张表 -**编码**:计算机只认识 0 和 1,所以所有数据都要"翻译"成二进制。文字用 ASCII 或 Unicode 编码,数字用二进制表示,图片用像素值,声音用采样值。 +试着在下方的模拟器里,切换不同的“解码密码本”,来读取同一串底层的电信号字节。 -**存储**:编码后的数据需要保存起来。存储介质从快到慢有:寄存器 → 缓存 → 内存 → SSD → 硬盘 → 云存储。越快的存储越贵、容量越小。 + -**传输**:数据需要在不同设备间流动。传输方式有串行(一位一位传)和并行(多位同时传)。现代高速接口(USB、PCIe)多采用串行方式。 -::: +**🎯 核心领悟:没对齐的密码本** + +字节(0和1序列)本身是没有绝对意义的,是人类制定的**「编码规则」**赋予了它们意义。 + +这就像是一串摩斯密码“滴滴答”,如果你用中文电报密码本去查,它是一个字;如果用美军密码本去查,它是另一个字。 + +**发件人用 UTF-8 密码本把汉字翻译成了数字发给你,你如果硬要用 GBK 密码本去解读这些数字,拼出来的当然全是乱码。** + +要彻底搞懂为什么没损坏的数据会变乱码,我们需要了解数据处理的完整链条。即数据的“一生”:**编码**、**存储**、**传输**。 --- -## 1. 数据编码:用 0 和 1 表示一切 +## 1. 什么是数据编码?(把万物变成数字) -### 1.1 文本编码 +简单来说: - +> **数据编码(Encoding)**,就是建立一本“双向翻译词典”,把现实世界中复杂多样的信息(文字、色彩、声音),强制映射成计算机能理解的 0 和 1 的规则。 -::: tip 💡 字符编码的演变 -**ASCII(1963年)**: -- 用 7 位二进制表示 128 个字符 -- 包括英文字母、数字、常用符号 -- 问题:只能表示英语,无法表示中文等 +### 1.1 把文字变成数字:从 ASCII 到万国码 -**Unicode(1991年)**: -- 统一编码标准,覆盖世界上所有文字 -- 目前已收录超过 14 万个字符 -- 常用编码方式:UTF-8(变长编码,1-4 字节) +我们每天在微信里打字,每按下一个键,计算机其实暗中都在做一件事:**查表替换**。 -**UTF-8 的巧妙设计**: -- ASCII 字符(0-127)只用 1 字节,完全兼容 -- 常用汉字用 3 字节 -- 根据"前导位"判断一个字符占几个字节 -::: +**第一阶段:ASCII 的小天地** -**常见字符编码对比:** +发明电脑初期,美国人觉得世界上只有 26 个英文字母、数字和一些标点符号,于是制定了一本很薄的密码本叫做 **ASCII 码**。 -| 编码 | 字节数 | 支持字符 | 特点 | -|------|--------|---------|------| -| **ASCII** | 1 字节 | 128 个 | 仅英语,兼容性好 | -| **UTF-8** | 1-4 字节 | 所有文字 | 变长编码,主流标准 | -| **UTF-16** | 2-4 字节 | 所有文字 | 定长为主,Windows 常用 | -| **GBK** | 1-2 字节 | 中英文 | 中文专用,不推荐新项目使用 | +它只规定了 128 个符号,比如规定数字 `65` 代表大写字母 `A`。由于字符很少,**1 个字节(Byte,等于 8 个比特位 Bit)** 的空间能容纳 256 种变化,绰绰有余。 -### 1.2 数字编码 +**第二阶段:群雄割据的战国时代** -::: tip 💡 整数如何用二进制表示? -**无符号整数**:直接用二进制表示 -- 8 位可以表示 0-255 -- 32 位可以表示 0 到约 42 亿 +但后来,电脑走向了世界。大家发现:**汉字有几万个,日本还有假名,光靠 1 个字节根本装不下!** -**有符号整数**:用补码表示 -- 最高位是符号位(0 正 1 负) -- 正数:直接用二进制 -- 负数:正数的二进制取反加 1 +于是,中国搞了 GBK 密码本(用 2 个字节存一个汉字),日本搞了 Shift_JIS……世界陷入了混乱。你在中国做好的网页,发给美国客户,他们电脑里没有 GBK 词典,打开全是一堆乱码。 -**为什么用补码?** -- 加法减法统一处理 -- 0 的表示唯一 -- 硬件实现简单 -::: +**第三阶段:天下一统的 Unicode(万国码)** -**浮点数表示(IEEE 754 标准):** +最后,计算机界的大神们坐在一起商量:“大家别各玩各的了,我们做一本收录地球上所有符号的超级大字典吧!”这就是大名鼎鼎的 **Unicode(万国码)**。它给世界上每一个文字、甚至你常用的每个 Emoji 表情都分配了一个独一无二的编号。 -| 部分 | 作用 | 位数(32位浮点) | -|------|------|-----------------| -| **符号位** | 正负 | 1 位 | -| **指数位** | 决定大小范围 | 8 位 | -| **尾数位** | 决定精度 | 23 位 | +而你经常听到的 **UTF-8**,就是 Unicode 字典目前最流行的一套“存储规则”。它最聪明的点在于它是**变长**的:遇到英文只用 1 个字节,遇到中文用 3 个字节,非常节省空间。 -### 1.3 多媒体编码 +👇 **动手点点看**: -**图像编码**: -- **位图**:每个像素用 RGB 值表示(红绿蓝各 8 位) -- **压缩**:JPEG(有损)、PNG(无损) -- **矢量图**:用数学公式描述形状(SVG) +在下面的输入框里随便打几个中英文或 Emoji(比如:`你好 Hello 🎉`),看看计算机底层是怎么“查表”占用空间的。 -**音频编码**: -- **采样**:把连续声波变成离散点 -- **量化**:把采样值变成数字 -- **压缩**:MP3(有损)、FLAC(无损) + -**视频编码**: -- 视频是一帧帧图像 -- 关键技术:帧间压缩(只记录变化部分) -- 常见格式:H.264、H.265、VP9 +**💡 惊奇发现**: + +- 一个英文字母在 UTF-8 里只占 **1 个字节**。 +- 一个普通汉字通常占 **3 个字节**。 +- 一个 Emoji 表情(🎉),竟然需要 **4 个字节**! + +> **冷知识**:为什么很多人觉得发同样长度的短信,纯英文能发好长一段,纯中文只能发几句?因为在底层的电信号序列里,中文的物理尺寸足足是英文的 3 倍大! + +### 1.2 颜色和声音怎么变数字? + +文字可以查表,那蒙娜丽莎的微笑、周杰伦的歌声怎么变成 0 和 1 呢? + +方法同样是:**切割与映射**。 + +* **图片的编码**: + 把一张照片无限放大,它其实是由几百万个发光的小方块(像素)组成的。我们只要规定每个颜色的编号(比如 `#FF0000` 代表红色),然后把几百万个方块的编号存下来,照片就变成了数字。 + + 👇 **动手点点看**:悬停在左侧画布的小格子上,看看图像颜色是怎么映射成十六进制代码的。 + + +* **声音的编码**: + 声音本质是空气的震荡波。如果我们每秒去测量这个波浪的高度 44100 次(采样),记录下代表高度的数值。连续存下来,连通的声波就变成了离散的数字数组。 + + 👇 **动手点点看**:拖动滑块,看看连续的模拟声波是怎么被“切片”成数字音频的。 + --- -## 2. 数据存储:速度与容量的权衡 +## 2. 存储桥梁:发出去之前,总得先放个地方 -### 2.1 存储层次结构 +数据编完码之后,准备发给别人。但在这之前,必须要先把它放在电脑的物理介质里。这就涉及到一个不可避免的硬件铁律。 - +你可能会想:**“既然都要存,全存在读写最快的地方不就好了吗?”** -### 2.2 存储器类型 +然而在硬件世界里,永远有个鱼和熊掌不可兼得的魔咒:**速度越快的存储介质,通常造价越贵,能做出的容量也越小。** -| 类型 | 原理 | 特点 | 应用 | -|------|------|------|------| -| **SRAM** | 触发器 | 极快,但昂贵 | CPU 缓存 | -| **DRAM** | 电容充放电 | 较快,需刷新 | 内存 | -| **Flash** | 浮栅晶体管 | 断电不丢失,有写入寿命 | SSD、U 盘 | -| **HDD** | 磁盘磁性记录 | 容量大,有机械延迟 | 机械硬盘 | +为了用尽可能少的钱换取尽可能快的电脑运行速度,计算机科学家不得已设计了**「存储层次结构」**(也就是存储金字塔)。 -### 2.3 存储的关键指标 +👇 **动手点点看**: -::: tip 💡 如何评估存储性能? -**访问时间**:从发出请求到获得数据的时间 -- 内存:约 100 纳秒 -- SSD:约 100 微秒 -- HDD:约 10 毫秒 +点击金字塔的不同层级,看看现代计算机是怎么精打细算的。 -**吞吐量**:单位时间能传输的数据量 -- 内存:几十 GB/s -- SSD:几 GB/s -- HDD:100-200 MB/s + -**IOPS**:每秒能进行的读写操作次数 -- SSD:几万到几十万 -- HDD:几百 -::: +**🎯 核心领悟:操作系统的搬运工哲学** + +世界上没有完美的存储器。因此,操作系统(如 Windows, macOS)就像一个极度聪明、一刻不停的仓库管理员: + +1. 它把海量的电影、游戏塞在速度慢、容量大(便宜)的仓库——**SSD 或机械硬盘**里。 +2. 当你要玩游戏时,它赶紧把相关的高清贴图文件,从硬盘搬运到速度极快但容量有限的操作台——**内存(RAM)**上。 +3. 当你关闭游戏时,它再把内存清空,腾出操作台给别的文件用。 + +> **解惑**:当你玩大型开放世界游戏时,遇到场景切换要黑屏很久(读条),本质上就是因为硬盘仓库太慢,搬运工(系统)正在玩命地把下一张地图的数据搬到内存操作台上呢。 --- -## 3. 数据传输:从串行到并行 +## 3. 什么是数据传输?(让 0 和 1 出发旅行) -### 3.1 传输方式 +数据编完码、存在了内存里,接下来就是发给朋友了。 - +> **数据传输**,就是把代表 0 和 1 的电信号(或光信号),顺着网线、电缆或无线电波,准确无误地从一台机器送到另一台机器的过程。 -### 3.2 常见接口标准 +### 3.1 硬件与局域网传输:一条导线的物理极限 -| 接口 | 类型 | 速度 | 应用 | -|------|------|------|------| -| **USB 3.0** | 串行 | 5 Gbps | 外设连接 | -| **USB 4** | 串行 | 40 Gbps | 高速外设 | -| **SATA III** | 串行 | 6 Gbps | 硬盘接口 | -| **PCIe 4.0 x16** | 串行(多通道) | 32 GB/s | 显卡、SSD | -| **以太网** | 串行 | 1-100 Gbps | 网络传输 | +在机箱内部,或者两台靠得很近的电脑之间发数据,我们面临的是**纯粹的物理挑战**。 -### 3.3 传输的可靠性 +很多人第一个想到的点子是:“一根电线一次发 1 个信号,那我并排接 8 根线,速度不就是 8 倍吗?” +这就是早期用来插硬盘的**并行传输(Parallel)**思路。 -::: tip 💡 如何保证传输不出错? -**校验机制**: -- **奇偶校验**:简单的错误检测 -- **CRC 校验**:更强的错误检测能力 -- **校验和**:快速检测数据完整性 +然而,今天手机的 Type-C、外部的 USB 和主板内部的 PCIe 接口,用的全都是**串行传输(Serial,只有一根主通道发数据)**。 -**纠错机制**: -- **重传**:发现错误就重新发送 -- **前向纠错**:发送冗余信息,接收方能自动纠正 +👇 **动手点点看**: +比较一下串行和并行传输的动画。 -**流量控制**: -- 防止发送方发太快,接收方来不及处理 -- 类似"确认收到再发下一个" -::: + + +**💡 为什么“一条小路”击败了“八车道”?** + +在速度不快时,8 根线确实强。但当我们需要每秒发几十亿次信号时,问题出现了: +并排的几根线上的微弱电流会产生极强的电磁波互相干扰(串扰 Crosstalk);而且你根本无法保证发送端同时发出的 8 个信号,能完美**同时**到达终点线。只要有一根线因为杂质阻抗慢了一丝拉,8 个拼在一起的字就彻底乱了。 + +所以,与其花天价去调平 8 条赛道,不如把所有技术资源砸在 1 辆跑车上,把它拉到光速。这就是串行接口一统天下的物理真相。 + +### 3.2 广域网与互联网传输:漂洋过海的防丢艺术 + +如果你的数据不是发给机箱里一寸外的显卡,而是要发给大洋彼岸美国服务器呢? + +一根连续的导线是不可能的。数据要穿过光缆、海底基站、无数个破旧的路由器。这时候,面临的不再是物理极限,而是**容错保全挑战**。 + +当你用微信发送 1GB 的超大视频时,底层的逻辑像极了国际搬家——你不可能整个集装箱直接扔给邮政。 + +1. **分包(Packetization)**:网络会把视频切成几万个信封大小的“数据包”(通常是 1500 字节)。 +2. **校验(Checksum)**:为防止途中海底光缆被鲨鱼咬断一根线,导致某个包里的 `0` 翻转成了 `1`,系统会在发件前,用复杂的数学公式对信封里的信件算出一个“特征码”贴在上面。 +3. **TCP重发与确认**:接收方拿到信封,先自己在纸上验算一遍特征码。如果不对(沿途受损),或者发现序号从 31 直接跳到了 33(丢包),就会通过网络大喊一声:**“我没收到 32 号,请你再重发一遍 32 号!”** + +正因为有了这种底层叫做 **TCP(传输控制协议)** 的极其严密的切包对账机制,你在地下室或者极不稳定的 WiFi 下下载微信文件,就算下了半小时,下载完的那一瞬间,文件也必定是 100% 完整、0 损坏的。 --- -## 4. 编码、存储、传输的协作 +## 4. 终局实战:从拍下快门到发朋友圈的全流程 -让我们看一个完整的例子:**保存一张照片到云端** +前面我们将“如何翻译成数字(编码)”、“放在哪里保管(存储)”、“如何完好地走完旅途(传输)”都分块讲了一遍。 -``` -1. 编码阶段 - - 相机传感器捕捉光线 → 模拟信号 - - ADC 转换 → 数字信号(RAW 格式) - - JPEG 编码 → 压缩后的二进制数据 +现在,让我们把这些积木搭起来,沉浸式观看一个日常中再普通不过的操作:**拍一张照片自动备份到云端。** -2. 存储阶段 - - 写入手机内存(RAM)→ 临时存储 - - 写入手机闪存(Flash)→ 持久存储 +当你按下快门的那一秒钟,手机内部其实已经打响了一场极其恢弘的数字战争。 -3. 传输阶段 - - 读取闪存数据 → 内存 - - 通过 Wi-Fi/4G 发送 → 网络传输 - - 云端接收 → 写入云端存储 -``` +👇 **动手点点看**: -::: tip 💡 理解这个流程 -每一步都涉及编码、存储、传输: +点击“执行这一步”,追踪这笔数据惊险的完整生命旅程。 -1. **编码**:把图像变成二进制数据 -2. **存储**:在本地保存 -3. **传输**:通过网络发送到云端 - -这三个环节紧密配合,才能完成"保存照片到云端"这个看似简单的操作。 -::: + --- -## 5. 总结:数据的三重奏 +## 5. 名词对照表 -让我们用一个比喻总结编码、存储、传输: +当你阅读其他文档时,可能会遇到下面这些行话,这里为你准备了一张速查表: -| 概念 | 比喻 | 核心任务 | -|------|------|---------| -| **编码** | 翻译 | 把信息变成 0 和 1 | -| **存储** | 仓库 | 把数据保存起来 | -| **传输** | 快递 | 把数据送到目的地 | - -::: tip 💡 核心启示 -**数据处理的本质是"转换、保存、移动"**。 - -- 编码解决"如何表示"的问题 -- 存储解决"如何保存"的问题 -- 传输解决"如何传递"的问题 - -理解了这三点,你就会明白: -- 为什么不同文件格式要选择不同的编码方式 -- 为什么需要不同层次的存储介质 -- 为什么传输速度和可靠性需要平衡 -::: +| 术语 / 缩写 | 中文对照 | 简单解释 | +| :--- | :--- | :--- | +| **Bit (b)** | 比特 / 位 | 计算机世界最小的单位,只能是 0 或者 1。 | +| **Byte (B)** | 字节 | 8 个 Bit 捆在一起就是一个 Byte。它是文件大小最基础的衡量单位。 | +| **Character Set** | 字符集 | 就像是“字典的目录”,规定了某个文字存在,并没有规定在硬盘里具体怎么写。 | +| **Encoding** | 编码 | 具体的“存储规则”,决定了字典里的那个字,对应底层到底是哪几个字节(如 UTF-8)。 | +| **RAM** | 内存 / 运行内存 | 极其快速但断电就清空的工作台。你手机的 8G/16G 运存指的就是这个。 | +| **SSD** | 固态硬盘 | 现代电脑负责永久保存数据的仓库,基于闪存芯片,比老式机械硬盘快几十倍。 | +| **Serial / Parallel** | 串行 / 并行 | 串行是一条通道挨个排队飞奔;并行是多条通道齐头并进(但不适合极高频率)。 | +| **Checksum** | 校验和 | 传输数据时附带的验证码。收件人算一遍,如果和包裹上写的一致,说明没坏。 | +| **TCP** | 传输控制协议 | 互联网的基石协议。负责把大文件切片、贴序号、丢包重发,保证数据 100% 完整送达。 | --- -## 延伸阅读 +## 总结 -- **字符编码详解**:深入学习 ASCII、Unicode、UTF-8 的设计原理 -- **存储技术发展**:了解从磁带到 SSD 的技术演进 -- **网络传输协议**:学习 TCP/IP 如何保证可靠传输 -- **数据压缩算法**:了解 ZIP、JPEG、MP3 等压缩原理 +文章一开始提出的诸多疑惑,现在你已经站在了系统底层的视角有了答案: + +- **为什么同样的文件你收到后变乱码了?** + 数据没坏,只是你的阅读软件没选对密码本(编码问题)。 + +- **为什么现在电脑背后的线大多是一根小小的 Type-C,却比以前很宽的线传输还要快?** + 因为以前是几辆马车并排慢跑容易撞车(并行),现在是一列高铁在专线上极速狂飙(串行)。 + +- **为什么大型游戏在读取场景时要黑屏很久?** + 因为它需要把动辄几十 GB 的大文件,从速度慢的硬盘(仓储区),拼命搬运拼接到速度快但昂贵的内存(核心工作台)里。 + +计算机的本质其实非常朴素: + +**它不过是一个擅长把所有的光影文字“转换(编码)”、放在某个硅片里“保管(存储)”、然后再把它切碎成电平脉冲“邮寄出去(传输)”的机器**。 + +读懂了这个循环往复的过程,你就真正握住了推开计算机底层原理大门的那把钥匙。 diff --git a/docs/zh-cn/appendix/2-development-tools/git-version-control.md b/docs/zh-cn/appendix/2-development-tools/git-version-control.md index 944eb9c..e625c75 100644 --- a/docs/zh-cn/appendix/2-development-tools/git-version-control.md +++ b/docs/zh-cn/appendix/2-development-tools/git-version-control.md @@ -1,348 +1,558 @@ # Git:代码的时光机 -::: tip 🎯 核心问题 -**写代码时最怕什么?** 写错了想回退、改崩了想重来、多人同时改同一个文件...这些头疼的事,Git 都能帮你搞定!它就像是代码世界的"时光机",让你随时回到过去,又能和队友在各自的"平行宇宙"里安全开发。 -::: + +> 💡 **学习指南**:这一章专门写给完全没用过 Git 的人。我们不会上来就让你背命令,而是先搞清楚"Git 到底在帮你解决什么问题",再一步步把命令和概念串起来。读完后,你应该能独立完成:本地提交、创建分支、推送到 GitHub。 --- -## 0. 最常用的 5 个场景(直接照抄) +## 0. 先问一个问题:你有没有经历过这些噩梦? -如果你只想"立刻能用",先把这块过一遍:每个场景都是现实工作中最常见的 Git 流程。 +**场景一:版本地狱** - +你写论文或者写代码,改到一半发现改错了,想回到三天前的版本——但你找不到了。 ---- - -## 1. 为什么要学 Git?三大痛点 - -### 1.1 痛点一:版本混乱 - -你是否经历过这种绝望? - -```text -论文_初稿.doc -论文_修改版.doc -论文_最终版.doc -论文_最终版_打死不改版.doc -论文_绝对是最后一次修改版.doc +``` +项目_v1.zip +项目_v2_修改版.zip +项目_v3_最终版.zip +项目_v3_最终版_真的最终版.zip +项目_v3_最终版_打死不改了.zip ``` -**Git 的解决方案**:不需要复制副本,一个文件夹搞定所有历史版本。想回到哪次修改,一键恢复。 +每次存一个新副本,硬盘越来越乱,而且你根本记不住哪个版本改了什么。 -### 1.2 痛点二:无法后悔 +**场景二:协作噩梦** -::: tip 💡 这个场景你一定遇到过 -写代码写了 3 小时,突然发现之前的思路更好,但已经改不回去了...或者删错了一段代码,想找回原来的版本。 +你和队友同时改同一个文件: +- 你改了第 10 行,添加了登录功能 +- 队友改了第 10 行,修复了一个 Bug +- 你们用邮件互发代码,结果合并时一个人的改动被另一个人覆盖了 +- 没人知道最后哪段代码是对的 -有了 Git,这种情况永远不会发生。每次重要节点都能"存档",出问题随时"读档"重来。 -::: +**场景三:没有"后悔药"** -### 1.3 痛点三:协作冲突 - -你和队友同时改同一个文件: - -- 你改了 A 文件的第 10 行 -- 队友改了 A 文件的第 15 行 -- 怎么合并?谁覆盖谁? - -**Git 的解决方案**:智能合并,自动处理大部分冲突。只有当你们真的改了同一行代码时,才需要手动决定用谁的。 +你在生产环境部署了新代码,结果出 Bug 了,想紧急回退到上一个稳定版本——但你不知道怎么回退,只能手忙脚乱地找备份。 --- -## 2. 核心概念:三区模型 +**Git 就是为了解决这三个问题而生的。** -Git 的设计哲学其实很像**寄快递**。 +Git 是一个**版本控制系统**(Version Control System)。它的本质是:**把你每一次"存档"操作都记录下来,形成一条完整的历史时间线,让你可以随时回到任意一个历史节点。** - - -### 2.1 三个区域是什么? - -::: tip 📦 用快递理解 Git -想象你在寄快递: - -- **工作区(Working Dir)** = 你的**书桌**。你在这里整理要寄的东西,想怎么乱改都行。 -- **暂存区(Staging Area)** = **快递盒**。你把要寄的文件放进去(`git add`),准备打包。 -- **仓库(Repository)** = **快递柜**。一旦你封箱寄出(`git commit`),这个版本就被永久记录下来了。 - ::: - -| 区域 | 作用 | 对应命令 | 状态 | -| ---------- | ------------------ | --------------------- | ------------- | -| **工作区** | 你当前正在改的代码 | `git status` 查看修改 | 红色 = 未暂存 | -| **暂存区** | 准备提交的文件 | `git add` 添加 | 绿色 = 已暂存 | -| **仓库** | 永久保存的历史版本 | `git commit` 提交 | 只读,不能改 | - -::: tip 💡 关键理解 -只有提交到**仓库**的内容才是安全的。工作区里没提交的内容,丢了就真丢了。所以经常`git commit`是好习惯! -::: +不夸张地说,Git 是现代软件开发最重要的工具之一。几乎所有的公司、所有的开源项目都在用它。 --- -## 3. 基础工作流:存档三步走 +## 1. Git 和 GitHub 是一回事吗? -日常开发中,你 90% 的时间都在重复这三个动作。 +很多初学者会混淆这两个概念,先澄清一下: - +| | Git | GitHub | +| :--- | :--- | :--- | +| **是什么** | 一个运行在你电脑上的版本控制工具 | 一个存放 Git 仓库的网站(云端) | +| **在哪里** | 你的本地电脑 | 互联网上 | +| **能独立使用吗** | ✅ 可以,只管理本地历史 | ❌ 需要配合 Git 使用 | +| **类比** | 你本地的日记本 | 存日记的云盘 | -### 3.1 第一步:修改代码(工作区) +简单说:**Git 是工具,GitHub 是托管服务。** 就像 Word 是工具,OneDrive 是云盘一样,两者配合使用,但并不是同一个东西。 -在工作区写写画画,想怎么改就怎么改。这时候修改只在你本地,还没记录。 +除了 GitHub,类似的服务还有 GitLab、Gitee(国内)等。 -### 3.2 第二步:挑选文件(git add → 暂存区) +--- -::: tip 🤔 为什么要先 add 再 commit? -你可能问:为什么不能直接 commit 所有修改? +## 2. 核心概念:三个区域 -**答案**:因为有时候你不想一次性提交所有改动。 +这是整个 Git 最重要的设计,理解了这三个区域,你就理解了 Git 的灵魂。 -- 今天改了 5 个文件,但只想提交其中 3 个(完成了一个功能) -- 另外 2 个文件还在调试中,不想现在提交 +Git 把你的文件状态分成三层: -`git add` 让你有选择权:决定这次提交包含哪些文件。 -::: +**工作区(Working Directory)** +就是你的**普通文件夹**,你现在看到的、正在编辑的所有文件都在这里。你随便改,Git 会感知到你改了什么,但不会做任何记录。 -**常用命令**: +**暂存区(Staging Area / Index)** +这是一个**"预备提交"的中转站**。你可以把工作区里想要保存的文件"放进"暂存区,就像把快递放进快递盒——还没寄出去,但已经选好了要寄什么。 + +**仓库(Repository)** +这是**永久存档的历史记录库**,藏在 `.git` 文件夹里。每次你执行 `git commit`,暂存区里的内容就会被封存进仓库,形成一条不可篡改的历史记录。 + +👇 **动手点点看**:依次点击命令按钮,观察文件在三个区域之间的流转。 + + + +### 为什么要"两步走"(add + commit)? + +很多初学者会问:为什么不能直接一键保存,非要先 `add` 再 `commit`? + +**因为现实开发中,你经常不想把所有改动都一起提交。** + +举个例子:你今天改了 5 个文件: +- `login.js`:完成了登录功能(想提交) +- `style.css`:调整了登录页样式(想提交) +- `debug.log`:临时调试输出(**不想**提交) +- `experiment.js`:正在测试的新功能,还没完成(**不想**提交) +- `todo.txt`:你的个人备忘(**不想**提交) + +如果没有暂存区,你要么把这 5 个文件全部提交(提交记录很混乱),要么一个都不提交。 + +有了暂存区,你可以精确控制:`git add login.js style.css`,只把这两个文件放进快递盒,然后 `commit`,这次提交就清清楚楚地记录"登录功能完成"。 + +--- + +## 3. 第一次使用 Git:初始化和基础工作流 + +### 3.1 安装和初始化 + +安装好 Git 后(macOS 自带,Windows 去 git-scm.com 下载),打开终端,进入你的项目文件夹: ```bash -# 添加单个文件 -git add index.html +# 在当前文件夹初始化一个 Git 仓库 +git init -# 添加所有修改 -git add . +# Git 会创建一个隐藏的 .git 文件夹,所有历史记录存在里面 +# 输出:Initialized empty Git repository in .../your-project/.git/ +``` -# 查看哪些文件被暂存了 +第一次使用还需要告诉 Git 你是谁(这个信息会附在每次提交记录上): + +```bash +git config --global user.name "你的名字" +git config --global user.email "你的邮箱" +``` + +### 3.2 日常工作流:三步存档 + +初始化之后,日常开发 90% 的操作就是反复执行这三步: + +**第一步:查看状态** + +```bash git status ``` -### 3.3 第三步:封箱提交(git commit → 仓库) +这是你用得最多的命令,没有之一。它告诉你: +- 你在哪个分支上 +- 哪些文件被修改了(红色 = 未暂存) +- 哪些文件在暂存区里(绿色 = 已暂存,等待提交) -给这次修改起个名字(比如"修复了登录 Bug"),永久存档。 - -**重要:commit message 要写清楚!** +**第二步:把文件放进暂存区** ```bash -# ❌ 不好的写法 -git commit -m "update" +# 添加单个文件 +git add login.js -# ✅ 好的写法 -git commit -m "feat: 添加用户登录功能" -git commit -m "fix: 修复首页在 iOS 的显示问题" -git commit -m "docs: 更新 README 的部署说明" +# 添加多个文件 +git add login.js style.css + +# 添加当前文件夹里所有修改过的文件(用 . 表示"全部") +git add . ``` -::: tip 💡 commit message 规范 -推荐用**类型+描述**的格式: +> ⚠️ 初学者常见误区:`git add .` 非常方便,但会把所有修改都加进去,包括你不想提交的临时文件。养成精确 add 的习惯,或者用 `.gitignore` 排除不想追踪的文件(后面会讲)。 -- `feat:` 新功能 -- `fix:` 修复 bug -- `docs:` 文档更新 -- `style:` 代码格式(不影响功能) -- `refactor:` 重构(不改变功能) -- `test:` 测试相关 -- `chore:` 构建/工具相关 +**第三步:提交,写上说明** -这样以后翻历史记录,一眼就知道每次提交做了什么。 -::: +```bash +git commit -m "feat: 添加用户登录功能" +``` + +`-m` 后面引号里的内容叫做 **commit message**(提交说明)。这是写给未来的自己和队友看的,要写得有意义。 + +### 3.3 Commit Message 怎么写才专业? + +```bash +# ❌ 没用的写法——看了不知道做了什么 +git commit -m "update" +git commit -m "fix" +git commit -m "改了一些东西" + +# ✅ 好的写法:类型 + 冒号 + 一句话描述 +git commit -m "feat: 添加用户登录功能" +git commit -m "fix: 修复首页在 iOS Safari 上的白屏问题" +git commit -m "docs: 更新 README 中的部署说明" +git commit -m "refactor: 将 UserService 拆分为独立模块" +git commit -m "style: 统一代码缩进为 2 空格" +``` + +**常用前缀含义:** + +| 前缀 | 含义 | +| :--- | :--- | +| `feat:` | 新功能(feature) | +| `fix:` | 修复 Bug | +| `docs:` | 文档改动 | +| `style:` | 代码格式调整(不影响功能) | +| `refactor:` | 代码重构(功能不变,结构优化) | +| `chore:` | 构建、工具、依赖相关 | +| `test:` | 测试相关 | + +养成这个习惯,几个月后翻历史记录,一眼就知道每次提交做了什么。这在团队协作中尤其重要。 + +### 3.4 查看历史记录 + +```bash +# 详细格式(每次提交的完整信息) +git log + +# 简洁格式(每行一条,推荐日常使用) +git log --oneline + +# 示例输出: +# a1b2c3d (HEAD -> main) feat: 添加用户登录功能 +# 9f3e1b2 init: 项目初始化 +``` --- -## 4. 平行宇宙:分支(Branch)的魔法 +## 4. 平行宇宙:分支(Branch) -这是 Git 最强大的功能! +**分支**是 Git 最强大、也是最让初学者困惑的功能。但理解了它之后,你会发现这个设计非常优雅。 -::: tip 🌌 用游戏理解分支 -想象你在玩游戏,前面有个大 Boss(上线新功能),你怕打不过导致游戏结束(系统崩溃)。 +### 4.1 分支是什么?用"平行宇宙"来理解 -这时候,你可以开一个**分支(Branch)**,相当于**复制了一个平行世界**: +想象你在玩一个角色扮演游戏,游戏里有一个关键选择: +- 选择 A:去挑战大 Boss(开发新功能) +- 选择 B:继续稳定当前局面(主线不动) -- 在**平行世界**(新分支)里打 Boss,输了也不怕,因为主世界(主分支)没影响 -- 打赢了就把成果"合并"回主世界 -- 多个队友可以在各自的平行世界开发,互不干扰 - ::: +如果你直接在主存档上做选择 A,万一失败了,整个游戏进度就毁了。 - +但如果你**复制一个存档**,在副本里去挑战 Boss: +- 打赢了?把副本的成果合并回主存档 +- 打输了?主存档完全没有影响,删掉副本重来 -### 4.1 主分支 vs 开发分支 +**Git 分支就是这个"副本存档"机制。** -| 分支类型 | 作用 | 特点 | -| ------------------- | -------------- | ------------------------------------ | -| **main/master** | 稳定的线上版本 | 只有测试通过的代码才能进来 | -| **dev/feature-xxx** | 你的试验田 | 这里炸了地球也没关系,不影响主分支 | -| **hotfix-xxx** | 紧急修复 | 生产出 bug 时,从 main 开分支快速修复 | +在 Git 里,`main`(或 `master`)分支是你的"主存档",永远保持稳定可用。当你要开发新功能时,你从 main 创建一个新分支,在那里开发、测试,完成后再合并回 main。 -### 4.2 分支操作流程 +### 4.2 分支的可视化演示 -**创建分支并切换**: +👇 **动手点点看**:依次点击命令按钮,观察下方分支图如何分叉、延伸、最终合并。重点关注 HEAD 标签的位置变化——它始终指向"你当前在哪里"。 + + + +### 4.3 分支操作详解 + +**创建并切换到新分支:** ```bash -# 创建新分支 -git branch feature-login +# 方式一:先创建,再切换(两步) +git branch feature-login # 创建分支 +git checkout feature-login # 切换过去 -# 切换到新分支 -git checkout feature-login - -# 或者一步到位:创建并切换 +# 方式二:一步到位(推荐) git checkout -b feature-login + +# 输出:Switched to a new branch 'feature-login' ``` -**在分支上开发**: - -```bash -# 在 feature-login 分支上改代码... -git add . -git commit -m "feat: 添加登录表单" +创建分支后,你的命令行提示符会显示当前分支名,比如: +``` +user@mac ~/project (feature-login) $ ``` -**合并回主分支**: +**查看所有分支:** ```bash -# 切回主分支 +git branch + +# 输出(* 表示当前所在分支): +# * feature-login +# main +``` + +**在分支上正常开发:** + +```bash +# 在 feature-login 分支上,改代码、add、commit,和平时完全一样 +git add login.js +git commit -m "feat: 添加登录表单 HTML 结构" + +git add login.js api.js +git commit -m "feat: 完成登录接口对接" +``` + +这些提交只在 `feature-login` 分支上,`main` 分支完全不知道你做了什么。 + +**切回主分支,合并:** + +```bash +# 切回 main git checkout main -# 合并 feature-login +# 把 feature-login 的所有改动合并进来 git merge feature-login -# 删除已合并的分支(可选) +# 合并完成后,可以删掉这个分支(可选) git branch -d feature-login ``` -::: tip 💡 什么时候用分支? -**个人开发**: +### 4.4 什么时候该开分支? -- 要尝试新想法,不确定会不会搞崩现有代码 → 开分支 -- 修一个复杂 bug,需要多次实验 → 开分支 +| 场景 | 建议 | 理由 | +| :--- | :--- | :--- | +| 开发一个新功能 | ✅ 开分支 | 功能完成前不影响主线,随时可以放弃 | +| 修复线上紧急 Bug | ✅ 从 main 开 `hotfix-xxx` 分支 | 修复完直接合并上线,不带入未完成的功能 | +| 和队友并行开发 | ✅ 各自开分支 | 互不干扰,完成后统一通过 Pull Request 合并 | +| 只改一个错别字 | ❌ 直接在 main 改 | 风险极低,没必要额外开分支 | -**团队开发**: +### 4.5 团队常用的分支策略 -- 每个功能一个分支,互不干扰 -- 开发完提 Pull Request,队友 review 后再合并 - ::: +在实际项目中,团队通常会约定好分支的命名和用途: + +| 分支名 | 用途 | 特点 | +| :--- | :--- | :--- | +| `main` / `master` | 生产环境的稳定代码 | 只有测试通过的代码才能进来,不能直接推送 | +| `dev` / `develop` | 日常集成分支 | 所有功能分支先合并到这里,测试通过再上 main | +| `feature/xxx` | 具体功能开发 | 如 `feature/user-login`,完成后合并到 dev | +| `hotfix/xxx` | 紧急修复 | 从 main 创建,修完直接合并回 main 和 dev | --- -## 5. 常用命令速查表 +## 5. 与队友协作:远程仓库 -| 命令 | 作用 | 人话解释 | 使用频率 | -| ----------------------- | ---------- | ------------------------------ | --------------------- | -| `git init` | 初始化 | "在这里建个新仓库" | 项目开始时用一次 | -| `git status` | 查看状态 | "现在乱不乱?有没有东西没提交?" | ⭐⭐⭐⭐⭐ 极高频 | -| `git add .` | 添加所有 | "把桌上所有文件都扔进快递盒" | ⭐⭐⭐⭐⭐ 每次提交前 | -| `git add file.txt` | 添加单个 | "只要这个文件" | ⭐⭐⭐⭐ 选择性添加 | -| `git commit -m "..."` | 提交 | "封箱!贴上标签,写上这次改了啥" | ⭐⭐⭐⭐⭐ 完成功能时 | -| `git log` | 查看历史 | "翻翻以前的日记" | ⭐⭐⭐ 回顾历史 | -| `git checkout -b dev` | 创建新分支 | "我要去平行宇宙 dev 探险了" | ⭐⭐⭐⭐ 开新功能 | -| `git checkout main` | 切换分支 | "回地球(主分支)看看" | ⭐⭐⭐⭐ 切换任务 | -| `git merge dev` | 合并分支 | "把平行宇宙的成果带回地球" | ⭐⭐⭐ 完成功能 | -| `git branch` | 查看分支 | "现在有哪些平行世界?" | ⭐⭐⭐ 查看状态 | -| `git branch -d feature` | 删除分支 | "这个平行世界不需要了,删掉" | ⭐⭐ 合并后清理 | -| `git push` | 推送 | "把本地存档上传到云端" | ⭐⭐⭐⭐⭐ 团队协作 | -| `git pull` | 拉取 | "把云端最新存档下载到本地" | ⭐⭐⭐⭐⭐ 团队协作 | +到目前为止,你学的都是**本地**的 Git 操作——所有历史记录都存在你自己的电脑上。要和队友共享代码,你需要一个**远程仓库**,也就是 GitHub、GitLab 这样的云端存储。 ---- +### 5.1 远程仓库的工作原理 -## 6. 进阶:解决冲突与远程协作 +可以把远程仓库理解为**团队共用的"公共存档"**: -### 6.1 冲突(Conflict)是什么? +- 每个人在本地写代码、commit +- 写完后 `push`(上传)到远程仓库 +- 队友 `pull`(下载)远程仓库的最新内容到自己本地 +- 这样大家的代码就保持同步了 -当你和队友**同时修改了同一个文件的同一行代码**,Git 就会懵:"我该听谁的?"这就是**冲突(Conflict)**。 +👇 **动手点点看**:依次点击命令,体验从关联远程仓库、推送、到拉取队友更新的完整流程。 - + -### 6.2 怎么解决冲突? +### 5.2 第一次推送项目到 GitHub -**Step 1**:打开冲突文件,会看到这样的标记: +**第一步**:在 GitHub 上创建一个新仓库(点击右上角 + → New repository),不要勾选初始化选项。 -```text -<<<<<<< HEAD -你的代码 -======= -队友的代码 ->>>>>>> feature-branch -``` - -**Step 2**:手动选择要保留的代码,或合并两者: - -```text -# 保留你的代码 → 删除队友的部分和标记 -# 保留队友的 → 删除你的部分和标记 -# 合并两者 → 综合两边的代码 -``` - -**Step 3**:删除所有标记,保存文件 - -**Step 4**:重新提交 +**第二步**:回到本地终端,关联远程仓库: ```bash -git add 解决冲突的文件 -git commit # Git 会自动生成合并提交 +# 把本地仓库和 GitHub 上的仓库关联起来 +# "origin" 是远程仓库的别名,是约定俗成的名字(也可以改,但没必要) +git remote add origin https://github.com/你的用户名/仓库名.git + +# 确认关联成功 +git remote -v +# 输出: +# origin https://github.com/你的用户名/仓库名.git (fetch) +# origin https://github.com/你的用户名/仓库名.git (push) ``` -::: tip 💡 避免冲突的最佳实践 +**第三步**:推送本地内容到远程: -- **频繁沟通**:队友改同一个文件前,先打个招呼 -- **小步提交**:不要攒着大量代码最后才提交,增加冲突概率 -- **分支隔离**:不同功能用不同分支,减少直接冲突 -- **用 Pull Request**:合并前让队友 review,提前发现问题 - ::: +```bash +# 第一次推送,-u 的意思是"以后 git push 时,默认推到 origin 的 main 分支" +git push -u origin main -### 6.3 远程仓库(Remote) +# 之后每次推送只需要: +git push +``` -**远程仓库**(比如 GitHub/GitLab)就是**云端的备份中心**。 +### 5.3 日常协作的命令 - +**推送(你改了东西,要让队友看到):** +```bash +git push +``` -**核心操作**: +**拉取(队友改了东西,你要同步):** +```bash +git pull +``` -| 操作 | 命令 | 作用 | -| ------------ | ---------------------------------------------- | ------------------------ | -| **关联远程** | `git remote add origin https://github.com/...` | 第一次连接云端 | -| **推送** | `git push -u origin main` | 把本地存档上传 | -| **拉取** | `git pull` | 把云端最新存档下载并合并 | -| **克隆** | `git clone https://github.com/...` | 复制整个仓库到本地 | +`git pull` 实际上是两个命令的组合: +1. `git fetch`:先去远程仓库下载最新的提交记录 +2. `git merge`:把下载回来的内容合并到你当前的分支 -::: tip 💡 push 和 pull 的区别 +**第一次从 GitHub 获取别人的项目:** +```bash +# 把整个远程仓库复制到本地(只需要做一次) +git clone https://github.com/某人/某项目.git -- **push**:你的本地代码 → 云端(你改了东西,要同步给队友) -- **pull**:云端代码 → 你的本地(队友改了东西,你要同步下来) +# clone 会自动建立与远程的关联,之后直接 push/pull 就行 +``` -**最佳实践**:每天开始工作前先`git pull`,下班前`git push`,这样减少冲突。 -::: +### 5.4 push 和 pull 的方向 + +``` +你的电脑(本地仓库) ←→ GitHub(远程仓库) + +git push: 本地 → 远程 (你改了东西,上传给队友) +git pull: 远程 → 本地 (队友改了东西,下载到你这里) +git clone: 远程 → 本地 (第一次完整复制整个仓库) +``` + +> **最佳实践**:每天开始工作前先 `git pull`,拿到最新代码;下班或完成一个功能后 `git push`,及时备份并让队友看到你的进展。 --- -## 7. 总结:Git 的核心思想 +## 6. 进阶:处理冲突 -Git 不是简单的"版本备份",而是一个**完整的代码协作系统**: +冲突是协作中不可避免的,但也没那么可怕。 -| 特性 | 解决的问题 | 生活类比 | -| ------------ | ------------------------------- | --------------------- | -| **版本管理** | 代码改错了怎么办? | 时光机,随时回退 | -| **分支** | 多人同时改同一个文件怎么办? | 平行宇宙,互不干扰 | -| **暂存区** | 这次提交不想包含所有修改怎么办? | 快递盒,挑拣要寄的东西 | -| **远程协作** | 怎么和队友共享代码? | 云备份,随时随地同步 | -| **冲突处理** | 真的改到同一行了怎么办? | 智能合并 + 手动协调 | +### 6.1 冲突是怎么发生的? -**学习建议**: +当你和队友**同时修改了同一个文件的同一行**,在合并时 Git 不知道该用谁的版本,就会产生冲突。 -1. **先用起来**:不要等"完全理解"再用,一边用一边理解 -2. **从简单开始**:个人项目先掌握`add/commit/push/pull`,团队项目再学分支 -3. **看 Git 图形化工具**:SourceTree、GitHub Desktop 等,可视化帮助理解 -4. **遇到问题不要慌**:Git 的设计就是为了让你能安全地尝试,大不了`git reset` +举个例子: +- 你在 `login.js` 第 5 行写了:`const timeout = 3000` +- 队友同时在同一行写了:`const timeout = 5000` +- 当你 `git pull` 或 `git merge` 时,Git 发现了这个矛盾,就会"暂停"并告诉你:我不知道该用哪个,你来决定。 + +### 6.2 冲突文件长什么样? + +Git 会在冲突的地方插入特殊标记: + +```javascript +function login() { + const url = '/api/login' + +<<<<<<< HEAD + const timeout = 3000 // 你的版本 +======= + const timeout = 5000 // 队友的版本 +>>>>>>> feature/update-timeout + + return fetch(url, { timeout }) +} +``` + +- `<<<<<<< HEAD` 到 `=======` 之间:是你当前分支的内容 +- `=======` 到 `>>>>>>> xxx` 之间:是合并过来的内容 + +### 6.3 如何解决冲突? + +**第一步**:打开冲突文件,找到所有 `<<<<<<<` 标记(通常 VS Code 等编辑器会自动高亮) + +**第二步**:决定保留哪段代码,然后手动编辑文件,删掉所有标记符号(`<<<<<<<`、`=======`、`>>>>>>>`)。 + +比如决定用 5000(队友的版本): +```javascript +function login() { + const url = '/api/login' + const timeout = 5000 // 采用队友的修改 + return fetch(url, { timeout }) +} +``` + +**第三步**:重新提交 + +```bash +# 标记冲突已解决 +git add login.js + +# 完成合并提交(Git 会自动生成合并提交信息) +git commit +``` + +### 6.4 减少冲突的好习惯 + +- **勤 pull**:开始工作前同步最新代码,减少"你落后太多"的情况 +- **小步提交**:不要写了一周代码才一次性提交,频繁小提交更容易发现和解决冲突 +- **分支隔离**:不同功能用不同分支,减少对同一行代码的竞争 +- **沟通**:要改公共文件(比如 `config.js`)前,跟队友打个招呼 --- -## 附录:名词速查表 +## 7. 常用命令速查 -| 名词 | 英文 | 用人话解释 | -| -------- | ---------- | ------------------------------------- | -| **仓库** | Repository | 存放所有版本历史的数据库 | -| **提交** | Commit | 一次完整的版本记录,像存档点 | -| **分支** | Branch | 独立的开发线,像平行宇宙 | -| **合并** | Merge | 把一个分支的改动整合到另一个分支 | -| **冲突** | Conflict | 同一行代码被修改多次,Git 不知道选哪个 | -| **暂存** | Stage | 把修改加入"准备提交"的列表 | -| **远程** | Remote | 云端的仓库副本(GitHub/GitLab) | -| **克隆** | Clone | 复制整个远程仓库到本地 | -| **推送** | Push | 本地 → 远程,上传代码 | -| **拉取** | Pull | 远程 → 本地,下载代码 | -| **检出** | Checkout | 切换到某个分支或版本 | -| **HEAD** | - | 当前所在的分支/版本的指针 | + + +--- + +## 8. 实战:加入一个团队项目的完整流程 + +这是你加入新团队或新项目时的标准操作流程,可以直接照抄: + +```bash +# ① 第一天:把项目 clone 到本地(只做一次) +git clone https://github.com/team/project.git +cd project + +# ② 每天开始工作:先拉取最新代码,确保你的代码是最新的 +git pull origin main + +# ③ 创建自己的功能分支(不要直接在 main 上改) +git checkout -b feature/user-profile + +# ④ 正常开发...写代码... + +# ⑤ 完成一个小功能点后,立即提交(不要攒着) +git add src/UserProfile.vue +git commit -m "feat: 完成用户头像上传功能" + +git add src/UserProfile.vue src/api/user.js +git commit -m "feat: 完成用户资料编辑接口" + +# ⑥ 把自己的分支推送到远程,让队友能看到 +git push origin feature/user-profile + +# ⑦ 在 GitHub 上创建 Pull Request(PR),请求合并到 main +# (这步在 GitHub 网页上操作) + +# ⑧ 等队友 Code Review,按反馈修改,继续 commit + push + +# ⑨ PR 合并后,回到 main,更新本地,删掉功能分支 +git checkout main +git pull +git branch -d feature/user-profile +``` + +--- + +## 9. .gitignore:哪些文件不应该被追踪? + +有些文件你**不想**提交到 Git 仓库里,比如: +- `node_modules/`:依赖包,体积巨大,可以用 `npm install` 重新生成 +- `.env`:环境变量文件,里面可能有数据库密码、API Key,**绝对不能上传到公开仓库** +- `*.log`:日志文件 +- `.DS_Store`:macOS 自动生成的隐藏文件 +- `dist/`、`build/`:编译产物,可以重新构建 + +在项目根目录创建一个 `.gitignore` 文件,写上不想追踪的文件规则: + +```gitignore +# 依赖包 +node_modules/ + +# 环境变量(重要!密码不能提交) +.env +.env.local + +# 构建产物 +dist/ +build/ + +# 系统文件 +.DS_Store +Thumbs.db + +# 日志 +*.log +``` + +GitHub 上有各种语言和框架的 .gitignore 模板:[github.com/github/gitignore](https://github.com/github/gitignore) + +--- + +## 名词速查表 + +| 名词 | 英文 | 解释 | +| :--- | :--- | :--- | +| **仓库** | Repository (Repo) | 存放项目所有版本历史的数据库,在 `.git` 文件夹里 | +| **提交** | Commit | 一次完整的版本记录,像游戏存档点,附有说明和时间戳 | +| **分支** | Branch | 独立的开发线,像平行时间线,互不影响 | +| **合并** | Merge | 把一个分支的改动整合到另一个分支 | +| **冲突** | Conflict | 同一行代码被多人修改,Git 不知道该用哪个,需要手动解决 | +| **暂存** | Stage / Index | 把修改放入"准备提交"列表的操作 | +| **远程** | Remote | 云端的仓库副本(GitHub / GitLab / Gitee) | +| **克隆** | Clone | 把整个远程仓库完整复制到本地 | +| **推送** | Push | 把本地提交上传到远程仓库 | +| **拉取** | Pull | 把远程最新内容下载并合并到本地 | +| **HEAD** | HEAD | 当前所在分支/提交的指针,表示"你现在在哪里" | +| **origin** | origin | 远程仓库的默认别名(约定俗成的名字) | +| **stash** | Stash | 临时保存还没 commit 的改动,切换任务时用 | +| **PR / MR** | Pull Request / Merge Request | 请求把你的分支合并进主分支,通常需要队友 review | diff --git a/docs/zh-cn/appendix/3-browser-and-frontend/graphics-animation.md b/docs/zh-cn/appendix/3-browser-and-frontend/graphics-animation.md index 70e8127..974ccd5 100644 --- a/docs/zh-cn/appendix/3-browser-and-frontend/graphics-animation.md +++ b/docs/zh-cn/appendix/3-browser-and-frontend/graphics-animation.md @@ -1,551 +1,146 @@ -# 图形与动画(Canvas / SVG / WebGL) +# 图形与动画(Canvas 与他的朋友们) + ::: tip 🎯 核心问题 -**如何在网页上画图、做动画、甚至开发游戏?** Canvas 提供了一个强大的 2D 绘图能力,让你用代码创造视觉内容。 + +以前的网页只能展示干巴巴的文字和图片。但如果你想做打砖块游戏、华丽的动态特效、或是可以自由拖拽的数据报表呢?这就是 **Canvas(画布)** 诞生的原因。 + ::: --- -## 1. 为什么要学 Canvas? +## 1. 什么是 Canvas? -### 1.1 Canvas 是什么? +如果说早期的那些 HTML 标签(如 `
`、``)是用**乐高积木**拼起一个静态的网页,那么 HTML5 的 `` 标签就是扔给你一张**巨大的数字白纸**,然后递给你一支靠代码控制的**画笔**,剩下的全交给你自由发挥。 -**Canvas (画布)** 是 HTML5 提供的一个通过 JavaScript 绘制 2D 图形的元素。 +这里面的画没有任何标签结构,你用画笔涂上去的心血,一旦落笔就变成了最纯粹的**“像素颜料”**。 -你可以把它想象成一张**数字画布**: +### 1.1 Canvas vs SVG:两种不同流派的艺术家 -- 🖌️ 你可以用代码"画笔"在上面作画 -- 🎨 可以画任何东西: 简单的形状、复杂的图表、流畅的动画 -- 🎮 甚至可以做成完整的游戏 +在前端画图界,Canvas 有个宿敌叫 **SVG**。它们代表了两种截然不同的绘画观念: -::: tip 💡 Canvas vs SVG:有什么区别? +**Canvas(位图画板):** +* **原理**:就像真实在纸上涂色,几笔画上去就变成一团颜料。 +* **优势**:电脑只管往屏幕上“洒颜料”,性能起飞!能同时画出大几千个活蹦乱跳的闪烁粒子。 +* **缺点**:画完就没法单独反悔(没法被 DOM 直接选择),而且你用浏览器一旦放大,画面就会马赛克发虚。 -在 Web 开发中,绘制图形主要有两种方式: +**SVG(矢量图拼接):** +* **原理**:就像在做幻灯片(PPT)。你画一个圆,它就生成一个圆圈的“实体对象”放在画面上。 +* **优势**:不管被放大成 100 倍还是 10 万倍,永远极其清晰。而且因为每一个形状都是一个独立标签,你可以在任何时候用鼠标点中某个小正方形,命令它换一种颜色。 +* **缺点**:如果你试图放几万个对象乱飞,繁重的排版引擎会直接把浏览器卡死。 -| 特性 | Canvas | SVG | -| -------- | -------------------- | --------------------- | -| **类型** | 位图(光栅图形) | 矢量图形 | -| **DOM** | 单个 `` 元素 | 每个图形都是 DOM 元素 | -| **交互** | 需要手动计算碰撞 | 天然支持事件绑定 | -| **性能** | 适合大量对象 | 适合少量复杂对象 | -| **缩放** | 放大会失真 | 无限缩放不失真 | -| **应用** | 游戏、数据可视化 | 图标、插画 | - -**简单总结**: - -- **Canvas** = 像素画,画完就变成像素,性能好但交互麻烦 -- **SVG** = 矢量图,每个图形都是对象,交互方便但对象多了会慢 - ::: - -### 1.2 Canvas 的应用场景 - -Canvas 的用途非常广泛,你可能每天都在用: - -1. **数据可视化**: ECharts、Chart.js 的图表 -2. **游戏开发**: 网页游戏(如 Phaser.js 引擎) -3. **图像处理**: 图片裁剪、滤镜、拼图(如 Fabric.js) -4. **创意效果**: 粒子特效、动画背景 -5. **工程绘图**: CAD、流程图、思维导图 +**🎮 简单总结:玩动态游戏、做酷炫粒子特效用 Canvas;画精密的 Logo、写交互清晰的小图表用 SVG。** --- -## 2. Canvas 基础 +## 2. 第一笔:用代码找坐标 -### 2.1 Canvas 元素和上下文 +### 2.1 这张纸的上下怎么颠倒了? -使用 Canvas 的第一步是在 HTML 中创建一个 `` 元素: +当你准备下笔时,得先明白 Canvas 里的尺子是反着的。对于传统的数学课坐标系,中心点零点在中间,越往上越大。 -```html - -``` +但在屏幕显示领域,几乎所有设备的“原点(0,0)”都定在**屏幕的最左上角**。向右走 X 轴变大没问题,但是**向下走,Y 轴变大。** -然后通过 JavaScript 获取**渲染上下文 (Rendering Context)**: +👇 **动手点点看**: +拖拽下面的这些点,直观地感受一下坐标是如何变化的: -```javascript -const canvas = document.getElementById('myCanvas') -const ctx = canvas.getContext('2d') // 获取 2D 上下文 -``` + -::: tip 💡 关键概念 +### 2.2 给你的魔法画笔上调料 -- **canvas** 是 DOM 元素,控制画布的大小和位置 -- **ctx** 是绘图工具,所有的绘制操作都通过它完成 -- **`"2d"`** 表示使用 2D 渲染上下文(WebGL 使用 `"webgl"`) - ::: +有了坐标,我们就能召唤那支画笔了(在代码里这支笔叫 `Context` 或简称 `ctx`)。 -### 2.2 坐标系统:Canvas 的"地图规则" +就像拿着调色盘作画,流程总是固定的三步: +1. **调色**:告诉它你需要什么填充色(`fillStyle`)和描边色(`strokeStyle`) +2. **构形**:构思你是画一个圈、还是一条直线? +3. **下笔**:实打实地去填充(`fill( )`)还是去勾勒边缘(`stroke( )`) -Canvas 使用的是**屏幕坐标系**,这与传统数学坐标系有所不同: +👇 **动手点点看**: +试试把下面代码面板里的形状颜色换换: -- **原点 (0, 0)**: 在**左上角**(不是中心) -- **X 轴**: 向右为正方向 -- **Y 轴**: **向下**为正方向(注意: 数学坐标系中 Y 轴向上) -- **单位**: 像素 (px) - -```javascript -// 在左上角绘制一个矩形 -ctx.fillRect(0, 0, 10, 10) - -// 在右下角绘制一个矩形 -ctx.fillRect(canvas.width - 10, canvas.height - 10, 10, 10) -``` - -::: tip 💡 记忆技巧 - -想象你在看**屏幕**: - -- 向右移 → X 增加 ✅ -- 向下移(滚动页面) → Y 增加 ✅ -- 向左移 → X 减少 -- 向上移(向上滚动) → Y 减少 - -这就是 Canvas 的坐标规则。 -::: - -### 2.3 绘制基本形状 - -Canvas 提供了几种绘制基本形状的方法: - -**矩形**: - -```javascript -// 填充矩形 -ctx.fillStyle = '#3498db' -ctx.fillRect(x, y, width, height) - -// 描边矩形 -ctx.strokeStyle = '#2c3e50' -ctx.lineWidth = 2 -ctx.strokeRect(x, y, width, height) - -// 清除矩形区域 -ctx.clearRect(x, y, width, height) -``` - -**圆形**: - -```javascript -ctx.beginPath() -ctx.arc(x, y, radius, startAngle, endAngle) -ctx.fill() // 或 ctx.stroke() -``` - -**参数说明**: - -- **x, y**: 圆心坐标 -- **radius**: 半径 -- **startAngle, endAngle**: 起始和结束角度(弧度制) - - `0` = 3 点钟方向 - - `Math.PI / 2` = 6 点钟方向 - - `Math.PI` = 9 点钟方向 - -**线条**: - -```javascript -ctx.beginPath() -ctx.moveTo(x1, y1) // 起点 -ctx.lineTo(x2, y2) // 终点 -ctx.stroke() -``` - -### 2.4 颜色和样式 - -Canvas 支持多种颜色设置方式: - -```javascript -// 纯色 -ctx.fillStyle = '#3498db' // 十六进制 -ctx.fillStyle = 'rgb(52, 152, 219)' // RGB -ctx.fillStyle = 'rgba(52, 152, 219, 0.5)' // RGBA(带透明度) - -// 线性渐变 -const gradient = ctx.createLinearGradient(x1, y1, x2, y2) -gradient.addColorStop(0, '#3498db') -gradient.addColorStop(1, '#e74c3c') -ctx.fillStyle = gradient - -// 径向渐变 -const radialGradient = ctx.createRadialGradient(x1, y1, r1, x2, y2, r2) -radialGradient.addColorStop(0, '#3498db') -radialGradient.addColorStop(1, 'transparent') -ctx.fillStyle = radialGradient -``` + --- -## 3. 路径:Canvas 的"笔画" +## 3. 翻页动画书:如何让画面动起来极度丝滑 -### 3.1 什么是路径? +我们刚才说过,Canvas 一旦你填上了颜色,这就变成了永久的马赛克。你怎么可能让马赛克奔跑呢? -**路径 (Path)** 是 Canvas 中的核心概念。你可以把它想象成用笔画线的过程: +**答案是“骗过你的眼睛”。这和翻页手翻书或者电影胶片的原理一模一样。** -1. **`beginPath()`** - 开始新路径(拿起笔) -2. **`moveTo()`** - 移动到起点(不画线) -3. **`lineTo()` / `arc()`** - 绘制线条或曲线 -4. **`closePath()`** - 闭合路径(可选) -5. **`fill()` / `stroke()`** - 填充或描边 +如果你想让一个球飞起来: +1. **擦黑板**:用 `clearRect` 把这整块画布上的内容毫不留情地清空! +2. **挪位置**:让那个球的 X 坐标往前偷偷加 2 毫米。 +3. **下笔重画**:把球在新的位置重新画一次。 +4. **疯狂循环**:浏览器内置了一个极其精准的神仙秒表叫 `requestAnimationFrame`。它会以每秒 60 次(即 60 FPS)的变态速度,重复着【擦除 -> 移动 -> 重绘】。由于人眼自带“视觉残留”,你在屏幕上看到的,不仅不是黑板被擦,反而是如同丝绸般顺滑的动画。 -```javascript -ctx.beginPath() -ctx.moveTo(100, 100) // 移动到起点 -ctx.lineTo(200, 100) // 画横线 -ctx.lineTo(150, 150) // 画斜线 -ctx.closePath() // 闭合路径(回到起点) -ctx.fill() // 填充 -``` +👇 **动手点点看**: +尝试添加或者减少物体的数量,感受每秒 60 帧带来的无缝快感: -### 3.2 绘制复杂形状 - -通过组合路径,可以绘制任意复杂的形状。 - -**三角形**: - -```javascript -ctx.beginPath() -ctx.moveTo(100, 50) -ctx.lineTo(150, 150) -ctx.lineTo(50, 150) -ctx.closePath() -ctx.fillStyle = '#e74c3c' -ctx.fill() -``` + --- -## 4. 动画基础 +## 4. 瞎子摸象:我在 Canvas 里面怎么点击? -### 4.1 动画循环 +因为 Canvas 画布就只是一张没有任何结构的“颜料布”。假设你在这个布上画了一只哥布林: -在 Canvas 中创建动画,核心是使用 **`requestAnimationFrame`** 方法。 +如果你想写个代码:“当玩家点中了哥布林,哥布林阵亡”。你根本没法像写普通网页那样通过 `getElementById` 去直接绑定这个外星怪物。因为在浏览器的眼里,**这里永远没有任何怪兽,只有一块宽 600 高 400 的 `` 标签死死挡在这里**。 -```javascript -function animate() { - // 1. 清除画布(或绘制半透明背景产生拖尾效果) - ctx.clearRect(0, 0, canvas.width, canvas.height) +那我们要怎么做事件交互呢? +1. **监听布面被点**:先获取你目前鼠标点在这个死板的 HTML 大布的哪个具体的 XY 位置。 +2. **拿账本去对**:然后你必须自己翻你的代码记录,“我记得刚刚我在(100,100)的位置画了一个半径 50 的哥布林”。 +3. **勾股定理**:我们用初中教的勾股定理公式去疯狂计算——当前鼠标点击的位置,是不是落在了那个(100,100)距离 50 半径的圆内?。 - // 2. 更新状态 - update() +恭喜你!这种疯狂算几何数学距离的方法就是你在各大 3A 游戏里听过的 **“碰撞检测 (Collision Detection)”** - // 3. 绘制 - draw() +👇 **动手点点看**: +打开最下面的“Hover 悬停模式”,你就能看到它内部拼命去算距离有多累了。 - // 4. 请求下一帧 - requestAnimationFrame(animate) -} - -// 启动动画 -animate() -``` - -::: tip 💡 为什么用 requestAnimationFrame 而不是 setInterval? - -- ✅ 自动优化,通常为 60FPS(每秒 60 帧) -- ✅ 页面不可见时自动暂停,节省资源 -- ✅ 与浏览器刷新周期同步,避免画面撕裂 - ::: - -### 4.2 动画的本质 - -动画的本质是**快速连续绘制静态画面**。每帧需要: - -1. **清除旧画面**: `ctx.clearRect()` 或用半透明背景覆盖 -2. **更新状态**: 计算新位置、新角度等 -3. **绘制新画面**: 重新绘制所有对象 - -```javascript -// 清除画布 -ctx.clearRect(0, 0, canvas.width, canvas.height) - -// 半透明背景(产生拖尾效果) -ctx.fillStyle = 'rgba(255, 255, 255, 0.1)' -ctx.fillRect(0, 0, canvas.width, canvas.height) -``` + --- -## 5. 事件处理 +## 5. 解放算力:粒子系统与视觉魔法 -Canvas 只是一个 DOM 元素,不像 SVG 那样每个图形都是独立的 DOM 元素。因此,我们需要**手动处理交互事件**。 +到了这一步,当你把【坐标不断重绘的动画】跟【颜色和大小变换】融合,再放进成百上千个小碎片里。这就是引爆视觉的终极杀器:**粒子系统**。 -### 5.1 鼠标事件 +你只需要建立一个巨大的数组,里面塞满了几百个拥有独立生命值、独立初始随机速度的数字对象。每次“重绘”,让所有的点根据重力或者惯性去减速。你的浏览器里马上就能发生逼真的大爆炸或者漫天飞雪。 -```javascript -canvas.addEventListener('click', (e) => { - const rect = canvas.getBoundingClientRect() - const x = e.clientX - rect.left - const y = e.clientY - rect.top +👇 **动手点点看**: +试试“烟花”和“鼠标轨迹”! - console.log(`Clicked at (${x}, ${y})`) -}) - -canvas.addEventListener('mousemove', (e) => { - const rect = canvas.getBoundingClientRect() - const x = e.clientX - rect.left - const y = e.clientY - rect.top - - // 检测是否悬停在某个对象上 - objects.forEach((obj) => { - const dist = Math.sqrt((x - obj.x) ** 2 + (y - obj.y) ** 2) - if (dist < obj.radius) { - canvas.style.cursor = 'pointer' - obj.hovered = true - } - }) -}) -``` - -### 5.2 拖拽实现 - -```javascript -let isDragging = false -let selectedObject = null - -canvas.addEventListener('mousedown', (e) => { - const { x, y } = getMousePos(e) - - objects.forEach((obj) => { - const dist = Math.sqrt((x - obj.x) ** 2 + (y - obj.y) ** 2) - if (dist < obj.radius) { - isDragging = true - selectedObject = obj - } - }) -}) - -canvas.addEventListener('mousemove', (e) => { - if (isDragging && selectedObject) { - const { x, y } = getMousePos(e) - selectedObject.x = x - selectedObject.y = y - draw() // 重绘 - } -}) - -canvas.addEventListener('mouseup', () => { - isDragging = false - selectedObject = null -}) -``` + --- -## 6. 性能优化 +## 6. 守护 FPS 荣耀:如何应对高烧的 CPU? -随着绘制的对象增多,Canvas 性能会下降。以下是一些常用的优化技巧: +让成千上万个对象在一秒内计算重画 60 遍,这是极其消耗电脑算力(CPU 和内存)的。 +很多野生小白刚做出来的游戏玩了两分钟可能风扇就起飞了。下面是真正的引擎大佬使用的降温护体绝技: -### 6.1 离屏 Canvas (Offscreen Canvas) +1. **局部擦黑板(脏矩形 Dirty Rect)!** 一个角色在一望无际的草原上奔跑。你千万别每帧把整块大草原都擦了重画!角色经过哪一小块,你就用小板擦把哪里擦掉然后只补哪里的洞,这能省下几千倍的力气。 +2. **隐藏后台魔法(离屏 Canvas)!** 如果游戏背景是繁星漫天、有各种复杂绚丽的山脉。最好先偷偷在没人的后台建一个内存 Canvas 把它一次性精美地画上去。以后每秒 60 下的刷新,你直接把这幅“定格全图”通过贴图的方式贴到前端(`drawImage`)就行了。 +3. **批量洗画笔!** 如果画画时你要反复交替使用“红、蓝、红、蓝、红”这几种笔,频繁切换。可以提前把所有红色的兵全归档画完,再清空换蓝颜料画,省去了昂贵的上下文来回切换。 -预渲染静态内容到离屏 Canvas,减少每帧的绘制操作: +👇 **动手点点看**: +先把对象数量拉满,看着网页快掉进卡顿的深渊,再依次打开右下方的绝技进行抢救。 -```javascript -// 创建离屏 Canvas -const offscreenCanvas = document.createElement('canvas') -const offscreenCtx = offscreenCanvas.getContext('2d') -offscreenCanvas.width = 600 -offscreenCanvas.height = 400 - -// 预渲染背景 -function drawBackground(ctx) { - ctx.fillStyle = '#f0f0f0' - ctx.fillRect(0, 0, 600, 400) -} -drawBackground(offscreenCtx) - -// 主渲染循环 -function draw() { - // 直接复制预渲染的背景 - ctx.drawImage(offscreenCanvas, 0, 0) - - // 只绘制动态对象 - objects.forEach((obj) => obj.draw(ctx)) -} -``` - -### 6.2 减少重绘(脏矩形优化) - -只重绘变化的部分: - -```javascript -function draw() { - objects.forEach((obj) => { - if (obj.moved) { - // 清除旧位置 - ctx.clearRect( - obj.oldX - obj.size, - obj.oldY - obj.size, - obj.size * 2, - obj.size * 2 - ) - - // 绘制新位置 - obj.draw(ctx) - - obj.moved = false - } - }) -} -``` - -### 6.3 批量渲染 - -减少状态切换(fillStyle、strokeStyle 等): - -```javascript -// 按颜色分组 -const batches = {} -objects.forEach((obj) => { - if (!batches[obj.color]) { - batches[obj.color] = [] - } - batches[obj.color].push(obj) -}) - -// 批量绘制相同颜色的对象 -Object.keys(batches).forEach((color) => { - ctx.fillStyle = color // 只设置一次颜色 - batches[color].forEach((obj) => { - ctx.beginPath() - ctx.arc(obj.x, obj.y, obj.size, 0, Math.PI * 2) - ctx.fill() - }) -}) -``` + --- -## 7. 常见库与框架 +## 7. 名词对照表 -虽然原生 Canvas 已经很强大,但在实际项目中,使用成熟的库可以大大提高开发效率。 - -### 7.1 Fabric.js - -**特点**: 对象模型,支持交互 - -```javascript -const canvas = new fabric.Canvas('c') - -// 创建圆形 -const circle = new fabric.Circle({ - radius: 20, - fill: '#3498db', - left: 100, - top: 100 -}) - -canvas.add(circle) - -// 自动处理事件 -circle.on('click', () => { - circle.set('fill', '#e74c3c') - canvas.renderAll() -}) -``` - -**适用场景**: 图片编辑器、白板工具、图形设计工具 - -### 7.2 PixiJS (WebGL) - -**特点**: WebGL 加速,超高性能 - -```javascript -const app = new PIXI.Application({ - width: 600, - height: 400, - backgroundColor: 0x1099bb -}) -document.body.appendChild(app.view) - -const graphics = new PIXI.Graphics() -graphics.beginFill(0x3498db) -graphics.drawCircle(300, 200, 50) -graphics.endFill() -app.stage.addChild(graphics) -``` - -**适用场景**: 大型游戏、粒子系统、大量对象的场景 +| 术语 | 解释 | +| --- | --- | +| **Canvas** | Html5 提供的 2D 画布。绘制极快,但画完就变成颜料像素,不支持通过 DOM 操作内容。 | +| **SVG** | 矢量图,放大永远不模糊,且每个图形都是独立的标签元素可以单独点击绑定事件。 | +| **Context (ctx)** | 获取到的“2D 上下文”,可以理解为用来在这张布上调各种颜色、干各种特殊效果的“画笔”。 | +| **requestAnimationFrame** | 浏览器内置的神级节拍器,会以显示器的刷新率(通常 60FPS)不断狂飙执行,专门用来做完美动画。 | +| **FPS / Frame Rate** | 帧率。60 FPS 代表一秒钟内浏览器帮我们默默擦除了 60 次黑板并画了 60 副新图,这骗过了视神经,看起来极其丝滑。 | +| **Dirty Rect / 脏矩形** | 只在画面中发生变化的微小矩形区域内进行擦除和重绘,强力保留性能。 | +| **Offscreen Canvas** | 藏在内存里的“影子画布”,把静态且复杂的树木和山脉先画好,当作死的一张贴图重复利用。 | --- -## 8. 总结与最佳实践 - -### 8.1 核心要点回顾 - -1. **Canvas 是位图画布**: 绘制后就是像素,无法直接修改已有内容 -2. **坐标系统**: 原点在左上角,Y 轴向下为正 -3. **路径系统**: beginPath → moveTo → lineTo → fill/stroke -4. **动画原理**: 清除 → 更新 → 绘制 → requestAnimationFrame -5. **事件处理**: 需要手动计算碰撞 -6. **性能优化**: 离屏 Canvas、脏矩形、批量渲染 - -### 8.2 最佳实践 - -**代码组织**: - -```javascript -// 使用类封装对象 -class GameObject { - constructor(x, y) { - this.x = x - this.y = y - } - - update() { - // 更新状态 - } - - draw(ctx) { - // 绘制 - } - - isHit(x, y) { - // 碰撞检测 - const dist = Math.sqrt((x - this.x) ** 2 + (y - this.y) ** 2) - return dist < this.radius - } -} -``` - -**性能优化清单**: - -- ✅ 使用 `requestAnimationFrame` 而不是 `setInterval` -- ✅ 减少状态切换(按颜色分组绘制) -- ✅ 使用离屏 Canvas 预渲染静态内容 -- ✅ 只重绘变化的部分(脏矩形) -- ✅ 限制对象数量,使用对象池 -- ✅ 避免 `save()` 和 `restore()` 的频繁调用 - ---- - -## 9. 名词速查表 (Glossary) - -| 名词 | 解释 | -| ------------------------- | ----------------------------------------------------------------------- | -| **Context / 上下文** | Canvas 的渲染环境,通过 `getContext("2d")` 获取,所有绘制操作都通过它完成 | -| **Path / 路径** | 由一系列点连接成的轨迹,是 Canvas 绘图的基础 | -| **Stroke / 描边** | 绘制路径的轮廓线 | -| **Fill / 填充** | 用颜色填充路径内部 | -| **requestAnimationFrame** | 浏览器提供的动画 API,在每次重绘前调用回调函数 | -| **Offscreen Canvas** | 离屏 Canvas,用于预渲染静态内容以提高性能 | -| **Dirty Rect** | 脏矩形优化,只重绘变化的部分 | -| **Collision Detection** | 碰撞检测,判断鼠标或对象是否点击了某个图形 | -| **Raster vs Vector** | 位图 vs 矢量图,Canvas 是位图,SVG 是矢量图 | - ---- - -## 总结 - -现在你已经掌握了 Canvas 2D 的核心概念: - -- **基本绘图**: 矩形、圆形、线条 -- **样式控制**: 颜色、渐变、阴影 -- **动画制作**: requestAnimationFrame + 清除重绘 -- **交互处理**: 鼠标事件、碰撞检测 -- **性能优化**: 离屏 Canvas、批量渲染 - -**下一步建议**: - -- 如果你想深入学习动画,可以尝试制作一个**贪吃蛇游戏**或**打砖块游戏** -- 如果你对数据可视化感兴趣,可以学习 **ECharts** 或 **D3.js** -- 如果你想做游戏开发,可以尝试 **Phaser.js** 游戏引擎 -- 如果你对 WebGL 感兴趣,可以学习 **Three.js** 或 **PixiJS** - -祝你学习愉快! 🎨 +现在,不管是一把简单的魔法画笔、还是由万千雪花组成的宏大粒子系统,整个能够不断刷新重绘的数字世界引擎,都在你的掌控之中了! diff --git a/docs/zh-cn/stage-2/backend/2.5-zeabur-deployment/extra6/index.md b/docs/zh-cn/stage-2/backend/2.5-zeabur-deployment/extra6/index.md index e58c7f7..e1efb12 100644 --- a/docs/zh-cn/stage-2/backend/2.5-zeabur-deployment/extra6/index.md +++ b/docs/zh-cn/stage-2/backend/2.5-zeabur-deployment/extra6/index.md @@ -41,6 +41,7 @@ |------|------|----------|----------| | **腾讯云 CloudBase** | 国内访问速度快,与微信生态深度整合 | 国内用户为主、需要微信小程序支持的项目 | 有免费额度 | | **Vercel** | 前端框架支持好,与 GitHub 集成紧密 | React/Vue/Next.js 等现代前端项目 | 有免费额度 | +| **Netlify** | 功能全面,支持表单处理和身份验证,与 Git 集成好 | 需要表单处理、身份验证等高级功能的静态网站 | 有免费额度 | | **Zeabur** | 支持多种语言和服务模板,配置灵活 | 需要部署多种服务(如 Dify、n8n)的复杂项目 | 每月约 5 美元免费额度 | --- @@ -162,7 +163,126 @@ Vercel 会自动识别项目类型并配置构建命令: --- -# 3. Zeabur +# 3. Netlify + +Netlify 是另一个非常流行的前端部署平台,与 Vercel 类似,特别适合部署静态网站和单页应用(SPA)。它的特点包括: + +- **功能全面**:除了静态网站托管,还支持表单处理、身份验证、边缘函数等高级功能 +- **与 Git 深度集成**:支持 GitHub、GitLab、Bitbucket,推送代码自动部署 +- **分支预览**:每个分支都会自动生成独立的预览链接 +- **全球 CDN**:网站自动分发到全球节点,访问速度快 +- **表单处理**:无需后端代码即可处理网站表单提交 +- **身份验证**:内置用户身份验证功能,可快速实现登录/注册 + +> ⚠️ **注意**:Netlify 的国内访问速度可能不如 CloudBase,建议主要面向海外用户的项目使用。 + +## 使用 Netlify 部署 Web 应用 + +### 步骤 1:注册账号 + +访问 [Netlify 官网](https://www.netlify.com),点击 "Sign up" 注册。你可以使用 GitHub、GitLab、Bitbucket 或邮箱注册。 + +### 步骤 2:导入项目 + +1. 登录后点击 "Add new site" → "Import an existing project" +2. 选择你的代码托管平台(如 GitHub) +3. 授权 Netlify 访问你的仓库 +4. 从列表中选择你要部署的仓库 + +### 步骤 3:配置构建设置 + +Netlify 会自动识别常见的前端框架并配置构建设置: + +| 框架 | 构建命令 | 发布目录 | +|------|----------|----------| +| React | `npm run build` | `build` | +| Vue | `npm run build` | `dist` | +| Angular | `ng build` | `dist/` | +| Next.js | `next build` | `out` | +| 纯 HTML | - | `.`(项目根目录) | + +如果自动识别不正确,可以手动配置: +- **Build command**: 构建命令,如 `npm run build` +- **Publish directory**: 构建输出目录,如 `dist` 或 `build` + +### 步骤 4:部署 + +点击 "Deploy site" 按钮,等待构建完成。构建成功后,你会获得一个 `xxx.netlify.app` 的域名,任何人都可以通过这个地址访问你的网站。 + +### 步骤 5:配置自定义域名(可选) + +1. 进入站点设置,点击 "Domain management" +2. 点击 "Add custom domain" +3. 输入你的域名并按照提示配置 DNS 记录 +4. Netlify 会自动申请并配置 HTTPS 证书 + +### 特色功能 + +#### 1. 表单处理 + +Netlify 提供了一个非常方便的功能:无需后端代码即可处理表单提交。 + +只需在 HTML 表单中添加 `netlify` 属性: + +```html +
+

+ +

+

+ +

+

+ +

+

+ +

+
+``` + +部署后,表单提交的数据会自动发送到 Netlify 后台,你可以在 "Forms" 页面查看所有提交记录,也可以设置邮件通知或将数据转发到其他服务。 + +#### 2. Netlify Functions(边缘函数) + +Netlify 支持部署无服务器函数(Serverless Functions),让你可以在不搭建完整后端服务器的情况下,实现简单的 API 接口。你可以使用 JavaScript 或 TypeScript 编写函数,部署后会自动获得一个可访问的 URL。 + +例如,创建一个 `hello.js` 文件: + +```javascript +exports.handler = async (event, context) => { + return { + statusCode: 200, + body: JSON.stringify({ message: "Hello from Netlify!" }) + }; +}; +``` + +部署后,你可以通过 `https://你的域名/.netlify/functions/hello` 访问这个函数。 + +#### 3. 本地开发支持 + +Netlify 提供了 CLI 工具,方便你在本地开发和测试: + +```bash +# 安装 Netlify CLI +npm install -g netlify-cli + +# 登录账号 +netlify login + +# 本地启动开发服务器 +netlify dev + +# 本地测试函数 +netlify functions:serve +``` + +使用 CLI 工具可以在本地模拟 Netlify 环境,包括表单提交、函数调用等功能,方便在部署前进行测试。 + +--- + +# 4. Zeabur Zeabur 是一个新兴的部署平台,特别适合需要部署多种服务的复杂项目。它的优势在于: @@ -354,15 +474,17 @@ Zeabur 是一个新兴的部署平台,特别适合需要部署多种服务的 # 总结 -在本教程中,我们介绍了三个常用的 Web 应用部署平台: +在本教程中,我们介绍了四个常用的 Web 应用部署平台: 1. **腾讯云 CloudBase**:适合国内用户,访问速度快,与微信生态整合好 2. **Vercel**:适合现代前端框架项目,与 GitHub 集成紧密,全球 CDN 加速 -3. **Zeabur**:适合复杂项目,服务模板丰富,支持多种部署方式 +3. **Netlify**:功能全面,支持表单处理和身份验证,适合需要高级功能的静态网站 +4. **Zeabur**:适合复杂项目,服务模板丰富,支持多种部署方式 选择哪个平台取决于你的具体需求: - 如果主要面向国内用户,推荐 **CloudBase** -- 如果使用 React/Next.js 等框架,推荐 **Vercel** +- 如果使用 React/Next.js 等框架,推荐 **Vercel** 或 **Netlify** +- 如果需要表单处理、身份验证等高级功能,推荐 **Netlify** - 如果需要部署 Dify、n8n 等服务,推荐 **Zeabur** 无论选择哪个平台,部署的核心流程都是相似的:准备代码 → 选择平台 → 配置构建设置 → 部署上线。掌握这些技能后,你就可以将自己开发的应用分享给全世界了!