🌐 第四章 Web网站的运行原理
📄 一句话概括

浏览器早就不只是"看网页的窗口",它是一个装程序的容器。 静态的 HTML 文档只能"读",加上 JavaScript 之后页面能响应、能改自己,再接上后端的程序与数据库之后,它就变成了能"用"的东西——这就是 Web 应用:在线文档、邮箱、地图、协作文档,全都是跑在浏览器里的程序,你什么都没安装,却什么都能干。判据只有一句:给你看的 = 文档,给你用的 = Web 应用。 它凭什么赢过装在本机的软件?免安装、跨平台、服务器一改全员立刻新版、数据跟着账号走;代价是依赖网络、性能差一截、数据存在别人那里、随时可能被改掉或关停。

📝 本节和前面几节的关系

这一节是整章的收口——前面九节拆开的零件,在这里要拼成一个能用的东西:

  • 4.2 的 HTML、4.3 的 CSS、4.4 的 URL、4.5/4.6 的 HTTP、4.7 的 Cookie:这些都是"一个 Web 应用"身上的一部分
  • 4.1 那句"一次打开网页从来不是一次请求":本节要补上后半句——那几十上百个请求里,有很多是应用在后台自己发的(不是你在点)
  • 4.8/4.9 那条"HTTPS 之后代理只剩信封":也正因为今天的主流 Web 应用都在 HTTPS 上,代理审核才只能看到域名那一层
  • 最后和下层的账:1.5 讲的"应用程序是网络的主角",在这里有了最熟悉的一个例子

一、从"文档"到"应用":浏览器身份的变化

回顾 4.2:HTML 只干一件事——给内容贴标签。浏览器把标签解析成一棵树,然后按这棵树画页面。

那个阶段的浏览器,本质是一个排版阅读器。它做的事情是"把文档显示出来"。

变化发生在两处:

阶段 浏览器在干什么 你的角色
① 静态文档 把 HTML 显示出来 读(只能看)
② + JavaScript 页面能响应你的操作、还能改自己那棵树 操作(点了有反应)
③ + 后端与数据 界面 + 逻辑 + 数据齐全,能完成一件完整的事 使用(这才是"应用")
📝 判据就一句:给你看的,还是给你用的?
  • 新闻页面:你读完就走 → 文档
  • 在线文档编辑器:你写、它存、别人还能同时改 → Web 应用
  • 电商网站:商品介绍页是文档,购物车和下单是应用 ——同一个网站里两种都有

所以"Web 应用"不是一个非黑即白的分类,而是一条谱系。越往"用"那头走,它就越像一个软件。

二、浏览器是怎么变成"应用容器"的

一个 Web 应用跑起来,靠的是四样东西各就各位(前三样你在 4.2、4.3 都见过):

能力 谁提供 干什么
结构 HTML / DOM 树(收 4.2) 内容与界面的骨架
外观 CSS(收 4.3) 长什么样
行为 JavaScript 能计算、能改 DOM、能发请求 —— "应用"的关键就在这一样
数据 后端程序 + 数据库 真正记账本的地方(收 4.7 的"账本在服务器")

除此之外,浏览器还向网页开放了一批能力——你可以把它们想成浏览器发给网页的一个工具柜:

📝 关键的一条界线:这些能力都是"浏览器给的"

网页不能自己去翻你的硬盘、装驱动、改系统设置。它能做的每一件事,都得先由浏览器开口子——这就是"沙箱"。它既是 Web 应用"打不穿"的原因,也是它"干不了重活"的原因(第八节会算这笔账)。

三、一个操作背后,谁在干活

这是本节最该看懂的一张分工图:同一件事,被拆到了两个地方做。

浏览器这一侧 服务器这一侧
负责 界面、交互、临时状态 业务规则、数据、会话
具体是 HTML / CSS / JavaScript / Cookie / 缓存 后端程序 / 数据库 / 会话(收 4.7)
看得见吗 你全都看得见(F12 一按就有) 你一点都看不见
能改吗 你能改(改的是本机那份) 只有运维能改

一条铁律,值得抄下来:

⚠️ 前端的一切都在你的控制之下——所以**永远不能拿前端做安全判断**

界面上写"最多能领 3 次",那是建议;真正管用的是服务器上那道检查。

