跳转至

动态 Web 是什么、开发环境怎么搭

这一章讲两件事: 第一,你在浏览器里看到的网页,背后到底发生了什么样的「问答」, 为什么有的网页每次答得一样、有的每次都不一样;第二,怎样在你自己的电脑上搭出这套问答所需的全部角色, 让浏览器能问、本机能答。 读完你会拿到全书的地基——后面二十章写的每一行代码,都跑在这套问答机制上。 不需要任何基础,遇到的缩写都在当场解释。

1. 顶层全景:一次网页访问,就是一次问答

把整章压成一张图:

  浏览器(客户) ──发问──▶ 服务器 ──▶ 磁盘上的文件? ──▶ 原样发回      (静态页)
                              │
                              └─▶ 里面是 PHP? ──▶ PHP 解释器执行
                                                    │ 需要数据? ──▶ MySQL 查库
                                                    ▼
                                            拼好的 HTML ──▶ 发回浏览器

  图说:静态页是「从磁盘取文件寄出」;动态页中间多两道工序——先算(PHP),再查(MySQL)。

这张图里的三个新角色——服务器、PHP、MySQL——下面逐个讲。这里先记住一句话: 网页不是存在服务器上等着被取走的「成品」,而是服务器现场「答」出来的。 答的过程可以很简单(把磁盘上的文件原样寄回),也可以很复杂(先执行一段程序、查一次数据库,再拼出结果)。 全书的主题就是把「答」做复杂——做得每次都不一样、因人而异、因时而异。

2. 从一次具体的访问走一遍(主线例子)

挑一个最普通的动作:在浏览器地址栏输入 http://server.com,敲回车。

原书把这次访问拆成了七步,我们照着走一遍,每一步旁边有具体的状态18:

  1. 浏览器拿到这串网址,先要查出 server.com 这台机器在网络上的「门牌号」。 网址是人看的名字,机器之间认的是 IP 地址(一串数字编号)。 浏览器会去问另一个网络服务——DNS(域名系统,Domain Name System 的缩写)——把名字换成地址19。
  2. 浏览器拿着地址,向那台机器发一个「请求」:把你的主页给我。 这个请求走的规矩叫 HTTP——它只规定两件事:请求怎么问、回答怎么答20。
  3. 请求穿过互联网,到达 server.com 的 web 服务器(一台守在门口、专门接请求的软件)。 服务器没事干的时候就在「听」,听到请求就处理21。
  4. 服务器从自己的磁盘上找到主页文件,寄回给浏览器。
  5. 浏览器把收到的 HTML(超文本标记语言,描述网页结构的那套标签)画出来——你看到了页面。

注意一个容易忽略的细节:上面这套流程,页面里每一样「附件」都要各走一遍。 一张图、一段视频、一个样式表,都是浏览器解析 HTML 时发现「还缺这个」, 再各发一次请求取回来的22。所以你打开一个网页,底下可能是几十次问答的叠加。

轮到动态页:中间多出来的五道工序

现在换成动态场景:你点击页面上一件雨衣的图片,想看清它的细节。

前四步和上面一样——查地址、发请求、到达服务器、取出文件。 从第 5 步开始分叉,原书列出的完整链条是23:

  1. 服务器发现取出的文件是个 .php 文件,把它交给 PHP 解释器—— 一个专门逐行执行 PHP 程序的软件24。
  2. PHP 代码里写着「去数据库查这件雨衣」:SQL 语句(数据库的查询语言)被转交给 MySQL——一个独立的数据库程序,也在同一台机器上跑着。
  3. MySQL 查完,把雨衣的价格、库存、颜色这些行返回给 PHP。
  4. PHP 把这些数据包进 HTML 模板(「包进」就是拼字符串:标签是固定的,数据是刚查出来的),交给服务器。
  5. 服务器把这份新鲜出炉的 HTML 寄回浏览器,浏览器画出来。

对比一下:静态页在第 4 步就结束了;动态页多走的 5~8 步,就是「PHP」和「MySQL」这两个名字的全部含义。 PHP 负责「算」——决定这一页上放什么;MySQL 负责「存」——记住所有该被放进页面的数据。 这也解释了书名的组合为什么是这样:PHP 和 MySQL 是这套问答里后端的两个分工。

3. 五个角色各管什么

第一章后半部分把动态网页的五个技术角色各用一节过了一遍。这一节按原书的讲法接住它们。

3.1 PHP:在服务器上「算」的那道工序

PHP 是一门运行在服务器上的编程语言。它的用法有一个非常具体的特点: 直接写在 HTML 中间,用一对标签圈出来。<?php 告诉服务器「从这里往下是程序,先执行」, ?> 告诉它「到这里为止,剩下的照旧发出去」25。

原书给的第一个例子只有一行:

<?php echo "Today is " . date("l") . ". "; ?>
Here's the latest news.

走一遍:服务器执行 date("l") 拿到服务器本机时间里的星期几(比如 Wednesday), 拼进字符串;标签外面的那句英文原样不动。浏览器最终收到的 HTML 是:

Today is Wednesday. Here's the latest news.

对浏览器来说,这份 HTML 和一个手写的静态文件毫无区别——它不知道也不在乎中间的星期三是怎么来的。 这就是「动态」的确切意思:浏览器收到的每次都是平平无奇的 HTML, 但 HTML 的内容是请求到达的那一刻才决定出来的。

顺带交代一个术语:在 web 语境里,「客户端」(client)指发起请求的一方——浏览器和它所在的电脑; 「服务器端」(server)指应答的一方。所以常说「PHP 是服务器端语言」, 意思是它运行在应答那一头,用户看不见它的代码,只看得见它算完的结果26。

3.2 MySQL:用「像英语的命令」管数据

光会动态地拼 HTML 还不够——「这件雨衣多少钱」这样的数据总得存在某个地方。 早年的网站把数据存在普通文本文件里,原书列了这种做法的两个死穴: 多个访客同时写入时文件容易写坏;文件大到一定程度后,合并、搜索都慢得没法用27。

数据库就是为解决这两个死穴而生的:它专门管「很多人同时读写一堆数据」这件事。 MySQL 是其中装得最广的一个,免费的,用一套像英语的命令(叫 SQL)操作。

它的结构用原书的比方最清楚:一个 MySQL 数据库像一个 Excel 文件,里面的表(table)像这个文件里的工作表; 表由行和列组成,列有名字和类型28。比如一张 users 表存用户,加一行用户是这样一条命令:

INSERT INTO users VALUES('Smith', 'John', 'jsmith@mysite.com');

