feat: 添加多个附录交互式组件和文档更新

- 添加浏览器前端组件:无障碍访问、国际化、实时通信
- 添加 Transformer 注意力机制系列组件
- 更新 Canvas、数据追踪等现有组件
- 修复 ESLint 变量名冲突问题
- 完善相关附录文档
This commit is contained in:
sanbuphy
2026-02-24 08:34:53 +08:00
parent 94f9db0834
commit 260d17ee8b
42 changed files with 5290 additions and 12173 deletions
@@ -1,3 +1,82 @@
# 无障碍与国际化
# 网页的隐藏维度:国际化与无障碍
> 待实现
::: tip 核心导读
**什么是 i18n 及其来龙去脉?**
在前端和软件工程领域,我们常说的 **i18n** 其实就是指 **多语言支持(国际化,Internationalization**。因为这个英文单词的首字母 `i` 和尾字母 `n` 之间恰好相隔了 18 个字母,为了书写简便,业界便发明了这个特定的缩写。
同理,**无障碍访问(Accessibility** 也因为首字母 `a` 与尾字母 `y` 之间有 11 个字母,因此被统称为 **a11y**
在浏览器将代码渲染出五彩斑斓的网页背后,其实还并行着两条肉眼往往看不见的“暗线”:
当你输入网址访问网页时,浏览器怎么知道该给你展示中文还是德文(即 i18n 多语言流程)?在浏览器将 HTML 解析成 DOM 树准备画图的同时,又是如何专门为视障人士构建出另一棵“盲文树”的(即 a11y 无障碍流程)?
本章我们将再次回到“网页访问与渲染”的微观流程中,解码浏览器及前端工程在这两个体现技术人文关怀的领域是如何默默工作的。
:::
---
## 1. 网页访问中的语言协商 (i18n)
当我们输入一个网址、按下回车,浏览器在向服务器发送 HTTP 请求时,通常会默默附带一个头信息:`Accept-Language`
- *例如:`Accept-Language: zh-CN,zh;q=0.9,en;q=0.8`*
这就好比你在餐厅点单前,浏览器私下对服务员说:“我的主人优先看简体中文,如果没有的话,英文也凑合能看。” 这就是 Web 访问时的**初次协商**。
### 1.1 前端工程与字典替换
而在现代前端框架中,页面的骨架通常是由 JavaScript 在本地动态生成的。在这个阶段,前端应用会主动读取浏览器的本地偏好(例如通过 `navigator.language` API),然后从服务器按需拉取对应的语言“字典包(JSON)”——遇到中文显示“确定”,遇到英文字典则显示“Confirm”。
### 1.2 排版的深渊:文字长度与 RTL 镜像
但除了字典替换,真正的国际化在浏览器布局(Layout)阶段面临着深渊般的挑战。
不同的语言表达相同的含义时,所需的字母长度可能天差地别。例如德语常将多个词根拼接成巨长的单词。如果我们在编写 CSS 时使用绝对固定宽度,很容易在切换德语时出现文字撑破容器的惨状。因此浏览器鼓励使用弹性盒模型(Flexbox)来自适应不同的文字体量。
更为颠覆的挑战在于阅读方向。阿拉伯语(Arabic)、希伯来语(Hebrew)等语言的阅读习惯是**从右向左(Right-to-Left, 简称 RTL**。
当页面切换到这类语言时,不仅仅是文本方向要变,**浏览器引擎还需要对整个网页的内容块进行水平方向的镜像反转**!浏览器为此提供了原生的 `dir="rtl"` 属性。我们在编写 CSS 时,应当避免使用绝对的方向词,例如用 Flexbox 的 `justify-content: flex-start` 来替代硬编码的 `margin-left`,从而让浏览器能够随着区域切换自动化反转布局。
### 1.3 告别正则:拥抱浏览器的 Intl 标准
除了界面排版,浏览器底层还自带了一个强大的“本地化格式引擎”。
对于同样的数字 `1200.5`,美国人习惯看到 `$1,200.50`,而欧洲许多国家习惯用逗号做小数点 `€ 1.200,50`。日期格式更是千奇百怪。
现代浏览器暴露了 **`Intl` 核心对象**(例如 `Intl.DateTimeFormat``Intl.NumberFormat`)。依靠这个 API,我们在代码里只要指明当前环境代号,浏览器便会直接调用底层的操作系统数据规范,准确生成符合当地习惯的展示字符串。
👇 操作下方组件,观察在不改变源数据的前提下,浏览器是如何通过底层 API 完成布局反转(RTL)与系统级数据转换的:
<InternationalizationDemo />
---
## 2. 浏览器内部的无形之树 (a11y)
回到浏览器渲染引擎。我们都知道,浏览器解析 HTML 时会生成一棵 **DOM 树**,然后再结合 CSS 计算生成用于绘制界面的**渲染树 (Render Tree)**。
但鲜为人知的是,在网页访问时,浏览器实际上还在并行构建一棵专供操作系统“看”的树——**AOM 树(Accessibility Object Model,无障碍对象模型)**。
### 2.1 屏幕阅读器与语义化的本质
为了让视力障碍用户使用计算机,操作系统内置了**屏幕阅读器(Screen Reader**辅助软件(如 macOS 的 VoiceOver)。这类软件“看不见”屏幕的颜色像素,它们**完全依赖浏览器暴露出来的 AOM 树来朗读网页**。
如果开发者用普通 `<div>` 标签加 CSS 样式,画出了一个外观无可挑剔的按钮,在常规的渲染树中它是完美的。但在屏幕阅读器连接的 AOM 树中,它只是一个毫无意义的纯文本节点。视障用户既无法听到“按钮”提示,也无法用 `Tab` 键选中它。
因此,为何我们要反复强调**“坚持使用语义化的 HTML 标签”**?因为当你使用 `<button>``<nav>``<a>` 标签时,浏览器引擎会自动在 AOM 树里补全它们内置的焦点管理与角色(Role)信息。语义化,本质上是给视障工具绘制出的高质量蓝图。
### 2.2 WAI-ARIA:手动修剪 AOM 树
在现代 Web 应用中,有很多复杂的定制交互组件(例如弹窗面板、带开关动画的手风琴菜单),浏览器原生标签无法完全覆盖。此时就需要利用 **WAI-ARIA** 规范。
ARIA 本质上是一组特殊的 HTML 属性,**它们不会改变任何视觉呈现,唯一的使命就是向浏览器发送强行修改 AOM 树节点的指令**:
- `aria-label`:给缺失可见文字的元素补充朗读说明(例如仅一个图标的“关闭”按钮)。
- `aria-hidden="true"`:告诉浏览器,这个节点仅具装饰性,不要将它塞入 AOM 树中。
- `role="alert"`:告诉浏览器这个区域极其关键,如果其内容刷新,需要立刻打断当前的语音阅读器进行插播。
👇 体验以下通过 AOM 树感知到的两个截然不同的“世界”:
<AccessibilityDemo />
---
## 3. Web 为所有人服务
结合我们在前面章节所学的网络层与浏览器渲染知识,我们可以重新理解这个宏大的图景:
| 网页访问维度 | 浏览器与工程师共同的职责 | 想要消弭的鸿沟 |
| :--- | :--- | :--- |
| **国际化 (i18n)** | 通过请求头协商、基于 Intl API 格式化、弹性支持 RTL 布局镜像反转。 | 跨越**语言与文化的鸿沟**,让应用能够无缝匹配不同国家的语言规范及排版直觉。 |
| **无障碍访问 (a11y)** | 除了构建渲染树,还要基于语义化 HTML 和 ARIA 规范构建高清晰度的 **AOM 树**。 | 跨越**生理与设备的鸿沟**,将控制权平滑地交接给屏幕阅读器等辅助工具。 |
真正的资深工程师,在其代码编译出绚丽界面的背后,依然精心雕琢着那些看不见的通信头和语义树,使得 Web 的能量能辐射至使用着完全不同语言或操作设备的每一种普通人。这就是 Web 作为全球最大平台最底气十足的人文底色。
@@ -2,7 +2,9 @@
::: tip 🎯 核心问题
以前的网页只能展示干巴巴的文字和图片。但如果你想做打砖块游戏、华丽的动态特效、或是可以自由拖拽的数据报表呢?这就是 **Canvas(画布)** 诞生的原因。
以前的网页只能展示干巴巴的文字和图片。但如果你想做打砖块游戏、华丽的动态特效、或是可以自由拖拽的数据报表,仅仅靠 `<div>` 是远远不够的。这就是 **Canvas(画布)** 诞生的原因。
本指南将带你从画下第一条线开始,一路打怪升级,最终亲手写出能在浏览器中流畅运行 60 帧的粒子引擎。
:::
@@ -10,52 +12,53 @@
## 1. 什么是 Canvas
如果说早期的那些 HTML 标签(如 `<div>``<img>`)是用**乐高积木**拼起一个静态的网页,那么 HTML5 的 `<canvas>` 标签就是扔给你一张**巨大的数字白纸**,然后递给你一支靠代码控制的**画笔**,剩下的全交给你自由发挥。
如果说早期的网页是用**乐高积木**(HTML 标签)拼凑起来的静态模型,那么 HTML5 的 `<canvas>` 标签就是扔给你一张**巨大的数字白纸**,然后递给你一支靠代码控制的**画笔**,剩下的全交给你自由发挥。
这里面的画没有任何标签结构你用画笔涂上去的心血,一旦落笔就变成了最纯粹的**“像素颜料”**。
这里面的画没有任何标签结构你用画笔涂上去的心血,一旦落笔就变成了最纯粹的**“像素颜料”**。
### 1.1 Canvas vs SVG:两种不同流派的艺术家
在前端画图界,Canvas 有个宿敌叫 **SVG**。它们代表了两种截然不同的绘画观念:
**Canvas(位图画板):**
* **原理**:就像真实在纸上涂色,几笔画上去就变成一团颜料。
* **优势**:电脑只管往屏幕上“洒颜料”,性能起飞!能同时画出大几千个活蹦乱跳的闪烁粒子。
* **缺点**:画完就没法单独反悔(没法 DOM 直接选择),而且你用浏览器一旦放大,画面就会马赛克发虚。
**SVG(矢量图拼接):**
* **原理**:就像在做幻灯片(PPT)。你画一个圆,它就生成一个圆圈的“实体对象”放在画面上
* **优势**:不管被放大成 100 倍还是 10 万倍,永远极其清晰。而且因为每一个形状都是一个独立标签,你可以在任何时候用鼠标点中某个小正方形,命令它换一种颜色
* **缺点**:如果你试图放几万个对象乱飞,繁重的排版引擎会直接把浏览器卡死。
- **Canvas(位图画板):**
- **原理**:就像真实在纸上涂色,几笔画上去就变成一团颜料(像素点)
- **优势**:电脑只管往屏幕上“洒颜料”,性能起飞!能同时画出大几千个活蹦乱跳的闪烁粒子。
- **缺点**:画完就没法单独反悔(没法通过 DOM 节点选择),且放大会造成马赛克发虚。
- **SVG(矢量图拼接):**
- **原理**:就像做 PPT。你画一个圆,它就生成一个独立标签的“圆实体”放在画面上。
- **优势**:不管放大 100 倍还是 10 万倍,永远极其清晰。每个形状都是独立的 DOM 节点,你可以随时用 CSS 和 JS 改变它的颜色或绑定点击事件
- **缺点**:如果你试图放几万个对象乱飞,繁重的 DOM 树和排版引擎会直接把浏览器卡死
**🎮 简单总结:玩动态游戏、做酷炫粒子特效用 Canvas;画精密的 Logo、写交互清晰的小图表用 SVG。**
---
## 2. 第一笔:用代码找坐标
## 2. 第一笔:理解反直觉的坐标
### 2.1 这张纸的上下怎么颠倒了?
当你准备下笔时,得先明白 Canvas 里的尺子是反着的。对于传统的数学课坐标系,中心点零点在中间,越往上越大。
当你准备下笔时,得先明白 Canvas 里的尺子是反着的。对于传统的数学课坐标系,中心点零点在中间,越往上越大。但在计算机屏幕显示领域,几乎所有设备的“原点(0,0)”都定在**屏幕的最左上角**。向右走 X 轴变大没问题,但是**向下走,Y 轴变大。**
但在屏幕显示领域,几乎所有设备的“原点(0,0)”都定在**屏幕的最左上角**。向右走 X 轴变大没问题,但是**向下走,Y 轴变大。**
**Canvas 坐标系统的核心原则:**
- **原生单位:** 像素 (px),与屏幕物理像素 1:1 对应。
- **X 轴:** 向右为正方向,从 `0``canvas.width`
- **Y 轴:** 向下为正方向,从 `0``canvas.height`
👇 **动手点点看**
拖拽下面的这些点,直观地感受一下坐标是如何变化的:
👇 拖拽下面的小圆点,直观感受计算机图形学中的坐标原点与走向
<CoordinateSystemDemo />
### 2.2 给你的魔法画笔上调料
有了坐标,我们就能召唤那支画笔了(代码里这支笔叫 `Context` 或简称 `ctx`)。
有了坐标体系,我们就能召唤画笔了(代码中称为 `Context`,或缩写 `ctx`)。就如同拿着真实的调色盘作画,Canvas 的 API 设计完美遵循了物理作画的三个步骤:
就像拿着调色盘作画,流程总是固定的三步:
1. **调色**:告诉它你需要什么填充色(`fillStyle`)和描边色(`strokeStyle`
2. **构形**:构思你是画一个圈、还是一条直线?
3. **下笔**:实打实地去填充(`fill( )`)还是去勾勒边缘(`stroke( )`
1. **调色(State**:通过 `fillStyle` 设置填充色,`strokeStyle` 设置描边色。
2. **构形(Path**:构思你是要画一条线(`lineTo`)、还是一个圆(`arc`)、亦或一个矩形(`rect`
3. **极简下笔(Render**:决定是内部填充(`fill()`)还是勾勒边缘(`stroke()`)。
👇 **动手点点看**
试试把下面代码面板里的形状颜色换换:
由于 Canvas 是纯粹的位图画布,“落子无悔”,你一旦画下,它立刻干涸成为像素,无法再被撤销为独立对象。
👇 尝试在下面的演示中挑选不同形状和颜色,看看背后的代码是如何执行上述“三步走”的:
<CanvasBasicsDemo />
@@ -63,38 +66,36 @@
## 3. 翻页动画书:如何让画面动起来极度丝滑
我们刚才说过,Canvas 一旦你填上了颜色,这就变成了永久的马赛克。你怎么可能让马赛克奔跑呢
既然 Canvas 一旦填色就变成了永久的像素,那么各种 HTML5 页游里满屏乱跑的角色是怎么做出来的
**答案是“骗过你的眼睛”。这和翻页手翻书或者电影胶片的原理一模一样。**
答案是**“骗过你的眼睛”**。这和手翻动画书或者电影胶片的原理一模一样。
如果你想让一个球飞起来:
1. **擦黑板**:用 `clearRect` 把这整块画布上的内容毫不留情地清空!
2. **挪位置**:让那个球的 X 坐标往前偷偷加 2 毫米
3. **下笔重画**:把球在新的位置重新画一次
4. **疯狂循环**:浏览器内置了一个极其精准的神仙秒表叫 `requestAnimationFrame`。它会以每秒 60 次(即 60 FPS)的变态速度,重复着【擦除 -> 移动 -> 重绘】。由于人眼自带“视觉残留”,你在屏幕上看到的,不仅不是黑板被擦,反而是如同丝绸般顺滑的动画。
1. **擦黑板(Clear):**`clearRect()` 把整块画布上的内容毫不留情地清空。
2. **计算新位置(Update):** 让角色的 X 坐标往前偷偷加 2 个像素点。
3. **下笔重画(Render):** 把角色在新的位置重新画一次
4. **疯狂循环(Loop):** 结合浏览器内置的极其精准的节拍器 `requestAnimationFrame`。它会以显示器的刷新率(通常是每秒 60 次,即 60 FPS)重复这三个动作
👇 **动手点点看**
尝试添加或者减少物体的数量,感受每秒 60 帧带来的无缝快感:
由于人眼自带“视觉残留”,在每秒 60 次的【擦除 -> 更新 -> 重绘】中,你看到的不仅不是闪烁的黑板,反而是如同丝绸般顺滑的动画。
👇 在下方的演示中调整播放速度,观察每一帧的位移是如何连缀成流畅运动的:
<AnimationLoopDemo />
---
## 4. 瞎子摸象:在 Canvas 里面怎么点击?
## 4. 瞎子摸象:在 Canvas 里面怎么点击交互
因为 Canvas 画布只是一张没有任何结构的“颜料布”。假设你在这个布上画了一只哥布林:
因为 Canvas 画布在浏览器眼里只是一张没有任何结构的“颜料布”。假设你在画布上用 `arc()` 画了一只怪兽,当你想要实现“点击怪兽扣血”时,你**根本没法**使用传统的 `document.getElementById` 来获取这个怪兽。因为在 HTML 结构中,只有那个宽 600 像素的死板 `<canvas>` 标签。
如果你想写个代码:“当玩家点中了哥布林,哥布林阵亡”。你根本没法像写普通网页那样通过 `getElementById` 去直接绑定这个外星怪物。因为在浏览器的眼里,**这里永远没有任何怪兽,只有一块宽 600 高 400 的 `<canvas>` 标签死死挡在这里**。
这就是图形编程中最经典的问题:**碰撞检测 (Collision Detection) 与事件代理**。
那我们要怎么做事件交互呢?
1. **监听布面被点**:先获取你目前鼠标点在这个死板的 HTML 大布的哪个具体的 XY 位置
2. **拿账本去对**:然后你必须自己翻你的代码记录,“我记得刚刚我在(100,100)的位置画了一个半径 50 的哥布林”
3. **勾股定理**:我们用初中教的勾股定理公式去疯狂计算——当前鼠标点击的位置,是不是落在了那个(100,100)距离 50 半径的圆内?。
由于浏览器只知道你的鼠标点击了 Canvas 的屏幕坐标 `(x, y)`,你需要自己去通过初中的几何数学进行反算:
- **对于圆形:** 通过勾股定理计算 `鼠标点击处``圆心位置` 的距离,如果距离小于半径,则说明“被点中了”
- **对于矩形:** 判断点击的 `x` 是否在矩形的左右边界内,同时 `y` 是否在上下边界内
恭喜你!这种疯狂算几何数学距离的方法就是你在各大 3A 游戏里听过的 **“碰撞检测 (Collision Detection)”**
无论你的画布上有多少元素,鼠标悬停或点击事件永远是绑定在 Canvas 这个唯一容器上的,这就是终极的“事件委托”。
👇 **动手点点看**
打开最下面的“Hover 悬停模式”,你就能看到它内部拼命去算距离有多累了。
👇 试着在下面使用鼠标(点击、拖拽、悬停)或键盘(方向键移动),体会这种“手动算距离”的底层交互逻辑
<EventHandlingDemo />
@@ -102,12 +103,16 @@
## 5. 解放算力:粒子系统与视觉魔法
到了这一步,当你把【坐标不断重绘的动画】跟【颜色和大小变换】融合,再放进成百上千个小碎片里。这就是引爆视觉的终极杀**粒子系统**。
到了这一步,当我们把“坐标系”、“动画循环”以及“颜色与形状”全部融合,并将其数量暴增到成百上千个小碎片时,你就掌握了引爆视觉的终极杀**粒子系统Particle System**。
你只需要建立一个巨大的数组,里面塞满了几百个拥有独立生命值、独立初始随机速度的数字对象。每次“重绘”,让所有的点根据重力或者惯性去减速。你的浏览器里马上就能发生逼真的大爆炸或者漫天飞雪。
其核心思路极其粗暴且有效:
1. 建立一个巨大的数组,里面塞满了几百个独立的“粒子对象”。
2. 每个对象拥有自己的独立生命周期(`life`)、加速度(`vx/vy`)、重力阻尼(`gravity`)。
3. 每次 `requestAnimationFrame` 触发时,遍历更新这几百个粒子,然后渲染,最后悄悄清理掉那些“死亡”(生命值耗尽/掉出屏幕)的粒子。
👇 **动手点点看**
试试“烟花”和“鼠标轨迹”!
你的浏览器一瞬间就能变成一台制造烟花、大雪和爆炸的梦工厂。
👇 点击不同的效果,调整重力与粒子数,观察它们是如何通过最简单的物理数学公式呈现出复杂的群体视觉:
<ParticleSystemDemo />
@@ -115,32 +120,35 @@
## 6. 守护 FPS 荣耀:如何应对高烧的 CPU?
让成千上万个对象在一秒内计算重画 60 遍,这是极其消耗电脑算力(CPU 和内存)的
很多野生小白刚做出来的游戏玩了两分钟可能风扇就起飞了。下面是真正的引擎大佬使用的降温护体绝技:
让成千上万个对象在一秒内计算重画 60 遍是非常消耗性能的。如果毫无章法,你的电脑风扇很快就会起飞
1. **局部擦黑板(脏矩形 Dirty Rect)!** 一个角色在一望无际的草原上奔跑。你千万别每帧把整块大草原都擦了重画!角色经过哪一小块,你就用小板擦把哪里擦掉然后只补哪里的洞,这能省下几千倍的力气。
2. **隐藏后台魔法(离屏 Canvas)!** 如果游戏背景是繁星漫天、有各种复杂绚丽的山脉。最好先偷偷在没人的后台建一个内存 Canvas 把它一次性精美地画上去。以后每秒 60 下的刷新,你直接把这幅“定格全图”通过贴图的方式贴到前端(`drawImage`)就行了。
3. **批量洗画笔!** 如果画画时你要反复交替使用“红、蓝、红、蓝、红”这几种笔,频繁切换。可以提前把所有红色的兵全归档画完,再清空换蓝颜料画,省去了昂贵的上下文来回切换。
以下是真正引擎大佬用来抢救帧率的“护体绝技”:
👇 **动手点点看**
先把对象数量拉满,看着网页快掉进卡顿的深渊,再依次打开右下方的绝技进行抢救
1. **局部擦黑板(脏矩形 Dirty Rect):**
一个角色在宽广的草原上奔跑,你千万不要每帧去 `clearRect` 整片大草原!角色经过哪一小块,你就用“小板擦”擦掉那一块并覆盖重绘,性能立刻飙升指数倍
2. **后台替身魔法(离屏 Canvas):**
如果背景是繁星漫天、有着各种复杂绚丽的山脉,每次都实时渲染太蠢了。我们通常在内存里偷偷建一个看不见的 `<canvas>`,把它精美地画上去一次。之后的每一帧刷新中,只需要通过 `drawImage()` 将这张合成好的“静态底片”直接贴出,免去了海量的基础计算。
3. **批量洗画笔(Batching):**
调色盘里从红色换到蓝色,在底层是昂贵的。如果画布上有 1000 个红色圆和 1000 个蓝色圆交叉散落。最快的方法是:先把红颜料准备好,遍历画完所有红圈,再换蓝颜料画所有蓝圈。这是著名的批量渲染(Batch Rendering)思想。
👇 将对象数量拉到 3000 以上,看着网页掉进卡顿的深渊,再依次打开右下方的“优化技术”开关,亲眼见证实打实的帧率抢救:
<PerformanceDemo />
---
## 7. 名词对照表
## 7. 专业名词总结
| 术语 | 解释 |
| 术语 | 通俗解释 |
| --- | --- |
| **Canvas** | Html5 提供的 2D 画布。绘制极快,但画完就变成颜料像素,不支持通过 DOM 操作内容。 |
| **SVG** | 矢量图放大永远不模糊,且每个图形都是独立的标签元素可以单独点击绑定事件。 |
| **Context (ctx)** | 获取到的“2D 上下文”,可以理解为用来在这张布上调各种颜色、干各种特殊效果的“画笔”。 |
| **requestAnimationFrame** | 浏览器内置的神级节拍器,会显示器的刷新率(通常 60FPS)不断狂飙执行,专门用来做完美动画。 |
| **FPS / Frame Rate** | 帧率。60 FPS 代表一秒内浏览器帮我们默默擦除了 60 次黑板并画了 60 副新图,这骗过了视神经,看起来极其丝滑。 |
| **Dirty Rect / 脏矩形** | 只在画面中发生变化的微小矩形区域内进行擦除和重绘,强力保留性能。 |
| **Offscreen Canvas** | 藏在内存里的“影子画布”,把静态且复杂的树木和山脉先画好,当作死的一张贴图重复用。 |
| **Canvas** | HTML5 提供的 2D 画布。绘制极快,但画完就变成颜料像素,不支持通过 DOM 操作内容。 |
| **SVG** | 矢量图放大永远不模糊,且每个图形都是独立的标签元素可以轻易绑定各种 CSS 样式和交互。 |
| **Context (ctx)** | 你申请到的那支“2D 魔法画笔”,用来调色、设定形状和绘制各种特殊效果。 |
| **requestAnimationFrame** | 浏览器内置的神级节拍器,会严格依照显示器的刷新率执行回调,是制作丝滑动画的不二之选。 |
| **FPS (Frame Rate)** | 帧率。60 FPS 代表一秒内浏览器帮你无缝擦除了 60 次画布并重画了 60 副新图。 |
| **脏矩形 (Dirty Rect)** | 只在发生变化的那一点微小区域内进行精准擦除和重绘,从而强力保留性能。 |
| **离屏 Canvas** | 藏在内存里的“影子画布”。把极度复杂但不会动的景物提前画好,以后就当死贴图拿来重复使用。 |
---
现在,不管是一把简单的魔法画笔、还是由万千雪花组成的宏大粒子系统,整个能够不断刷新重绘的数字世界引擎,都在你的掌控之中了!
> 从一条简单的直线段,到宏大绚丽的粒子系统引擎;一切看似魔法的特效,不过是每秒 60 次的坐标计算与重绘轮回罢了。
@@ -1,3 +1,75 @@
# 实时通信WebSocket / SSE
# 实时通信机制(Polling / SSE / WebSocket
> 待实现
::: tip 核心导读
**浏览器如何实现数据的实时更新?**
传统的 HTTP 协议基于“请求-响应”模型,客户端必须主动发起请求,服务端才能返回数据。如果我们需要实现聊天室、股票行情推送等实时场景,这种模型将面临挑战。
本章将介绍前端应对实时数据通信的三种主要技术:短轮询(Polling)、服务器推送事件(SSE)与全双工 WebSocket,并探讨它们的原理与适用场景。
:::
---
## 1. 传统 HTTP 的局限性
HTTP 协议的设计初衷是用于文档检索,它具有**无状态(Stateless)**和**由客户端单向发起**的特点:
1. 客户端发起 HTTP 请求。
2. 服务端处理请求并返回响应。
3. 连接完成任务后通常会释放对应的逻辑请求(HTTP/1.1 虽然支持长连接复用,但业务层面的请求-响应模型并未改变)。
在此模式下,服务端无法主动将状态的改变随时通知正在等待的客户端。为了获取最新数据,必须寻找其他技术架构方案。
---
## 2. 短轮询(Polling
最直接的解决方案是**短轮询**。即客户端利用定时器(如 `setInterval`),每隔一段固定的时间,自动向服务端发送 HTTP 请求,询问是否有新数据到达。
<PollingDemo />
**技术特点与局限:**
- **优点**:实现机制极其简单,完全依赖标准的 HTTP 协议和 AJAX/Fetch 技术。
- **缺点**:可能产生巨大的网络开销与资源浪费。大多数时间里,服务端的响应可能是“无新数据”。无论有无数据,每次请求都需要携带完整的 HTTP 头部(Headers、Cookies 等),在并发量较高的场景下,会导致网络资源被大量无意义的查询占据。
---
## 3. 服务器推送事件(Server-Sent Events
为了降低频繁建立 HTTP 连接的开销,**Server-Sent Events (SSE)** 提供了一种轻型的单向数据流推送架构。
SSE 建立在 HTTP 协议之上。客户端发起一个包含特殊请求头(`Accept: text/event-stream`)的 HTTP 请求后,服务端在返回响应时会保持底层的 TCP 连接不断开。随后,服务端可以通过这条持久的通道,持续不断地向客户端推送文本格式的数据。
<SSEDemo />
**技术特点与局限:**
- **优点**:连接持久化,网络开销小;浏览器原生支持断线自动重连机制;非常适合从服务端向客户端**单向**传输流式数据(例如大语言模型的文本逐字输出、实时交易行情推送)。
- **缺点**:通信通道是单向的。如果客户端需要向服务端发起控制指令或发送新数据,必须另外建立普通的 HTTP 请求。
---
## 4. WebSocket:全双工通信协议
当应用场景涉及高频的双向交互(如多人在线动作游戏、精密的协同文档编辑)时,我们需要一种既能降低通信开销,又能实现真正双工通信的技术——**WebSocket**。
WebSocket 是一种独立的网络通信协议。它精妙地借助了 HTTP 协议来完成初始建连:
1. **握手阶段**:客户端发送一个特殊的 HTTP 请求,声明希望将其升级为新协议(携带 `Upgrade: websocket` 头部)。
2. **连接质变**:服务端若支持并同意该协议,则回复 `101 Switching Protocols` 状态码。
3. **彻底自由**:此时 HTTP 的规范使命结束,底层的 TCP 连接被移交给 WebSocket 协议。此后,客户端与服务端享有平等的全双工(Full-Duplex)通信权利,双方可随时收发极简格式的数据帧。
<WebSocketDemo />
**技术特点与局限:**
- **优点**:支持真正意义上的双向实时通信;数据帧的头部信息极小,通信延迟低、吞吐效率高;支持原生二进制数据(ArrayBuffer)的传输。
- **缺点**:架构与开发复杂性较高;由于维护着持久长连接,对服务器端的系统架构、负载均衡策略和心跳监测设计提出了更严格的工程要求。
---
## 5. 总结:技术选型对比
| 维度 | 短轮询 (Polling) | 服务器推送事件 (SSE) | WebSocket |
| :--- | :--- | :--- | :--- |
| **通信方向** | 客户端主动轮询拉取 (单向) | 服务端持续主动推送 (单向) | 客户端与服务端享有平等收发权 (双向全双工) |
| **底层协议** | 标准 HTTP | 标准 HTTP | 独立的 WebSocket 协议 (基于 TCP) |
| **数据开销** | 极高 (包含完整的 HTTP 头部) | 较低 | 极低 (极简的数据帧头部) |
| **典型应用场景** | 定时检查后台异步任务的完成状态 | 大模型对话单向流输出、新闻或系统通知推送 | 实时音视频信令、多人在线对战、协同白板与编辑 |
在实际工程中,开发者应依据具体业务场景对实时性与双向交互频率的要求,在系统的维护复杂度和通信效率之间取得平衡,选择最契合的技术栈。