Web 基础:从页面源文本到可恢复交互
这一部分面向已经做过业务页面、但想把浏览器原理补扎实的开发者。重点不在重新背标签和语法,而在理解一份页面如何从 HTML 响应变成可见内容,用户操作又如何经过事件、网络和存储返回界面。
学完后,你应该能从浏览器里的实际现象向下追查原因:为什么查看源代码和 Elements 面板不同,为什么某条 CSS 声明没有生效,为什么旧请求覆盖了新结果,为什么 TypeScript 没报错却仍在运行时崩溃,以及离线、权限变化或发布切换后如何保留用户正在做的事。
开始前需要具备什么
你需要能独立完成常规页面和接口联调,理解现代 JavaScript 模块、Promise 与基本 TypeScript。这里不会从变量、选择器或标签定义讲起;遇到某个语法点时,可以随文查阅对应规范和 MDN。
五篇文章怎样衔接
| 文章 | 主要问题 | 读完后能解释什么 |
|---|---|---|
| HTML | 浏览器怎样解析文档并建立语义、表单和焦点关系 | 源文本、DOM、可访问性树和默认交互为何不同 |
| CSS | 层叠、包含块和布局算法怎样得到最终画面 | 声明为何胜出,布局为何溢出,动画为何卡顿 |
| JavaScript | 对象、模块、任务队列和内存怎样组织程序 | 异步顺序、取消、资源清理和旧状态覆盖 |
| TypeScript | 静态类型能保证什么,在哪里必须回到运行时验证 | 类型收窄、公共类型、编译产物和数据边界 |
| Browser / Web APIs | 页面怎样使用网络、存储、Worker、权限和性能观察能力 | 请求、缓存、事务、并发上下文和降级路径 |
建议按表格顺序阅读。HTML 先确定内容和操作的含义,CSS 再处理呈现,JavaScript 接管时间与状态,TypeScript 收紧开发期关系,浏览器 API 最后把程序连接到网络、存储和宿主策略。实际排障时则可以从现象直接进入对应文章,不必每次从头读起。
职责对比 用于判断一个问题应该由哪一层解决,以及什么时候原生实现已经足够;阶段总结 把五层重新接成一条完整请求和渲染路径。
贯穿练习:技术知识浏览器
整个学习过程只使用一个小应用。用户可以查看技术条目、搜索、打开详情、加入本地对比,并通过 URL 恢复查询状态。数据来自固定 JSON;网络失败、空结果、旧缓存、窄屏、键盘操作和减少动效偏好都要有明确结果。
打开技术知识浏览器pnpm --filter @roadmap/web-foundations test
pnpm preview命令会启动 Vite,并自动打开示例。若端口已占用,以终端显示的地址为准。
这个练习刻意不引入 UI 框架、全局状态库、账户系统和数据库。应用足够小,读者可以直接看见每层的输入和输出;它又包含真实链接、表单、异步请求和本地状态,足以暴露常见工程问题。
从静态页面开始
第一轮只写 HTML。页面需要有正确语言、标题、主区域、搜索表单、结果列表、详情链接和错误说明。禁用 JavaScript 后,读者仍应知道页面提供什么内容,能通过链接和表单完成最基本的阅读与查询。
这一轮先不要追求视觉效果。用浏览器的 Elements 与 Accessibility 面板检查标题层级、可访问名称、焦点顺序和表单提交内容。源文本若已经缺少语义,后面的 CSS 和脚本无法可靠补回来。
再处理布局和视觉反馈
第二轮加入 CSS,但保持 DOM 阅读顺序不变。布局需要经受窄容器、200% 缩放、长文本、不同字体、深浅主题和减少动效偏好。把计算样式、包含块、滚动容器和层叠来源查清楚,再决定是否增加选择器权重或定位规则。
性能检查也从这里开始。图片尺寸、字体加载、布局变化和昂贵动画都会影响用户何时能稳定阅读页面。记录真实设备或受控模拟下的结果,不用单张桌面截图代替布局验证。
让交互成为可解释的状态变化
第三轮加入 JavaScript。查询文本、选中条目和 URL 参数应有明确所有者;派生结果由纯函数计算;事件监听、计时器、请求和 Worker 都要有创建与清理位置。
搜索请求需要处理快速输入和乱序返回。新查询开始时取消旧请求,即使取消已经来不及,也要用请求标识或当前查询确认响应仍然有效。加载、空结果、失败和成功是不同状态,不能只用一个布尔值拼凑。
这一轮适合故意制造失败:让旧请求晚于新请求返回,让数据解析抛错,让页面在请求中被关闭,再观察是否还有监听器、未处理 Promise 或过期写入。
把类型放在正确边界
第四轮接入 TypeScript。外部 JSON、URL 参数、本地存储和表单内容都以 unknown 或原始字符串进入,在边界处验证后再转换为领域对象。类型声明描述已经验证的关系,不能替代 JSON 解析、权限检查或服务器约束。
同时检查编译产物。target、module、moduleResolution 和 lib 会影响语法输出与类型可见范围,但不会自动补齐目标浏览器缺少的 API。公共类型要让调用方读得懂,也要避免用复杂条件类型把编译时间和错误信息拖垮。
最后接入网络、存储和后台能力
第五轮讨论 Fetch、Streams、Cache、IndexedDB、Service Worker、Worker、权限和性能观察。每种能力都有自己的生命周期和失败方式:请求可以在服务器写入后断线,存储可能被回收,后台任务可能没有机会执行,多个标签页也可能同时修改同一份状态。
先保证没有缓存和后台增强时功能正确,再逐步加入这些能力。需要离线写入时,保存的是带操作 ID 和起始版本的用户意图,不是把整个对象无条件覆盖回服务器。恢复联网后要区分确认成功、可重试失败、权限拒绝和版本冲突。
一条实用的排障顺序
页面出问题时,按第一个错误状态向下追:
- Network 中是否拿到了预期 HTML、数据和资源。
- Elements 与可访问性树中是否存在正确结构和名称。
- Computed、Layout 和绘制记录是否解释当前画面。
- 事件、任务队列和请求时序是否仍属于当前用户意图。
- 外部数据是否经过运行时验证,类型与真实值是否一致。
- 缓存、存储、权限和多个浏览上下文是否改变了结果。
每次只验证一个假设,并保存 URL、输入、版本、浏览器记录和用户可见结果。这样积累的证据可以直接带到框架、水合、构建产物和后端问题中。
什么算学完
完成这一部分后,你应能独立解释一条完整路径:用户打开带查询参数的 URL,浏览器解析 HTML 和样式,脚本恢复状态并发出可取消请求,响应经过验证后更新页面,安全数据可以缓存,离线或权限失败时仍有明确退路。
还要能说清每一步由谁负责。HTML 负责内容和原生操作,CSS 负责呈现,JavaScript 负责时序与状态,TypeScript 提供开发期关系检查,浏览器 API 提供宿主能力,服务器负责最终数据和权限。框架会改变这些责任的组织方式,但不会让其中任何一项消失。
准备好后进入 前端框架,用同一个用户任务观察 React、Vue、Angular、Svelte 及其服务端框架如何组织更新、数据和部署。