① HTML — 网页的骨架

HTML(HyperText Markup Language)是一种标记语言,用语义化标签描述网页结构。 浏览器解析 HTML 生成 DOM 树,是渲染流程的起点。

1.1 渲染原理

  1. 解析 HTML → 构建 DOM 树
  2. 解析 CSS → 构建 CSSOM 树
  3. 合并生成 渲染树(Render Tree)
  4. 布局(Layout / Reflow):计算节点几何信息
  5. 绘制(Paint):将像素填充到图层
  6. 合成(Composite):交给 GPU 合成为最终画面

1.2 语义化标签示例

<header>头部</header>
<nav>导航</nav>
<main>
  <article>文章</article>
  <aside>侧栏</aside>
</main>
<footer>底部</footer>

语义化的优势:可访问性(无障碍)、SEO 友好、代码可读性强。

1.3 实战演示:表单与验证

② CSS — 视觉与布局

CSS(Cascading Style Sheets)控制元素的外观与排版。核心机制: 层叠(Cascade)继承(Inheritance)特异性(Specificity)

2.1 盒模型(Box Model)

每个元素由 content → padding → border → margin 四层组成。

margin
border
padding
content

通过 box-sizing: border-box 可让 width 包含 padding+border,更易布局。

2.2 Flexbox 演示(可调)

1
2
3
4

2.3 Grid 网格布局

A · header
B · sidebar
C · main
D · footer
.grid-stage {
  display: grid;
  grid-template-columns: 200px 1fr;
  grid-template-areas:
    "a a"
    "b c"
    "d d";
}

2.4 动画与过渡

原理:transformopacity 由合成线程处理,不触发重排,性能最佳。

③ JavaScript — 让页面活起来

JavaScript 是一门单线程、基于原型、动态弱类型的语言,运行于 V8 等引擎之上,通过事件循环实现并发。

3.1 数据类型

  • 原始类型:string / number / boolean / null / undefined / symbol / bigint
  • 引用类型:object(函数、数组、Date 等都是对象)
  • 检测:typeof(无法区分 null / 数组);推荐 Object.prototype.toString.call(x)

3.2 闭包(Closure)

函数与其词法作用域的组合。即使外部函数已返回,内部函数仍能访问其变量。

function counter() {
  let n = 0;
  return () => ++n;
}
const inc = counter();
inc(); // 1
inc(); // 2
0

3.3 原型链

每个对象都有一个隐式原型 __proto__ 指向其构造函数的 prototype,访问属性时沿链向上查找直到 null

function Animal(name) { this.name = name; }
Animal.prototype.say = function () { return 'I am ' + this.name; };
const a = new Animal('Tom');
a.say();              // I am Tom
a.__proto__ === Animal.prototype; // true

3.4 事件循环(Event Loop)

同步代码 → 微任务(Promise.then / queueMicrotask)→ 宏任务(setTimeout / I/O)→ 渲染 → 下一轮。


      

3.5 异步:Promise & async/await

async function loadUser(id) {
  const res = await fetch('/api/user/' + id);
  if (!res.ok) throw new Error('Network');
  return res.json();
}

      

3.6 防抖 & 节流

移动鼠标查看触发次数,对比效果:

在此区域内移动鼠标
原始:0 防抖(300ms):0 节流(300ms):0

④ 现代框架 — React / Vue

框架本质:用声明式 UI + 响应式数据替代手动操作 DOM。底层都基于虚拟 DOMDiff 算法

4.1 虚拟 DOM 原理

  1. 用 JS 对象描述 UI 结构(VNode)
  2. 状态变化时生成新的 VNode 树
  3. 通过 Diff 算法对比新旧树,得到最小变更集(Patch)
  4. 仅更新真实 DOM 中变更的节点

4.2 React 示例(JSX)

import { useState } from 'react';

export function Counter() {
  const [n, setN] = useState(0);
  return (
    <button onClick={() => setN(n + 1)}>
      点击次数:{n}
    </button>
  );
}

4.3 Vue 3 示例(Composition API)

<script setup>
import { ref } from 'vue';
const n = ref(0);
</script>

<template>
  <button @click="n++">点击次数:{{ n }}</button>
</template>

4.4 用原生 JS 实现一个迷你响应式

双向绑定输入框:

实时输出:

function reactive(obj, onChange) {
  return new Proxy(obj, {
    set(t, k, v) { t[k] = v; onChange(k, v); return true; }
  });
}
const state = reactive({ msg: '' }, (k, v) => render(v));

⑤ 工程化 — 让项目可维护

现代前端工程涉及:包管理、模块化、构建打包、代码规范、测试、CI/CD。

5.1 模块化演进

  • IIFE 立即执行函数 → 全局污染解决方案
  • CommonJS(Node.js)→ require / module.exports
  • AMD / CMD(浏览器异步)
  • ES Modules(标准)→ import / export

5.2 构建工具

  • Webpack:万物皆 Loader / Plugin
  • Vite:基于 ESM + esbuild,秒级冷启动
  • Rollup:库打包首选,Tree-shaking 出色
  • esbuild / SWC:底层 Go/Rust 加速器

5.3 包管理

  • npm / yarn / pnpm
  • pnpm 通过硬链接节省磁盘 + 严格依赖隔离
  • package.json^ / ~ 的语义

5.4 代码规范

  • ESLint:JS/TS 静态检查
  • Prettier:统一格式化
  • Husky + lint-staged:提交前自动校验
  • TypeScript:类型安全的 JavaScript

⑥ 性能优化 — 极致体验

6.1 关键指标(Core Web Vitals)

  • LCP(最大内容绘制)≤ 2.5s
  • FID / INP(交互延迟)≤ 100ms / 200ms
  • CLS(累积布局偏移)≤ 0.1

6.2 优化清单

加载优化

  • 资源压缩(gzip / brotli)
  • HTTP 缓存(强缓存 + 协商缓存)
  • CDN 分发 / HTTP/2 多路复用
  • 代码分割 + 路由懒加载
  • 图片懒加载 + WebP/AVIF

运行时优化

  • 虚拟列表渲染长列表
  • 避免强制同步布局
  • 使用 transform 实现动画
  • Web Worker 卸载主线程
  • 合理使用 requestIdleCallback

6.3 实测:requestAnimationFrame 帧率监测

FPS:--

⑦ 进阶与新技术 — 现代 Web 能力

浏览器早已不只是「文档查看器」。下面这些 API 让 Web 拥有了实时通信、多线程、原生级性能、离线能力与硬件访问。

7.1 WebSocket — 全双工实时通信

基于 TCP 的持久化双向协议,握手通过 HTTP Upgrade 完成,之后客户端与服务器均可主动推送消息, 典型场景:聊天、协同编辑、实时行情、游戏同步。

const ws = new WebSocket('wss://echo.example.com');

ws.onopen    = () => ws.send('hello');
ws.onmessage = (e) => console.log('收到:', e.data);
ws.onclose   = () => console.log('连接关闭');
ws.onerror   = (err) => console.error(err);

下方演示使用本地模拟服务端(Promise + setTimeout),离线也能体验消息往返:

对比:HTTP 轮询(高延迟、资源浪费)→ 长轮询 → SSE(服务端单向推送)→ WebSocket(双向实时)

7.2 Server-Sent Events (SSE) — 服务端单向推送

基于 HTTP,服务器持续向客户端推送 text/event-stream。比 WebSocket 简单, 自动断线重连。AI 流式输出(如 ChatGPT 打字效果)就是典型应用。

const es = new EventSource('/api/stream');
es.onmessage = (e) => console.log(e.data);
es.addEventListener('done', () => es.close());

      

7.3 Web Workers — 浏览器多线程