「INSERT INTO」就是「插入到……里面」——SQL 的英语味就是这么直给。 查人也一样:SELECT surname,firstname FROM users WHERE email='jsmith@mysite.com'; 大意是「从 users 表里选出姓氏和名字,条件是邮箱等于这一串」29。 这两条的细节(为什么值要加引号、条件怎么写)第六章和第八章再展开,这里只需要建立印象: SQL 说的是「要什么」,不管「怎么找」——怎么找是 MySQL 内部的事。

3.3 JavaScript:在浏览器里「演」的那道工序

JavaScript 运行在客户端——它是唯一一个跑在用户浏览器里的角色,所以它能做的事 PHP 做不到: 不经过服务器,当场改动页面上任何一个元素。鼠标悬停弹提示、拖放、无刷新更新局部内容,都是它在干活30。

它和 Java 的名字相似纯属 1995 年诞生时的营销巧合,两门语言没有关系—— 书里特意点破,因为初学者常被这个名字误导31。

JavaScript 还有一个身份值得记住:它是「异步通信」的主角。 异步通信的意思是:页面已经显示完了,JavaScript 还在后台偷偷和服务器继续问答—— 你在注册 Gmail 时输入用户名,它当场告诉你要不要换一个,页面却没有整个刷新,就是这个机制32。 这个机制第 17 章细讲,第一章只负责让你知道它存在、并且需要 JavaScript 在场。

3.4 CSS:管「长什么样」

CSS(层叠样式表)管的是呈现:颜色、间距、字体、布局。 书里有句话点出了 CSS 近年的变化:CSS3 让样式表自己就能做一部分以前只能靠 JavaScript 做的动态效果 (悬停动画、过渡),所以能用 CSS 做的动效就别动用 JavaScript33。 第 18 章会展开 flexbox、CSS Grid 这些现代布局技术。

3.5 HTML5:补媒体的窟窿

HTML5 补上了老 HTML 的几个硬缺口:不用插件就能在页面里放音频、视频,以及一块可以画图的画布(<canvas>)34。 它还顺手收拾了历史遗留——比如当年为了迎合从未实现的 XHTML 标准而写成的 <br />, 现在写 <br> 就行35。HTML5 今天由一个叫 WHATWG 的组织以「活标准」的方式持续维护,不再按版本号发布36。

4. 幕后两位:Apache 与 Node.js

上面五个角色都在「答」的那一侧干活,而真正把请求接进来、把回答发出去的,是 web 服务器软件本身。 这本书用的是 Apache——世界上装机量最大的开源 web 服务器37。

Apache 本身也是个「大管家」:

  • 它不只发 HTML,图片、音频、RSS 订阅源什么都发;
  • 它靠模块扩展功能——PHP 就是以模块形式挂在它身上的;
  • 它还带着一批自己的模块,比如处理网址重写的 Rewrite、管安全的各种模块38。

Node.js 是 Apache 的当代挑战者。 2009 年,Ryan Dahl 不满 Apache 应付大量同时连接的方式, 用 Google 浏览器的 V8 引擎造了 Node.js——让 JavaScript 也能跑在服务器上39。 两者的模型差别很本质:Apache 给每个请求开一个线程、线程等着回应期间被占死; Node.js 是「事件驱动、非阻塞」——把耗时的活(比如读文件)交出去后立刻回头接下一个请求,等结果回来再通知它。 同样的等待,一个占着人手干等,一个先去干别的40。第 21 章专门讲它。

书里还有一个补充角色:MariaDB。Oracle 收购 MySQL 的原东家 Sun 之后, 社区担心 MySQL 不再完全开源,于是从它分叉(fork,拿一份源代码另起炉灶的意思)出了 MariaDB, 由原班人马带队,和 MySQL 高度兼容——这本书的所有例子两边都能跑41。

5. 把五个角色串起来:一个真实的协作场景

第一章的收尾例子是全书第一个「多角色合奏」的完整走查, 也是本组后面所有章节的缩影:注册时检查用户名是否已被占用(Gmail 就长这样)42。

  ① 服务器发出注册表单(HTML)
  ② 同一份 HTML 里附带的 JavaScript 盯着用户名输入框:
     一旦有字、且用户移开了光标(点了别处或按 Tab 跳走)……
  ③ JavaScript 在后台把这个用户名发回服务器,等答复 —— 页面没有刷新
  ④ 服务器查库,答复「占用/可用」
  ⑤ JavaScript 在输入框旁贴上标记(绿勾或红叉)
  ⑥ 若名字被占而用户还是点了提交,JavaScript 拦下提交,再强调一次

  图说:一次「输入即校验」,PHP、MySQL、JavaScript 各出了一段力,用户只看到一个勾。

注意这里每一步是谁在干活:表单是 PHP 发的(第 11 章),查库是 MySQL 干的(第 9 章), 后台通信是 JavaScript 的异步通信(第 17 章),连「拦下提交」也是浏览器里的 JavaScript。 服务器和浏览器两头各管一段,中间靠 HTTP 传话——这就是「动态网站」四个字的全貌。

6. 开发环境:把整套问答搬到本机(原书第 2 章)

上面讲的问答需要四样东西同时在场:web 服务器、PHP、MySQL,再加上浏览器。 第二章的任务就是让你自己的电脑上这四样齐备——这就是「开发服务器」的意思。

6.1 为什么要本机一套

最省事的替代方案是用别人的服务器(比如租一台),但那样每改一行代码都要先传到远端才能看效果; 而且写得半半拉拉的代码带着错误和安全漏洞暴露在公网上。本机一套,保存文件、刷新浏览器,立刻看到结果43。

6.2 WAMP/MAMP/LAMP:一体化安装包

四个组件各自安装配置都很折磨人,所以社区做了打包好的集成软件,按操作系统起名:

缩写 展开 平台
WAMP Windows + Apache + MySQL + PHP Windows
MAMP Mac + 同上 macOS
LAMP Linux + 同上 Linux

装一个就等于四样齐备,自带默认配置,开箱即用——代价是默认配置不适合当生产服务器用, 安全配置偏松,只该用来开发44。

这本书这一版推荐的是 AMPPS(一个跨平台的 WAMP/MAMP/LAMP 类产品)。 安装本身只有几步:下载安装包(写书时 Windows 版 4.4、约 46 MB)、一路 Next、 最后可能附带装一个微软运行库;装完桌面上出现一个控制窗口, 从里面一键启动/停止 Apache 和 MySQL。写书时 AMPPS 自带的默认 PHP 版本是 8.2, 想换版本在控制窗口的选项菜单里操作45。

