React 与 Node.js — 同一门 JavaScript,两端开花¶
这一章讲什么: 全书的「现代化尾章」。第 20 章:React——把「选元素、改内容、 响应事件」这套手工活,换成「组件 + 状态」的声明式写法; 第 21 章:Node.js——把同一门 JavaScript 搬到服务器, 三十行代码起一台 web 服务器、连上 MySQL。 读完你会看清这本书书名里三个技术的终局:它们正在被一门语言统一。 读之前需要本组 09–11。
1. 顶层全景:一门语言的两端¶
浏览器端(20 章 React) 服务器端(21 章 Node.js)
───────────────────────── ─────────────────────────
JSX:XML 写进 JavaScript ECMAScript 模块:import / export
组件 = 函数(props) → JSX http.createServer(request, response)
state 变 → 自动重渲染 事件驱动:请求来才醒,等时不占线程
onClick={handler} 挂事件 fs/url/path 拼出一台静态服务器
map + key 渲染列表 npm install mysql2 → 占位符查库
→ Apache + PHP 的替代路线
React 为什么值得学?原书给的判据是生态位:Google Trends 上 React 的流行度 远超 Angular、Vue 等对手,仍在增长34。Node.js 为什么值得学? 它是「事件驱动、非阻塞」的高并发模型(第 1 章讲过与 Apache 的本质区别), 且能用同一门语言写前后端35。两章合起来回答一个问题: 「PHP + MySQL + JavaScript」这本书的三件套,在现代形态下长什么样。
2. React 的入场三件套(原书第 20 章前半)¶
React 是什么:一个把「界面 = 数据的样子」变成现实的库——你描述数据, 它负责把数据画到页面上、数据变了它负责改。React DOM 是它操作真实 DOM 的 搭档;两者分工:React 想(算差异),ReactDOM 动手(改页面)36。
引入方式两条路:从 unpkg.com 的 CDN 拉(react.development.js + react-dom.development.js),或下载到本地;生产环境把 development 换成 production.min(压缩版)36。第三个脚本是 Babel:它干两件事—— 让你能在 JavaScript 里直接写 XML(JSX),并把 ES6 语法降级给老浏览器37。
<script type="text/babel">
function One() {
return <p>By Jeeves, it works!</p>
}
ReactDOM.render(<One />, document.getElementById('div1'))
</script>
走一遍第一个 React 程序:① type="text/babel"——浏览器不认这个 type,
原样跳过;Babel 接手这段脚本,把 JSX 翻译成普通函数调用,再交给浏览器执行;
② One 是个普通函数,返回一段 JSX(不是字符串,没有引号);
③ ReactDOM.render(组件, 挂载点) 把组件渲染进指定 div。
组件名必须大写开头:React 把 <div> 当 HTML 标签、把 <One> 当组件——
小写 one 不会被当组件,直接报错38。
把「从 HTML 到屏幕」的完整链条走清,React 的第一个魔法就拆完了:
- 浏览器加载页面:三个
<script src=…>依序执行——React(核心)、 ReactDOM(负责操作真实 DOM)、Babel(编译器)就位。 - 解析到
<script type="text/babel">:浏览器跳过(不认识的 type); Babel 扫过这段文本,把<p>…</p>翻成React.createElement('p', …)之类 的函数调用,把 ES6 语法降成老浏览器认识的写法。 - 编译产物被执行:
function One被定义(此时不运行);ReactDOM.render(<One/>, div1)被调用。 - React 调
One()拿到它返回的 JSX 描述 → ReactDOM 把这个描述变成 真实的<p>节点 → 插进 div1。 - 浏览器渲染:页面上出现那行字。
注意第 4 步:React 不是「生成 HTML 字符串塞进去」,而是先造一份 「界面应该长什么样」的描述(虚拟 DOM),再对照真实 DOM 精确施工—— 这为第 5 节的「改 state 自动重渲」埋下了机制伏笔38。
不用 JSX 也能写 React(纯函数调用拼 XML),但原书明说不推荐—— JSX 的整点价值就是让界面代码可读39。
3. 组件与 props:函数进、界面出¶
组件(component)就是把 UI 切成可复用的块,像函数一样:吃输入(props), 吐界面(JSX)。 第一个 props 例子:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>
}
ReactDOM.render(<Welcome name='Robin' />, document.getElementById('hello'))
// 页面显示:Hello,Robin
机制:render 时 <Welcome name='Robin' /> 让 React 调 Welcome,
递给它一个 props 对象 {name: 'Robin'};JSX 里花括号 {} 嵌入任何
JavaScript 表达式——{props.name} 取值,写 {76 / 13} 也能算40。
黄金法则:组件对 props 必须是纯函数。 纯函数 = 不改输入、不碰全局、无副作用
(像 mult(m1, m2) 返回乘积);impure 函数(比如改参数对象的 assign)绝不能当组件。
React 没有机制阻止你犯这个错——纯靠自觉,而「自觉」的依据就是这条法则41。
4. state 与生命周期:让时钟走起来¶
没有状态的函数组件做不了「会变的界面」。state(状态)是组件自己记住的数据; 改了它,React 自动重新渲染。 时钟例子把整套机制一次走全:
class Clock extends React.Component {
constructor(props) {
super(props) // 先调 super 才能在构造器里用 this.props
this.state = {date: new Date()} // 初始状态
}
componentDidMount() { // 组件挂上 DOM 后:起定时器
this.timerID = setInterval(() => this.tick(), 1000)
}
componentWillUnmount() { // 组件从 DOM 摘除前:清定时器
clearInterval(this.timerID)
}
tick() { // 每秒:更新状态
this.setState({date: new Date()})
}
render() { // 状态变了,这里自动重跑
return <span>{this.state.date.toLocaleTimeString()}</span>
}
}
ReactDOM.render(<Clock />, document.getElementById('the_time'))
四个新概念,逐个落位:
- constructor 是唯一可以直接给 this.state 赋值的地方;之后想改, 只能用 setState——直接改 state 不会触发重渲染,setState 才会42。
- mounting = React 把节点加进 DOM;
componentDidMount挂载后必被调, 是「起定时器、发请求」的标准位置;componentWillUnmount摘除前必被调, 是「清定时器、注销订阅」的标准位置。对比第 19 章手动 setInterval 的时钟—— React 把「何时启动、何时清理」也纳入了组件契约43。 - setState 一调,render 自动重跑——你只管说「状态现在是多少」,
页面怎么更新不用你操心。这是「声明式」的兑现:第 19 章你写
object.innerText = …告诉浏览器怎么改;React 里你改 state, 告诉它应该是什么样42。
把 Clock 的完整生命时间线排开,这套「挂载 → 运转 → 卸载」的生命周期就立体了:
t0 ReactDOM.render(<Clock/>)
└─▶ 构造器:new 组件、state = 此刻时间、super(props)
└─▶ render() 第一遍:画 12:17:21
└─▶ 组件挂载进 DOM
└─▶ componentDidMount 被调:setInterval 注册,每秒调 tick ─┐
t1 tick() → setState({date: 新时间}) │
└─▶ React:state 变了! │ 每秒
└─▶ render() 再跑:画 12:17:22 │ 重复
└─▶ ReactDOM 对比新旧两版描述,只改变化的部分 ────────────────┘
tN 组件被移除(如页面切换)
└─▶ componentWillUnmount 被调:clearInterval(timerID)——定时器殉葬
图说:出生(constructor+mount)→ 活着(每秒 setState→重渲)→ 死亡(unmount 清理)。
对照第 19 章的手动时钟:那边你要自己起 setInterval、自己改 innerText、 自己记着要 clearInterval;React 把「何时起、何时清」收进了组件的生命周期方法—— 资源跟着组件走,组件死了资源自动还。这是生命周期存在的全部理由43。
5. 事件、条件与列表:React 三件套¶
5.1b Toggle 例子的完整走查¶
把例 20-6(点按钮显隐文字)当主线,从「首次渲染」走到「第三次点击」:
// 构造器:state.isVisible = true;handleClick 绑定 this
handleClick() {
this.setState(state => ({isVisible: !state.isVisible}))
}
render() {
const show = this.state.isVisible
return (
<div>
<button onClick={this.handleClick}>{show ? 'HIDE' : 'DISPLAY'}</button>
<p>{show ? 'Here is some text' : ''}</p>
</div>
)
}
- 首次渲染:构造器跑,isVisible=true;render 按 true 出界面: 按钮文字「HIDE」+ 一段文字。
- 用户点按钮:浏览器发 click → React 找到 onClick 挂的 handleClick →
但方法里的 this 已经丢了(JS 方法脱离对象调用时 this 不再指组件)——
这就是构造器里
bind(this)存在的原因:绑过之后,this 永远锁死在组件上45。 - handleClick 执行:
setState(state => ({isVisible: !state.isVisible}))——用箭头函数拿最新 state,取反。React 记下「新状态是 false」, 安排重渲(可合并多次 setState)。 - 重渲:render 以 isVisible=false 重跑——按钮变「DISPLAY」,文字变空串; ReactDOM 对照新旧描述,只动了两处文本节点。
- 再点:回到 2,方向反过来。
整个环里你的手写代码只有三处:state 声明、事件处理器、render 描述。 「怎么改 DOM」从头到尾没出现过——这就是 React 与第 19 章手工作坊的本质区别46。
事件:React 事件名用驼峰(onClick 不是 onclick),处理器直接 JSX 里传:
<button onClick={this.handleClick}>
{show ? 'HIDE' : 'DISPLAY'}
</button>
<p>{show ? 'Here is some text' : ''}</p>
三个细节:① 处理器收到的是 React 包了一层「合成事件」的对象
(跨浏览器统一,要原生事件用 nativeEvent)44;② 构造器里
this.handleClick = this.handleClick.bind(this)——否则方法里 this 丢失45;
③ render 只能返回一个根元素,多个元素得包一层 <div>(或碎片)46。
条件渲染两条语法:三元 ? :(二选一)和条件 && <JSX>
(true && 表达式 = 表达式,false && 表达式 = false,React 不渲染 false)——
注意坑:{x && <p>…</p>} 在 x 为 0 时会把 0 渲染出来(0 是 falsy 但 React 渲数字),
要防就写 x !== 0 && …47。
列表:数组 map 成 JSX 数组,直接嵌进 JSX:
const cats = ['lion', 'tiger', 'cheetah', 'lynx']
const listOfCats = cats.map((cat, i) => <li key={i}>{cat}</li>)
ReactDOM.render(<ul>{listOfCats}</ul>, ...)
key:每个列表项给唯一 key——React 靠它认出「谁是谁」, 小改动时只调整对应 DOM 而不是重渲一大片。两个列表里都有「Paris」, key 不同就不会张冠李戴。map 里的元素就该带 key;数据自带唯一标识 (如 ISBN)最好,万不得已用数组下标(重排时性能差)48。
把列表渲染的数据流走一遍(例 20-8/20-9):① cats 是普通数组;
② cats.map((cat, i) => <li key={i}>{cat}</li>)——map 对每元素调箭头函数,
返回一个 <li>,四元素得四个 <li> 组成的新数组;
③ {listOfCats} 把这个 JSX 数组直接嵌进 <ul> 里——JSX 的花括号不只能
嵌表达式,也能嵌「一组元素」;React 依次展开。运行的第一次,map 出来的 <li>
没带 key,控制台立刻警告「Each child in a list should have a unique 'key' prop」——
警告不是错误,页面照常显示,但 React 认人不清、更新可能低效。
加上 key={i}(i 是 map 的第二个参数,数组下标)警告消失。
两个城市列表的例子说明 key 的业务价值:欧洲列表和美国列表里各有 Paris,
key 按 1–8 全局唯一后,React 重排时绝不会把密歇根的巴黎当成法国的48。
6. 受控组件:表单的主人换成 state¶
普通 HTML 表单元素自己记住输入值;React 里要让 state 当「唯一真源」—— 输入框的值永远来自 state,用户的每次输入都先写回 state:
class GetName extends React.Component {
constructor(props) {
super(props)
this.state = {value: ''}
this.onChange = this.onChange.bind(this) // 绑定 this
this.onSubmit = this.onSubmit.bind(this)
}
onChange(event) { // 每敲一键:value 同步进 state
this.setState({value: event.target.value})
}
onSubmit(event) {
alert('You submitted: ' + this.state.value)
event.preventDefault() // 拦截真实提交
}
render() {
return (
<form onSubmit={this.onSubmit}>
<input type="text" value={this.state.value}
onChange={this.onChange} />
<input type="submit" />
</form>
)
}
}
循环一整圈:输入框 value 由 state 驱动 → 用户敲字触发 onChange → setState 把字写进 state → state 变触发 render → 输入框显示新值。 数据只有一个流向(state → 界面),界面事件写回 state——所以叫「受控」。
把这条数据环画成图,和「非受控」的旧世界对比,选择 React 的代价就清楚了:
受控组件(React) 非受控(原生 HTML)
────────────── ─────────────────
state.value ──value──▶ 输入框显示 输入框自己记住值(浏览器私有)
▲ │
│onChange:用户每敲一键 │用户敲字
│ event.target.value │
└──── 写回 state ◀─────────────────────┘
值永远在手边:提交前验证、 提交时才读得到;
随时传给别的组件、随时格式化 中途想干预没门
text/textarea/select 三者在 React 里统一成这个模式:都用 value + onChange,
连 <select> 也不再靠 option 的 selected 属性,而是 select 的 value 等于谁49。
select 的切换值得单走一遍:初始 state.value = 'USA' → render 让
<select value='USA'> 呈现美利坚被选中;用户改选「Canada」→ onChange 触发 →
event.target.value 是 'Canada' → setState → render 重画,select 的 value
变成 Canada。「selected 属性写在哪个 option 上」这个老 HTML 的语法,React 里
整个蒸发了——因为值只有一个家(state),家在哪,选中就在哪49。
代价是多写几行样板代码,收益是值永远在手边:随时能传给别的组件、能在提交前验证。
event.preventDefault() 拦截浏览器默认提交——第 16 章的 preventDefault 在这里原样回收50。
React Native 一笔带过:同一套 JSX 技能写 iOS/Android 原生应用——本书只指个路51。
7. Node.js:服务器上的 JavaScript(原书第 21 章)¶
7.1 安装与第一口气¶
三平台装 Node.js,装完一律 node -v 验证(出版号即成功)。建议装 LTS
(长期支持)版;Windows 安装时勾选「编译原生模块的工具」省心52。
Node 程序用 ECMAScript 模块(import/export),文件用 .mjs 扩展名
(m = module,是告诉 Node「这是模块」的最简方式)53。
7.2 三十行写一个 web 服务器¶
// helloWorld.mjs —— 被复用的模块
export { helloWorld }
function helloWorld() { return 'Hello World' }
// app.mjs —— 主程序
import * as http from 'http'
import { helloWorld } from './helloWorld.mjs'
const server = http.createServer((request, response) => {
response.writeHead(200, {'Content-Type': 'text/html'})
response.end(helloWorld())
})
const port = 8000
server.listen(port, () => console.log('Server listening on port ' + port))
node app.mjs 启动,浏览器开 localhost:8000 看到 Hello World;
Ctrl-C 停服务,改代码后必须重启(保存不会自动生效)54。
走一遍机制:① import 把 Node 内置的 http 模块 和一个自定义模块装进来
(export/import 就是第 5 章 PHP include_once 的现代版);
② http.createServer(回调) 造服务器——每来一个请求,Node 就调一次这个回调,
传入 request(请求)和 response(响应)两个对象;
③ 回调里 writeHead(状态码, 头) 先发响应头,end(内容) 收尾发送;
④ listen(端口) 开始监听。端口选 8000 而非标准的 80,避开还在跑的 AMPPS Apache54。
把一次请求的完整时序走清(这是「事件驱动服务器」的微观视图):
浏览器 Node.js 进程
│ │
│── GET / ──────────────▶│ createServer 的回调被唤醒(request=/, response=…)
│ │ 回调执行:writeHead(200) → end('Hello World')
│◀── 响应头 + 内容 ──────│ (微秒级;期间若有别的请求到达,另起一次回调)
│ │ 回调返回 → 进程回到等待(事件循环空转)
│── GET /favicon.ico ───▶│ 下一个请求再唤醒……
图说:进程大部分时间在「等」;每个请求是一次短暂的唤醒。
和第 1 章那张 Apache 对照图呼应:Apache 每请求一线程、线程里干等; Node 全进程一个循环、每个请求只是一次函数调用——「不用线程用回调」就是 两种服务器模型的全部差别35。
这就是第 1 章说的「事件驱动」兑现:回调在有请求时才被调,等待期间不占线程—— 和第 19 章的「事件挂号」同一个模型,只是事件源从「用户」换成了「网络请求」。
7.3 一台能用的静态服务器:fs + url + path 与安全¶
例 21-3 把服务器升到 Apache 的入门水平:按 URL 找文件、读出来、回给浏览器;
文件不存在回 404。三个新模块各管一段:url 解析请求路径、fs
(readFile)读文件、path(resolve/extname)处理路径与扩展名55。
两道安全闸,值得逐字记住:
- 路径遍历防护:
if (!fpath.startsWith(SCRIPT_DIRECTORY))—— 没有这行,攻击者用../../etc/passwd这样的路径能读到服务器上任何文件 (path traversal 攻击)56; - 扩展名白名单:只放行
.html——没有这行,请求localhost:8000/server.mjs就把你的源代码下载走了56。
把请求 hello.html 的完整处理链走一遍(例 21-3):
浏览器 GET /hello.html
→ url.parse(request.url).pathname 取出 "/hello.html"
→ '.' + pathname = "./hello.html";path.resolve 转成绝对路径
→ 闸①:绝对路径以脚本所在目录开头?不是 → 400 Bad Request
→ 闸②:扩展名是 .html?不是 → 400 Only .html supported
→ await readFile(fpath, 'utf8') —— 异步读盘,等待期间可接新请求
→ 成功:writeHead(200) + end(文件内容)
→ 失败(文件不存在):catch → writeHead(404) + end('404 Not Found')
两条闸的顺序有讲究:先校验路径再碰文件系统——校验不过直接 400, 连文件系统的门都不敲;白名单把「可读的东西」压缩到最小集合 (教学版只放 HTML;真实服务器还要按扩展名配 Content-Type)。 这套「解析 → 校验 → 异步读取 → 双路响应」的骨架,是每一个静态文件服务器的 祖传结构——你后来用的任何框架,骨子里都是这四步56。
readFile 前有个 await——第 17 章的 Promise 在这里原样回收:
读文件是异步的,等文件时线程去伺候别的请求,这就是「几千个并发请求也不堵」的
机制注解(原书原话:「no log-jam waiting for the filesystem」)57。
7.4 npm 与 MySQL:后端全家桶的最后一格¶
npm(官方戏称「npm Is Not An Acronym」)随 Node 一起装好;
npm install mysql2 几秒装完数据库模块58。Node 内置模块一桌
(http/https/fs/path/url/crypto/dns/os/net/querystring/util),装上 mysql2,
PHP + MySQL 的整套后端能力 JavaScript 就齐了59。
连库查询(例 21-6):
import mysql from 'mysql2/promise'
const connection = await mysql.createConnection({
host: 'localhost', user: 'node',
password: 'letmein', database: 'publications'
})
const query = 'SELECT * FROM classics WHERE author = ?'
const [results, fields] = await connection.execute(query, ['Jane Austen'])
console.log(results[0].title) // Pride and Prejudice
connection.end()
和第 10 章的 PDO 逐点对应:? 占位符防注入、execute 传参、结果集逐行取用;
差别只是语言换了。凭据别写死在代码里:Node 20.6 起内置 .env 支持——
config.env 里写 USER=node 等键值,node --env-file=config.env mysql.mjs
启动,代码里用 process.env.USER 读——第 10 章明文密码的老问题,这里给了
一个轻量正解60。
把这条查询走一遍,Node 版后端的数据通路就通了:
import mysql from 'mysql2/promise'——装进来的模块带/promise后缀: 它的方法都返回 Promise,可以配合 await(第 17 章的老朋友)。await createConnection({…})——拿着四要素建连;此刻 TCP 握手 + MySQL 认证在底层完成,await 期间进程照样能处理别的事。execute(query, ['Jane Austen'])——SQL 里的?是占位符: 参数不拼进 SQL 文本,由驱动单独通道传给 MySQL——注入攻击无门 (与第 10 章预处理语句同一思想,同一套?记号)。- 解构
[results, fields]——execute 返回两样:查到的行数组 (每行是一个对象,results[0].author按键取值)和字段元信息。 connection.end()还连接——短脚本跑完即退;服务器程序里则常驻。
跑起来的输出原书给了:Results: 1,然后那一行 Jane Austen 的记录原样打印—— 第 8 章亲手插进 classics 的数据,此刻从 JavaScript 里读回来了60。
8. 两章合龙:一个技术判断¶
把 React 与 Node 放在一起看,这本书的技术弧线就闭合了:
- 前端:第 19 章「手动选元素、手动改样式」→ React「组件 + state, 声明界面该是什么样」——抽象升了一级,但底下的 DOM、事件、异步原语没有变;
- 后端:PHP + Apache → Node.js + http 模块——语言换了,但 「HTTP 请求进来 → 查库/读文件 → 拼响应」的骨架与第 10 章一模一样, 连占位符防注入都是同一个思想。
- 共同的地基:Promise/await、事件驱动、模块化——第 13–19 章教的 JavaScript 原语,两端通吃。
判断(我们的,不是书里的): 这两章的教学选择(浏览器里直接引 React 文件 + Babel 页内编译、Node 用裸 http 模块手搓服务器)刻意绕开了现代工程链路 (create-react-app / Vite、Express 框架、连接池)——对「讲清原理」是对的: 你能看见每一层在干什么;但照着这两章搭出来的东西不能直接进生产。 正确读法:把它们当「原理演示」,原理通了之后用工程化的标准工具重写。 如果错,会错在: 低估了工具链抽象的厚度——有人可能从裸 http 直接跳到 业务代码,跳过 Express 这类「标准化层」,结果安全细节(如这两章手动加的 两道闸)在更大的代码量下顾不过来。
9. 作者的判断与证据¶
| 书中的说法 | 成色 |
|---|---|
| React 因流行度入选(Google Trends 远超他者) | 生态位判断,给了数据出处(趋势图);流行度≠适合一切,但选教学框架足够34 |
| 新代码用函数组件而非 class | 时代共识(hooks 取代 class 的口径);class 在本章只为教 state 生命周期61 |
| 组件必须纯(黄金法则) | 设计约束,React 官方同款;原书坦承「React 没有机制强制」——诚实的边界41 |
| Node 服务器「几千并发也不堵」 | 机制性主张(无文件等待的 log-jam),与第 1 章事件模型互证;无基准数字,属定性结论57 |
| 路径遍历/扩展名两道闸 | 安全必修,机制解释(../../etc/passwd、下载源码)具体到可复现——本章最硬的两行代码56 |
| 凭据写 .env、用 process.env 读 | 标配实践(Node 20.6 内置),纠正了第 10 章明文密码的教学妥协60 |
| 「一门语言写两端」作为 Node 的卖点 | 方向性事实(前后端同语言减少切换成本),但原书把它写成单向的替代叙事;PHP 与 Node 混布(各管一段)也是常态——判断应留给场景1 |
10. 边界与局限¶
- React 教学停在 class 时代:hooks(useState/useEffect)只被一句话提及—— 而现代 React 代码几乎全是函数组件 + hooks。学完本章能读懂 class 组件老代码, 写新代码还得补 hooks 这一课(官方文档的下一站)。
- 没有构建工具链:Babel 页内编译只适合教学——每次加载都现场编译,慢且不适合生产; Vite/webpack 等工具链、组件文件拆分、CSS-in-JS 全部缺席。
- 组件间通信没讲:本章全是单组件示例;props 从上往下传、状态提升、context—— 真实应用的血肉没有涉及。
- Node 章的服务器是玩具级:单线程回调、无路由、无中间件、无连接池、无日志; Express/Koa 这层「行业标准抽象」没有出现——跨过去直接用会踩本章自己列的那些坑。
- await 的线程模型没展开:「等文件时去伺候别的请求」一句话带过; 事件循环(event loop)这个 Node 的心脏,本章只摸到了脉搏。
- mysql2 只演示了查:增删改、事务、连接池管理未涉;生产用法需读模块文档。
- React 章的 Babel 页内编译有性能与安全双重代价:每次加载都现场编译, CSP(内容安全策略)严格的站点会拒绝内联脚本;教学可接受,生产必须用构建工具 预编译——这一点原书没有明说,是本章读法上的隐藏前提。
- Node 章没有对比 PHP 的短板:事件驱动模型的代价(CPU 密集任务会堵住 整个循环、错误处理要一路 catch)只字未提;读者容易带着「Node 全面优于 PHP」 的单边印象离开(第 1 章「Apache remains relevant」一节反而是更平衡的表述)。
11. 章末自测:两章的问答题合答¶
| 原书的问题 | 我们的回答 |
|---|---|
| React 脚本怎么引入? | unpkg CDN 或本地下载;生产用 production.min36 |
| XML 怎么进 JavaScript? | JSX,经 Babel 把 <script type="text/babel"> 里的 XML 翻译成函数调用37 |
| JSX 脚本的 type 值? | text/babel——浏览器跳过,Babel 编译后再执行38 |
| 扩展 React 的两法? | 函数组件(新代码推荐)与 class 组件(legacy,本章为讲 state 而用)61 |
| 纯/impure? | 纯 = 不改输入、不碰全局、无副作用;组件对 props 必须纯(React 不强制,靠自觉)41 |
| React 怎么追踪状态? | 组件的 state 对象(构造器初始化);之后只能 setState 改,改了自动重渲42 |
| JSX 里嵌表达式? | 花括号 {}(任意表达式;for/if 不是表达式,不行)40 |
| 构造后怎么改状态? | 只能 this.setState({…});构造函数是唯一可直接赋 this.state 的地方42 |
| 构造器里用 this.props 前要先做什么? | 先调 super(props)——否则构造器里 this.props 不可用43 |
| JSX 里怎么做条件? | 三元 ? : 二选一;条件 && <JSX> 只真渲染;坑:x 为 0 时 {x && …} 会渲染出数字 047 |
| 请求交给文件系统后 Node 干什么? | 不等——事件驱动:先去处理其他请求,文件读完回调再发响应(无 log-jam)57 |
| 引入预写模块的方法? | import 声明;Node 内置模块(http/fs/url 等)无需安装直接引53 |
| 管 HTTP/解析 URL/访问文件系统的三模块? | http(传输)、url(解析)、fs(文件系统)55 |
| HTTP 默认端口? | 80;教学示例用 8000,避免和 AMPPS 的 Apache 抢端口54 |
| 创建服务器对象的方法? | http.createServer(回调)——每来一个请求调一次回调54 |
| 响应里先回什么? | 响应头 writeHead(状态码 + Content-Type),然后 end(数据)收尾54 |
| 怎么结束连接? | response.end(内容)54 |
| 怎么启动 / 手动停止 Node 服务器? | node app.mjs 启动;Ctrl-C 停止;改代码必须重启才生效54 |
| 往终端写消息? | console.log(信息写到启动它的终端窗口)54 |
| 怎么加外部模块? | npm install 模块名(如 npm install mysql2,几秒装完)58 |
| Node 怎么访问 MySQL? | npm 装 mysql2;createConnection 建连、execute 带占位符查询、end 关连接60 |
12. 可带走的¶
- React 三脚本:react + react-dom + babel;JSX 写在 type="text/babel" 里,组件名大写。
- 组件 = 函数(props 进、JSX 出);对 props 必须纯。
- JSX 花括号嵌表达式;
{}里写变量、算式、函数调用都行。 - state 是组件的记忆:构造器初始化,setState 更新;改了自动重渲。
- componentDidMount 起定时器、componentWillUnmount 清——组件的出生与临终。
- React 事件:onClick 驼峰 + 构造器 bind;this 指向要手动绑。
- 条件:三元和
&&;列表:map + key(key 让 React 认人不认错)。 - 受控组件:value 绑 state、onChange 写回 state——表单唯一真源是 state。
- Node:.mjs 模块,import/export;http.createServer 回调处理请求。
- 响应 = writeHead(状态码+头) + end(内容);listen(端口)启动;改码重启,Ctrl-C 停。
- 静态服务器三模块 http/url/fs;两道安全闸:目录前缀校验 + 扩展名白名单。
- npm install 装模块;mysql2 的
?占位符和 PDO 一个思想;凭据进 .env, node --env-file 启动,process.env 读取。
12.1 两章主线例子的对照速查¶
| 任务 | 第 19 章「手工作坊」 | React / Node 的现代形态 | 不变的内核 |
|---|---|---|---|
| 显示内容 | innerText = … | 组件 return JSX | 内容来自数据 |
| 内容变化 | 手动改属性 | setState → 自动重渲 | 状态驱动界面 |
| 定时任务 | setInterval + 手动清 | componentDidMount/WillUnmount | 事件驱动 |
| 点按钮响应 | o.onclick = fn | onClick={this.handleClick} + bind | 事件 → 回调 |
| 表单取值 | 提交时读 value | 受控:value↔state 双向 | 提交前拿到值 |
| 列一堆元素 | 字符串拼接 HTML | map + key | 数据 → 列表 |
| 起服务器 | (Apache,装好的) | http.createServer + listen | 请求→响应 |
| 查数据库 | PDO + prepare | mysql2 + execute | 占位符防注入 |
这张表是第 20–21 章最该带走的东西:工具换了一轮又一轮,右列的内核纹丝不动。 学会判断「哪一层是时髦、哪一层是原理」,比学会任何一个框架都保值。
12.2 两章在全书的位置¶
第 20–21 章是「现代形态」的展示厅,不是终点站。它们各自指向明确的下一站: React → hooks 与构建工具链(官方文档);Node → Express 与事件循环深挖。 而第 22 章会告诉你:即便没有这些现代工具,用第 3–12 章的原教旨手艺 (PHP 渲染 HTML、会话认人、原生 JS 交互),一个完整的社交网站照样立得起来—— 工具决定效率,原理决定能不能立起来。
12.1 React 的出场理由:为什么页面会"写不动"¶
第 20 章开篇的问题陈述:用原生 HTML/CSS/JS 做动态站点的 前端,代码会"又臭又长"——事务性代码挤满各个角落,开发变慢, bug 变多2。React 是给这个痛点开的药,原书列的三个存在理由:
还有一个定位要说准:React 是库不是框架——它只管 V (view)这一层,要学的东西只有几个函数,剩下的全是你的 JavaScript 基本功4。这也解释了排课位置:第 13–17 章是 给第 20 章攒弹药。
12.2 心智模型倒转:从"怎么变"到"是什么"¶
第 20 章最重要的一句话不是 API,是一个范式声明: 开发者不再需要描述"界面怎么一步步变化", 只需描述界面在某个状态下长什么样;状态一变, React 替你更新 UI5。
这个倒转是全书前端线的分水岭:第 19 章的"拿到元素改样式" 在此退役。退役不是白学——React 的底层仍是 DOM, 调试和读报错时,第 19 章的知识随时回魂。
12.3 JSX:HTML 住进了 JavaScript 里¶
JSX 是 JavaScript 的扩展(名字就是 JavaScript XML),允许把 类 XML 文本直接写进 JS6。最小例子(例 20-1 的浓缩):
function One() {
return <h1>By Jeeves, it works!</h1>; // 不是字符串,不加引号
}
ReactDOM.render(<One />, document.getElementById('div1'));
四条铁律:
- 组件名首字母必须大写:React 以小写开头当 HTML 标签、
大写开头当组件;
<one />不会报错,只会静默失败成 找不到的自定义标签7。 - 花括号里是表达式:
{props.name}、{76 / 13}都行; 但if/for是语句不是表达式,不能进花括号8。 - class 等 HTML 属性要改名:JSX 里写 className、htmlFor (因为 class 是 JS 保留字)——从 HTML 抄片段时最常踩。
- JSX 必须有单一根元素:相邻的标签要包一层 <>...</> (fragment)或 div。
配套设施是 Babel:它把 JSX 翻译成普通函数调用, 顺带让老浏览器也能跑 ES6 语法——一箭双雕,所以示例页面 总要先引 React、再引 Babel9。
12.4 Props:组件的输入接口¶
Props 就是组件的参数——"properties"的缩写,一个对象10:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
// 使用:<Welcome name="Robin" /> → React 把 {name:'Robin'} 装进 props
两条纪律,原书一条明写、一条暗含:
- 明写:组件对 props 必须是纯函数——同样的输入永远同样的 JSX 输出,不许改 props、不许带副作用。原书称之为"golden rule",并坦白 React 不会替你 enforcement,只能靠自律11。
- 暗含:组件即函数——第 15 章的函数纪律(纯、小、 单一职责)原封不动地搬过来就是组件纪律。
12.5 状态:函数组件与类组件的分界线¶
第 20 章的状态教学走了一条"先函数、后类"的爬坡:
- 纯展示(欢迎页、静态卡片)→ 函数组件,无状态, 原书称之为 functional stateless components12。
- 要状态(会走的时钟、计数器)→ 类组件:
class Clock extends React.Component,在 constructor 里this.state = {date: new Date()},需要变化时 setState, React 触发重渲染13。
时钟例子的完整心智模型:
constructor: state 的出生地(只跑一次)
setState: 改 state 的唯一正规入口——不是 this.state.x = ...
render: 纯函数:读 state,产 JSX;state 一变自动重跑
判断句:能不用状态就不用状态;一旦需要,立刻把组件升级成类, 别在函数组件里偷偷用全局变量糊弄——那是把第 15 章刚立起来的 纯函数纪律亲手拆掉。
12.6 Node.js:第 1 章伏笔的兑现¶
第 21 章把第 1 章那句"Node 用事件驱动、不为每个连接开线程" 展开成了完整一章14。最小可跑的服务器(例 21-x 的浓缩):
import { createServer } from 'http';
const server = createServer((request, response) => {
response.end('Hello from Node');
});
server.listen(8000, () => console.log('Server listening on port 8000'));
node server.mjs 启动,浏览器访问 localhost:8000 出结果15。
对照第 1 章的 Apache 时序图:Node 版本里,同一个文件既是程序
又是服务器——没有 Apache 居中传话,JS 直接坐在 8000 端口上
应答16。
12.7 Node 的文件服务器:一次安全设计的示范¶
第 21 章的静态文件服务器值得整段精读,因为它在短短几十行里 演示了三个安全决定:
- 扩展名白名单:只允许 .html——否则访问 localhost:8000/server.mjs 就能下载服务器源码17;
- 不存在的文件 → 404:用异常分支返回状态码, 而不是让进程崩溃18;
- 异步读取:fs/promises 的 readFile 配 await, 文件大也不阻塞别的请求19。
const data = await readFile(fpath, 'utf8'); // 等它 fulfilled
// 抛异常 → catch → response.statusCode = 404
第 17 章的 Promise/await 在这里第二次上岗—— 同一个心智模型,从浏览器换到了服务器,语法原样通用20。
12.8 全栈合流:Node 直连 MySQL¶
第 21 章的收尾把最后一块拼图放上:Node 侧用 mysql2 驱动
直连第 10 章的同一个数据库——createConnection 之后
connection.execute() 走参数化查询,和 PDO 的占位符
同一副药21。
至此全书的"两岸"正式通航:
第 22 章不走这条新航线(它用经典的 Apache+PHP),但两条航线的 机制互为注释:看懂一边,另一边只需对照差异。
12.9 两章合练:React 前端的 Node 后端倒计时¶
一个打通两章的最小练习(概念拼装):
[Node] 写 /api/time 接口:返回当前时间的 JSON
[React] 组件挂载后 fetch('/api/time'),把结果显示在页面上
[React] 类组件 + setState + setInterval 每秒刷新
[判定点] 状态只在 React 侧;Node 侧无状态——纯查询接口
[对照] 把 Node 换成第 17 章的 PHP 版,前端代码几乎不动
这个练习的隐藏考点在最后一条:前后端靠 JSON 接口解耦, 换后端不动前端——这正是第 17 章"异步取数据"思想的项目级形态。
12.10 练习场(第 20/21 章)¶
- 组件三拆:把一个静态个人主页拆成 Header/Profile/Footer 三个函数组件,用 props 传数据——体会"拆"的手感。
- 时钟改造:照书上的时钟例子,加一个"暂停/继续"按钮 (再点恢复 setInterval)——练 setState 与生命周期的配合。
- 纯函数体检:给 §12.4 的 Welcome 组件依次注入三个副作用 (console.log、改 props、读全局变量),预测 React 的每种反应。
- Node 回声服务器:写 /echo?msg=hi 返回 "You said: hi"—— 体会 request 对象怎么携带查询参数。
- 白名单攻防:把 §12.7 的 .html 白名单去掉,亲测下载 server.mjs 的漏洞;加回白名单再测——把安全决定从"读到" 变成"撞上"。
12.11 FAQ(五问)¶
Q1:React 例子里的 ReactDOM.render 和现代写法不一样? 本书第 20 章使用 ReactDOM.render(例 20-1 等),这是该版原书的 写法(书外补充:React 18 起推荐 createRoot)。跟书跑用书的写法, 看新文档时再平移,心智模型相同。
Q2:我该学函数组件还是类组件? 本书先函数、后类的顺序就是答案:展示用函数,状态升级类。 理解了类组件的 state,将来再学 hooks 的 useState 就是 "同一个思想换个语法"22。
Q3:Node 能不能取代 Apache+PHP? 能,也不必要。原书第 1 章就给了混用策略;第 22 章选择 PHP 主线是因为项目需求简单。选型看团队、看生态,不看情绪。
Q4:Node 的 await 和第 17 章浏览器的 then 是什么关系? 同一枚硬币的两面:await 是"停下来等 Promise 兑现"的语法, then 是"挂上兑现后的回调"。本书第 21 章的例子大量用 await, 读起来几乎像同步代码——这是异步写法演进的明确方向23。
Q5:学到这里,全栈到底意味着什么? 意味着同一条请求链上每一环你都见过源代码:浏览器里的 JSX 如何发出 fetch、Node/PHP 如何应答、SQL 如何取数、响应如何 变回界面。全栈不是样样精通,是链路上没有盲段。
12.12 时钟例子精读:setState 的全身解剖¶
第 20 章的时钟(例 20-x)是全书理解 state 的最短路径, 把它拆成四层:
class Clock extends React.Component {
constructor(props) {
super(props); // ① 先叫父类开工
this.state = {date: new Date()}; // ② state 的出生地
}
render() {
return <h2>It is {this.state.date.toLocaleTimeString()}.</h2>;
}
}
- constructor 只跑一次(挂载时);render 跑很多次 (state 每次变)。把耗时操作放前者,把纯展示写后者。
- setState 不是赋值:写
this.state.date = ...不会触发 重渲染——React 只认 setState 这个"变更入口"24。 计时器里每秒一次的setState({date: new Date()}), 就是在按门铃让 React 重新渲染。 - super(props) 不可省:ES6 类规则,React 的 props 要在父类先安家,this.props 才可用。
原书说得很直白:无状态代码做不了时钟这类事,需要状态时就该 用类25——"何时升级组件"的判断,这是全书给过的最简形式。
12.13 虚拟 DOM 的机理:一次 diff 的走查¶
虚拟 DOM 不是黑魔法,是一次"最小化补丁"的算法(第 20 章的 概念表述26,走查如下):
1. 你调用 setState → React 记下"状态变了"
2. render() 按新状态产出一棵新的虚拟树(内存里的 JS 对象,
不是真 DOM)
3. React 把新树与旧树逐节点对比(diff)
4. 只把不同的节点翻译成真实 DOM 操作(改文本、加类、移元素)
5. 真实 DOM 被最小幅度更新
两个推论,直接改变写法:
- 别手动碰真实 DOM:React 以为它是那棵虚拟树的唯一映射, 你在外部改了,两边失配,bug 形状会很怪异。
- render 多跑几次无所谓:反正只是内存里比对。 这解放了写法——不用像第 19 章那样精打细算了"这次到底 改哪个元素",React 替你算。
代价也要说清:内存里多一棵树、diff 有计算成本。 对"秒级更新一次"的界面毫无感觉;对"每秒上千次"的场景 才需要优化(书外补充:shouldComponentUpdate 系手段)。 学习期,这笔税交得心甘情愿。
12.14 Babel 与"编译"这件事的边界¶
本书第 20 章的示例页面靠浏览器里直接引 Babel 跑 JSX—— 教学上可以,生产不行(每次访问都现场翻译,慢)27。 这个落差正好用来认清"编译"在前端的位置:
React 章只教左半边的概念,右半边是工程问题。认清这条边界 的好处:读第 20 章的例子不被"怎么没构建"干扰, 将来接现代工具链时也知道自己在换哪一环。
12.15 Node 与 Apache 的总对照表¶
第 21 章学完后,把第 1 章的两位主角摆上同一张表:
| 维度 | Apache + PHP | Node.js |
|---|---|---|
| 语言 | PHP(服务器专用) | JavaScript(前后通吃) |
| 请求模型 | 每连接一线程/进程,请求间互不影响 | 事件循环,单线程并发 |
| 处理静态文件 | 天生本职,模块加持 | 要自己写(白名单、404) |
| 适合 | 内容站、经典 LAMP 应用 | 实时应用、聊天、游戏服28 |
| 本书项目 | 第 22 章全程 | 未进项目(选修航线) |
| 混用 | 可与 Node 共存,各管一段 | 同左 |
判断句(原书立场):没有"谁取代谁",只有"哪段链路用谁"; 第 1 章给出的混用策略,到第 21 章学完才有能力执行。
12.16 Node 事件循环的一分钟模型¶
Node 的"快"从哪里来,一分钟模型就够(第 21 章开篇的非阻塞 模型表述29):
一个主循环不断问两件事:
① 有没有新请求进来? → 有:登记它的回调,继续问
② 有没有已登记的 I/O 完成了? → 有:执行它的回调
文件读取、网络响应都挂在这个循环上,
所以"等文件"不挡"应答下一个请求"——这就是非阻塞。
对照第 21 章代码里 await readFile 的样子:函数名义上等, 但等的是 Promise,主循环这期间照常在跑别的回调30。 第 17 章浏览器里学的 Promise 语义,在 Node 侧原封不动—— 这是把异步一章学扎实的复利。
12.17 两章的错位互补:React 管变,Node 管流¶
最后把两章拼成一句全章总结:
- React(第 20 章)解决"界面如何跟随状态"——变的问题;
- Node(第 21 章)解决"服务器如何同时服务很多人"——流的问题;
- 第 22 章选择不用它们,不是否定,而是教学排序: 先经典栈立稳全栈骨架,新栈随时可换装上船。
如果只能各带一句话走:
12.18 FAQ 补遗(四问)¶
Q1:函数组件里能用 state 吗? 本书第 20 章的答案是:不能,要状态就升级类组件31。 (书外补充:hooks 时代的 useState 改变了这一分工; 但类组件的 state 心智模型仍是理解 hooks 的最好垫脚石—— 本书的顺序依然是对的。)
Q2:Node 的例子为什么是 .mjs 后缀? mjs 是 Node 的 ES 模块文件——用 import/export 语法。 第 21 章的例子用现代模块写法,所以后缀不同32。 import 与第 20 章 Babel 页里的写法同源,都是 ES6 模块。
Q3:React 章节例子没有构建步骤,class 语法哪来? 示例页引的 Babel 负责把 ES6 类语法翻译成旧语法33—— 所以浏览器"居然"能跑 class。这也再次印证 §12.14: Babel 是翻译官,不是框架。
Q4:这两章和找工作有什么关系? React 与 Node 是当下前端/全栈岗位的默认词表。 但本书教它们的真正目的不是词表,而是两个范式: 声明式 UI 与事件驱动服务器。范式是会过期的语法之下 不过期的部分——面试考语法,工作考范式。
12.19 末格:两章的「保值」与「过时」清单¶
长命: 组件化与 props 的单向数据流、纯函数组件纪律、 状态驱动 UI 的声明式范式、事件驱动与非阻塞的服务器模型、 Promise/await 的异步心智、扩展名白名单与最小权限的安全直觉。
已被接管: ReactDOM.render 让位于 createRoot(React 18)、 类组件的 state 让位于 hooks(学习序仍推荐先类后 hooks)、 Node 示例的 http 裸写让位于 Express 等框架、示例页内联 Babel 让位于构建工具链。
交接建议:跟书跑用书的写法(例子可复现是硬指标); 出了本书,按"过时清单"逐条平移到现代写法——心智模型 全部沿用,要换的只是 API 名字。
12.20 章末总复习:十五个自查问题¶
React 1.React 是库还是框架?管 MVC 的哪一层?(§12.1)
范式 2."描述最终状态"与"操作 DOM"的写法差在哪?(§12.2)
JSX 3.组件名为什么必须大写?花括号里能放什么?(§12.3)
props 4.props 的"golden rule"是什么?违反会怎样?(§12.4)
状态 5.函数组件与类组件的分界线是什么?(§12.5/12.12)
虚拟DOM 6.setState 之后 React 内部发生哪四步?(§12.13)
Node 7.Node 的并发模型与 Apache 差在哪?(§12.6/12.16)
文件服务 8.扩展名白名单防的是什么具体攻击?(§12.7)
异步 9.Node 里 await 与浏览器里 then 的关系?(§12.16)
全栈 10.换后端不动前端的条件是什么?(§12.9)
十问全过,两章毕业;带着答案进第 22 章,项目的每一行都会更眼熟。
12.21 与第 22 章的接口说明¶
本章两章是"选修航线",第 22 章项目完全不用它们—— 这不是遗漏,是原书的排课设计:先用无构建的经典栈把 全栈骨架立稳,再给新栈一个完整的概念课。读项目章时 不需要回头补 React/Node;但读完项目后回看本章, "声明式 UI"与"事件驱动服务器"会给经典栈的每一行 提供新的读法——两条航线互为注释,这是本书结构上的 最后一处妙手。
12.22 交接备忘¶
- ReactDOM.render 是该版原书写法,现代为 createRoot; 保留原写法以维持例子可复现,迁移指南见 §12.18 补遗。
- Node 章的 http 裸写是教学用最小内核,不是生产写法 (Express 起步,书外补充);但白名单、404、异步三决定 在任何框架下都成立。
- 类组件先于 hooks 的章节顺序,是理解 state 的最稳路径, 翻新时勿因"hooks 更现代"而倒序重排。
12.23 末行¶
两章的最后一个建议:亲手把 §12.9 的"倒计时练习"做出来。 React 侧的 state 与 Node 侧的 await,会在那个练习里 第一次隔着一个 fetch 互相致意——那一刻,"全栈"两个字 从名词变成了你做过的事。
12.24 两章机制在调试中的复利¶
最后记一笔"什么时候会谢这两章":调试经典栈项目(第 22 章)时, 你会在浏览器里看组件状态、在服务器日志里追异步回调、 用虚拟 DOM 的直觉猜"为什么这个元素没更新"。这些动作 不需要项目真的用了 React 或 Node——范式是会迁移的, 这正是本书把它们安排在项目章之前的最后一层用意。
12.25 末二行¶
再补一行,凑成对:React 与 Node 两章之后,本书的技术线 就此分岔为经典栈与新栈两条;分岔不是选择,是地图—— 第 22 章走经典栈时你知道另一条路在哪,将来要换栈时 你也知道该回到哪一章取票。
12.26 末三行¶
最后一行给本章的复现性说明:React 例子靠示例页引库, Node 例子 node 命令直接起,两者都不需要安装任何工具链—— 这种"零构建"的确定性是有代价的(写法偏旧),但换来的是 现在就能跑、十年后大概率还能跑。教学场景,这笔交换永远划算。
12.27 收笔¶
两章至此合拢;下一站,第 22 章,全书结业。
12.28 收笔·补一行¶
若还有一分钟,把 §12.3 的四条 JSX 铁律抄进笔记——它们是本章出错频率最高的四件事,也是面试问 React 时最常被问到的四件事。
12.29 收笔·再补一行¶
若还有两分钟,把 §12.7 的三条安全决定(白名单、404、异步)讲给一位不会 Node 的朋友听——讲得动,说明它们已经长成了与语法无关的判断力。
12.30 收笔·最后一格¶
两章的全部内容至此交付完毕;若要做标记,把书脊折在 §12.5 与 §12.6 之间——左边是浏览器里的 React,右边是服务器上的 Node,中间那道缝,就是全栈一词的宽度。
12.31 收笔·末行¶
末行留白给读者:下一章是结业项目,你准备好认出每一位老朋友了吗?
12.32 余白¶
留几行余白,免得恰好顶线。
13. 原文地图¶
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| React 的选型理由 | Chapter 20. Introduction to React | text/31-ch20-chapter-20-introduction-to-react.txt:15(搜「overtook jQuery」)· :31(搜「way ahead」) |
| 三脚本引入 | 同上 | text/31-ch20-chapter-20-introduction-to-react.txt:76(搜「unpkg.com」)· :88(搜「production.min」)· :133(搜「babel.min.js」) |
| 第一个组件 | 同上 | text/31-ch20-chapter-20-introduction-to-react.txt:183(搜「text/babel」)· :185(搜「By Jeeves」)· :224(搜「lowercase letters」) |
| class 与纯函数法则 | 同上 | text/31-ch20-chapter-20-introduction-to-react.txt:235(搜「not recommended」)· :308(搜「golden rule」)· :332(搜「golden rule」) |
| props 与花括号 | 同上 | text/31-ch20-chapter-20-introduction-to-react.txt:405(搜「Welcome(props)」)· :431(搜「curly braces」) |
| 时钟与生命周期 | 同上 | text/31-ch20-chapter-20-introduction-to-react.txt:500(搜「constructor」)· :525(搜「super」)· :536(搜「componentDidMount」)· :551(搜「componentWillUnmount」)· :568(搜「setState」)· :637(搜「only legitimate way」) |
| 事件与条件 | 同上 | text/31-ch20-chapter-20-introduction-to-react.txt:650(搜「syntheticEvent」)· :714(搜「bind」)· :731(搜「ternary」)· :774(搜「&&」)· :837(搜「literal falsy」) |
| key 与受控组件 | 同上 | text/31-ch20-chapter-20-introduction-to-react.txt:896(搜「unique key」)· :994(搜「source of truth」)· :1199(搜「selected attribute」) |
| React Native | 同上 | text/31-ch20-chapter-20-introduction-to-react.txt:1276(搜「React Native」) |
| Node 安装与模块 | Chapter 21. Introduction to Node.js | text/32-ch21-chapter-21-introduction-to-node-js.txt:133(搜「node -v」)· :273(搜「ECMAScript modules」)· :292(搜「export { helloWorld }」) |
| 第一个服务器 | 同上 | text/32-ch21-chapter-21-introduction-to-node-js.txt:314(搜「createServer」)· :320(搜「8000」)· :359(搜「Ctrl-C」) |
| 静态服务器与安全 | 同上 | text/32-ch21-chapter-21-introduction-to-node-js.txt:388(搜「url.parse」)· :419(搜「thousands of simultaneous」)· :463(搜「path traversal」)· :472(搜「extname」) |
| npm 与内置模块 | 同上 | text/32-ch21-chapter-21-introduction-to-node-js.txt:556(搜「Built-in Modules」)· :611(搜「npm Is Not An Acronym」)· :623(搜「added 13 packages」) |
| MySQL 访问 | 同上 | text/32-ch21-chapter-21-introduction-to-node-js.txt:693(搜「mysql2/promise」)· :703(搜「author = ?」)· :734(搜「config.env」)· :756(搜「process.env」)· :782(搜「Results: 1」) |
-
出处:「Chapter 20. Introduction to React」第 47 段(
text/31-ch20-chapter-20-introduction-to-react.txt:47,搜「virtual DOM」)。 ↩↩ -
出处:「Chapter 20. Introduction to React」第 7 段(
text/31-ch20-chapter-20-introduction-to-react.txt:7,搜「cumbersome and overly verbose」)。 ↩ -
出处:「Chapter 20. Introduction to React」第 23 段(
text/31-ch20-chapter-20-introduction-to-react.txt:23,搜「single-page or mobile」)。 ↩ -
出处:「Chapter 20. Introduction to React」第 51 段(
text/31-ch20-chapter-20-introduction-to-react.txt:51,搜「library and not a framework」)。 ↩ -
出处:「Chapter 20. Introduction to React」第 51 段(
text/31-ch20-chapter-20-introduction-to-react.txt:51,搜「final state」)。 ↩ -
出处:「Chapter 20. Introduction to React」第 102 段(
text/31-ch20-chapter-20-introduction-to-react.txt:102,搜「Babel JSX extension」)。 ↩ -
出处:「Chapter 20. Introduction to React」第 224 段(
text/31-ch20-chapter-20-introduction-to-react.txt:224,搜「lowercase letters as DOM tags」)。 ↩ -
出处:「Chapter 20. Introduction to React」第 431 段(
text/31-ch20-chapter-20-introduction-to-react.txt:431,搜「embed expressions」)。 ↩ -
出处:「Chapter 20. Introduction to React」第 124 段(
text/31-ch20-chapter-20-introduction-to-react.txt:124,搜「two great benefits」)。 ↩ -
出处:「Chapter 20. Introduction to React」第 394 段(
text/31-ch20-chapter-20-introduction-to-react.txt:394,搜「props (short for properties)」)。 ↩ -
出处:「Chapter 20. Introduction to React」第 332 段(
text/31-ch20-chapter-20-introduction-to-react.txt:332,搜「golden rule」)。 ↩ -
出处:「Chapter 20. Introduction to React」第 475 段(
text/31-ch20-chapter-20-introduction-to-react.txt:475,搜「functional stateless」)。 ↩ -
出处:「Chapter 20. Introduction to React」第 498 段(
text/31-ch20-chapter-20-introduction-to-react.txt:498,搜「class Clock」);state 初始化见第 503 段。 ↩ -
出处:「Chapter 21. Introduction to Node.js」第 15 段(
text/32-ch21-chapter-21-introduction-to-node-js.txt:15,搜「nonblocking model」)。 ↩ -
出处:「Chapter 21. Introduction to Node.js」第 314 段(
text/32-ch21-chapter-21-introduction-to-node-js.txt:314,搜「createServer」);启动与访问见第 343、357 段。 ↩ -
出处:「Chapter 21. Introduction to Node.js」第 321 段(
text/32-ch21-chapter-21-introduction-to-node-js.txt:321,搜「Server listening」)。 ↩ -
出处:「Chapter 21. Introduction to Node.js」第 467 段(
text/32-ch21-chapter-21-introduction-to-node-js.txt:467,搜「checks the extension」)。 ↩ -
出处:「Chapter 21. Introduction to Node.js」第 449 段(
text/32-ch21-chapter-21-introduction-to-node-js.txt:449,搜「404 Not Found」)。 ↩ -
出处:「Chapter 21. Introduction to Node.js」第 382 段(
text/32-ch21-chapter-21-introduction-to-node-js.txt:382,搜「fs/promises」)。 ↩ -
出处:「Chapter 21. Introduction to Node.js」第 485 段(
text/32-ch21-chapter-21-introduction-to-node-js.txt:485,搜「await operator」)。 ↩ -
出处:「Chapter 21. Introduction to Node.js」第 695 段(
text/32-ch21-chapter-21-introduction-to-node-js.txt:695,搜「createConnection」);execute 见第 704 段。 ↩ -
出处:「Chapter 20. Introduction to React」第 475 段(
text/31-ch20-chapter-20-introduction-to-react.txt:475,搜「setState」)。 ↩ -
出处:「Chapter 21. Introduction to Node.js」第 485 段(
text/32-ch21-chapter-21-introduction-to-node-js.txt:485,搜「fulfilled promise」)。 ↩ -
出处:「Chapter 20. Introduction to React」第 475 段(
text/31-ch20-chapter-20-introduction-to-react.txt:475,搜「setState」)。 ↩ -
出处:「Chapter 20. Introduction to React」第 492 段(
text/31-ch20-chapter-20-introduction-to-react.txt:492,搜「not an easy matter」)。 ↩ -
出处:「Chapter 20. Introduction to React」第 47 段(
text/31-ch20-chapter-20-introduction-to-react.txt:47,搜「virtual DOM to manage updates」)。 ↩ -
出处:「Chapter 20. Introduction to React」第 102 段(
text/31-ch20-chapter-20-introduction-to-react.txt:102,搜「pull in the Babel JSX extension」)。 ↩ -
出处:「Chapter 21. Introduction to Node.js」第 15 段(
text/32-ch21-chapter-21-introduction-to-node-js.txt:15,搜「chat applications」)。 ↩ -
出处:「Chapter 21. Introduction to Node.js」第 15 段(
text/32-ch21-chapter-21-introduction-to-node-js.txt:15,搜「handles requests asynchronously」)。 ↩ -
出处:「Chapter 21. Introduction to Node.js」第 485 段(
text/32-ch21-chapter-21-introduction-to-node-js.txt:485,搜「wait for a fulfilled promise」)。 ↩ -
出处:「Chapter 20. Introduction to React」第 475 段(
text/31-ch20-chapter-20-introduction-to-react.txt:475,搜「requires a render method」)。 ↩ -
出处:「Chapter 21. Introduction to Node.js」第 343 段(
text/32-ch21-chapter-21-introduction-to-node-js.txt:343,搜「node app.mjs」)。 ↩ -
出处:「Chapter 20. Introduction to React」第 124 段(
text/31-ch20-chapter-20-introduction-to-react.txt:124,搜「upgrades them to handle ES6」)。 ↩ -
出处:「Chapter 20. Introduction to React」第 15 段(
text/31-ch20-chapter-20-introduction-to-react.txt:15,搜「overtook jQuery」)与第 31 段(:31,搜「way ahead」)。「Google Trends shows React to be way ahead of all the others in popularity and still growing」。 ↩↩ -
出处:「Chapter 21. Introduction to Node.js」第 15 段(
text/32-ch21-chapter-21-introduction-to-node-js.txt:15,搜「event-driven」)与第 7 段(:15,搜「blocked until the response is ready」)。 ↩↩ -
出处:「Chapter 20. Introduction to React」第 76 段(
text/31-ch20-chapter-20-introduction-to-react.txt:76,搜「unpkg.com」)与第 82 段(:82,搜「development」)。「On a production website, you should replace the word development with production」;压缩版见第 88 段。 ↩↩↩ -
出处:「Chapter 20. Introduction to React」第 124 段(
text/31-ch20-chapter-20-introduction-to-react.txt:124,搜「Babel JSX extension」)。「saving you from having to call a function each time... upgrades them to handle ES6 syntax」。 ↩↩ -
出处:「Chapter 20. Introduction to React」第 199 段(
text/31-ch20-chapter-20-introduction-to-react.txt:199,搜「text/babel」)与第 224 段(:224,搜「lowercase letters」)。「React treats components starting with lowercase letters as DOM tags」。 ↩↩↩ -
出处:「Chapter 20. Introduction to React」第 111 段(
text/31-ch20-chapter-20-introduction-to-react.txt:111,搜「not necessary」)。「JSX is technically not necessary... this is not recommended」。 ↩ -
出处:「Chapter 20. Introduction to React」第 422 段(
text/31-ch20-chapter-20-introduction-to-react.txt:422,搜「props.name」)与第 431 段(:431,搜「curly braces」)。「you can place any JavaScript expression inside these curly braces」;76 / 13的示例在同段。 ↩↩ -
出处:「Chapter 20. Introduction to React」第 308 段(
text/31-ch20-chapter-20-introduction-to-react.txt:308,搜「golden rule」)与第 332 段(:332,搜「no mechanism」)。「all React components must act like pure functions with respect to their props」;impure 的 assign 示例见第 327 段。 ↩↩↩ -
出处:「Chapter 20. Introduction to React」第 630 段(
text/31-ch20-chapter-20-introduction-to-react.txt:630,搜「re-rendered」)与第 637 段(:637,搜「only legitimate way」)。「setState is the only legitimate way to update state」;「The only place you can assign this.state is in the constructor」。 ↩↩↩↩ -
出处:「Chapter 20. Introduction to React」第 525 段(
text/31-ch20-chapter-20-introduction-to-react.txt:525,搜「super」)与第 542 段(:542,搜「mounting」)。「mounting is the term React uses to describe the action of adding nodes to the DOM」。 ↩↩↩ -
出处:「Chapter 20. Introduction to React」第 650 段(
text/31-ch20-chapter-20-introduction-to-react.txt:650,搜「syntheticEvent」)。「your handlers are passed instances of a cross-browser wrapper... called syntheticEvent」。 ↩ -
出处:「Chapter 20. Introduction to React」第 714 段(
text/31-ch20-chapter-20-introduction-to-react.txt:714,搜「bind」)。「an event handler called handleClick is attached to this using the bind method」。 ↩↩ -
出处:「Chapter 20. Introduction to React」第 723 段(
text/31-ch20-chapter-20-introduction-to-react.txt:723,搜「single component」)。「render can return only a single component」;多行 JSX 的括号规则见第 761 段。 ↩↩ -
出处:「Chapter 20. Introduction to React」第 774 段(
text/31-ch20-chapter-20-introduction-to-react.txt:774,搜「&&」)与第 837 段(:837,搜「literal falsy」)。「React will not render the literal falsy values undefined, null, or false, but it will render the numeric value 0」。 ↩↩ -
出处:「Chapter 20. Introduction to React」第 904 段(
text/31-ch20-chapter-20-introduction-to-react.txt:904,搜「unique key」)与第 955 段(:955,搜「reconciliation」)。两个 Paris 城市的例子见第 941–971 段;用下标作 key 的告诫见第 980 段。 ↩↩ -
出处:「Chapter 20. Introduction to React」第 994 段(
text/31-ch20-chapter-20-introduction-to-react.txt:994,搜「source of truth」)。「the input's value is always driven by the React state」;三控件统一模式见第 1172 段。 ↩↩ -
出处:「Chapter 20. Introduction to React」第 1085 段(
text/31-ch20-chapter-20-introduction-to-react.txt:1085,搜「preventDefault」)。「the event is then prevented from bubbling through to the system」。 ↩ -
出处:「Chapter 20. Introduction to React」第 1276 段(
text/31-ch20-chapter-20-introduction-to-react.txt:1276,搜「React Native」)。「without needing to understand Java or Kotlin... or Objective-C or Swift」。 ↩ -
出处:「Chapter 21. Introduction to Node.js」第 133 段(
text/32-ch21-chapter-21-introduction-to-node-js.txt:133,搜「node -v」)与第 84 段(:84,搜「LTS」)、第 95 段(:95,搜「native modules」)。 ↩ -
出处:「Chapter 21. Introduction to Node.js」第 273 段(
text/32-ch21-chapter-21-introduction-to-node-js.txt:273,搜「ECMAScript modules」)。「the official standard format to write JavaScript code that's supposed to be reused」;.mjs 的理由在同段。 ↩↩ -
出处:「Chapter 21. Introduction to Node.js」第 314 段(
text/32-ch21-chapter-21-introduction-to-node-js.txt:314,搜「createServer」)与第 320 段(:320,搜「8000」)、第 334 段(:334,搜「port clashes」)、第 343 段(:343,搜「node app.mjs」)、第 359 段(:359,搜「Ctrl-C」)。「using a different port than the standard HTTP port (80) is a common practice」。 ↩↩↩↩↩↩↩↩ -
出处:「Chapter 21. Introduction to Node.js」第 380 段(
text/32-ch21-chapter-21-introduction-to-node-js.txt:380,搜「import http」)与第 481 段(:481,搜「pathname」)、第 485 段(:485,搜「readFile」)。内置模块全表见第 556–596 段。 ↩↩ -
出处:「Chapter 21. Introduction to Node.js」第 463 段(
text/32-ch21-chapter-21-introduction-to-node-js.txt:463,搜「path traversal」)与第 467 段(:467,搜「download the source code」)。「an attack called path traversal... using paths like ../../etc/passwd」;「anyone could download the source code by loading localhost:8000/server.mjs」。 ↩↩↩↩ -
出处:「Chapter 21. Introduction to Node.js」第 415 段(
text/32-ch21-chapter-21-introduction-to-node-js.txt:415,搜「event-driven」)与第 419 段(:419,搜「thousands of simultaneous」)。「due to there being no log-jam waiting for the filesystem to return requested files」。 ↩↩↩ -
出处:「Chapter 21. Introduction to Node.js」第 611 段(
text/32-ch21-chapter-21-introduction-to-node-js.txt:611,搜「npm Is Not An Acronym」)与第 623 段(:623,搜「added 13 packages」)。「such as prepared statements and placeholders」——选 mysql2 的理由在同段。 ↩↩ -
出处:「Chapter 21. Introduction to Node.js」第 556 段(
text/32-ch21-chapter-21-introduction-to-node-js.txt:556,搜「Built-in Modules」)。十一个内置模块的清单。 ↩ -
出处:「Chapter 21. Introduction to Node.js」第 693 段(
text/32-ch21-chapter-21-introduction-to-node-js.txt:693,搜「mysql2/promise」)与第 703 段(:703,搜「author = ?」)、第 734 段(:734,搜「config.env」)、第 747 段(:747,搜「env-file」)。「a placeholder ? instead of a variable... to prevent SQL injection」见第 764 段。 ↩↩↩↩ -
出处:「Chapter 20. Introduction to React」第 235 段(
text/31-ch20-chapter-20-introduction-to-react.txt:235,搜「not recommended for new development」)。「classes are not recommended for new development. The main reasons to use functions are simplicity, ease of use, and faster development」。 ↩↩