JavaScript 主线程负责 UI 与脚本,耗时计算会卡顿页面。Worker 在独立线程运行,通过 postMessage 与主线程通信,无 DOM 访问权限。

// main.js
const worker = new Worker('/worker.js');
worker.postMessage(40);
worker.onmessage = (e) => console.log('结果:', e.data);

// worker.js
self.onmessage = (e) => {
  const fib = (n) => (n < 2 ? n : fib(n - 1) + fib(n - 2));
  self.postMessage(fib(e.data));
};

对比时观察右上角小球是否依然丝滑:


      

7.4 Service Worker & PWA — 离线可用 / 可安装

Service Worker 是浏览器与网络之间的代理层,可拦截请求、读写 Cache,实现离线访问、推送通知、后台同步。 配合 manifest.json 即可让网站像原生 App 一样安装到桌面。

// 注册
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js');
}

// sw.js
self.addEventListener('install', (e) => {
  e.waitUntil(
    caches.open('v1').then((c) => c.addAll(['/', '/main.js', '/style.css']))
  );
});

self.addEventListener('fetch', (e) => {
  e.respondWith(caches.match(e.request).then((r) => r || fetch(e.request)));
});
  • 缓存策略:Cache First / Network First / Stale-While-Revalidate
  • 生命周期:install → activate → fetch / push / sync
  • 限制:仅 HTTPS(localhost 除外)、独立线程、无 DOM

7.5 WebAssembly (Wasm) — 接近原生的性能

一种二进制指令格式,可由 C / C++ / Rust / Go 编译而来,在浏览器中以接近原生速度运行。 适合:音视频处理、3D 引擎、游戏、加密、AI 推理。

const { instance } = await WebAssembly.instantiateStreaming(
  fetch('/add.wasm')
);
console.log(instance.exports.add(1, 2)); // 3

代表项目:FFmpeg.wasm、Figma 渲染引擎、AutoCAD Web、Photoshop Web。

7.6 Canvas — 像素级绘图

<canvas> 通过 JS 绘制 2D / 3D 图形。下方为粒子动画演示(点击切换):

WebGL / WebGPU:在 Canvas 之上提供 GPU 加速 API,是 Three.js、Babylon.js、Unity Web 的底层。 WebGPU(2023 起)是更现代、更接近 Vulkan/Metal 的下一代标准。

7.7 Web Components — 浏览器原生组件化

三大基石:Custom Elements(自定义元素)、Shadow DOM(样式封装)、HTML Templates。 无需框架即可写出可复用、样式隔离的组件。

class FancyBtn extends HTMLElement {
  constructor() {
    super();
    const root = this.attachShadow({ mode: 'open' });
    root.innerHTML = `
      <style>button{padding:8px 16px;border-radius:8px;}</style>
      <button><slot></slot></button>`;
  }
}
customElements.define('fancy-btn', FancyBtn);

真实的自定义元素(已注册):

我是 Web Component!

7.8 浏览器存储全景

方案容量结构适用场景
Cookie~4KB键值(随请求发送)会话、身份
localStorage~5MB同步键值用户偏好
sessionStorage~5MB同步键值,会话级临时数据
IndexedDB数百 MB+异步对象数据库大数据、离线
Cache StorageRequest/ResponsePWA 离线

下方使用 localStorage 演示笔记持久化(刷新仍在):

已保存:--

7.9 File API & 拖拽上传

支持 FileReaderBlobURL.createObjectURL,无需上传即可在前端预览图片。

📁 拖拽图片到此处,或点击选择文件

7.10 还有更多现代 Web API

实时与媒体

  • WebRTC:浏览器之间 P2P 音视频/数据
  • Media Capture:getUserMedia 摄像头/麦克风
  • Web Audio API:实时音频合成与可视化
  • WebCodecs:低层级编解码(4K 实时)

设备与硬件

  • WebUSB / Web Serial / Web Bluetooth
  • Web NFC:读写 NFC 标签
  • Geolocation:定位
  • Sensor API:陀螺仪/加速度

性能与体验

  • View Transitions API:原生页面过渡动画
  • Container Queries:基于容器的响应式
  • Popover API / Dialog:原生弹层
  • Speculation Rules:预渲染下一页

AI 与未来

  • WebGPU:下一代 GPU 接口,可跑本地 LLM
  • Built-in AI (Chrome):浏览器内置 Gemini Nano
  • WebTransport:基于 HTTP/3 的低延迟通道
  • Origin Private File System:高性能文件系统

7.11 视频通话 — 媒体捕获 + WebRTC + WebSocket 信令

视频通话由三件套协作完成:

  • 📹 getUserMedia:调用摄像头/麦克风,拿到 MediaStream
  • 🔗 WebRTC(RTCPeerConnection):在两端建立 P2P 媒体通道,自动处理编解码、丢包、抖动、NAT 穿透
  • 📡 WebSocket(信令):交换 SDP(offer/answer)与 ICE candidate,建连后媒体流不经服务器

协议交互流程

  1. 双方用 getUserMedia 获取本地音视频轨道
  2. A 创建 RTCPeerConnectioncreateOffer() → 通过 WS 把 SDP 发给 B
  3. B 收到 offer → setRemoteDescriptioncreateAnswer() → 回传 SDP
  4. 双方互相通过 WS 推送 icecandidate,直到找到最佳网络路径
  5. 连通后通过 ontrack 拿到对端 MediaStream,挂到 <video> 即可
// === 1. 采集媒体 ===
const localStream = await navigator.mediaDevices.getUserMedia({
  video: { width: 1280, height: 720, facingMode: 'user' },
  audio: { echoCancellation: true, noiseSuppression: true },
});
localVideo.srcObject = localStream;

// === 2. 建立连接(含 STUN/TURN) ===
const pc = new RTCPeerConnection({
  iceServers: [{ urls: 'stun:stun.l.google.com:19302' }],
});

// 把本地轨道加入连接
localStream.getTracks().forEach((t) => pc.addTrack(t, localStream));

// 收到对端轨道
pc.ontrack = (e) => (remoteVideo.srcObject = e.streams[0]);

// 收集 ICE 候选并通过信令发出去
pc.onicecandidate = (e) => {
  if (e.candidate) signaling.send({ type: 'ice', candidate: e.candidate });
};

// === 3. WebSocket 信令 ===
const signaling = new WebSocket('wss://your-server/signal');
signaling.onmessage = async ({ data }) => {
  const msg = JSON.parse(data);
  if (msg.type === 'offer') {
    await pc.setRemoteDescription(msg.sdp);
    const answer = await pc.createAnswer();
    await pc.setLocalDescription(answer);
    signaling.send({ type: 'answer', sdp: answer });
  } else if (msg.type === 'answer') {
    await pc.setRemoteDescription(msg.sdp);
  } else if (msg.type === 'ice') {
    await pc.addIceCandidate(msg.candidate);
  }
};

// 主叫方发起
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
signaling.send({ type: 'offer', sdp: offer });

实战演示(本地回环 P2P)

下方在同一页面建立两个 RTCPeerConnection 互连,把摄像头画面通过真实的 WebRTC 链路传到「远端」窗口。 首次点击会请求摄像头权限。
⚠️ 浏览器要求 HTTPS 或 localhost 才能调用摄像头。

📷 本地摄像头(A)
未启动
🌐 远端接收(B)· 经 WebRTC 传输
等待连接...


        

关键知识点

SDP(Session Description Protocol)

  • 描述「我支持的编解码、分辨率、加密方式」
  • 双方协商出共同支持的能力
  • 纯文本,可在 console 打印 offer 看一下

ICE / STUN / TURN

  • ICE:枚举所有可能的网络路径
  • STUN:帮你查到自己的公网 IP
  • TURN:双方都在严格 NAT 后时的中继服务器

