English
Web and application development combines interfaces, protocols, persistent data, and computation into a service people can use. A simple task such as reserving a seat crosses several boundaries: the user expresses an intention, a client sends a request, the server checks permissions and availability, a transaction records the result, and the interface reports what happened.
These boundaries explain many failures. A request can succeed while its response is lost. A browser can display stale data. Two users can try to reserve the same seat. A frontend type annotation cannot establish that an incoming network value has the expected shape. Understanding the sequence and its guarantees helps more than treating each framework call as an isolated feature.
This section connects networks (§4.3), databases (§4.4), security (§4.5), distributed systems (§4.6), and HCI (§6.2). It does not require mastering every field before building a small application. Start with one complete user task, then make its state, failures, and performance observable.
Prerequisites: Programming in one language (§2.1), basic HTTP and database operations. Add transaction, security, and concurrency concepts as the application crosses those boundaries; a working tutorial is the beginning of that investigation.
From Linked Documents to Networked Applications
The Web grew from Berners-Lee’s work at CERN in 1989–1991, using linked resources, addresses, a transfer protocol, and document formats on top of existing Internet infrastructure. The first browser also supported editing. The Web should not be identified with the Internet itself, or its early design reduced to permanently static files. Common interfaces let independently managed systems publish and link information.
CGI connected web servers to programs that generated responses, while server-side languages and application servers made dynamic content easier to build. CGI conventions developed in the 1990s; RFC 3875 documented CGI/1.1 in 2004. Process creation was one implementation cost, not the definition of dynamic web computation. Persistent application processes and database connections changed that cost model.
Frameworks organized recurring work such as routing, templates, database access, validation, and sessions. Rails popularized conventions and integrated tools for a particular style of application, building on older ideas including MVC. Other frameworks made different choices. A monolithic application can be modular and scalable; dividing it into network services introduces additional failure and coordination costs as well as possible benefits.
Client-side scripting and asynchronous requests allowed parts of a page to change without full navigation. Ajax named a combination of techniques rather than inventing all of them at once. This improved some interactions while moving more state, failure handling, and accessibility responsibilities into client code. Document-oriented and application-oriented uses continued to coexist.
Node.js combined server-side JavaScript with event-driven I/O. An event loop can coordinate many waiting operations, but CPU-heavy JavaScript can still block that loop unless work is moved or divided appropriately. Component libraries such as React encouraged expressing views from state. Reconciliation uses implementation choices and heuristics; it does not guarantee a globally minimum sequence of DOM edits, and component-based design predates React.
TypeScript added static checking to JavaScript programs while preserving a route for incremental adoption. Its checks do not replace runtime validation of untrusted inputs. Server rendering, client rendering, streaming, and hydration offer different allocations of work. Sending HTML early can improve initial display while later scripts still impose download and execution costs.
Managed functions and edge execution changed deployment and scaling responsibilities. They retain servers, quotas, startup behavior, data-location constraints, and operational work. Placing computation near a user helps only when the critical dependencies also permit lower latency. Adding AI-generated outputs or tool calls introduces further evaluation and authorization requirements (§5.5–5.7), while ordinary application concerns such as transactions, retries, and clear feedback remain.
Architecture, Performance, and the Full Stack
The HTTP Layer: Understanding the Protocol
An HTTP exchange has a request method, target, and fields, with a body where applicable; a response carries status and fields, with content when permitted. Method semantics affect clients, intermediaries, and retries. GET requests retrieval and is defined as safe; PUT and DELETE are idempotent in their intended effect, while arbitrary POST operations are not. Idempotency does not require repeated responses to have identical status or content. RFC 9110 defines these semantics.
HTTP/2 multiplexes streams over TCP, whose ordered delivery can still delay unrelated streams after packet loss. HTTP/3 uses QUIC streams to avoid that cross-stream transport dependency. Loss can still delay affected data, and streams share congestion and resource constraints. A protocol version changes transport behavior, not the application’s transaction semantics.
Caching separates freshness from validation. A cache may reuse a fresh response according to its policy; a stale response may be validated using a validator such as an ETag. A 304 response can authorize reuse of a stored representation without retransmitting its body. no-cache permits storage but requires validation before reuse; no-store directs caches not to store the message. Apply the rules to the relevant cache and response, including personalization and cache keys. MDN’s cache-control reference explains the directives.
REST is an architectural style, not a synonym for JSON over HTTP or resource-shaped URLs. Stateless interaction concerns the information needed to understand a request; it does not prohibit persistent business data on the server. GraphQL supplies a query language and execution model for selecting fields, but does not automatically remove expensive database work or authorization requirements. Choose an interface around its consumers and observable semantics.
Authentication establishes identity; authorization decides whether that identity may perform this operation on this object. A cookie is a browser storage and transmission mechanism; a token is a credential or data representation and may itself travel in a cookie. Signed JWT contents are not automatically encrypted. Validate credentials and claims appropriately, enforce permissions on the server, and consider both script injection and forged cross-site requests. CORS controls certain browser cross-origin access; it is not a substitute for authorization. OAuth supplies delegated authorization, while authentication protocols such as OpenID Connect add identity-related semantics.
State Management: Ownership, Consistency, and Recovery
For each state value, identify its owner, lifetime, authoritative source, and allowed transitions. Local interface state, a cached server response, a durable database record, and an in-progress operation have different roles. A state-management library can organize updates, but the application still has to define what counts as success.
Consider creating a reservation. The server commits it, but the response is lost. A client timeout means the outcome is unknown, not that nothing happened. Retrying an unprotected create request can duplicate the reservation. An application-level idempotency key can help when the server atomically associates the key with one operation and its result, defines scope and retention, and rejects incompatible reuse. A key attached only by the client provides no such guarantee.
Concurrent edits create a different problem. Two clients read version 7 and make changes. An update conditional on that version can let one succeed while the other receives a conflict instead of silently overwriting it. The application then needs a policy for refreshing, merging, or asking the user to choose. Database constraints and transactions remain responsible for invariants such as one confirmed reservation per seat.
Optimistic interfaces display an anticipated result before confirmation. They can improve responsiveness but must reconcile rejection, corrections, and delayed responses. If requests for two search terms return out of order, the older response must not overwrite the newer result. Associate responses with the relevant request or state version; cancellation alone need not undo work already performed on the server.
Push mechanisms such as WebSocket and Server-Sent Events can deliver updates, while polling remains useful for some workloads. A connection does not guarantee that a client has every event or the latest state after disconnection. Define event identifiers, ordering, replay, or snapshot resynchronization as needed. This connects application state management to the stream and distributed-system principles of §4.6–4.8.
Performance: Where Measurement Replaces Intuition
Measure the user’s task across the request and rendering path. Network setup, transfer, server queueing, database work, script execution, layout, and painting can each contribute. Some overlap; others form a dependency chain. Optimizing a stage that is not limiting the task may have little visible effect.
Browser metrics distinguish different experiences. LCP concerns when the largest qualifying content is displayed; INP concerns responsiveness to interactions; CLS measures unexpected layout shifts. Web Vitals documents their definitions. Field distributions across devices and connections complement repeatable lab measurements. A single performance score does not represent every user or establish that a task is usable.
Inspect a network waterfall and a main-thread trace before deciding what to reduce. Compression and appropriate image sizes reduce transfers; code splitting can defer unused work; caching can avoid requests or computation. Preloading every resource can instead create competition. Server rendering can accelerate visible content while substantial hydration work still delays interaction.
An N+1 query pattern is a concrete dependency cost. If one list query is followed by 100 sequential queries for related data, there are 101 round trips. At an illustrative 5 ms of fixed overhead each, that is about 505 ms before other work. Two batched queries would incur about 10 ms of that overhead, but may transfer more data or require different indexing. Measure the real query plan, rows, and concurrency before applying a join indiscriminately.
Connection pools bound and reuse database connections. If 20 application instances each allow 30 connections, they can collectively request 600, which may exceed a shared database’s capacity. Larger pools can increase contention rather than throughput. Queue time, transaction duration, admission limits, and timeouts matter alongside connection reuse; establishing a connection has no universal fixed latency.
Caching requires a correctness policy as well as a hit-rate target. Define keys, invalidation, freshness, isolation of private data, and behavior on misses or backend failure. Compare the complete request path after a change, including tail latency and failure recovery. A faster average with occasional severe stalls can still worsen the user experience.
What Studying This Changes
You should be able to follow one user action through client state, protocol messages, server checks, database effects, and visible feedback. State what happens when a response is lost, data is stale, or another user changes the same record.
Build a small reservation or editing application and deliberately delay, duplicate, and reorder requests in a controlled development environment. Verify that retries do not duplicate effects, stale responses do not overwrite current state, and conflicting updates are handled explicitly. Include an accessible recovery flow.
Then measure one slow task and identify its critical dependency. Use browser traces, server timings, and query plans to connect the symptom to a mechanism. These skills transfer across frameworks because they explain the behavior the framework must ultimately implement.
Resources
Books and Texts
Use MDN Web Docs for practical explanations and browser behavior, and the linked standards when precise protocol or API semantics matter. Designing Data-Intensive Applications supplies database and distributed-system depth; Fowler explains recurring application patterns. Use OWASP’s testing guidance and ASVS to specify relevant security checks. Read architecture books against the application’s actual scale and coordination needs.
| Book/Resource | Role | Type |
|---|---|---|
| Kleppmann, Designing Data-Intensive Applications | Backend and distributed systems depth | Depth |
| Fowler, Patterns of Enterprise Application Architecture | Architectural patterns vocabulary | Depth |
| OWASP Testing Guide + ASVS (free) | Application security reference | Reference |
| Newman, Building Microservices (2nd ed.) | Microservices architecture | Depth |
| MDN Web Docs (free) | Web-platform explanations and reference | Reference |
| React Docs (free) | Modern React official learning path | Practice |
| TypeScript Handbook (free) | TypeScript language reference | Reference |
| Crockford, JavaScript: The Good Parts | JavaScript conceptual foundation | Auxiliary |
| Beck, Test-Driven Development | Testing discipline foundation | Depth |
| Haverbeke, Eloquent JavaScript (online edition, free) | JavaScript and programming exercises | Entry |
| Osmani, Learning JavaScript Design Patterns (free) | JavaScript patterns | Depth |
Courses and Lectures
The Odin Project and Full Stack Open offer project sequences; choose one instead of repeatedly restarting framework introductions. Study HTML semantics, CSS layout, HTTP, and persistent state through the project. Official framework and language documentation then explains particular implementation choices. PostgreSQL documentation is useful when a query or transaction’s behavior needs checking.
| Course | Platform | Type |
|---|---|---|
| The Odin Project (free) | theodinproject.com | Practice |
| Full Stack Open (free) | fullstackopen.com | Entry |
| Josh W Comeau CSS for JS Developers | joshwcomeau.com | Depth |
| PostgreSQL documentation (free) | postgresql.org/docs | Reference |
Practice, Tools, and Current Sources
Use browser developer tools and curl to inspect a request directly, then relate it to server logs and a database query plan. EXPLAIN ANALYZE executes the statement, so use an appropriate development dataset for experiments. Browser automation can reproduce a user flow and its failure cases, but a passing test covers only the conditions asserted. When reading an open-source application, trace one feature through its layers before attempting to understand the entire repository.
| Resource | Platform | Type |
|---|---|---|
| Browser DevTools (free, built-in) | Chrome / Firefox / Safari | Practice |
| Postman / Insomnia / curl (free) | Various | Practice |
| PostgreSQL + EXPLAIN ANALYZE (free) | postgresql.org | Practice |
| Playwright / Cypress (free) | playwright.dev / cypress.io | Practice |
| Discourse / GitLab / Cal.com source | GitHub | Reference |
| Chrome Lighthouse (free, built-in) | Chrome DevTools | Practice |
| web.dev Learn Performance (free) | web.dev | Practice |
| HTTP Archive / Web Almanac (free) | httparchive.org | Reference |
Traps
| Trap | Why it misleads | Better response |
|---|---|---|
| Treating framework knowledge as a substitute for protocol and data semantics | Libraries organize work but do not choose every consistency or failure policy. | Trace one complete user operation across its boundaries. |
| Interpreting a timeout as proof that a write failed | The write may commit before its response is lost. | Define retry semantics and check the operation result. |
| Confusing cookies, tokens, and authorization | Credential format or transport does not establish permission for an object. | Validate identity and enforce each operation’s permissions on the server. |
| Assuming a frontend type proves incoming data is valid | Static annotations do not inspect arbitrary runtime input. | Validate network data and handle malformed or unexpected values. |
| Optimizing a score without inspecting the critical path | Lab averages can hide dependency delays and slow-device behavior. | Combine field observations with traces and representative tasks. |
| Adding services or caches without specifying their failure behavior | More components add coordination, stale data, and recovery requirements. | Measure the need, then define ownership, invalidation, and recovery. |
中文
Web 与应用开发把界面、协议、持久数据和计算组合成人们可使用的服务。预订座位这样一个简单任务,也会跨越多个边界:用户表达意图,客户端发送请求,服务器检查权限和余位,事务记录结果,界面再报告发生了什么。
这些边界解释了许多失败。请求可能成功,而响应丢失;浏览器可能显示旧数据;两个用户可能同时预订同一个座位;前端类型标注也不能证明收到的网络值符合预期结构。理解过程及其保证,比把每次框架调用当成独立功能更有帮助。
本节连接网络(§4.3)、数据库(§4.4)、安全(§4.5)、分布式系统(§4.6)和 HCI(§6.2)。这不意味着开始小应用前必须掌握全部领域。可以从一个完整用户任务开始,再让其状态、失败和性能变得可观察。
前置知识:一门语言的编程能力(§2.1)、基本 HTTP 和数据库操作。随着应用跨越相关边界,补充事务、安全和并发概念;跑通教程是调查这些问题的开始。
从链接文档到网络应用
Web 来自 Berners-Lee 在 CERN 于 1989–1991 年开展的工作,在已有互联网基础设施上使用链接资源、地址、传输协议和文档格式。第一个浏览器也支持编辑。Web 不等于互联网本身,早期设计也不能简化为永远不变的静态文件。共同接口让独立管理的系统能够发布和链接信息。
CGI 将 Web 服务器与生成响应的程序连接起来,服务器端语言和应用服务器使动态内容更容易构建。CGI 约定在 20 世纪 90 年代发展,RFC 3875 于 2004 年记录了 CGI/1.1。创建进程是一种实现成本,不是动态 Web 计算的定义。持久应用进程和数据库连接改变了这种成本模型。
框架组织路由、模板、数据库访问、验证和会话等重复工作。Rails 在 MVC 等更早思想之上,为特定应用风格推广了约定与集成工具。其他框架作出不同选择。单体应用可以模块化,也可以扩展;拆成网络服务在带来潜在好处的同时,也增加失败与协调成本。
客户端脚本和异步请求,使页面可以局部变化而不必完整导航。Ajax 为一组技术命名,而不是一次发明了全部技术。这改善了部分交互,也把更多状态、失败处理和无障碍责任移入客户端。面向文档与面向应用的用途仍然并存。
Node.js 将服务器端 JavaScript 与事件驱动 I/O 结合起来。事件循环可以协调大量等待中的操作,但计算密集的 JavaScript 仍可能阻塞循环,需要适当地迁移或拆分工作。React 等组件库鼓励从状态表达视图。协调更新依赖具体实现与启发式,并不保证全局最少的 DOM 编辑序列,组件式设计也早于 React。
TypeScript 为 JavaScript 程序增加静态检查,并保留渐进采用的途径。这些检查不能替代对不可信输入的运行时验证。服务器渲染、客户端渲染、流式传输和水合,将工作作出不同分配。尽早发送 HTML 可能改善初始显示,而后续脚本仍有下载和执行成本。
托管函数与边缘执行改变了部署和扩缩容的责任分工,仍然存在服务器、配额、启动行为、数据位置约束和运维工作。把计算移近用户,只有在关键依赖也允许时才能降低延迟。加入 AI 输出或工具调用,会增加评估和授权要求(§5.5–5.7),而事务、重试和明确反馈等应用问题仍然存在。
架构、性能与全栈
HTTP 层:理解协议
HTTP 交互包括请求方法、目标和字段,并在适用时携带请求体;响应携带状态和字段,在允许时携带内容。方法语义影响客户端、中间节点和重试。GET 请求获取表示,语义上应是安全方法;PUT 与 DELETE 的预期效果是幂等的,任意 POST 操作则不具备这一保证。幂等不要求每次响应的状态或内容完全一致。RFC 9110 定义了这些语义。
HTTP/2 在 TCP 上多路复用流,但 TCP 的有序交付仍可能在丢包后延迟无关流。HTTP/3 使用 QUIC 流,避免这种跨流的传输依赖。丢包仍会延迟受影响的数据,各流也共享拥塞和资源约束。协议版本改变传输行为,不改变应用的事务语义。
缓存区分新鲜度与验证。缓存可以按策略复用仍新鲜的响应,过期响应可以通过 ETag 等验证器核实。304 响应可以允许复用已存表示,而不用重传其内容。no-cache 允许存储,但要求复用前验证;no-store 则要求缓存不存储该消息。规则需要结合具体缓存和响应解释,包括个性化内容与缓存键。MDN 的缓存控制参考 说明了这些指令。
REST 是架构风格,不等于在 HTTP 上传 JSON,也不等于 URL 看起来像资源。无状态交互关心理解请求所需的信息,并不禁止服务器保存持久业务数据。GraphQL 提供选择字段的查询语言与执行模型,但不会自动消除昂贵的数据库操作或授权要求。应围绕消费者与可观察语义选择接口。
身份验证确定身份,授权决定该身份能否对这个对象执行这项操作。cookie 是浏览器的存储和传输机制,令牌则是凭据或数据表示,也可以通过 cookie 传输。签名 JWT 的内容不会自动加密。应适当验证凭据与声明,在服务器执行权限检查,并考虑脚本注入和跨站伪造请求。CORS 控制部分浏览器跨源访问,不能代替授权。OAuth 提供委托授权,OpenID Connect 等身份协议再增加身份相关语义。
状态管理:归属、一致性与恢复
对每个状态值,应明确所有者、生命周期、权威来源和允许的转移。本地界面状态、缓存的服务器响应、持久数据库记录与进行中的操作,承担不同角色。状态管理库可以组织更新,但应用仍需要定义什么才算成功。
考虑创建预约:服务器已经提交,响应却丢失。客户端超时表示结果未知,不代表什么都没发生。重试未经保护的创建请求,可能产生重复预约。当服务器把幂等键与一次操作及其结果原子关联、定义作用域和保留期,并拒绝不兼容的键复用时,应用层幂等键才有帮助。仅在客户端附加一个键,不会提供这种保证。
并发编辑是另一种问题。两个客户端都读取版本 7,再各自修改。附带版本条件的更新,可以让一方成功,让另一方收到冲突,而不是悄悄覆盖。随后应用需要刷新、合并或请用户选择的策略。数据库约束与事务,仍负责维护一个座位只能有一份已确认预约等不变量。
乐观界面在确认前显示预期结果,可以改善响应感,但必须处理拒绝、纠正和延迟响应。如果针对两个搜索词的请求按相反顺序返回,旧响应不应覆盖新结果。应把响应与对应请求或状态版本关联;取消请求本身不一定撤销服务器已经执行的工作。
WebSocket、Server-Sent Events 等推送机制可以交付更新,轮询也适合某些工作负载。建立连接不保证客户端获得所有事件,也不保证断线后仍持有最新状态。应按需要定义事件标识、顺序、重放或快照重新同步。这把应用状态管理与 §4.6–4.8 的流处理、分布式系统原则联系起来。
性能:测量取代直觉的地方
沿请求和渲染路径测量用户任务。网络建立、传输、服务器排队、数据库操作、脚本执行、布局和绘制,都可能贡献时间。有些可以重叠,有些构成依赖链。优化不是任务瓶颈的阶段,可能几乎没有可见效果。
浏览器指标区分不同体验。LCP 关心最大合格内容何时显示,INP 关心交互响应,CLS 衡量非预期布局偏移。Web Vitals 记录了它们的定义。不同设备与连接下的真实使用分布,与可重复的实验室测量相互补充。一个性能分数不代表所有用户,也不能证明任务可用。
先检查网络瀑布图和主线程轨迹,再决定减少什么。压缩与合适的图片尺寸减少传输,代码拆分可以推迟暂不用的工作,缓存可以省去请求或计算。但预加载所有资源,反而可能造成竞争。服务器渲染可以加快内容显示,而大量水合工作仍可能延迟交互。
N+1 查询提供了具体的依赖成本例子。若一次列表查询之后,顺序执行 100 次相关数据查询,就有 101 次往返。假设每次固定开销为 5 ms,仅这一部分就约为 505 ms。两次批量查询只承担约 10 ms 的这类开销,但可能传输更多数据,或需要不同索引。应测量真实查询计划、行数和并发,再决定是否使用连接。
连接池限制并复用数据库连接。如果 20 个应用实例各允许 30 个连接,总计就可能请求 600 个,超出共享数据库的容量。更大的池可能增加竞争,而不是吞吐量。除了连接复用,排队时间、事务时长、准入限制和超时也重要;建立连接并没有统一的固定延迟。
缓存除了命中率目标,还需要正确性策略。应定义键、失效、新鲜度、私有数据隔离,以及未命中或后端失败时的行为。改动后比较完整请求路径,包括尾延迟与失败恢复。平均更快、偶尔严重卡顿,仍可能使用户体验变差。
学习这一部分会改变什么
学完后,应能沿一次用户操作,跟踪客户端状态、协议消息、服务器检查、数据库效果和可见反馈,说明响应丢失、数据过期或另一用户修改同一记录时会发生什么。
构建一个小型预约或编辑应用,在受控开发环境中故意延迟、重复和重排请求。验证重试不会重复产生效果,旧响应不会覆盖当前状态,并发冲突会被明确处理,同时提供可访问的恢复流程。
再测量一项慢任务,找出关键依赖。用浏览器轨迹、服务器计时和查询计划,将症状与机制联系起来。这些技能能够跨框架迁移,因为它们解释了框架最终需要实现的行为。
资源
书籍与文本
用 MDN Web Docs 查阅实用解释与浏览器行为,需要精确协议或 API 语义时,再读其链接的标准。Designing Data-Intensive Applications 补充数据库与分布式系统知识,Fowler 解释反复出现的应用模式。用 OWASP 测试指南与 ASVS 定义相关安全检查。阅读架构书时,应结合应用真实的规模与协调需求。
| 书籍/资源 | 作用 | 类型 |
|---|---|---|
| Kleppmann, Designing Data-Intensive Applications | 后端与分布式系统深度 | 深入 |
| Fowler, Patterns of Enterprise Application Architecture | 架构模式词汇 | 深入 |
| OWASP Testing Guide + ASVS(免费) | 应用安全参考 | 参考 |
| Newman, Building Microservices(第 2 版) | Microservices 架构 | 深入 |
| MDN Web Docs(免费) | Web 平台解释与参考 | 参考 |
| React Docs(免费) | 现代 React 官方学习路径 | 实践 |
| TypeScript Handbook(免费) | TypeScript 语言参考 | 参考 |
| Crockford, JavaScript: The Good Parts | JavaScript 概念基础 | 辅助 |
| Beck, Test-Driven Development | 测试纪律基础 | 深入 |
| Haverbeke, Eloquent JavaScript(免费在线版) | JavaScript 与编程练习 | 入门 |
| Osmani, Learning JavaScript Design Patterns(免费) | JavaScript 模式 | 深入 |
课程与讲座
The Odin Project 与 Full Stack Open 提供项目学习顺序;选择一条,不必反复重学不同框架的入门。通过项目研究 HTML 语义、CSS 布局、HTTP 和持久状态,再用框架与语言官网文档理解具体实现选择。需要核对查询或事务行为时,可以查阅 PostgreSQL 文档。
| 课程 | 平台 | 类型 |
|---|---|---|
| The Odin Project(免费) | theodinproject.com | 实践 |
| Full Stack Open(免费) | fullstackopen.com | 入门 |
| Josh W Comeau CSS for JS Developers | joshwcomeau.com | 深入 |
| PostgreSQL documentation(免费) | postgresql.org/docs | 参考 |
实践、工具与当前资料
用浏览器开发者工具和 curl 直接检查请求,再将其与服务器日志、数据库查询计划关联起来。EXPLAIN ANALYZE 会执行语句,因此实验应使用合适的开发数据集。浏览器自动化可以复现用户流程及失败情形,但测试通过只覆盖其断言的条件。阅读开源应用时,先跨层跟踪一个功能,再逐步理解整个仓库。
| 资源 | 平台 | 类型 |
|---|---|---|
| Browser DevTools(免费,内置) | Chrome / Firefox / Safari | 实践 |
| Postman / Insomnia / curl(免费) | Various | 实践 |
| PostgreSQL + EXPLAIN ANALYZE(免费) | postgresql.org | 实践 |
| Playwright / Cypress(免费) | playwright.dev / cypress.io | 实践 |
| Discourse / GitLab / Cal.com source | GitHub | 参考 |
| Chrome Lighthouse(免费,内置) | Chrome DevTools | 实践 |
| web.dev Learn Performance(免费) | web.dev | 实践 |
| HTTP Archive / Web Almanac(免费) | httparchive.org | 参考 |
陷阱
| 误区 | 为什么会误导 | 更好的做法 |
|---|---|---|
| 用框架知识替代协议与数据语义 | 库组织工作,但不会决定所有一致性和失败策略。 | 跨边界跟踪一次完整用户操作。 |
| 把超时当成写入失败的证明 | 写入可能已经提交,只是响应丢失。 | 定义重试语义并核对操作结果。 |
| 混淆 cookie、令牌和授权 | 凭据格式或传输方式不确定对象权限。 | 验证身份,并在服务器执行每项操作的权限检查。 |
| 认为前端类型证明输入数据有效 | 静态标注不会检查任意运行时输入。 | 验证网络数据,处理格式错误或非预期值。 |
| 不检查关键路径就优化分数 | 实验室平均值可能隐藏依赖延迟和慢设备表现。 | 结合真实使用观察、轨迹和代表性任务。 |
| 未定义失败行为就增加服务或缓存 | 更多组件带来协调、旧数据和恢复要求。 | 先测量需求,再定义归属、失效和恢复。 |