同理:网页上的"密码强度校验""金额不能为负""你没有权限点这个按钮"——都只是体验。真正的判断必须在服务器那一侧。这条在 4.9 讲审计时也出现过同一张底牌:记录和裁判都在别人那一侧。

3.1 两种请求:整页重来 vs 只搬数据

整页请求 只搬数据
要回来的是什么 一整个 HTML 文档 + CSS + JS + 图片 一小段数据(通常是 JSON)
页面会闪一下吗 会(整页重画) 不会(只是那一小块内容换了)
什么时候用 第一次打开、按 F5 之后每一次操作
名字 传统页面跳转 Ajax / fetch ——从"网页"变"应用"的分水岭

这里的 JSON 顺便值得认识一下:它是给程序吃的格式,长得像一堆键值对:

{ "now": "16:48:03", "user": "bozai", "unread": 2 }

对照 4.2 讲 HTML 的那句话就很清楚:HTML 是给人看的,JSON 是给程序吃的。 只不过两者进浏览器时都走同一套 HTTP(收 4.5),也都靠 Content-Type 告诉浏览器该怎么处理(收 4.6)——这一处正好把 4.6 那个"后缀 → 类型 → 浏览器行为"的链条又走了一遍。

四、Web 应用凭什么赢过"装在本机的软件"

装在本机的软件 Web 应用
安装 要下载安装包、点下一步 打开网址就能用
升级 手动更新(或替你自动更新) 服务器一改,所有人立刻是新版
平台 Windows 版 / Mac 版 / 手机版各来一份 一份代码,全平台都跑
数据在哪 在你自己硬盘上 在服务器上(换设备也在,但受制于人)
离线能用吗 能 大多要联网
性能 能吃满本机硬件 差一截(受浏览器沙箱限制)
能碰系统吗 能(装驱动、改设置) 不能(关在沙箱里)

这张表里有一条推论特别值得记住:

💡 "安装"这件事被消灭了

对开发者:不用为每个系统各做一份。 对用户:不用等更新、不用腾硬盘。

这两条加起来,就是为什么今天很多新软件只做网页版。

而收 1.8 的账也在这里对上了:SaaS 最常见的样子,就是一个 Web 应用——租来的软件,用浏览器打开。

五、代价也很实在:Web 应用的四笔账

好处说完了,另外半张脸也得看清:

  1. 服务器说改就改,你没有选择权。装在本机的软件,你可以不升级;Web 应用是"一次改动、全员生效"——昨天的用法今天可能就变了,而且没有"回退到旧版"这个选项。
  2. 数据存在别人那里。服务一关停,你的数据能不能拿回来,全看它有没有导出功能——所以"能不能导出"是一个服务值不值得用的硬指标。
  3. 离线就废。现在有 PWA / Service Worker 这类技术补一部分(能离线、能装到桌面像本地软件),但本质仍是补丁:数据最终还是要和服务器对齐。
  4. 它是很耗资源的一种"软件"。浏览器同时开几十个标签页,就是同时跑着几十个应用——每个都在吃内存和电。
⚠️ 还有一条容易被忽略:**它把"你能看到的东西"也交出去了**

前端代码(HTML、CSS、JavaScript)全都下载到了你的浏览器里,谁都能看。 所以:不要指望界面上的"隐藏"能守住秘密——F12 一按,都在那里(收 4.2 那条"F12 的元素面板和源码常常不一样",正面用是调试,反面用就是"前端没有秘密")。

六、站在网络这一侧看:Web 应用改了什么

这一点是本章最该带走的视角转换——Web 应用把网络的"流量形状"整个改掉了。

文档时代 应用时代
请求次数 少 多
每次大小 大(整页 HTML + 图) 小(一小段 JSON)
频率 你点一下才有 你一敲键盘、一滚动就可能有一次
有没有"服务器主动推" 没有,只能你去问 有(聊天、协作编辑:WebSocket)

于是 4.1 那句话有了后半句:

一次打开网页从来不是一次请求——而在 Web 应用里,其中很大一部分请求根本不是你点的,是应用自己在后台发的(自动保存、消息轮询、状态同步、埋点统计)。

这也解释了 4.5 讲的无状态为什么在今天更重要:每次自动请求都要自己带上凭据(Cookie / Token,收 4.7),服务器那边不记得你是谁。

七、动手 Demo(Demo47):亲手看见"页面没刷新,内容却变了"