媒体约束(Constraints)

  • video: { width, height, frameRate, facingMode }
  • audio: { echoCancellation, noiseSuppression, autoGainControl }
  • getDisplayMedia():屏幕共享

常见扩展能力

  • DataChannel:基于同一连接的低延迟数据通道(聊天/文件)
  • SFU 架构:多人会议必备(Janus / mediasoup / LiveKit)
  • 录制:MediaRecorder + Blob
  • 美颜/虚拟背景:Canvas/WebGL/MediaStreamTrackProcessor

7.12 多人会议 — SFU 中转架构

上一节的 P2P 只适合 1v1 或最多 3 人。人数变多时,每人都要把自己的流上传给 (N-1) 个人, 上行带宽呈 O(N²) 爆炸。生产级多人会议必须走中转服务器

三种架构对比

架构拓扑上行带宽服务器压力画质适用
Mesh (P2P)人人互连 O(N-1) 份≤3 人
SFU 转发上传 1 份,服务器复制分发 O(1) 份中(只转发)会议、直播连麦
MCU 混流服务器解码/合成后再编码下发 O(1) 份高(算力贵)受损兼容老设备/电话接入

SFU 协议流程

  1. 每个参会者通过 WebSocket 连到信令服务器,发送 join(roomId)
  2. 服务器返回当前房间成员列表
  3. 参会者与 SFU 建立一条 RTCPeerConnection
    • 上行 (publish):把本地轨道 addTrack 给 SFU
    • 下行 (subscribe):SFU 把其他成员的轨道 addTrack 推给自己
  4. 有人加入/离开时,SFU 通过 WS 通知所有人,动态 addTrack / removeTrack
  5. 离开时关闭 PC,停止轨道
// 参会者侧伪代码
const ws = new WebSocket('wss://sfu.example/ws');
const pc = new RTCPeerConnection({ iceServers: [...] });

ws.onopen = () => ws.send(JSON.stringify({ cmd: 'join', room: 'R-1', uid }));

ws.onmessage = async ({ data }) => {
  const msg = JSON.parse(data);
  switch (msg.cmd) {
    case 'peer-join':
      await subscribe(msg.uid);   // 订阅新成员的流
      break;
    case 'peer-leave':
      removeVideo(msg.uid);       // 清理 UI
      break;
    case 'answer':
      await pc.setRemoteDescription(msg.sdp);
      break;
  }
};

// 上行:发布自己
localStream.getTracks().forEach((t) => pc.addTrack(t, localStream));
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
ws.send(JSON.stringify({ cmd: 'publish', sdp: offer }));

// 下行:收到别人的轨道时渲染
pc.ontrack = (e) => renderTile(e.streams[0], e.track.id);

实战演示(模拟 SFU Hub)

下方在本页面内用一个 SfuHub 纯 JS 类模拟中转服务器:真实摄像头只打开一份( getUserMedia),然后通过 Hub "分发"给多个虚拟参会者,每个参会者独立渲染一个画面格。 真实项目里把 Hub 替换成 mediasoup/Janus/LiveKit 服务端即可。

未在会议中
📊 SFU 实时状态

            
📡 上行/下行带宽模拟
  • 我的上行:0 Mbps(始终 1 份)
  • 我的下行:0 Mbps(随人数线性增长)
  • SFU 服务器转发总量:0 Mbps

生产级要点

Simulcast(分层编码)

  • 同一路视频编多个码率(高/中/低)
  • SFU 根据订阅者网络自动选层
  • 主讲人高清,小窗口低清,省带宽

SVC 可伸缩编码

  • 一路码流包含多时域/空域层
  • 比 Simulcast 更省上行
  • VP9 / AV1 / H.265 支持

QoS 保障

  • NACK / FEC / PLI:丢包补偿
  • JitterBuffer:抖动平滑
  • 拥塞控制:GCC / BBR

开源方案

  • mediasoup:Node.js,灵活,社区最活
  • Janus:C 写的老牌稳
  • LiveKit:Go 写的云原生,自带客户端 SDK
  • Pion:纯 Go WebRTC 栈

7.13 WebRTC 深度解析 — 协议栈 / DataChannel / 质量监控

WebRTC 不是一个 API,而是一整套实时通信协议栈。它把复杂的音视频采集、编解码、 网络传输、NAT 穿透、加密、QoS 全部封装成三个核心对象: MediaStreamRTCPeerConnectionRTCDataChannel

协议栈全景

┌──────────────────────────────────────────────────┐
│  JavaScript API:getUserMedia / RTCPeerConnection │
├──────────────────────────────────────────────────┤
│       SRTP(加密音视频)     │    SCTP(数据)     │
├──────────────────────────────────────────────────┤
│              DTLS(密钥协商 / 加密)               │
├──────────────────────────────────────────────────┤
│    ICE(候选枚举)  +  STUN(打洞)  +  TURN(中继) │
├──────────────────────────────────────────────────┤
│                        UDP                        │
└──────────────────────────────────────────────────┘
  • ICE:综合调度 STUN/TURN,产出可达的"候选对"
  • DTLS:握手生成密钥;SRTP/SCTP 基于它加密
  • 音视频走 SRTP;任意数据走 SCTP(DataChannel)
  • WebRTC 默认全程 E2E 加密,无法关闭

NAT 类型与穿透成功率

NAT 类型映射规则穿透成功率备注
Full ConeIP+端口外网固定✅ 很高家用路由器常见
Restricted Cone按远端 IP 限制✅ 高大部分办公网
Port Restricted按 IP+端口限制⚠️ 中STUN 仍可穿
Symmetric NAT每个目的地址映射不同端口❌ 低必须 TURN 中继(对称型)

经验值:纯 STUN ≈ 70%~80%,加 TURN 可达 99%+,生产必须部署 TURN。

RTCDataChannel — 被忽视的利器

同一条 PeerConnection 上可开多个 DataChannel,用于任意二进制/文本数据。 相比 WebSocket 的优势:

  • P2P:数据不过服务器,零延迟、零费用
  • 可选 UDP 或 TCP 语义:游戏用 unordered + maxRetransmits=0,文件用 ordered reliable
  • 默认加密(DTLS-SRTP)
  • ⚠️ 受 MTU 限制(~16KB),大文件需分片
// 创建可靠有序(默认,类似 TCP)
const chat = pc.createDataChannel('chat');

// 游戏用 "不可靠+无序"(类似 UDP,延迟最低)
const game = pc.createDataChannel('game', {
  ordered: false,
  maxRetransmits: 0,
});

chat.onopen    = () => chat.send('hi');
chat.onmessage = (e) => console.log('收到:', e.data);

// 发二进制
const ab = new Uint8Array([1, 2, 3]).buffer;
chat.send(ab);

实战 A:DataChannel P2P 聊天 & 文件传输

在同页面建立 dcA ↔ dcB,通过 DataChannel 传输文本和文件(自动分片)。 真实场景下这就是协同编辑、游戏同步、WebRTC 网盘(如 snapdrop.net)的底层。

👤 Alice
👤 Bob

实战 B:实时质量监控(getStats API)

生产级 WebRTC 必须监控 pc.getStats(),用来观察:RTT、丢包率、抖动、码率、帧率、分辨率。 下方面板每秒采样一次并折线展示:

RTT 往返时延-- ms
丢包率--
抖动 Jitter-- ms
码率 Bitrate-- kbps
帧率 FPS--
分辨率--
源:7.11 视频通话的 pcB

💡 getStats() 返回的是 RTCStatsReport(Map)。 常用 type:inbound-rtpoutbound-rtpcandidate-pairremote-inbound-rtp

实战 C:MediaRecorder 本地录制

WebRTC 的 MediaStream 可直接交给 MediaRecorder 编码成 webm 文件, 实现"本地录制"而无需走服务端,广泛用于在线面试录像、Loom 式屏幕录制。

