HTML 30 年进化史
现在我们谈前端,大多数谈框架,对于前端最基础的 HTML 你了解多少呢?本文作者用近两万字为你详解 HTML 的进化史,涉及前身SGML、到浏览器大战,HTML 模块的详解、HTML5的进化……
随着近年来,前端框架战争的白热化,战斗溢出到后端、移动端、小程序端等层面……大家似乎不像以前那么关注底层,HTML、JavaScript、CSS 被一些更高级的语言所转译,成为了前端的汇编语言。
或许在许多年后,培训公司将会教授新人:TypeScript 怎么写,Sass 怎么写,JSX 怎么写,这时可能会出现 jQuery 时代的荒诞问题——jQuery 与JavaScript 哪一个更快?这是一种悲哀,也是时代的进步吧。正如我们开心敲着电脑,不用关心底层的芯片是如何运作的。这是文明建立在极度脆弱的生态下的至高产品。
而我们浏览的复杂页面的底层是 HTML+CSS+JavaScript 构建的,其中HTML 是基础中的基础,但它经常被人所忽视。要不想页面文明轻易湮灭,我们需认真封存这部分知识,就像亚述人把他们的知识楔入到泥板中一样。
01 HTML 的前身 SGML
战争驱动文明发展,互联网是美国军队为了快速传送情报而发明的。如果单纯传送文字,当时的电报就可以实现,但显然像地图上的图像信息,电服就不行了。这时候需要一种语言来组织这些文字与影像,最好还能存在交互性。交互虽然不能实时,但总好过没有。于是学者们找到当时最流行的文档描述语言 SGML。
SGML 是国际上定义电子文档和内容描述的标准。它源于 1969 年 IBM 公司开发的文档描述语言 GML,GML 主要用来解决不同系统中文档格式不同的问题。后经过多年发展,1986 年经 ISO 批准为国际标准 ISO8897,并被称为 SGML。
它有许多 HTML 的特征,如内容与样式分离。在 SGML 中,标记分两种:一种用来描述文档显示的样式,称为程序标记;另一种用来描述文档中语句的用途,称为描述标记。一个 SGML 文件通常分三个层次:结构、内容和样式。结构为组织文档的元素提供框架,内容是信息本身,样式控制内容的显示。正因为如此,它的使用范围很广,被许多大型公司用来创建和发布信息。诸如布告、技术手册、章节目录、设计规范、各种信函等,都可以用它来设计描述。
但是 SGML 有致命的缺憾,并且它的优点成为了它的缺点。SGML 设计精良,规范严谨,导致了其复杂性也高,在军情紧急的情况下,需要快速交换情报,不能慢吞吞写好每个标签,因为当时每秒几个比特的网速也是一个问题。
因此它需要裁减,于是有了HTML。
02 浏览器大战对 HTML 的影响
当时大家原本要的是一个精简版的 SGML,但最终他们得到的一个完全不同的东西。
这要从解析网页的浏览器说起,Web 之父 Tim Berners Lee 设计的浏览器WorldWidWeb 过于简单,于是让商业浏览器有可乘之机。
在刚开始时,浏览器商还是按照 W3C 的制定的规则,如 Mosia、Netscape,但微软介入之后就发生改变。微软制定自己的浏览器语法与标准,于是导致了两套标准的出现。
在规划中,HTML 也是一门严谨的语言,是高度有组织性、规范化、模块化。例如,规范化是有一个文档类型声明 DOCTYPE 来指明它怎么解析的。模块化,是说各种标签其实也是有组强性的,几个几个地划分地不同的族群,合起来实现一个功能,最著名的是表单与表格。
但是后来出现了一些意外,一些用来装饰用的标签(s、b、i、u、font)因为 CSS 的出现,被人们诉之败作,渐渐被边缘化与废弃。一些用来模拟 Excel 功能的标签(table、tbody、tr)被人们用来布局,弄得页面难以维护。
在 CSS 标准化时代,又矫枉过正,硬生生地用 DIV 来模拟表格。一些用来实现广告功能的标签,导致会有满屏飘动、不让用户关闭的乱象。在这几个大事故中,许多标签就是被胡乱使用或边缘化。
我个人认为最大原因是 W3C 没能自己开发一个浏览器,一直倚重某一个方,导致造成 HTML 的失控。
HTML 在语法上设计得非常简单易学。
HTML 标签是包在小括号里面,没有人规定标签名是大写还是小写。开标签中,标签名旁边有一些属性,这些属性的属性值没有人规定它是否能引号,引号是单引号还是双引号,没有规定标签是否一定要闭合。
可能当初是有规定的,但无法遵循。浏览器需要最快地将内容呈现给用户,但当时的网速不太可能,于是浏览器大厂允许用户可以不用闭合标签,不用严格括起属性值,也能跑起页面。这种纵容在当时成了优势,被其他浏览器争相模仿。
此外,还有更厉害的传输优化,比如页面源码是这样的:
实际在 IE6~8 的控制台下看到的源码是这样,通过大写化与对布尔属性的特殊处理,一点点地减少传输内容。
在 Google 早期的首页中,html、head、body 标签是刻意省略掉,因为它知道浏览器会支持这样残缺的页面。在自然界中,这是不可想象的,残缺的东西会被淘汰。
对于 HTML 的失控,W3C 决定开发另一种标签语言,于是 XML 诞生了。但 XML 出现得太晚了,无力回天。它一直作为数据的传输载体而存在,而不是描绘界面用。它其实是能描绘界面的,两个明证是 SVG 语言,及安卓里面的 XML 模板。
03 HTML 的版本
HTML 发布以来,迭代过许多版本,现在是小步快跑的第 5 版,但 HTML5 已经不是 W3C 所规范的了,是由一个浏览器大厂们组成的俱乐部 WHATWG 发布的。
下面是一个简单的路线图:
可以看出几个断层,HTML1.0 还没有准备好时,大家就争先抢后地开始做,于是当时相当地混乱。
早期的浏览器包括了:Tim Berners 的 WorldWideWeb 浏览器,兼具浏览器和编辑器功能,但只能运行在NeXTStep操作系统上;CERN的一位数学实习生 Nicola Pellow 开发出 Line-mode 浏览器,能运行在 UNIX 和 MS-DOS 上;Erwise 是第一个带图形界面的浏览器,支持搜索网页中的单词,由四名芬兰大学生开发,在 1992 年发布;加州伯克利的 Pei-Yuan Wei 在1992 年 4月发布了 ViolaWWW,这个浏览器受到了 Mac 程序 HyperCard 的启发,但他没有 Mac 只能接触到 UNIX 机器,1992 年夏天,斯坦福线性加速器中心物理学家 Tony Johnson 为斯坦福的物理学家发布了图形浏览器 Midas;
与此同时,CERN 的 Nicola Pellow 和 Robert Cailliau 发布了第一个Mac浏览器 Samba;基于 Viola和 Midas 的 Mosaic 在 1993 年发布;堪萨斯大学发布了 Lynx;康奈尔大学法学院学生 Tom Bruce 发布了 Cello。——节选自《被遗忘的早期浏览器》
HTML3.0时,改了又改,那时五大浏览器的四个玩家已经全场(IE、Netscape、Opera、Safari),要满足四家的喜好非常难。这也是目前还在支持的早期标准。
XHTML 是 W3C 最后一次赌博,想用 XML 的规范来修正 HTML 一直以来松散的编写形式,即要来闭合的地方必须闭合,属性值必须要括起来,废弃的标签不能再使用,还要求对 script 标签的内容使用 CDATA 包裹起来,DOCTYPE 变得很长……它们还专门编写一个网站给大家做校正:
https://validator.w3.org/
下面这段 XHTML 标准模式代码,符合 W3C XHTML 语法,能够执行(保存为 itworks.xhtml 文件,用 Firefox 打开):
俗话说,由俭入奢易,由奢入俭难。用户不习惯,如果写的页面不合格,浏览器不解析,意味着用户会跑去竞争对手中。因此这闹剧草草收场了。浏览器商也看到自己的能量所在,把 HTML 的规范制定收归手中。
04 HTML的模块介绍
在语义化时代,人家过于关注于单个标签所表示的意思,而忽略了组件是分群体的。只不过有的群体是弱联连,可以混杂其他标签,加之浏览器的自动纠错功能导致人们对它们的误解。这些群体我称之为模块。
我稍微归纳一下,当然在 W3C 的 HTML5.2 中它们也有另一套划分。那套划分很奇怪,某一种标签可能归类到多个类别中。
文档模块
文档模块就是<html>、<body>、<head>、<title>这几个标签,我们可以在 document 上访问到它,document.documentElement、document.body、document.head、document.title。它们可以省略不写,浏览器魔术般地补全它们。这些标签只是提供一个框架,告诉人们一些内容性的东西应该放在 body 中,功能性的东西应该放在 head 中。
功能模块
它是用来设置文档整体的功能或一些元信息,涉及的标签有<link>、<meta>、<base>、<script>与<style>。除了后两个,由于页面功能的膨胀,需要多人协作一个页面,于是出现一个页面存在多个<script>与<style>标签,它们可以分散到其他位置。<link>、<meta>、<base> 都集中在head 标签内。我也尝试过将 div 放在 head 中, head标签会自动将它赶回body里面。
<link> 标签有两个作用:1. 定义文档与外部资源的关系;2. 是链接样式表。HTML5 新加的预加载,预渲染等功能就在这标签上添加。
<meta> 标签可提供有关页面的元信息(meta-information),比如针对搜索引擎和更新频度的描述和关键词。最有用的就是设置页面的字符集。
下面是简书的 meta 标签使用情况,它可以做许多事情,如百度 SEO、360 SEO、移动端的水滴屏设置、缩放设置,还有外国的一些社交网站的设置,需要有一些专门的知识才能玩得转。
<base>标签为页面上的所有链接规定默认地址或默认目标。
通常情况下,浏览器会从当前文档的 URL 中提取相应的元素来填写相对 URL 中的空白。
使用 <base> 标签可以改变这一点。浏览器随后将不再使用当前文档的 URL,而使用指定的基本 URL 来解析所有的相对 URL。这其中包括 <a>、<img>、<link>、<form> 标签中的 URL。在 IE6-8 中这标签有一个可怕的 Bug,会将整个页面装进它的 innerHTML 中,导致你的选择器失灵。
排版模块
这涉及到的都是一些块狀元素,如div、h1-h6、blockquote、有序列表、无序列表、定义列表等,它们是自带样式的一种元素。在 HTML5 时代,会继续加上 article、aside、nav、section 等标签。
装饰模块
这是覆行 SGML 的样式显然功能,于是才出现这类标签,常见的有 s、u、i、b、big、small、sub、sup、center、font、br、nobr、hr。在强调语义化的时代,除了 br、nobr、hr 都完了。此外,还有一些不起眼的标签,被忘了,又赋以特殊含义又活下来,如:code、kbd、samp、cite、tt、var……它们自带样式,通常以另一种字体特别显示。
大片文本的显示模块
我们知道页面在传输过程会被加工,处理掉一些换行符与空白,如果我们想保留这些换行符与空白就需要这里面的三种标签了:pre、plaintext、xmp。其中 plaintext 已经被浏览器废弃掉了,彻底不能用了,不像那些被声明废弃还能苟活着的标签。pre 标签,大家可能熟悉些,它与 code 标签经常组合在一起,用于语法高亮。
xmp 则是在语法高亮没有发明之前,能完整地显示某一段 HTML 片断的结构的唯一标签。
xmp 会把里面的内容当成一个字符串,不会生成对应的 DOM。
这种能原样保持用户HTML的元素是非常有用的,常常用于做模板容器, 其他的如<script type="template"></script>、<noscript></noscript>,都有其优缺点,直到 HTML5 推出了 <template> 标签才解决这问题。
表单模块
表单模块是 HTML 最早支持的标签集,包括我们一直熟悉的 <form>、<input type="text" />、<input type="password" />、<input type="radio" />、<input type="checkbox" />、<input type="button" />、<textarea>、<select>、<optgroup>、<option> 。这些元素都是有一个特点,可以通过 tab 键切换下一个表单元素,可以设置 readony 属性防止用户修改它的值,可以设置 disabled 属性忽略该元素的值。在 HTML5 中,还存在 pattern 与 required,实现对表单的自动验证。
表格模块
表格开始时用来呈现execl类似的数据, 后来被人们用来做布局用了。许多网页制作工具,如上古时代的 Dreamweave 就是它生成页面。这页面能完美还原大家拖拽出来的效果,但是想再次加工就麻烦了。table 家族包括了 <table>、<thead>、<tbody>、<tfoot>、<th>、<td>、<col>、<colgroup>、<th>。
table 模板的标签是具有很强的排他性,只能在 td 与 th 加东西标签与文本,在 tr 或 tbody 之间加东西,它们会漏到外面去。
这就类似于 p 标签放 div 标签,会发生意料不到的解析。
页面组装模块
在网速起来后,页面的功能就会越堆越多,于是需要许多人共同开发一个页面。如果协作开发页面,在 JavaScript 没有模块化技术的情况下,浏览器提供了几个标签实现它:<frameset>、<frame>、<iframe>。它们都是自己独立的作用域,不用考虑全局变量的问题,不也用考虑某人的代码出 Bug 影响到其他人的情况。唯一要注意的是,防止别的网站用 frame 来装载我们的网站来做坏事,当然浏览器也会在这些标签上添加越来越多这样的属性。现在许多政府后台系统还是基于框架结构来构建页面。
广告模块
广告是众多网站最主要的收入来源。浏览器也有相应的标签做这样的事,一个是闪啊闪的 <blink> 标签,一个是飘啊标的 <marquee> 标签。时至今天,它们的际遇完全不一样,前者被干掉,后者被标准化了。
地图模块
地图模块涉及两个特别的标签 <map>、<area> 及一个常见的 <img> 标签。map 标签起来统帅的作用,而 area 标签则起着与其他标签不一样的触发区域。一般的可视标签,都是呈现为矩形。而 area 标签则根据它的 share 属性呈现为各种形状及不规则的多边形。
容灾模块
互联网早期是充满了意外,浏览器各自为战,实现也不同步,因此一些标签或功能并不支持,需要一些容灾处理。于是出现了 <noscript>、<noembed>、<noframes> 这几种标签。当浏览器禁用 JavaScript,或不支持 embed 标签,或不支持框架技术时,就会显示这几种标签里面的提示信息。
多媒体模块
为了让页面不只是单调的文字,浏览器先后支持以下标签来满足我们的官感:<img>、<bgsound>、<embed>、<object>、<applet>。
img 标签就不说了,早期要播放音乐,IE 下可以选择 bgsound,也可以使用万能的 object 标签,object 可以加载 flash 播放器,也可以加载 Windows 下的各种控件,如 Windows Media Player、RealONE Player。在 Firefox 中承担这个角色的是 embed 标签,音频视频都可以播放。Applet 则是 Java 如日中天时加入的标签,可以执行 Java JAR 包,在做银行安全控件或教学显示时非常有用。
05 多种文档的混排
上面走马看灯地回顾了一些标签,我们也了解到不断有标签被废弃,也不断有新的标签涌现出来。这些标签默认具有什么功能,有什么属性,有什么外观,或者如何与其他标签共存,都是规定在 DOCTYPE 中。所谓 HTML 规范其实就是这个文档类型声明。
一个页面加载下来,经过比特到字符到字符串到标签到 DOM 的层层转换,其中标签到 DOM 的解析就需要文档类型声明来处理,当然这里可能存在浏览器私下的非正式的纠错处理。但是文档类型声明作为页面的 DNA 角色是不变的。
在HTML5之前文档类型的声明基本都使用 DTD(document type definition),由于之前的版本基于 SMGL,DTD 规定了标记语言的规则,才能使浏览器正确的解析并呈现需要展示的内容。
下面是几种常见的文档类型声明:
HTML 4.01 Frameset
HTML 4.01 Strict
XHTML 1.0 Frameset
XHTML 1.0 Transitional
XHTML 1.0 Strict
XHTML 1.1
这么长的代码不借助于工具或贴粘复制,是很难写的。因此 HTML5 的写法是异常简洁,也屏蔽了里面的 dtd 链接,反正也没有几个人打开它。
加之 HTML5 本来就是遵循小步快跑的规则,不断小版本迭代,因此 dtd 文件会时不时变化,这个就内置到浏览器中就行了。时至今天,所有大厂的网页都用上 HTML5 了。
下面是 HTML5 的 DOCTYPE 写法:
但是 html 上面的文档类型声明只是告诉浏览器如何处理 HTML 标签,在 HTML 文档中可能混杂其他文档。
比如我们要让页面更有说明力,需要出现雷达图、线段图、饼状图等等,就要用到 SVG。我们的页面是为高校制作的,需要用到专业的数字公式,这时需要用 math 标签。事实上,它们都是出自同一渊源,多种文档混排可能在 IE4 时代就存在了。
1998 年,微软发布了 VML 矢量标签语言,对于 XML 的混排则使用一种叫 XML 数据岛的技术实现了。
因此多文档的混排在IE4~8的情况是 HTML+VML+XML, 在其他浏览器则是 HTML+SVG+math,并且 SVG 里有 <foreignObject> 再加载 HTML 文档。在 HTML 规范转交给 WHATWG 的今天,SVG 与 math 继续交由学术性更强更严谨的 W3C 所维护升级。毕竟 HTML5 有许多新功能是玩票性质的,而学术性的东西则不能这样弄。
06 HTML5 的进化
现在我们对 HTML 的关注不如以前了,事实上 WHATWG 对它做了非常宏大的规划,非常多的功能需要逐年来迭代。但 React 这些大型前端框架的出现,掩盖了浏览器的努力。以前 Chrome 每次发版本常常吸引眼球,但 babel 可以让你用上还在讨论中的语言特性!那么让我们略微看一下 HTML5 的新特征吧。
语义化标签的崛起
在人们受够了table布局,前端工程师开始关注每个标签在发明时赋以它们的本来意义。
这些标签能做功能CSS也能做,因此被干掉。HTML5带来了全新的标签,它们富有语义,对SEO或机器学习分析内容有帮助。
更多表单元素
早期的表单元素,我们可以从收音机里挖到它们的本源。但经过20年来的发展,网页世界也有自己的创新,各式各样的日历、调试器,于是它们成为浏览器的自带控件了。下面是一些新标签:<meter>、<progress>、<outer>、 <datalist>,此外更多功能是复用到 input 标签,各种类型的日历、月历、年历、时间。
更多布局元素
在新布局元素没有出来时,人们对页面也是划分成不同区域。每个区域标识不同的 ID 或类名,以前谷歌做过一次调整,发现大家对这些ID或类名的取名都很相似,比如顶部的区域都叫 header,底部的区域都叫 footer,侧边栏都叫 leftside 或 rightside, 主内容区都叫 main,导航区几乎都叫 nav,还有弹出层,也统一叫 dialog。
下面是 HTML5 没有流行时,人们对页面的一个布局范例。
为了讨好用户,HTML5 提供了新的语义元素来明确一个 Web 页面的不同部分。
在一个网页中,这些新的语义标签元素位置如下图所示:
这些新标签中最特别的是 dialog 标签,它自带全局居中与隐藏显示功能。
多媒体的增强
进入 HTML5 时代,<img> 支持多个图片源,这方面在不同的屏幕下适配不同大小的照片。播音频则交给更语义化的<audio>标签,播视频则交给更语义化的<video>标签,并且支持字幕文件(通过<track>标签挂载)。<canvas>本来是显示矢量图,随着浏览器性能越来越强,对位图也游刃有余,并结合一些 File API 来解析二进制质材, 让我们在浏览器跑起类似 flash 的动画与浏览。这几年,又兴起 WebAssembly, 3D动画与游戏也能跑了。canvas 在VR之风刮起时,又支持 WebGL,成为时下最强大的标签了。
观摩地址:https://gallery.echartsjs.com/editor.html?c=xHku9OE96l
DOM的标准化
标签只是一个描述性语言,它们用来描述什么呢?就是描述 DOM。由于浏览器大战的缘故,DOM 一直没有统一,给开发平添了不少麻烦。jQuery 时代,前端开发者发掘了许多浏览器差异问题,除了给出兼容方案外,还提交给到浏览器厂商中,这些举动最终推动了浏览器的同一化。
最开始是自定义属性与固有属性的分离,在 IE6~8 中, 这两种属性是拎不清的,这就导致遍历元素属性时,一下子多出上百个属性,而在火狐中可能只有三个。
其次是标签对应的 DOM 元素的构造器公开化,以前 IE 是不公开这些接口,纯粹是黑盒子。而 Chrome、Firefox 都是根据 W3C 官网上的文档来制定接口。如果公开了这些构造器,我们就可以对它们做一些测试。
HTML5 兴起时,全世界也恰好转入移动互联网时代,浏览器添加了更多与手机相关的事件,如划动、长按、页面隐藏显示、是否离线、屏幕是否发生旋转、是否发动震动。
在 jQuery 的驱动下,选择器引擎官方化,querySelector比 getElementById 更加便利。
自定义标签
自定义标签是 HTML5 添加的最强大的功能。早在 IE5 时代,微软就推出过 HTC 来创建自定义标签。一些前端模板引擎也支持过自定义标签,可见这是一个非常热门的功能。
自定义标签可以让我们制定一个标签的外观,有什么固有属性及对生命周期进行干涉。在创建自定义标签时,我们需要用到一些新的标签如上面提到<template>及让用户传入其他标签的占位标签<slot>, 如果大家用过 vue,一定能快迅上手。
有关自定义标签可以参考以下链接:
http://www.ruanyifeng.com/blog/2019/08/web_components.html
各种页面性能优化方案的内置实现
人们对页面性能的优化是永无止境,即便网速已经这么快了,CPU已经这么多核了。于是为了尽快加载资源,我们有DNS, 为了防止<script>堵塞页面渲染,我们把它们放到body的后面……这些奇技淫巧都被浏览器所收吸,做成一个个配置项了。
我们先看<link>标签上的优化方案:
preload
对当前页面的要用到资源优化加载,并且不阻塞渲染和 document 的 onload 事件。使用了rel='preload'的 link 标签还可以指定 onload 事件,方便你进行后继处理。
prefetch
它的作用是告诉浏览器加载下一页面可能会用到的资源,注意,是下一页面,而不是当前页面。
prerender
而 prerender 不仅会加载资源,还会解执行页面,进行预渲染,但是这都是根据浏览器自身进行判断。
浏览器可能会分配少量资源对页面进行预渲染,挂起部分请求直至页面可见时可能会放弃预渲染,如果消耗资源过多等等情况。。。
preconnect
浏览器要建立一个连接,需要经过 DNS 查找,TCP 三次握手和 TLS 协商(Https 需要),这些过程需要相当的耗时。因此 preconnect 能浏览器预先建立一个连接,等真正需要加载资源的时候能直接请求。
dns-prefetch
DNS prefetch,即DNS预获取。前端优化中与DNS有关的两点:一个是减少DNS的请求次数,另一个是进行DNS预获取。
DNS 预获取可以加快页面渲染速度,无需用户点击链接就能在后台解析,所以能减少用户的等待时间,提升用户体验。默认情况下,浏览器会对当前页面中和当前域名(正在浏览网页的域名)不在同一个域的域名进行预获取,并且缓存结果,这就是隐式的DNS Prefetch。
<script>标签上的优化方案
IE 发明了一个 defer 属性,HTML5 规范则又加上另一个async属性。它们都能让<script>不会堵塞当前页面的加载与渲染,现在所有浏览器的script标签都支持这两种属性。它们两者也有稍微的区别。
defer
如果页面有多个设置了defer属性的script标签,它们在执行时,会根据它们在页面的位置从上到下依次执行。
defer属性的script标签的执行时机为,页面结构渲染之后,DOMContentLoaded回调触发之前。
async
如果页面有多个设置了async属性的script标签,它们在执行时是乱序,谁的资源加载完就立即执行,不会等待上面某一个defer属性的script标签执行。
async属性的script标签的执行时机,资源加载好就立即执行,与页面结构渲染无关,与DOMContentLoaded回调无关。
假如我们页面有两个script标签,给它们加上属性或不加属性,在chrome控制台的显示如下:
普通script:
defer
async
它们的执行时期与文件距离用户的服务器距离或文件大小有关,因此下面两种情况都可能发生。如果设置async的script标签越多,产生的情况就越多,无法预估。
推荐的应用场景
defer
如果你的脚本代码依赖于页面中的DOM元素(文档是否解析完毕),或者被其他脚本文件依赖。如评论框、代码语法高亮、语言补丁polyfill.js。
async
如果你的脚本并不关心页面中的DOM元素(文档是否解析完毕),并且也不会产生其他脚本需要的数据。如百度统计。
如果不能确定的话,用defer比较稳妥。
07 结语
HTML发展到今天,功能远远超过当初展示页面提交表单的需求了。我的文章还有许多HTML5的新特征没有提到,浏览器不断提高它的外延,相当于把它自己当成一个操作系统了。
虽然现在有了非常好用的框架,让我们暂时忘记这些底层知识,但是我们所有一切的功能都是筑建在HTML之上,这一点万万不能忘记。我们要把自己当成一个建筑师,了解每一个材料的质地,才能打造更好的大厦。