思路:用一个文件做一个最小的 Web 应用——一个页面 + 一个数据接口。点按钮时,页面上的时间会变,但整页不会刷新。这一眼,就是"网页"和"应用"的分界线。

第 1 步 · 新建 app_demo.py

import http.server
import json
import datetime

PAGE = """<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8">
  <title>最小 Web 应用</title>
</head>
<body>
  <h1>这是一个 Web 应用</h1>
  <p>服务器时间:<span id="t">(还没取)</span></p>
  <p>你点了 <span id="n">0</span> 次</p>
  <button id="btn">刷新时间(不刷新页面)</button>

  <script>
    let n = 0;
    document.getElementById("btn").onclick = async () => {
      const resp = await fetch("/api/time");     // 只取数据,不要整页
      const data = await resp.json();
      document.getElementById("t").textContent = data.now;
      n = n + 1;
      document.getElementById("n").textContent = n;
    };
  </script>
</body>
</html>
"""


class Handler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == "/api/time":
            body = json.dumps(
                {"now": datetime.datetime.now().strftime("%H:%M:%S")},
                ensure_ascii=False).encode("utf-8")
            ctype = "application/json; charset=utf-8"
        else:
            body = PAGE.encode("utf-8")
            ctype = "text/html; charset=utf-8"

        self.send_response(200)
        self.send_header("Content-Type", ctype)
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)

    def log_message(self, fmt, *args):
        print("[服务器] " + fmt % args)


http.server.HTTPServer(("127.0.0.1", 8000), Handler).serve_forever()

第 2 步 · 起起来

python app_demo.py

第 3 步 · 打开页面

浏览器访问 http://127.0.0.1:8000。

第 4 步 · F12 → 网络面板 → 把筛选器点成 Fetch/XHR

第 5 步 · 连点几次"刷新时间"(本节核心现场)

你会看到:

对比一下就懂了:按一下 F5,网络面板里会多出一条 document 请求,而且页面整体重画一次。

"整页 document" 和 "一小段 fetch",就是网页与 Web 应用的分界。

第 6 步 · 点开那条 /api/time,看它的响应

{"now": "16:48:03"}

对照一下:4.2 那个 HTML 是给人看的(有标签、有结构),这一段是给程序吃的(只有数据)。同一条 HTTP 通道,运的东西不一样。

第 7 步 · 断网再点一次

点击后页面没反应,控制台报错——Web 应用对网络的依赖,就在这里。你本地那个 HTML 文件明明还在,可它没有数据可用了。

第 8 步 · 看后端那一侧的日志

app_demo.py 的窗口里,每点一次就落一行——你点的是界面,真正记事的在服务器(收 1.7 门房日志的老套路)。

八、30 秒实验

  1. 30 秒:F12 → 网络面板 → 筛选Fetch/XHR`` —— 打开一个你常用的网站(邮箱、在线文档),什么都不点光看,也会有几个请求自己在跑。
  2. 30 秒:在同一个网站上做一次操作(收藏一条、改个设置)—— 数一数多出来的是 document 还是 fetch。这一眼就是"文档"和"应用"的分别。
  3. 30 秒:右键 → 查看网页源代码,再对照 F12 元素面板 —— 两处内容不一样(收 4.2):源码是"送来的那份",元素面板是"应用跑过之后的样子"。