const recorder = new MediaRecorder(stream, { mimeType: 'video/webm;codecs=vp9' });
const chunks = [];
recorder.ondataavailable = (e) => chunks.push(e.data);
recorder.onstop = () => {
  const blob = new Blob(chunks, { type: 'video/webm' });
  const url = URL.createObjectURL(blob);
  // 触发下载
};
recorder.start(1000); // 每 1s 触发一次 ondataavailable
已录制 0

安全 / 合规关键点

E2E 加密

  • DTLS 握手后所有流量加密,SFU 也看不到明文
  • SFU 架构下可用 Insertable Streams 做业务层二次加密(如 Zoom 真端到端)
  • 证书指纹通过 SDP 交换,防中间人

隐私泄漏

  • 默认 ICE 会暴露本机内网 IP("WebRTC IP Leak")
  • iceTransportPolicy: 'relay' 强制走 TURN,隐藏真实 IP
  • mDNS 候选(Chrome 默认)已缓解该问题

常见坑

  • 必须 HTTPS(localhost 除外)
  • Safari 对编解码支持较新版才全
  • 移动端后台会挂起 PeerConnection
  • TURN 费用:流量按出口计,生产要限流

进阶主题

  • Perfect Negotiation:应对双方同时 offer 的协商模式
  • RTCRtpTransceiver:细粒度控制收发方向
  • Trickle ICE:候选产出即发送,不等全部完成
  • RTP Header Extensions:扩展 RTP 头,实现带宽估计等

7.14 现代 Web API 实战集 — 12 个让网页像原生 App 的能力

浏览器正在加速补齐"原生应用才有"的能力:底层网络、编解码、硬件、支付、认证、XR…… 下面每个 API 都配能力检测 + 代码示例 + 实测按钮。 ⚠️ 部分 API 仅在 Chrome / Edge 支持,Firefox/Safari 会显示"未支持"。

🚀
WebTransportHTTP/3 · 网络

基于 HTTP/3 (QUIC) 的多路复用通道,同时提供可靠流(类 TCP)与不可靠数据报(类 UDP)。相比 WebSocket,队头阻塞更少、连接迁移更快。

const wt = new WebTransport('https://example.com/wt');
await wt.ready;

// 1) 不可靠数据报(游戏状态同步)
const writer = wt.datagrams.writable.getWriter();
writer.write(new Uint8Array([1, 2, 3]));

// 2) 可靠双向流(大文件/信令)
const stream = await wt.createBidirectionalStream();
const w = stream.writable.getWriter();
w.write(new TextEncoder().encode('hello'));
--
🎞
WebCodecs音视频 · 编解码

直接访问浏览器的硬件编解码器,拿到逐帧 VideoFrame,可做 Web 版剪映、帧级合成、WebRTC 前处理。

const decoder = new VideoDecoder({
  output: (frame) => {
    canvasCtx.drawImage(frame, 0, 0);
    frame.close(); // 必须手动释放!
  },
  error: (e) => console.error(e),
});
decoder.configure({ codec: 'vp09.00.10.08' });
decoder.decode(chunk);
--
⚡️
WebAssembly (WASM)高性能计算
✅ 全支持

近原生速度执行,承载 ffmpeg / Photoshop / Pyodide / SQLite / AutoCAD。下方实测:用 WASM 字节码算斐波那契,对比 JS 版本。

// fib.wat 编译后加载
const { instance } = await WebAssembly.instantiate(wasmBytes);
instance.exports.fib(30); // ~1ms vs JS 10ms
--
🔒
Web Locks API并发 · 同步

跨 Tab / iframe / Worker 的异步互斥锁。最典型:多 Tab 打开时只让一个 Tab 刷 Token。

await navigator.locks.request('auth-token', async (lock) => {
  // 此块代码同一 origin 下同时只有一个在执行
  const token = await refreshToken();
  localStorage.setItem('token', token);
});
--
💳
Payment Request API支付

调起系统/浏览器原生支付面板(Apple Pay / Google Pay / 信用卡),无需手动填表

const req = new PaymentRequest(
  [{ supportedMethods: 'basic-card' }],
  {
    total: { label: '订单', amount: { currency: 'CNY', value: '99.00' } },
  }
);
const response = await req.show();
await response.complete('success');
--
🔐
WebAuthn无密码登录

指纹 / Face ID / 硬件密钥代替密码。底层是公私钥签名,网站只存公钥。

// 注册(生成密钥对)
const cred = await navigator.credentials.create({
  publicKey: {
    challenge,
    rp: { name: 'My App' },
    user: { id, name: 'zhangke', displayName: 'ZK' },
    pubKeyCredParams: [{ type: 'public-key', alg: -7 }],
  },
});
// 登录(验证签名)
await navigator.credentials.get({ publicKey: { challenge } });
--
📂
File Handling APIPWA · 系统集成

让 PWA 注册为系统上某类文件(.md / .txt)的默认打开方式,双击即用网页打开。需要在 manifest.json 配置:

{
  "file_handlers": [{
    "action": "/open",
    "accept": { "text/markdown": [".md"] }
  }]
}
if ('launchQueue' in window) {
  launchQueue.setConsumer(async (launchParams) => {
    for (const handle of launchParams.files) {
      const file = await handle.getFile();
      editor.setValue(await file.text());
    }
  });
}
--
💤
Idle Detection API状态感知

感知用户是否离开电脑(系统级空闲),聊天/网银场景自动改"离开"或退出登录。

const idle = new IdleDetector();
idle.addEventListener('change', () => {
  console.log(idle.userState, idle.screenState);
  // active / idle          unlocked / locked
});
await idle.start({ threshold: 60_000 });
--
📋
Clipboard API(高级)富内容
✅ 全支持

除纯文本外还能读写 HTML / 图片 / 自定义 MIME,实现 Office/Docs 那种"保留格式粘贴"。

// 写入富文本 + 纯文本
await navigator.clipboard.write([
  new ClipboardItem({
    'text/html':  new Blob(['<b>hi</b>'], { type: 'text/html' }),
    'text/plain': new Blob(['hi'], { type: 'text/plain' }),
  }),
]);
// 读取图片
const items = await navigator.clipboard.read();
for (const it of items) {
  if (it.types.includes('image/png')) {
    const blob = await it.getType('image/png');
  }
}
--
🎮
WebHID API硬件 · HID

直连 HID 设备:游戏手柄、绘图板侧键、条码枪、专业键盘。

const [device] = await navigator.hid.requestDevice({ filters: [] });
await device.open();
device.oninputreport = (e) => {
  const data = new Uint8Array(e.data.buffer);
  console.log(device.productName, data);
};
--
🔌
Web Serial API硬件 · 串口

直接与 Arduino / STM32 / GPS / 工业仪表通信,浏览器里做 3D 打印机控制台、烧录工具。

const port = await navigator.serial.requestPort();
await port.open({ baudRate: 115200 });

const reader = port.readable.getReader();
while (true) {
  const { value, done } = await reader.read();
  if (done) break;
  console.log(new TextDecoder().decode(value));
}
--
🎹
Web MIDI API音乐设备

连接电子琴/鼓垫/MIDI 控制器。按键触发时收到 [status, note, velocity] 三字节消息。

const midi = await navigator.requestMIDIAccess();
midi.inputs.forEach((input) => {
  input.onmidimessage = (e) => {
    const [cmd, note, vel] = e.data;
    if (cmd === 144 && vel > 0) playNote(note);
  };
});
--
🥽
WebXR Device APIVR · AR

在 VR 头显 (Quest) / AR 眼镜 (HoloLens) 里渲染 Web 内容,获取手柄与手势。免下载 App 的 Web VR。