装完先验收:浏览器打开 http://localhost——localhost 是「本机」的专用名字, 指向你自己的电脑——看到 AMPPS 的管理页,就说明服务器已经在本机跑起来了46。

6.3 document root:网站的「家门口」

第二个必须搞清的概念是 document root(文档根目录):服务器收到不带路径的请求时(比如 http://localhost), 就去这个目录里找文件。AMPPS 在 Windows 上默认是 C:\Program Files\Ampps\www,macOS 上是 /Applications/Ampps/www47。

验收方法: 写一个只有一行 Hello World! 的 test.html,存进 document root, 然后浏览器访问 http://localhost/test.html——看到 Hello World,整条链路就通了49。

这里有一个全书最重要的易错点,原书用 Note 框专门提醒:

从 document root 访问,和直接双击打开文件,是两回事。 前者走了完整的服务器问答(能执行 PHP、能连 MySQL);后者只是浏览器把文件读进来显示, PHP 代码会原样出现在页面上,什么都不会发生50。

如果某页出现了「源码裸露」的怪象,九成是这个原因——检查地址栏里有没有 http://localhost。

6.4 三个平台的差异一句话版

Windows 走 AMPPS 安装向导,文档根目录在 C:\Program Files\Ampps\www; macOS 下载 .dmg 后把 AMPPS 文件夹拖进 Applications,文档根目录在 /Applications/Ampps/www; Linux 发行版太多,原书不逐步讲,只教一个自查法:浏览器开 http://localhost, 能出默认页面就多半装好了 Apache,MySQL 是否在场再问系统管理员48。 差异只在这一步;装好之后,三平台上跑的例子一模一样——这也是这本书敢押注 PHP/MySQL 栈的原因之一: 上层代码完全不挑操作系统。

6.5 远程开发的规矩(如果不得不用远端服务器)

书里也给出了「不得不用远端服务器」时的两条安全规矩,值得记住:

  • 登录用 SSH(安全外壳协议,加密的远程登录),不要用它前身 Telnet——内容明文传输,谁都能偷看; Windows 推荐 PuTTY,Mac 自带 SSH51。
  • 传文件用 SFTP/FTPS,不要用老 FTP——理由一模一样,老 FTP 的用户名密码和文件内容在网上裸奔。 书里推荐的开源工具是 FileZilla52。

6.5 代码编辑器:别用记事本硬写

写 HTML/PHP/JavaScript 用纯文本编辑器(记事本)也能写,但专用代码编辑器有两个立刻值回票价的功能: 语法高亮(用颜色区分代码的角色,一眼看出结构)和括号配对、甚至在你运行前就标出语法错误。 书里选的是 Visual Studio Code(微软出的,免费,三大系统都能跑),理由是它正在成为行业标准53。

7. 作者的判断与证据

这一章是综述章,断言不多,挑三条标明成色:

书中的说法 成色
PHP 的简单加上与 MySQL 的原生联动,用户量是 Perl 的两倍多 作者陈述的行业印象,给了对比但没给数据来源54
Node.js 的事件驱动模型适合高并发(同时很多访客)、低延迟(响应快)场景 成立,且是业界共识;但「优于 Apache」是有条件的——CPU 密集的旧式网站 Apache 仍然合适(第 21 章开头原书自己也这么写)55
一切 JavaScript 校验都不能替代服务器端校验 全书反复强调的硬规矩,理由在第 6 节异步例子里就露了苗头:用户提交的数据你永远不能预先信任(第 16 章展开)56

8. 边界与局限

  • 版本时效: 写书时 AMPPS 是 4.4 版、PHP 默认 8.2、Node.js 22.6——都是 2024 年的快照, 安装界面和版本号今天多半已经不同。装的时候照「道理」装(哪一步在干什么),不要照抄按钮位置。
  • 操作系统偏向: Windows 和 macOS 有详细步骤;Linux 一句「各发行版不同」带过, Linux 用户得自己查发行版的包管理器。
  • jQuery 的位置: 这一版把 jQuery 降格为「历史遗留」,主推 React 和原生 JavaScript—— 书里明确说浏览器们如今兼容性够好,不必再靠 jQuery 抹平差异57。对读者这是省事的取舍, 但意味着你接手老项目时满眼的 jQuery 代码这本书不管教。
  • MariaDB 与 MySQL 的「无差别」: 说「交换使用无感」是就这本书的例子而言; 两边这些年各有新特性,新项目选型时这条要重新核实。

9. 可带走的

  1. 网页 = 问答。 浏览器问、服务器答;静态页答磁盘文件,动态页答 PHP 现场算的 HTML。
  2. 一次动态请求的完整链条:服务器认出 .php → PHP 解释器执行 → SQL 交给 MySQL → 结果包进 HTML → 发回。
  3. 五个角色分工:PHP 算(服务器端)、MySQL 存、JavaScript 演(浏览器端)、CSS 妆扮、HTML5 补媒体。
  4. Apache 是接请求的管家(模块化);Node.js 用「事件驱动、非阻塞」挑战它——等的时候不闲着。
  5. 开发服务器 = 本机的 Apache+PHP+MySQL;一体化包叫 WAMP/MAMP/LAMP,这本书用 AMPPS。
  6. document root 是「家门口」;验收方法:test.html + http://localhost/test.html。
  7. 从 localhost 访问 ≠ 双击打开文件——后者 PHP 不执行。
  8. 远端操作的安全底线:登录 SSH、传文件 SFTP,明文的老 Telnet/老 FTP 一律不用。
  9. 用代码编辑器(如 Visual Studio Code),不要拿记事本写代码。
  10. 浏览器收到的 HTML 里看不出「动态」——动态发生在服务器端,浏览器永远只收平平无奇的 HTML。

9.5 把问答旅程再走深一层(四个常被跳过的细节)

第 12 章讲 cookie 时有一句硬规矩:setcookie 必须在任何 HTML 输出之前调用。 当时只说是"规矩",这里的"为什么"补上:HTTP 响应是分两截的——先是一行状态 加一组「头」(header,键值对,如 Content-Type、Set-Cookie),空一行之后才是 「体」(body,也就是 HTML 本身)。浏览器按这个顺序解析:先读完所有头,再渲染体。

PHP 的 header() 和 setcookie() 都是往「头」里写东西——一旦 echo 输出 了哪怕一个字符,PHP 就默认「头已经发完了」,后面的头命令全部失效并告警。 这就是"输出之前"的物理含义,和第 3 章省掉 ?> 防换行泄漏是同一条线: 看不见的空白也算输出。

9.5.2 缓存与代理:中间那些"帮工"

第 1 章的问答图里,浏览器和服务器之间"可以有几台别的设备"。最常见的两种:

  • 缓存(cache):某台中间服务器把热门页面留一份副本;后来的请求若命中, 直接由它应答,源头服务器完全不知道。好处是快、省源头负载;代价是 你看到的可能不是最新内容——动态网站(如本项目的所有 .php)通常要让 响应头声明"不要缓存"(Cache-Control),静态图片则可以放心缓存。
  • 代理(proxy):替一群人转发请求的服务器(公司网关、翻墙工具都是)。 Apache 自己也带 Proxy 模块,可以把某些路径转交给别的服务(第 21 章的 Node.js 常与 Apache 这样搭配:静态 PHP 走 Apache,实时通道走 Node)。

9.5.3 localhost 与 127.0.0.1:本机的两个名字

开发时你会见到 localhost 和 127.0.0.1 两种写法——它们是同一件事: 指向你自己的机器(回环地址,loopback)。区别在于:localhost 是名字, 127.0.0.1 是数字地址;浏览器访问 localhost 时也会先做一次"翻译" (查 hosts 文件,通常直接映射到 127.0.0.1)。书里的例子两种写法都出现, 知道是同一个门牌即可。

9.5.4 排错清单:页面"什么都不显示"时

按出现频率排,逐条自查:

  1. 地址栏是不是 http://localhost/...? 双击打开文件的话 PHP 根本不执行 (§6.3 的易错点,九成的新手卡壳在这)。
  2. AMPPS 的 Apache 启动了吗? 控制面板看状态灯;80 端口被别的程序 (如 IIS、另一个 Apache)占用也会启动失败,改端口(如 8080)后访问要 带上端口号(如 localhost:8080/)。
  3. 文件放对目录了吗? 必须在 document root(AMPPS 的 www)里或它的 子目录里,不在桌面、不在下载文件夹。
  4. 保存了吗? 编辑器没保存就刷新,看的是旧文件。
  5. PHP 语法错误? 页面全白是 PHP 致命错误的典型症状;打开 AMPPS 的 错误日志或先把 phpinfo() 放一个探针页确认 PHP 活着。

这份清单不在原书某一页,是把第 2 章安装验收、第 3 章 Parse error、 第 10 章"别输出错误给用户"三处的教训合起来的实战口袋书。

9.6 WAMP 之外的第六个角色:编辑器与版本控制(我们的延伸)

书里没有讲版本控制,但对"改动—刷新"这个开发循环,还有一件工具和第 2 章的 AMPPS 一样属于"开工前就该装好":版本控制(用 Git)。它解决的是 AMPPS 管不了的那类事故——"刚才改坏的版本找不回来了"。

最小用法只要三条命令,够你学完这本书:

  git init            # 在项目目录建仓库(一次性)
  git commit -am "改了登录页"   # 每完成一小步,存一个快照
  git diff            # 看看这次改了哪些行

它和安全无关、和功能无关,只和"能不能放心大胆地试"有关——综合项目那种 十几个文件来回改的场景,没有快照的胆子会小一半。(补充(不在书里,来自通用知识): Git 的版本快照存的是「差异」,几个快照只占原文件体积的一小部分。)

9.7 判定树:这一次访问,静态还是动态

同样是回一个页面,"谁来生成、何时生成"决定了整个链路。遇到任何一个 真实 URL,用三个问题判定它走哪条路:

URL 进来
├─ 文件后缀是 .html/.htm/.jpg/.css/.js 之类?
│   └─ 是 → 静态路:Apache 直接读文件,逐字节发回,不惊动 PHP
├─ 后缀是 .php?
│   └─ 是 → 动态路:Apache 把请求转给 PHP,PHP 产出 HTML,Apache 再发回
└─ 没有后缀、路径像 /user/42?
    └─ 是 → 重写路:Apache 的 Rewrite 模块先翻译成内部真实路径,再走上面两路之一

三个追问,防止误判:

  • "静态文件里也能有 JavaScript,算动态吗?" 不算。JavaScript 在 浏览器里跑,动态/静态的分界线画在服务器这一侧:响应离开服务器之前 需不需要现算。JS 再花哨,服务器发的仍是那个一成不变的 .html。
  • ".php 文件里全是 HTML、一行 PHP 没有呢?" 仍是动态路—— Apache 按后缀分配处理器,不看内容。这也解释了为什么纯 HTML 页面 起名叫 .php 仍能打开,但白白多付一次 PHP 的解释成本。
  • "Ajax 之后呢?" 那是本书后半(第 17 章)的异步通信: 浏览器在页面渲染完之后,再单独发一次请求只取数据。 判定树对每一次请求分别成立,一次页面访问可能串好几次。

9.8 一次 GET /index.php 的完整时序(再走深一层)

把 §2 的主线例子展开成带时序的走查,每一步标注"谁在场、谁不在场":

时刻  动作                                  谁在场
t0    浏览器解析出 http://localhost/index.php
t1    建立到 80 端口的 TCP 连接           浏览器 ↔ Apache(HTTP 守护)
t2    发出请求行 GET /index.php HTTP/1.1  同上
t3    Apache 按后缀认出这是 PHP 的活,
      把请求转给 PHP 模块(同一台机内)   Apache + PHP
t4    PHP 从头解释 index.php,
      遇到 echo 就攒下输出缓冲          只有 PHP
t5    PHP 若查库:再向 MySQL 发一条查询   PHP ↔ MySQL
t6    PHP 收尾,把攒好的 HTML 交还        PHP → Apache
t7    Apache 加上响应头,发回浏览器        Apache
t8    浏览器收完 HTML,开始解析渲染,
      遇到 <script>/<img> 再各自发请求    浏览器 ↔ Apache(静态路)

两个常被忽略的事实:

  • t3 的"转交"发生在同一台机器内部,不是另一次网络请求。 PHP 模块是 Apache 的一部分(或以 CGI 方式被唤起),浏览器全程 只跟 Apache 说话——PHP 的存在对浏览器不可见1。
  • t8 的后续请求各自独立走完判定树。一张网页引用的每个 CSS、 每张图都是一次全新的请求/响应,各自独立判定静态还是动态。 所谓"一次访问",其实是请求的风暴。

9.9 Apache 的模块生态:不只是 PHP 的房东

第 1 章明确说:Apache 不只是静态文件的搬运工,它的能力来自模块2。 把书里点名的模块排成职责表:

模块 管什么 本书哪里用得上
PHP 模块(mod_php) 把 .php 交给 PHP 解释 全书每一章
Rewrite 模块 URL 改写:把好看的地址翻译成内部路径 社交项目(第 22 章)的友好地址
Proxy 模块 代理与缓存:常被请求的页面从缓存出 高流量站点的减负手段
GD 库 PHP 侧创建/处理图像 PHP 动态生成图片
安全类模块 访问控制、加密连接 登录与认证(第 12 章)

模块化的启示:Apache 是一个"按模块拼装"的服务器。 装什么模块,它就变成什么服务器;这也解释了为什么同一台 Apache 既能发静态文件、又能跑 PHP、还能做反向代理——不是它全能, 是它的积木全3。

9.10 Node.js 的出场逻辑:不是替代,是换了一种排队方式

第 1 章给 Node.js 的辩护词值得原样压一遍:Ryan Dahl 2009 年不满 Apache 应对大量同时连接的方式,于是用 Google 的 V8 引擎做了 Node, 让 JavaScript 也能跑在服务器侧;npm 随后解决了包的分发与共享4。

书里给的三个选择理由,浓缩成对照:

  • 一种语言走全程:前端后端都是 JavaScript,少一次换脑; 但注意——Node 跑不了你的 PHP 脚本,混合系统要划清边界5。
  • 事件驱动模型:Apache 传统上为每个连接配一个进程/线程, 连接一多,内存与切换成本陡增;Node 用一个主循环同时照看大量 连接,等 I/O 的空档去服务别人。原书把它列为 Node 优于 Apache 的 核心6。
  • 可以混着用:一个复杂应用完全可以让 Apache+PHP 管一部分、 Node 管另一部分,各干各擅长的。

判断句(我们的判断,原书证据支持):学习期跟书走 Apache+PHP, 工程期按团队栈选 Node;两者不是宗教,是工具。

9.11 开发服务器安装决策:打包 vs 手工

第 2 章的 WAMP/MAMP/LAMP 是一组"打包哲学":把 Apache、MySQL、PHP 捆成一个安装包,一次装好、互相配套7。原书这一版推荐 AMPPS (Windows 上的选择;macOS、Linux 各有对应路线)8。

决策表:

你的处境 建议 理由
刚开始学,只想快 打包(AMPPS/MAMP/XAMPP) 安装即配套,排错面小
已有现成集成环境 直接用 第 2 章明说各组件若已装好互通,例子照样跑
想理解每个部件 手工分装 慢、需要查配置,但每个报错都是一次学习
只想跑前端(后半本书) Node + npm 即可 第 20/21 章的路

第 2 章还有一句诚实的警告:手工下载拼装各部件"可能非常耗时", 要配置到完全互通需要大量查证——这不是劝退,是把"打包的价值" 讲透:打包省的不是安装时间,是"部件之间互相认不认"的排错时间9。

9.12 localhost、控制页与文档根:三个别混淆的"入口"

装完开发服务器,第 2 章让你访问了几个地址,它们各是各的门:

  • http://localhost —— 本机的约定俗成名,等价于 127.0.0.1。 浏览器访问它,就是访问你自己机器上那个 Apache10。
  • http://localhost/ampps/ —— 安装器落下的控制页: 管 MySQL 开关、看组件版本、配参数。它是"机房的管理台", 不是你的网站11。
  • 文档根目录(DocumentRoot) —— Apache 认领的"网站根": 你把 .php 放进它里面,localhost 下才看得见; 放在别处,Apache 一律 404(或 403)。第 2 章让你确认它的位置, 就是建立"文件放对门牌"的条件反射12。

三个迷思各一句话:

  • "localhost 是个网站" —— 不是,它是"本机"这个地址的别名, 后面挂什么服务器由你装什么决定。
  • "控制页打不开 = 服务器坏了" —— 不一定。先看 Apache 是否在跑 (控制页本身也依赖它),再谈坏没坏。
  • "文件在硬盘上就一定看得到" —— 只在文档根里成立。 这条迷惑过几乎所有初学者:文件明明存在,浏览器却 404, 九成是放错了目录。

9.13 Web 1.0 到动态网页:一条简史线(第 1 章背景)

第 1 章开头用 Web 1.0 与增强技术铺开背景:Java、JavaScript、Flash、 ActiveX 在浏览器侧各领风骚;服务器侧,CGI(Common Gateway Interface) 用 Perl 等脚本语言把"运行一个本地程序的输出"嵌进页面, 服务器端脚本由此登场13。这条线的意义在于定位 PHP:

CGI(每次请求现起进程,慢、重)
  → 服务器端脚本模块(Apache 内置解释器,常驻、快)
    → PHP(mod_php):HTML 里嵌代码,学习门槛最低的那一支

PHP 的胜利不是技术上的"最先进",而是入门路径最短: 改一个 .php 文件、刷新浏览器、立刻看到变化。 这个"编辑-刷新"循环正是第 2 章开发环境存在的全部意义。

9.14 练习场(第 1+2 章)

  1. 判定树实操:对本机开发服务器,分别访问一个 .html、一个 .php、 一个 .css,用浏览器开发者工具的 Network 面板核对每一步的响应, 标注走的是静态路还是动态路。
  2. 改名实验:把一个纯 HTML 文件改成 .php 后缀,刷新看是否照常 显示;再用 Network 面板对比响应时间差异,理解"白白多付解释成本"。
  3. 404 实验:把一个 .php 移出文档根,访问它,读 Apache 的错误日志 找原因;移回去再验证恢复——亲手建立"文档根"的条件反射。
  4. 模块侦察:访问控制页,列出当前 Apache 装的所有模块, 对照 §9.9 的表,圈出 Rewrite、PHP 各是哪个。
  5. Node 小体验(预习第 21 章):node -e 跑一行 console.log, 感受"JavaScript 离开了浏览器也能跑"——这是第 1 章埋的伏笔。

9.15 FAQ(五问)

Q1:我非要用 Nginx 行不行? 第 1 章的主角是 Apache,Node.js 是对照组;Nginx 本书未展开 (书外补充:它是另一个流行的 Web 服务器,常与 PHP-FPM 搭配)。 学概念不受影响——请求/响应、静态/动态、模块拼装,换谁都成立。

Q2:开发服务器和线上服务器有什么区别? 开发服务器装在你本机、只服务你自己、出错直接看屏幕; 线上服务器服务全世界、出错要给用户看体面页面、给日志看细节。 同一个 Apache,两种性格——第 10 章的生产环境错误处理就是 "性格切换"的第一课。

Q3:PHP 为什么"每请求从头解释"?这对写法有什么影响? 两次请求之间,PHP 不替你记住任何全局状态——想跨请求记住东西, 得借 Cookie、Session 或数据库(第 11/12 章)。每请求无状态, 既是 PHP 好上手的原因(脚本内不用关心别人),也是写登录、 购物车时必须额外搭桥的原因。

Q4:AMPPS、XAMPP、MAMP 选哪个? 第 2 章这一版定的是 AMPPS;三者内核相同(Apache+MySQL+PHP), 差别在控制界面与更新节奏。选一个装上就别看别的了—— 纠结安装器是新手期最贵的拖延。

Q5:学完这两章,我该记住什么? 三条:请求/响应是万事的骨架;静态/动态的分界线画在服务器侧; 开发环境存在的意义是让"编辑-刷新"循环在三十秒内完成。 其余全是细节,用到再查。

9.16 开发环境排错速查:五种高频故障

装环境不是终点,是排错的起点。第 2 章的内容可以反推出一张 "症状→病因"速查表,按出现频率排:

症状 第一嫌疑 验证方法
localhost 打不开 Apache 没在跑 回控制页看 Apache 状态灯14
控制页能开、自己的页面 404 文件不在文档根 对照控制页显示的文档根路径15
.php 文件被当文本显示 PHP 模块没挂上 访问控制页的 phpinfo 区块
MySQL 相关例子全挂 数据库服务没启动 控制页里启动 MySQL
改了代码刷新没变化 浏览器缓存或改了错目录 硬刷新(加 Shift)再核路径

排错的两条元规则:

  • 从下往上查:页面出错,先看 Apache 活没活,再看 PHP 挂没挂, 再看 MySQL 通没通,最后才看你的代码。初学者九成反过来查, 于是越查越乱。
  • 一次只改一处:排错时同时动两个变量,等于没排。 AMPPS 文档和支持入口是官方给的兜底16—— 但先用速查表把"环境问题"和"代码问题"分开,再决定找不找支持。

9.17 本机与线上:部署差异清单(第 2 章的镜像问题)

开发服务器和线上服务器的差异,第 2 章在讲上传工具时露了一角: FTP 明文传输,不安全;SFTP、SCP 这类基于 SSH 的方式才是方向17。 把这一角展开成完整清单:

维度 本机(开发) 线上(生产)
谁在看错误 你自己,直接看屏幕 用户看体面页面,你看日志
文件怎么上去 无所谓,本来就都在 SFTP/版本库部署,FTP 不要用
phpinfo() 排错利器 安全隐患,绝不保留
数据库连接 root 空密码的玩具配置 专用账号、最小权限
出错时的 PHP 报错全开,越响越好 报错关给用户、记日志给运维

这张表会在第 10 章(PDO 错误处理)、第 12 章(密码散列)、 第 22 章(项目收尾)反复被引用。现在就把它抄在案头: "在我的机器上是好的"不是部署完成的标准,"在线上是对的"才是。

9.18 五条主线的一次合流:这两章真正的交付物

第 1 章给全景,第 2 章给本机。把两章的机制合起来看, 真正交付的不是"装了什么软件",而是五条可以在任何 web 项目上 复用的主线:

  1. 问答主线:浏览器问、服务器答——此后每一章都只是给这个 问答加器官:PHP 让答案可算,MySQL 让答案有记性,JavaScript 让提问可以在答完之后继续。
  2. 分工主线:五个角色(HTML/CSS/JS/PHP/MySQL)+ 幕后两位 (Apache/Node)各管一摊,接口只有两样——HTTP 与 SQL。 换任何一项技术,接口形状不变。
  3. 边界主线:静态/动态的分界线画在服务器侧;信任的分界线 也画在服务器侧。两条线是同一根。
  4. 环境主线:开发环境的全部意义,是让"编辑-刷新"循环在 三十秒内完成;所有偏离这条的仪式(手工部署到远程再测) 都是在给学习加税。
  5. 排错主线:从下往上查(服务器→解释器→数据库→代码), 一次只改一处——第 2 章的安装排错,和第 22 章的项目排错, 是同一套动作。

如果你合上第 2 章时只记住了"AMPPS 装好了",那等于只看了序幕。 验收标准是:随便给一个 URL,你能用 §9.7 的判定树说出它走哪条路, 能画出 §9.8 的时序图标出每一步谁在场——能,这两章就毕业了。

9.19 本章机制与后文的接口预告

本章埋下的线 在后文哪里接通
请求/响应问答 第 11 章表单(浏览器替用户提问)、第 17 章异步(浏览器替页面继续问)
Apache + PHP 分工 第 10 章 PDO(PHP 反向去问 MySQL)
localhost / 文档根 第 20 章 React 开发服务器会换端口,判定树原样适用
Node.js 初印象 第 21 章整章展开
每请求无状态 第 12 章 Cookie/Session 补状态

接口预告的意义:读后面章节时,一旦发现"这又是请求/响应", 就回本章对照——同一根骨架,每章只添一两块新骨头。

9.20 一分钟自测(第 1+2 章)

合上书,回答五问;答不上来的,回标的小节:

  1. 浏览器发出的一次请求,在哪几个时刻会惊动 PHP?(§9.8)
  2. 静态与动态的分界线画在哪一侧?JavaScript 很花哨的页面算哪种?(§9.7)
  3. localhost、控制页、文档根,分别是哪扇门?(§9.12)
  4. 环境排错为什么"从下往上查"?(§9.16)
  5. 本书为什么选 AMPPS 而不让你手工拼装?(§9.11)

全对,两章毕业;错两问以上,值得重走一遍 §9.8 的时序图—— 那是这两章的承重墙。

9.21 交接备忘:这两个最容易被下一轮翻新者误解的点

  1. 第 1 章把 Apache 与 Node.js 并列为"幕后两位",不是"新旧两位"—— 原书明确说复杂应用可以让两者各管一部分。写总结时别写成 "Node 取代 Apache",那是过度概括,原书不支持。
  2. 第 2 章推荐的 AMPPS 是"这一版的选择",不是永恒答案; 不同版本换过推荐对象。引用时带版本语境,别把推荐写成教义。

10. 原文地图

主题 原书章 原文位置
Web 的起源(CERN、Tim Berners-Lee) Chapter 1. Introduction to Dynamic Web Content text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:7(搜「State-of-the-art experiments at CERN」)
Web 1.0 与 2.0 之别 同上 text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:20(搜「content consumers」)
HTTP 与 HTML 的定义 同上 text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:52(搜「communication standard」)
静态请求七步 同上 text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:71(搜「asking the web server」)· :131(搜「once for each object」)
DNS 查 IP 同上 text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:135(搜「Domain Name System」)
动态请求完整链条 同上 text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:139(搜「picture of a raincoat」)· :186(搜「PHP interpreter」)
MariaDB 分叉始末 同上 text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:273(搜「forked from it」)
PHP 嵌入 HTML 的写法 同上 text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:294(搜「Today is」)
MySQL 与平面文件之争 同上 text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:341(搜「flat」)
数据库≈Excel 文件的比方 同上 text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:349(搜「Excel spreadsheet」)
JavaScript 与 Java 的命名误会 同上 text/24-ch13-chapter-13-exploring-javascript.txt:11(搜「marketing ploy」)
CSS3 接替部分 JavaScript 动效 同上 text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:453(搜「level of dynamic interactivity」)
HTML5 的 audio/video/canvas 同上 text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:502(搜「canvas」)
Node.js 的三条理由 同上 text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:551(搜「Ryan Dahl」)· :559(搜「nonblocking」)
用户名检查的多角色协作 同上 text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:618(搜「necessary details」)· :661(搜「nowhere near as tidy」)
本地开发的两大好处 Chapter 2. Setting Up a Development Server text/05-ch02-chapter-2-setting-up-a-development-server.txt:15(搜「embarrassing errors」)
WAMP/MAMP/LAMP 的含义与安全提醒 同上 text/05-ch02-chapter-2-setting-up-a-development-server.txt:48(搜「never install such a setup」)
AMPPS 安装与 PHP 8.2 默认值 同上 text/05-ch02-chapter-2-setting-up-a-development-server.txt:72(搜「best option」)· :163(搜「8.2」)
document root 定义与位置 同上 text/05-ch02-chapter-2-setting-up-a-development-server.txt:220(搜「contains the main web documents」)
localhost 访问 ≠ 打开文件 同上 text/05-ch02-chapter-2-setting-up-a-development-server.txt:264(搜「different from loading」)
SSH 与 PuTTY 同上 text/05-ch02-chapter-2-setting-up-a-development-server.txt:443(搜「much more secure」)
禁用 FTP、推荐 SFTP 同上 text/05-ch02-chapter-2-setting-up-a-development-server.txt:474(搜「insecure」)
Visual Studio Code 的选择理由 同上 text/05-ch02-chapter-2-setting-up-a-development-server.txt:508(搜「Visual Studio Code」)


  1. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 71 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:71,搜「request/response process」)。 ↩

  2. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 520 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:520,搜「Apache Web Server」)。 ↩

  3. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 536 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:536,搜「Rewrite module」)。Rewrite、Proxy、安全模块并列见同段。 ↩

  4. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 551 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:551,搜「Ryan Dahl」);npm 见同段。 ↩

  5. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 563 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:563,搜「full-stack development environment」)。 ↩

  6. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 730 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:730,搜「event-driven」)。章末复习题把"事件驱动为何更优"列为考点。 ↩

  7. 出处:「Chapter 2. Setting Up a Development Server」第 40 段(text/05-ch02-chapter-2-setting-up-a-development-server.txt:40,搜「abbreviations」)与第 44 段(:44,搜「bind the bundled programs」)。 ↩

  8. 出处:「Chapter 2. Setting Up a Development Server」第 72 段(text/05-ch02-chapter-2-setting-up-a-development-server.txt:72,搜「AMPPS looks like the best option」)。 ↩

  9. 出处:「Chapter 2. Setting Up a Development Server」第 59 段(text/05-ch02-chapter-2-setting-up-a-development-server.txt:59,搜「very time-consuming」)。 ↩

  10. 出处:「Chapter 2. Setting Up a Development Server」第 177 段(text/05-ch02-chapter-2-setting-up-a-development-server.txt:177,搜「localhost」)。 ↩

  11. 出处:「Chapter 2. Setting Up a Development Server」第 191 段(text/05-ch02-chapter-2-setting-up-a-development-server.txt:191,搜「main control page」)。 ↩

  12. 出处:「Chapter 2. Setting Up a Development Server」第 195 段(text/05-ch02-chapter-2-setting-up-a-development-server.txt:195,搜「document root」)。 ↩

  13. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 238 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:238,搜「Common Gateway Interface」)。 ↩

  14. 出处:「Chapter 2. Setting Up a Development Server」第 191 段(text/05-ch02-chapter-2-setting-up-a-development-server.txt:191,搜「main control page」)。控制页是组件状态的统一入口。 ↩

  15. 出处:「Chapter 2. Setting Up a Development Server」第 195 段(text/05-ch02-chapter-2-setting-up-a-development-server.txt:195,搜「document root」)。 ↩

  16. 出处:「Chapter 2. Setting Up a Development Server」第 152 段(text/05-ch02-chapter-2-setting-up-a-development-server.txt:152,搜「AMPPS documentation」)。 ↩

  17. 出处:「Chapter 2. Setting Up a Development Server」第 474 段(text/05-ch02-chapter-2-setting-up-a-development-server.txt:474,搜「FTP is insecure」)。 ↩

  18. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 88 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:88,搜「You enter」)。原书列了七步,从输入网址到浏览器显示。 ↩

  19. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 135 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:135,搜「Domain Name System」)。原话:浏览器「consults an additional internet service called the Domain Name System」把名字换成地址。 ↩

  20. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 52 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:52,搜「communication standard」)。HTTP 被定义为「governing the requests and responses」的标准;服务器「accept a request from the client」。 ↩

  21. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 60 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:60,搜「listening for an incoming connection」)。 ↩

  22. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 131 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:131,搜「once for each object」)。页面里每个对象——图、视频、样式表——都要各走一遍这个流程。 ↩

  23. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 139 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:139,搜「picture of a raincoat」)与第 150 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:150,搜「dynamic sequence」)。雨衣例子之后紧跟动态序列的九步清单。 ↩

  24. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 186 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:186,搜「passes the page to the PHP interpreter」)。第 192 段(:192,搜「executes the PHP code」)是执行;第 198 段(:198,搜「SQL statements」)是 SQL 转交 MySQL;第 204 段(:204,搜「returns the results」)是结果返回。 ↩

  25. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 299 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:299,搜「all of the following code」)。<?php 到 ?> 之间的代码由 PHP 处理,之外的一切作为纯 HTML 直接发给客户端。 ↩

  26. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 33–37 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:52,搜「the term server is used」)。原文:「The natural counterpart to a server is a client, so that term is applied both to the web browser and the computer on which it’s running.」 ↩

  27. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 341 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:341,搜「flat」)。原文指出平面文件的问题:同时访问容易写坏、变大后难管理难搜索。 ↩

  28. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 349 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:349,搜「Excel spreadsheet」)。「the spreadsheet file can be viewed as a database and the individual sheets as tables」。 ↩

  29. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 358 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:358,搜「INSERT INTO users VALUES」)与第 369 段(:369,搜「SELECT surname,firstname」)。两条命令在原书里都是独立成段的原样引用。 ↩

  30. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 394 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:394,搜「checking email address validity」)。「JavaScript was created to enable scripting access to all the elements of an HTML document」。 ↩

  31. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 11 段(text/24-ch13-chapter-13-exploring-javascript.txt:11,搜「marketing ploy」)。命名「was just a marketing ploy」,借 Java 的名气。 ↩

  32. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 442 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:442,搜「resemble standalone programs」)。「web pages perform data handling and send requests to web servers in the background」。 ↩

  33. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 453 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:453,搜「previously supported only by JavaScript」)。第 18 章开头作者明确建议用 CSS 而不是 JavaScript 实现动态特性。 ↩

  34. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 502 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:502,搜「audio」)。<audio>、<video>、<canvas> 三元素。 ↩

  35. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 511 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:511,搜「full circle」)。XHTML 未实现,<br> 写法回转;React 的 JSX 例外,需要 />。 ↩

  36. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 498 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:498,搜「HTML Living Standard」)。HTML5 现由 WHATWG 维护。 ↩

  37. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 524 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:524,搜「sixth hero」)。「there’s a sixth hero in the dynamic web: the web server. For this book, that means the Apache web server」。 ↩

  38. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 536 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:536,搜「Rewrite module」)。模块举例:安全模块、Rewrite(网址重写)、Proxy(缓存转发)。 ↩

  39. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 551 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:551,搜「Ryan Dahl」)。「dissatisfied with Apache and its difficulties with handling large numbers of concurrent connections」。 ↩

  40. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 559 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:559,搜「nonblocking I/O model」)与「Chapter 21. Introduction to Node.js」第 15 段(text/32-ch21-chapter-21-introduction-to-node-js.txt:15,搜「blocked until the response is ready」)。线程模型的「占着等」与事件模型的「先去干别的」是第 21 章开头的核心对比。 ↩

  41. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 273 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:273,搜「forked from it」)。「you may well encounter MariaDB on some servers in place of MySQL」。 ↩

  42. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 601 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:601,搜「checking whether a desired username」)。第 636 段(:636,搜「has been taken」)是服务器查库答复;第 661 段(:661,搜「nowhere near as tidy」)对比了无异步时整表提交重载的笨办法。 ↩

  43. 出处:「Chapter 2. Setting Up a Development Server」第 15 段(text/05-ch02-chapter-2-setting-up-a-development-server.txt:15,搜「embarrassing errors」)。本机测试「can be as easy as saving an update」,且不必担心「embarrassing errors or security problems」暴露公网。 ↩

  44. 出处:「Chapter 2. Setting Up a Development Server」第 48 段(text/05-ch02-chapter-2-setting-up-a-development-server.txt:48,搜「never install such a setup」)。「The security configurations of such an installation will not be as tight... you should never install such a setup as a production server.」 ↩

  45. 出处:「Chapter 2. Setting Up a Development Server」第 72 段(text/05-ch02-chapter-2-setting-up-a-development-server.txt:72,搜「best option」)与第 163 段(:163,搜「8.2」)。安装包版本与默认 PHP 版本都是写书时的快照。 ↩

  46. 出处:「Chapter 2. Setting Up a Development Server」第 172 段(text/05-ch02-chapter-2-setting-up-a-development-server.txt:172,搜「verify that everything is working」)。用 localhost 地址调出管理页验收。 ↩

  47. 出处:「Chapter 2. Setting Up a Development Server」第 220 段(text/05-ch02-chapter-2-setting-up-a-development-server.txt:220,搜「contains the main web documents」)与第 229 段(:229,搜「Ampps」)、第 364 段(:364,搜「Applications」)。两个平台的默认 document root。 ↩

  48. 出处:「Chapter 2. Setting Up a Development Server」第 408 段(text/05-ch02-chapter-2-setting-up-a-development-server.txt:408,搜「dozens of popular flavors」)与第 412 段(:419,搜「probably have the Apache server installed」)。Linux 发行版众多、装法各异,原书只给自查思路。 ↩

  49. 出处:「Chapter 2. Setting Up a Development Server」第 231 段(text/05-ch02-chapter-2-setting-up-a-development-server.txt:231,搜「Hello World」)与第 250 段(:255,搜「test.html」)。 ↩

  50. 出处:「Chapter 2. Setting Up a Development Server」第 264 段(text/05-ch02-chapter-2-setting-up-a-development-server.txt:264,搜「different from loading」)。「The former will ensure access to PHP, MySQL... the latter will simply load the file into the browser」。 ↩

  51. 出处:「Chapter 2. Setting Up a Development Server」第 443 段(text/05-ch02-chapter-2-setting-up-a-development-server.txt:443,搜「much more secure」)。「SSH is much more secure than the Telnet protocol」。 ↩

  52. 出处:「Chapter 2. Setting Up a Development Server」第 474 段(text/05-ch02-chapter-2-setting-up-a-development-server.txt:474,搜「insecure」)。「FTP is insecure and should not be used.」 ↩

  53. 出处:「Chapter 2. Setting Up a Development Server」第 504 段(text/05-ch02-chapter-2-setting-up-a-development-server.txt:504,搜「colored syntax highlighting」)与第 508 段(:508,搜「Visual Studio Code」)。「show your syntax errors before you even run a program」。 ↩

  54. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 242 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:242,搜「more than double the number of users」)。这是作者对 PHP 与 Perl 装机量对比的陈述,未附数据来源。 ↩

  55. 出处:「Chapter 21. Introduction to Node.js」第 32 段(text/32-ch21-chapter-21-introduction-to-node-js.txt:32,搜「Apache remains a strong choice」)。原文承认 Apache 在 CPU 密集场景仍是强选择,两者可搭配用。 ↩

  56. 出处:「Chapter 1. Introduction to Dynamic Web Content」第 394 段(text/04-ch01-chapter-1-introduction-to-dynamic-web-content.txt:394,搜「cannot be relied upon for security」)。「it cannot be relied upon for security, which should always be performed on the web server」。 ↩

  57. 出处:「Chapter 20. Introduction to React」第 11 段(text/31-ch20-chapter-20-introduction-to-react.txt:11,搜「need to rely on frameworks」)。「these days JavaScript has grown sufficiently in scope and flexibility that programmers need to rely on frameworks like jQuery a lot less」。 ↩