📘 第十章 构建Web内容的技术
📄 一句话概括

动态 HTML 不是一个标准,是「HTML 加 CSS 加 JavaScript」三样合起来的说法:不重新请求整页, 就能改内容、改样式、响应操作。

📝 本节和前面几节的关系

上一节讲结构和表现,这一节讲行为。它没有 RFC、没有版本号,只是浏览器里三样技术配合出来的效果。

一、「动」有三种

第一种是样式动:鼠标移上去按钮变色。这是 CSS 的 :hover 干的,一行脚本都不用写。 第二种是内容动:点一下展开一段说明、弹出一个对话框。这要脚本去改那棵 DOM 树(10.2.2)。 第三种是数据动:下拉刷新、翻页不跳转、聊天消息往上冒。这要脚本去要数据,再把结果填进页面。 三种的技术含量依次递增,但共同点是同一个:页面没有重新加载。 这一点才是「动态」的本意——不是有动画,是不用整页重来。

二、服务端渲染与客户端渲染的账

同一批数据有两种给法。一种是把 HTML 全拼好发过去(服务端渲染),浏览器收到就能画。 另一种是先发一个空骨架,脚本再去要一段 JSON 自己拼(客户端渲染)。 Demo 用 20 条数据量了一遍:服务端渲染 1756 字节、1 个请求; 客户端渲染 骨架 147 加数据 521 等于 668 字节、2 个请求。 结果和直觉相反——这个例子里客户端渲染反而小了 62%。 原因是每行数据的标签比数据本身重:<li class="item"> 加两个 <span>, 几十个字节就为装十几个字的正文。 但真实项目里结论常常翻过来,因为客户端渲染还得先下载一个几百 KB 的框架脚本才能画出界面。 本 Demo 没引框架,那一块不在数字里,我不编它的大小。 客户端渲染确定的代价是多一次请求:首屏要等两趟。 它换来的是后面——翻页、筛选只传变化的数据,不用重传整页标签。

三、它为什么不算一个标准

「动态 HTML」这个词九十年代末很流行,但它从来没有对应的 RFC 或 W3C 规范。 它是厂商造出来的营销词,指的就是那三样东西一起用。 所以教材把它放在「HTML」这一章里讲,而不是单独开一章——因为它不是一个协议,是一种用法。 这一点值得记一下:技术名词里有不少是这类「约定俗成的叫法」,Ajax、Web 2.0、单页应用都是。 名字好记,但没有规范可查。

四、动手 Demo:同一批数据,两种给法,量字节和请求数

把 20 条同样的数据做成两份东西:一份是拼好的整页 HTML,一份是空骨架加一段 JSON。 量各自的字节数,数各自的请求次数。

ROWS = u''.join(u'<li class="item"><span class="t">课程 %02d</span>'
                 u'<span class="p">¥%d</span></li>' % (i, 99 + i)
                 for i in range(1, 21))
SSR = u'<!DOCTYPE html>...<ul id="list">' + ROWS + u'</ul>...'

DATA = json.dumps([{'t': ..., 'p': ...}], ensure_ascii=False,
                  separators=(',', ':'))

实跑输出:

--------------------------------------------------------------------
服务端渲染和客户端渲染:字节与请求数
--------------------------------------------------------------------
  20 条数据                  每条约 87 字节(含标签)
  服务端渲染整页             1756 字节,1 个请求
  客户端渲染:骨架           147 字节
  客户端渲染:数据           521 字节
  客户端渲染合计             668 字节,2 个请求
  客户端比服务端             少 1088 字节,62%

  这里有个和直觉相反的结论:**这个例子里客户端渲染反而更小**。
  原因是每行数据带的标签比数据本身重得多——`<li class="item">` 加两个 `<span>`,几十个字节就为装十几个字的正文。

  真实项目里这个结论常常翻过来,因为客户端渲染还得先下载一个几百 KB 的
  框架脚本才能画出界面。本 Demo 没引框架,所以那一块不在上面的数字里,
  我不编它的大小。

  客户端渲染确定的代价是**多一次请求**:2 对 1,首屏要等两趟。它换来的是后面的事——翻页、筛选、排序只传变化的数据,不用重传整页标签。

  所以两种做法没有绝对优劣:看「第一次打开」重要,还是「后面频繁操作」重要。

  JSON 那一段长这样(前 90 个字节):

服务端渲染 1756 字节,一次请求就把能画的都给了。 客户端渲染 668 字节(骨架 147 加数据 521),但要两次请求。 客户端渲染反而少了 62%,因为标签比数据重得多。这一点和很多人的印象相反,值得记下来。 但真实项目的对比不能只看这个:客户端渲染还要加载框架脚本,几百 KB 起步。 Demo 里没有那块,所以上面的数字不代表真实项目。 两次请求的意义不只是多了个往返:首屏必须等第二趟回来才能画,用户看到的是白屏时间变长, 不是字节变少。

30 秒实验

  1. 把数据的条数从 20 改成 200,看两种做法的字节差怎么变。
  2. 把每行的标签减少到一个 <li>,再看差值——标签越少,客户端渲染越不吃亏。
  3. 打开一个常用网站,用开发者工具看 Network 面板,数它发了多少个请求。

常见误区

  1. 「动态 HTML 是一个标准」——它没有 RFC,只是三样技术合用的叫法。
  2. 「客户端渲染一定更省流量」——标签重的时候恰恰相反。
  3. 「动态就是有动画」——动态的本意是不整页重载。
自测三问(点开对答案) 1. 动态 HTML 指的是哪三样技术? —— HTML、CSS、JavaScript。它本身不是标准。 2. 服务端渲染和客户端渲染最确定的差别是什么? —— 请求次数:前者一次给全,后者要骨架加数据两次。 3. 为什么客户端渲染的总字节可能反而更少? —— 因为标签比数据重,服务端为每条数据重复了整套标签。

不管是服务端拼还是客户端拼,拼的都是同一棵 DOM 树。下一节就看这棵树。

相关笔记