if (navigator.xr) {
  const ok = await navigator.xr.isSessionSupported('immersive-vr');
  if (ok) {
    const session = await navigator.xr.requestSession('immersive-vr');
    // 接入 Three.js: renderer.xr.setSession(session)
  }
}
--

📌 以上 API 中,涉及硬件或需要用户手势授权的(HID/Serial/MIDI/Payment/WebAuthn)会弹出浏览器原生选择框; 硬件类在没有连接对应设备时会返回空列表,这是正常行为。

7.15 流程图 & 画布编排 — 可视化工作流的核心技术

低代码平台、工作流引擎、白板、数据血缘、DAG 编排、节点式 Shader 编辑器……背后都是同一套能力: 节点(Node)+ 连线(Edge)+ 画布(Canvas)。本节系统拆解三种主流实现方案。

三大渲染方案对比

方案定位性能上限优势劣势适合
DOM + SVG元素级 ≤ 千级节点 可用 CSS 样式/事件/无障碍,矢量清晰 大量节点时 DOM 树重 流程图、ER 图、思维导图
Canvas 2D栅格绘制 万级节点 绘制快、内存省 没有 DOM 节点,命中测试要自己算 白板、绘图、大规模 DAG
WebGL / PixiGPU 加速 十万级节点 性能天花板最高,支持粒子/滤镜 学习曲线陡,调试难 地图、游戏、可视化大屏

主流开源库

流程图 / DAG

  • AntV X6:阿里,功能完善,SVG + 插件生态
  • LogicFlow:滴滴,更轻量,BPMN 场景
  • React Flow:React 生态首选,社区最活
  • Drawflow:Vanilla JS,一键集成
  • jsPlumb:老牌,连线算法强
  • Dagre:只做自动布局,常与上述配合

画布 / 白板 / 图形

  • Konva.js:Canvas 2D 图形框架,事件系统完整
  • Fabric.js:图像编辑,类 Photoshop
  • tldraw:现代白板库,Figma 级交互
  • Excalidraw:手绘风白板,内嵌友好
  • Rough.js:手绘风 SVG/Canvas
  • PixiJS:WebGL 2D 渲染引擎

核心算法

  • 自动布局:Dagre(层次)、ELK(Eclipse Layout Kernel)、Force(力导向)
  • 连线路由:曼哈顿路由、贝塞尔曲线、正交避让
  • 命中测试:AABB 包围盒、四叉树(Quadtree)加速
  • 缩放:CTM(Current Transform Matrix)+ 矩阵逆运算

典型应用

  • 低代码:钉钉宜搭、腾讯微搭、Mendix
  • 工作流:审批流、BPMN
  • 数据编排:Airflow UI、Dify、n8n、LangFlow
  • 白板协作:Figma、Miro、腾讯文档画板
  • AI 流水线:ComfyUI、Rivet

核心原理

  1. 数据模型{ nodes: [...], edges: [...] },节点存坐标/类型,边存 source/target
  2. 渲染:根据数据绘制节点与连线(声明式),状态变化 → Diff → 最小重绘
  3. 交互:拖拽移动节点 / 从锚点拖出连线 / 框选 / 右键菜单 / 键盘删除
  4. 连线路径:贝塞尔曲线 M x1 y1 C cx1 cy1, cx2 cy2, x2 y2
  5. 命中测试elementFromPoint (DOM) 或几何判断(Canvas)
  6. Undo/Redo:命令模式 + 栈,或直接保存快照
  7. 协同:CRDT (Yjs / Automerge) 或 OT 算法
/** 贝塞尔连线 · 横向流程图最常用 */
function bezierPath(s, t) {
  const dx = Math.max(Math.abs(t.x - s.x) * 0.5, 50);
  return `M ${s.x} ${s.y} C ${s.x + dx} ${s.y}, ${t.x - dx} ${t.y}, ${t.x} ${t.y}`;
}

/** 正交连线(曼哈顿) · BPMN 风格 */
function orthogonalPath(s, t) {
  const mx = (s.x + t.x) / 2;
  return `M ${s.x} ${s.y} L ${mx} ${s.y} L ${mx} ${t.y} L ${t.x} ${t.y}`;
}

🧩 实战 A:SVG 流程编辑器(纯原生,零依赖)

完整支持:左侧拖入节点 · 拖动移动 · 锚点拖出连线 · 点击选中 · Delete 删除 · 导出 JSON · 导入 JSON · Undo/Redo

拖入节点
开始
任务
判断
结束
操作
👈 从左侧拖入节点,鼠标按住节点右侧锚点拖到另一个节点即可连线
📤 导出/导入 JSON(点击展开)

🎨 实战 B:Canvas 无限画布(Pan / Zoom / 坐标系)

所有画布工具(Figma / Miro / Excalidraw)的共同基础:一个可平移、可缩放的虚拟坐标系。 操作:鼠标拖拽平移 · 滚轮缩放(以光标为中心)· 点击节点选中 · 双击空白处新建。

偏移 (0, 0) · 缩放 100%

关键代码片段

/** 屏幕坐标 → 世界坐标(Pan/Zoom 的核心) */
function screenToWorld(sx, sy, scale, offsetX, offsetY) {
  return { x: (sx - offsetX) / scale, y: (sy - offsetY) / scale };
}

/** 以光标为中心缩放 —— Figma/Miro 的标准做法 */
function zoomAt(cx, cy, delta) {
  const factor = delta > 0 ? 0.9 : 1.1;
  const wx = (cx - offsetX) / scale;
  const wy = (cy - offsetY) / scale;
  scale *= factor;
  offsetX = cx - wx * scale;
  offsetY = cy - wy * scale;
}

选型建议

🟢 业务项目推荐

  • React 项目 → React Flow(API 设计优秀)
  • Vue/多技术栈 → AntV X6(功能最全)
  • BPMN 流程 → LogicFlow 或 bpmn-js
  • 白板 → 直接魔改 tldrawExcalidraw

🛠 自研时的常见坑

  • 节点多时频繁 setState 会卡顿,建议 batch + 惰性渲染
  • SVG 连线用 will-change / transform 替代 d 属性频繁更新
  • 拖拽用 Pointer Events 统一鼠标/触摸
  • 撤销栈不要存全量快照,存 diff/命令
  • 坐标系要区分"屏幕坐标" vs "世界坐标",导出时用世界坐标

⑧ 小程序开发 — 原生 & UniApp 跨端

小程序是介于 H5 与原生之间的产物:用类 Web 技术栈开发,运行在宿主 App 提供的 受限 WebView + 原生渲染容器中,享受「免安装、秒开、可调用原生能力」的体验。

8.1 原生微信小程序 — 双线程架构

小程序最大特色是逻辑层(AppService)与渲染层(WebView)物理隔离,两者通过 Native 桥通信。这样 JS 无法直接操作 DOM,既保证了安全沙箱,也为多端渲染(Skyline)打基础。

┌────────────────┐      postMessage      ┌────────────────┐
│  渲染层 View    │ <───────────────────> │  逻辑层 Service │
│  (WebView)     │      JSBridge         │  (JSCore/V8)   │
│  WXML / WXSS   │                       │   app.js / 业务 │
└────────┬───────┘                       └────────┬───────┘
         │                                        │
         └─────────── Native 层 ──────────────────┘
            (网络 / 文件 / 相机 / 蓝牙 / 支付…)
  • 逻辑层:执行业务 JS,调用 wx.* API,通过 setData 把数据传给渲染层
  • 渲染层:每个页面一个独立 WebView,解析 WXML → 虚拟 DOM → 真实 DOM
  • 通信代价setData 是跨线程 JSON 序列化,避免频繁大对象
  • Skyline:新一代渲染引擎,合并双线程,提升性能

8.2 原生小程序四件套