九、常见误区

  1. "Web 应用就是把网页做得花哨一点。" —— 判据不是花不花哨,是它是给你读的还是给你用的、有没有跑在浏览器里的程序逻辑。
  2. "页面不刷新,就跟本地软件一样了。" —— 它仍然住在沙箱里:不能随意读写硬盘、不能改系统设置、每一次要设备能力都得你点同意。
  3. "Web 应用不用安装,所以什么都没存到我电脑上。" —— 缓存、localStorage、Cookie(收 4.7)都在你电脑上,只是你"没做安装这个动作"而已。
  4. "浏览器里打开的都叫网页。" —— 浏览器里可能跑着应用,也可能是本地文件(file://,收 4.2),还可能是个 PDF 阅读器。浏览器是个平台,不只干一件事。
  5. "Web 应用每次操作都要下载整个页面。" —— 主流做法是只取数据(JSON);每次整页重来反而是老旧的做法。
  6. "服务器改版了,我这边不受影响。" —— 一次改动全员生效正是它的特点,而且你没有"回退旧版"这个选项(本机软件可以做版本选择,Web 应用不行)。
  7. "数据放云端比放本机更安全。" —— 安全性取决于服务方怎么管,跟"在云端"没有必然关系;而且你同时把数据主权交了出去。
  8. "前端界面写"最多 3 次",那就只能 3 次。" —— 前端说的一切都在你的掌控范围内、也都能被改。真正的规则必须在服务器上判(本节第三节那条铁律)。
  9. "Web 应用性能天生就差,做不了正事。" —— 差的是"受沙箱与 JS 限制",但差距在缩小(WebAssembly 等),很多正经工作已经够用。
  10. "SaaS 和 Web 应用是两码事。" —— SaaS 最常见的样子就是一个 Web 应用:软件在别人服务器上跑(收 1.7/1.8),你用浏览器"租着用"。

十、动手自测

❓ 自测一下
  1. 判断"文档"还是"Web 应用"的那条分界线是什么?
  2. 让静态页面变成"能用的东西",多了哪三样东西?
  3. 浏览器为什么不能随意读写你的硬盘?这带来什么好处、什么限制?
  4. 界面与交互在哪一侧跑?业务规则与数据在哪一侧跑?
  5. 为什么说"前端做的一切安全判断都不算数"?
  6. "整页请求"和"只搬数据"有什么差别?后者为什么是"网页到应用"的分水岭?
  7. Web 应用相比本机软件的四个优势、四个代价各是什么?
  8. 为什么说"安装这件事被消灭了"?这对应 1.8 的哪一类云服务?
  9. Web 应用把网络流量的形状改成了什么样?
  10. 为什么"查看网页源代码"和 F12 元素面板里看到的不一样?

答案: ① 给你看的 = 文档;给你用的 = Web 应用。 它是一条谱系而不是非黑即白:一个网站里常常两种都有(商品详情页是文档、购物车是应用)。② JavaScript(行为:能计算、能改 DOM、能发请求)、后端程序(业务规则)、数据库(真正的数据账本)。前两样让页面"活"起来,第三样让它"记得住"。③ 因为网页的能力全部由浏览器开口子——它够不到沙箱之外(沙箱)。好处是打不穿(网页再坏也翻不了你的硬盘);限制是干不了重活(装驱动、深度读写文件都不行)。④ 界面与交互在浏览器里跑(HTML/CSS/JavaScript、Cookie、缓存);业务规则和数据在服务器上跑(后端程序、数据库、会话,收 4.7)。⑤ 因为前端的一切都下载到了你的浏览器里、全在你掌控之下、也都能被改。"密码校验""金额不能为负""没有权限"写在界面上只是体验;真正的判断必须在服务器上——和 4.9 那条"记录与裁判都在别人那一侧"是同一张底牌。⑥ 整页请求要回一整个 HTML 文档 + CSS + JS + 图片、页面会闪;只搬数据只要回一小段 JSON、页面不闪。因为页面不重画却内容变了,用户感受到的就不再是"翻了一页",而是"点了个按钮应用响应了"——这就是从"网页"变成"应用"的那一步(fetch / Ajax)。⑦ 优势:免安装 / 服务器一改全员立刻新版 / 一份代码全平台 / 数据跟着账号走(换设备还在)。代价:依赖网络(大多离线就废)/ 性能差一截 / 数据在别人那里(服务关停可能拿不回来,所以"能不能导出"是硬指标)/ 说改就改、没有版本选择权。⑧ 因为开发者不用为每个系统各做一份、用户不用下载安装也不用等更新——打开网址就能用。对应 1.8 的 SaaS:软件在别人服务器上跑,你用浏览器租着用。⑨ 从"请求少而大、你点一下才有",变成"请求多而小、还很频繁(你一敲键盘、一滚动就可能有一次),并且出现服务器主动推(WebSocket,聊天与协作编辑)"。这也是 4.1 那句"一次打开网页几十上百次请求"的后半句:其中很大一部分根本不是你点的(自动保存、状态同步、埋点)。⑩ 源码是服务器送来的那份静态文本;元素面板是应用(JavaScript)跑过之后那棵 DOM 树的样子——脚本改的是树,硬盘上的 .html 一个字没变(收 4.2)。

十一、下一步学什么

相关笔记