app.json(全局配置)

{
  "pages": ["pages/index/index", "pages/my/my"],
  "window": {
    "navigationBarTitleText": "我的小程序",
    "navigationBarBackgroundColor": "#6c8cff"
  },
  "tabBar": {
    "list": [
      { "pagePath": "pages/index/index", "text": "首页" },
      { "pagePath": "pages/my/my",      "text": "我的" }
    ]
  }
}

index.wxml(模板)

<view class="wrap">
  <text>{{ title }}</text>
  <button bindtap="onTap">点击 +1</button>
  <view wx:for="{{ list }}" wx:key="id">
    {{ item.name }}
  </view>
</view>

index.wxss(样式,rpx 自适配)

.wrap {
  padding: 32rpx;
  background: #f7f8fc;
}
/* 750rpx = 屏幕宽,自动按比例换算 */
button { margin-top: 24rpx; }

index.js(页面逻辑)

Page({
  data: { title: '你好小程序', count: 0, list: [] },
  onLoad() {
    wx.request({
      url: 'https://api.example.com/list',
      success: (res) => this.setData({ list: res.data }),
    });
  },
  onTap() {
    this.setData({ count: this.data.count + 1 });
  },
});

8.3 生命周期与路由

App 生命周期

  • onLaunch:启动(只一次,可做登录)
  • onShow:进入前台
  • onHide:切后台
  • onError:全局错误

Page 生命周期

  • onLoad(query):首次加载,收参数
  • onShow / onHide
  • onReady:首次渲染完成
  • onPullDownRefresh / onReachBottom

路由 API

  • wx.navigateTo:入栈(可返回)
  • wx.redirectTo:替换当前
  • wx.switchTab:切 tabBar 页
  • wx.reLaunch:清栈重启

组件化

  • Component({ properties, data, methods })
  • 支持 behaviors(类似 mixin)
  • 自定义组件可用 observers 监听数据变化

8.4 UniApp — 一套代码跨 10+ 端

基于 Vue.js,编译产物可跑在:微信 / 支付宝 / 抖音 / QQ / 百度 / 快手 / 钉钉 小程序,H5, iOS App,Android App(App 端基于 WeeX / uni-app X)。

项目结构

my-uni-app/
├── pages/
│   └── index/index.vue    # 页面(Vue 单文件)
├── components/            # 公共组件
├── static/                # 静态资源
├── App.vue                # 入口
├── main.js                # 挂载
├── manifest.json          # 各端配置(appid 等)
├── pages.json             # 路由 + TabBar
└── uni.scss               # 全局样式变量

Vue 3 + Composition API 写法

<template>
  <view class="page">
    <text>{{ title }}</text>
    <button @click="add">点击 {{ count }}</button>
    <!-- #ifdef MP-WEIXIN -->
    <button open-type="getUserInfo">微信授权</button>
    <!-- #endif -->
  </view>
</template>

<script setup>
import { ref } from 'vue';
const title = ref('UniApp Demo');
const count = ref(0);
const add = () => {
  count.value++;
  uni.showToast({ title: '+1', icon: 'success' });
};
</script>

<style lang="scss">
.page { padding: 32rpx; }
</style>

条件编译

UniApp 在同一源码里用注释区分不同平台逻辑,编译时只保留对应端代码:

// #ifdef H5
window.location.href = '/login';
// #endif

// #ifdef MP-WEIXIN
wx.login({ success: (r) => console.log(r.code) });
// #endif

// #ifdef APP-PLUS
plus.runtime.openURL('tel:10086');
// #endif

8.5 常用 API 对比(原生 wx / UniApp uni)

能力原生小程序UniApp说明
网络请求wx.requestuni.requestPromise 化需自封装
本地存储wx.setStorageSyncuni.setStorageSync单 key 上限 1MB
路由跳转wx.navigateTouni.navigateTo页面栈最多 10 层
Toastwx.showToastuni.showToast支持 icon/image
登录wx.loginuni.login({ provider })UniApp 统一多端鉴权
支付wx.requestPaymentuni.requestPayment需后端签名
文件上传wx.uploadFileuni.uploadFile支持 multipart
扫码wx.scanCodeuni.scanCode需相机权限

8.6 实战演示:网页版小程序模拟器

下方在浏览器里模拟了一个完整的小程序环境(手机外壳 + 顶部胶囊 + TabBar + 多页面 + 下拉刷新 + 常用 wx.* API),真实复刻「双线程通信 → setData 驱动视图」的行为。

12:00 ●●●●
首页
↓ 下拉刷新
🏠
首页
📋
列表
👤
我的
🛠 小程序控制台

          

✅ 已实现:页面切换(switchTab)、setData 响应式更新、showToastshowModalrequest(模拟)、setStorageSync、下拉刷新、生命周期日志。

8.7 踩坑 & 技术选型

性能优化

  • 避免频繁 setData,合并批量更新
  • 只传变化字段:setData({ 'list[0].name': 'x' })
  • 长列表用虚拟列表(recycle-view)
  • 图片用 CDN + WebP
  • 分包加载 / 独立分包 / 分包预下载

如何选型

  • 只做微信:原生性能最佳、更新最快
  • 多端(抖音/支付宝/H5/App):UniApp / Taro
  • 团队熟悉 React:Taro 4(React + 编译)
  • 团队熟悉 Vue:UniApp
  • 极致性能 + 新能力:uni-app x(原生渲染)

调试工具

  • 微信开发者工具(必装)
  • vConsole:真机调试控制台
  • 抓包:Charles / Whistle + 证书
  • HBuilderX:UniApp 官方 IDE

发布流程

  • 配置合法域名(request / socket / upload)
  • 体验版 → 审核版 → 正式版
  • 版本回滚、灰度发布
  • 数据助手监控:崩溃率 / 启动耗时

⑨ Web 游戏开发 — 浏览器里的 3A 之梦

现代浏览器已经具备完整的游戏开发能力:GPU 渲染、接近原生的性能、低延迟网络、手柄/震动/音频全配套。 从《Among Us Web》到 Unity 导出的 3D 游戏,Web 正成为真正的游戏平台。

9.1 核心渲染技术

技术定位性能上限适用场景
Canvas 2D最基础 2D 绘图数千精灵 休闲小游戏、棋牌、2D 动画
WebGLGPU 加速渲染(OpenGL ES)数万物体 所有 60fps 的 3D Web 游戏,Three.js/Babylon.js 底层
WebGPU下一代 GPU 接口(Vulkan/Metal 风格)十万+物体 3A Web 游戏、光追、粒子爆炸、GPGPU 计算
WebAssembly高性能引擎代码接近原生 Unity / Unreal 导出游戏的核心逻辑

9.2 主流游戏引擎与框架

引擎类型特点适合游戏
Phaser2D 全能引擎开源 + 完整游戏循环 / WebGL+Canvas 自动切换平台跳跃、RPG、俯视角射击、卡牌
PixiJS2D 渲染库极致性能 / 无游戏循环弹幕、贪吃蛇大作战、UI 密集
Three.js3D 渲染库最流行的 WebGL 抽象3D 展示、第一/三人称探索、低模游戏
Babylon.js3D 引擎微软主导 / 内置物理+GUI+粒子 / 早期支持 WebGPU高品质 3D、大型场景
Unity(WebGL 导出)传统大型引擎导出 HTML+JS+WASM,生态最大中大型 2D/3D 商业游戏
Unreal(Pixel Streaming)3A 引擎画质顶级,导出体积大高端演示、云游戏
Cocos Creator国产 H5 引擎微信小游戏首选 / 双端打包微信小游戏、4399 H5、捕鱼棋牌

9.3 实时网络同步方案

🔌 传输协议

  • WebSocket:回合制、休闲、棋牌。简单可靠
  • WebRTC DataChannel:P2P 低延迟,1v1 格斗/双人协作
  • WebTransport:基于 HTTP/3,不可靠数据报 + 可靠流,FPS/MOBA 最佳
  • WebSocket over QUIC:兼顾兼容性与低延迟

🎯 同步策略

  • 权威服务器:服务端持最终裁决,反作弊刚需
  • 帧同步(Lockstep):只传指令,所有端独立计算;带宽省但需确定性引擎(MOBA/格斗)
  • 状态同步:服务端算完发状态;通用但带宽大(FPS/MMO)
  • 客户端预测 + 服务端校正:位置提前渲染,错了再回滚
  • 插值 / 外推:平滑其他玩家画面

9.4 游戏辅助 API

输入

  • Gamepad API:Xbox / PS / Switch Pro 手柄
  • Pointer Lock API:隐藏鼠标,FPS 必备
  • Keyboard Lock API:在全屏时锁定 Tab/Esc
  • Touch / Pointer Events:移动端虚拟摇杆

输出

  • Web Audio API:3D 空间音效、节奏精确
  • Fullscreen API:沉浸式全屏
  • Vibration API:移动端触觉反馈
  • Screen Orientation:锁定横竖屏
  • Wake Lock:防止屏幕休眠

性能

  • OffscreenCanvas:渲染移至 Worker 不阻塞 UI
  • SharedArrayBuffer:Worker 间零拷贝共享内存
  • requestAnimationFrame:VSync 对齐的帧循环
  • performance.now():高精度时间戳

分发

  • PWA:可安装到桌面
  • Cloud Gaming:Pixel Streaming / GeForce NOW
  • WebXR:VR/AR 游戏
  • 微信小游戏:基于 Web 技术的超级入口

9.5 实战 A:Canvas 2D 小游戏(打砖块)

经典 Breakout,演示游戏循环碰撞检测输入处理三件套。 控制:← → 或鼠标/触屏拖动挡板。

分数:0 生命:3 关卡:1

🎮 打砖块 Breakout

按空格或点击开始

9.6 实战 B:Three.js 3D 场景(WebGL)

从 CDN 加载 Three.js,构建:带光照/阴影的地面 + 自转立方体 + 可旋转摄像机。 拖拽旋转、滚轮缩放。这是所有 3D Web 游戏的最小可运行模板。

FPS:-- Triangles:-- Draw calls:--
脚本首次加载约 150KB
import * as THREE from 'three';

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(60, w / h, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });

const geo = new THREE.BoxGeometry(1, 1, 1);
const mat = new THREE.MeshStandardMaterial({ color: 0x6c8cff });
const cube = new THREE.Mesh(geo, mat);
scene.add(cube);

scene.add(new THREE.DirectionalLight(0xffffff, 1));
camera.position.z = 4;

renderer.setAnimationLoop(() => {
  cube.rotation.x += 0.01;
  cube.rotation.y += 0.01;
  renderer.render(scene, camera);
});

9.7 实战 C:Web Audio 3D 空间音效

PannerNode 实现空间化音频:拖动画布上的小球(音源), 中心是听众,离得近声音大、在哪侧从哪侧出来,就像真实 3D 游戏里脚步声定位一样。请戴耳机体验。

左声道 0 · 右 0

9.8 实战 D:手柄 / 全屏 / 震动 / 屏幕方向

🎮 Gamepad API 实时面板

插入 Xbox/PS 手柄并按任意键即可自动识别;没有手柄则显示未连接。

暂未检测到手柄。请按手柄上任意键激活。

🌐 其他游戏 API

点击即真实调用浏览器原生能力。

点击上方按钮试试 →

9.10 实战 E:FPS 射击游戏(5 把武器 + 手雷抛物 + 范围伤害)

一款完整的第一人称射击游戏:5 把武器(手枪/步枪/狙击/霰弹/火箭筒)+ 手雷抛物 + 范围爆炸 + Raycaster 命中判定 + 部位伤害 + 血量/护甲 + AI 敌人 + 复活机制。 每把武器伤害/射速/弹道差异化,所有玩法均由纯 JS 实现。

FPS:-- 位置:(0, 0, 0) 在线:1 延迟:-- ms
❤ HP
100
🛡 护甲
50
🔫 AK-47
30/90
💣 手雷 3
击杀 0 死亡 0
1 🔫 手枪
2 🎯 步枪
3 🔭 狙击
4 💥 霰弹
5 🚀 火箭筒
G 💣 手雷

💀 你被击杀

3 秒后复活...

WASD 移动 · Shift
1~5 切换武器 · G 投手雷
左键 开火 · R 换弹 · Esc 退出

🎯 FPS 射击演练场

点击加载场景 → 进入游戏 → 消灭所有 Bot!
鼠标左键开火,R 换弹,Esc 退出锁定

武器系统(5+1)

武器伤害射速弹匣散布射程特点
🔫 手枪 Glock22200ms150.8°40m精准、备弹无限
🎯 步枪 AK-4732100ms301.5°80m全能、连发
🔭 狙击 AWP1201500ms5200m一枪致命,慢
💥 霰弹 SPAS-1214×8800ms810°20m近战之王,散射 8 颗
🚀 火箭筒 RPG120 + 范围 801500ms1抛物线弹道 + 4m 爆炸
💣 手雷范围 1003 颗抛物投掷 + 5m 爆炸

核心系统设计

🔫 武器切换

  • 1-5 切换 5 把武器,G 投手雷
  • 每把独立配置:damage / fireRate / magSize / spread / range / projectileType
  • 霰弹一次射 8 条 raycast 模拟散射
  • 狙击 spread=0 + 一发致命,体现"精准武器"差异

💣 投掷物 + 抛物线

  • 手雷 = THREE.Mesh + 速度向量 v
  • 每帧 v.y -= g·dt(重力 18m/s²)
  • 触地或 1.5s 后 爆炸:5m 范围扣血
  • 火箭弹 = 直线匀速 + 命中即炸(4m)
  • 距离衰减:dmg × (1 - dist/radius)

🎯 命中判定

  • 子弹:Raycaster 取最近障碍/敌人
  • 霰弹:8 条 ray 各自结算,点状散布
  • 头/胸/腿三段倍率(2× / 1× / 0.75×)
  • 距离衰减:1 - dist/range

🛡 掩体战术

  • 开局玩家在南掩体
  • 3 个 AI 分别藏在北/东/西掩体
  • AI 视野含遮挡判定,藏好真的安全
  • 火箭/手雷可越过掩体清场
// 手雷抛物线
function throwGrenade() {
  const dir = camera.getWorldDirection(new THREE.Vector3());
  const grenade = {
    mesh: greenSphere,
    pos: camera.position.clone(),
    vel: dir.multiplyScalar(18).setY(8), // 初速度 + 抬头
    bornT: now,
  };
  grenades.push(grenade);
}

// 每帧物理更新
function updateGrenades(dt) {
  for (const g of grenades) {
    g.vel.y -= 18 * dt;          // 重力
    g.pos.addScaledVector(g.vel, dt);
    if (g.pos.y <= 0.2 || age > 1500) {
      explode(g.pos, 100, 5);    // damage 100, radius 5m
      remove(g);
    }
  }
}

// 范围伤害
function explode(center, dmg, radius) {
  for (const e of enemies) {
    const dist = e.position.distanceTo(center);
    if (dist < radius) hitEnemy(e, dmg * (1 - dist/radius));
  }
  // 玩家也会被自己手雷炸到!
  const myDist = camera.position.distanceTo(center);
  if (myDist < radius) takeDamage(dmg * (1 - myDist/radius));
}

9.11 实战 F:真实 Node.js WebSocket 房间服务端

上一节用 MockWs 在浏览器里模拟服务端,下面给出可直接运行的 Node.js 版本。 支持多人房间、广播、心跳、断线清理。已附在项目根目录 server/room-server.js,一行命令启动:

npm install ws
node server/room-server.js
# 监听 ws://localhost:3001/room/{roomId}

server/room-server.js 完整实现

// Node.js 18+ | pnpm add ws
import { WebSocketServer } from 'ws';
import { randomUUID } from 'node:crypto';

const wss = new WebSocketServer({ port: 3001, path: '/room' });

/** @type {Map<string, Set<WebSocket>>} */
const rooms = new Map();
/** @type {Map<WebSocket, {uid: string, roomId: string, state: any}>} */
const clients = new Map();

const join = (ws, roomId) => {
  if (!rooms.has(roomId)) rooms.set(roomId, new Set());
  rooms.get(roomId).add(ws);
};
const leave = (ws) => {
  const info = clients.get(ws);
  if (!info) return;
  rooms.get(info.roomId)?.delete(ws);
  clients.delete(ws);
  broadcast(info.roomId, { type: 'peer-leave', uid: info.uid });
};
const broadcast = (roomId, msg, exclude) => {
  const data = JSON.stringify(msg);
  rooms.get(roomId)?.forEach((c) => {
    if (c !== exclude && c.readyState === 1) c.send(data);
  });
};

wss.on('connection', (ws, req) => {
  const roomId = new URL(req.url, 'http://x').pathname.split('/').pop() || 'lobby';
  const uid = randomUUID().slice(0, 8);
  clients.set(ws, { uid, roomId, state: {} });
  join(ws, roomId);
  ws.send(JSON.stringify({ type: 'welcome', uid, roomId }));
  broadcast(roomId, { type: 'peer-join', uid }, ws);

  // 心跳
  ws.isAlive = true;
  ws.on('pong', () => (ws.isAlive = true));

  ws.on('message', (buf) => {
    try {
      const msg = JSON.parse(buf.toString());
      const info = clients.get(ws);
      if (msg.type === 'move') {
        info.state = { x: msg.x, y: msg.y, z: msg.z, rot: msg.rot };
        broadcast(roomId, { type: 'state', uid: info.uid, ...info.state }, ws);
      } else if (msg.type === 'chat') {
        broadcast(roomId, { type: 'chat', uid: info.uid, text: msg.text });
      }
    } catch (e) {
      console.error(e);
    }
  });

  ws.on('close', () => leave(ws));
});

// 30s 心跳
setInterval(() => {
  wss.clients.forEach((ws) => {
    if (!ws.isAlive) return ws.terminate();
    ws.isAlive = false;
    ws.ping();
  });
}, 30_000);

console.log('🚀 Room server running on ws://localhost:3001/room/{roomId}');

客户端接入(替换 MockWs)

const ws = new WebSocket('ws://localhost:3001/room/playground');

ws.addEventListener('open', () => console.log('connected'));
ws.addEventListener('message', (e) => {
  const msg = JSON.parse(e.data);
  switch (msg.type) {
    case 'welcome':    myUid = msg.uid;                     break;
    case 'peer-join':  chat(`${msg.uid} 加入`, 'join');      break;
    case 'peer-leave': removeAvatar(msg.uid);               break;
    case 'state':      setTarget(msg.uid, msg.x, msg.y, msg.z, msg.rot); break;
    case 'chat':       chat(`${msg.uid}: ${msg.text}`);     break;
  }
});

// 上报自己位置
setInterval(() => {
  if (ws.readyState !== 1) return;
  ws.send(JSON.stringify({
    type: 'move',
    x: camera.position.x, y: camera.position.y, z: camera.position.z, rot: yaw,
  }));
}, 100);
未连接

💡 没装 Node.js 时点击会报错(正常),装好后 node server/room-server.js 再点。 房间 URL:ws://localhost:3001/room/playground

9.12 实战 G:WebSocket vs WebRTC vs WebTransport 对比

不同游戏类型对网络要求差异极大。下面是同一个"位置广播"协议在三种传输上的实现对比, 并模拟延迟、丢包、队头阻塞三种场景观察差异。

能力与差异

能力WebSocketWebRTC DataChannelWebTransport
底层TCPUDP + SCTPUDP + QUIC (HTTP/3)
可靠传输✅ 强制✅ 可选✅ Stream 模式
不可靠传输✅ unordered+noRetrans✅ Datagram 模式
队头阻塞❌ 有(TCP)✅ 无✅ 无(多路复用)
延迟中(~50-200ms)低(P2P ~20ms)低(~20-50ms)
连接建立HTTP Upgrade,1 RTT信令+ICE,数秒HTTP/3,0-1 RTT
浏览器支持✅ 全✅ 全⚠️ Chrome 97+
典型场景聊天、回合制1v1 P2P、WebRTC 会议FPS / MOBA / 大型 MMO

三种协议的客户端代码对比

① WebSocket(最简单)

const ws = new WebSocket('wss://srv/room');
ws.onmessage = (e) => handle(JSON.parse(e.data));
ws.send(JSON.stringify(msg));
// 所有包按发送顺序到达
// 丢一个会卡住后面所有包

② WebRTC DataChannel(P2P)

const pc = new RTCPeerConnection({ iceServers });
// "游戏模式":不可靠、无序(类 UDP)
const dc = pc.createDataChannel('game', {
  ordered: false,
  maxRetransmits: 0,
});
dc.onmessage = (e) => handle(e.data);
dc.send(JSON.stringify(msg));

③ WebTransport Stream(可靠)

const wt = new WebTransport('https://srv/wt');
await wt.ready;
const stream = await wt.createBidirectionalStream();
const writer = stream.writable.getWriter();
writer.write(new TextEncoder().encode(JSON.stringify(msg)));
// 多条 stream 互不阻塞

③' WebTransport Datagram(不可靠)

const wt = new WebTransport('https://srv/wt');
await wt.ready;
const w = wt.datagrams.writable.getWriter();
w.write(new TextEncoder().encode(JSON.stringify(msg)));
// 类 UDP,最低延迟
// 不保证到达、不保证顺序
// 适合每帧位置更新

模拟三种传输的延迟与丢包

下方模拟每秒发 20 个"位置包",可调节延迟抖动与丢包率,观察三种协议下"感知延迟"差异:

📦 WebSocket (TCP 有序)

收到 0 · 平均延迟 --ms · 队头阻塞时全部卡住

🎮 WebRTC DataChannel (UDP 不可靠)

收到 0 · 平均延迟 --ms · 丢了就丢了,不阻塞

🚀 WebTransport Datagram

收到 0 · 平均延迟 --ms · 不可靠但零阻塞

🎯 结论:在 5% 丢包率下,WebSocket 会出现"一卡一卡"(前面包丢需重传,后续包排队); WebRTC / WebTransport 不可靠模式只丢那一帧,后续完全不受影响。 FPS/MOBA 之所以强烈倾向 UDP,就是因为宁愿丢一帧,也不要卡一秒

9.9 技术选型速查

按品类选

  • 棋牌 / 卡牌 / 休闲:Phaser + WebSocket
  • 弹幕 / 贪吃蛇大作战:PixiJS + Web Audio + WebRTC
  • 3D 探索 / 独立:Three.js 或 Babylon.js
  • MOBA / FPS:Unity 或 Unreal 导出 + WebTransport + WebGPU
  • 微信小游戏:Cocos Creator

性能优化

  • 纹理打包 TextureAtlas 减少 Draw Call
  • 对象池(Pool)避免 GC 抖动
  • 脏矩形:只重绘变化区域
  • OffscreenCanvas + Worker 分离逻辑与渲染
  • 静态资源 HTTP/2 多路复用 / Brotli 压缩