Web如何工作的

Web如何工作的


英文原文链接:https://github.com/vasanthk/how-web-works

中文转载自:https://me.femaster.cn/misc/how-web-works.html

当我们在浏览器中输入google.com时,幕后发生了什么?

目录

按下Google的’g’键

当你按下“g”时,浏览器会接收到这个事件,整个自动完成机制会进入高速运转模式。根据你所使用的浏览器算法,以及你是否处于私密/隐身模式,浏览器会在URL栏下方的下拉框中向你提供各种建议。大多数这些算法会根据搜索历史和书签优先排列结果。虽然你要输入“google.com”,这些都无关紧要,但在你完成输入之前,仍然会运行大量代码,并且每按一个键,建议会不断优化。它甚至可能在你输入完整之前就建议“google.com”。

当你按下’Enter’

选择一个起点,我们选择键盘上的Enter键按到底部时的瞬间。此时,Enter键特定的电路被闭合(无论是直接还是电容式)。这允许少量电流流入键盘的逻辑电路,逻辑电路扫描每个按键开关的状态,消除开关快速间歇闭合所产生的电噪声,并将其转换为键码整数,在这种情况下为13。然后键盘控制器对键码进行编码,以便传输到计算机。现在几乎都是通过通用串行总线(USB)或蓝牙连接传输。

对于USB键盘:

  • 内部键盘电路内存将生成的键码存储在称为“端点”的寄存器中。
  • 主机USB控制器每隔约10毫秒轮询一次该“端点”,以获取存储在其上的键码值。
  • 该值通过USB SIE(串行接口引擎)以1.5Mb/s的最大速度(USB 2.0)传输。
  • 然后该串行信号在计算机的主机USB控制器中解码,并由计算机的人体接口设备(HID)通用键盘设备驱动程序解释。
  • 按键的值随后传递到操作系统的硬件抽象层。

对于触摸屏键盘:

  • 当用户将手指放在现代电容式触摸屏上时,少量电流会被转移到手指上。这通过导电层的静电场完成电路,并在屏幕上的该点产生电压降。然后,屏幕控制器会触发中断,报告“点击”的坐标。
  • 然后,移动操作系统通知当前聚焦的应用程序其GUI元素之一(此时为虚拟键盘应用程序按钮)上发生了点击事件。
  • 虚拟键盘现在可以触发软件中断,将“按键”消息发送回操作系统。
  • 此中断通知当前聚焦的应用程序发生了“按键”事件。

解析URL

浏览器现在获取了URL(统一资源定位符)中包含的以下信息:

  • 协议 “http”:使用“超文本传输协议”
  • 资源 “/”: 获取主页面(索引页)

当未提供协议或有效域名时,浏览器会将地址栏中输入的文本传递给浏览器的默认搜索引擎。

HTTP协议

你可以确定,像Facebook或Gmail这样动态的网站不会从浏览器缓存中提供服务,因为动态页面要么很快过期,要么立即过期(过期日期设置为过去的时间)。

如果使用的是Google开发的浏览器,它将不会发送HTTP请求来获取页面,而是发送请求尝试与服务器协商,将HTTP协议“升级”到SPDY协议。请注意,SPDY正在被弃用,在最新版本的Chrome中,SPDY协议将被HTTP/2取代。

GET http://www.google.com/ HTTP/1.1
Accept: application/x-ms-application, image/jpeg, application/xaml+xml, [...]
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; [...]
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Host: google.com
Cookie: datr=1265876274-[...]; locale=en_US; lsd=WW[...]; c_user=2101[...]

GET请求指定了要获取的URL:“http://www.google.com/”。浏览器在请求头中标识了自己(User-Agent),并声明它可以接受的响应类型(Accept 和 Accept-Encoding)。Connection请求头请求服务器保持TCP连接以便进一步的请求。

请求还包含浏览器为该域名保存的Cookie。你可能已经知道,Cookie是跟踪网站在不同页面请求之间状态的键值对。因此,Cookie会存储登录用户的姓名、服务器分配给用户的秘密号码、一些用户的设置等。这些Cookie会存储在客户端的一个文本文件中,并在每次请求时发送到服务器。

HTTP/1.1定义了“关闭”连接选项,发送方可以通过它通知服务器响应完成后将关闭连接。例如,Connection: close。

在发送请求和请求头之后,Web浏览器会向服务器发送一个空行,表示请求内容已完成。服务器会以一个响应码表示请求的状态,并以以下格式响应:200 OK [响应头]

紧接着一个空行,然后发送www.google.com的HTML内容。服务器可能会在此之后关闭连接,或者如果客户端请求头中包含保持连接的请求,则保持连接以便进一步的请求使用。

如果Web浏览器发送的HTTP头中包含足够的信息,让Web服务器能够确定自上次检索以来,Web浏览器缓存的文件版本是否未被修改(例如,Web浏览器包含了ETag头),服务器可能会以以下格式响应请求:304 Not Modified [响应头],并且没有负载,Web浏览器将从缓存中获取HTML。

在解析HTML之后,Web浏览器(和服务器)会对HTML页面引用的每个资源(图像、CSS、favicon.ico等)重复此过程,只不过请求将从GET / HTTP/1.1变为GET /$(相对于www.google.com的URL) HTTP/1.1

如果HTML引用的资源在不同于www.google.com的域名下,Web浏览器会回到解析其他域名的步骤,并为该域名重复到目前为止的所有步骤。请求中的Host头将被设置为适当的服务器名称,而不是google.com。

注意事项:

  • URL中尾部的斜杠“http://facebook.com/”非常重要。在这种情况下,浏览器可以安全地添加斜杠。而对于像http://example.com/folderOrFile这样的URL,浏览器不能自动添加斜杠,因为无法确定folderOrFile是文件夹还是文件。在这种情况下,浏览器会访问没有斜杠的URL,服务器会响应一个重定向,导致一次不必要的往返。
  • 服务器可能会响应301 Moved Permanently,以通知浏览器访问“http://www.google.com/”而不是“http://google.com/”。服务器坚持重定向而不是立即响应用户想要看到的网页,背后有一些有趣的原因。
    其中一个原因与搜索引擎排名有关。如果同一个页面有两个URL,比如http://www.vasanth.com/和http://vasanth.com/,搜索引擎可能会将它们视为两个不同的网站,每个网站的入站链接较少,因此排名较低。搜索引擎理解永久重定向(301),并将两个来源的入站链接合并为一个排名。
    此外,对于同一内容的多个URL,不利于缓存。当一个内容有多个名称时,它可能会在缓存中出现多次。

注意:
HTTP响应从服务器返回的状态码开始。以下是状态码所表示的内容的简要概述:

  • 1xx表示仅为信息性消息
  • 2xx表示某种成功
  • 3xx将客户端重定向到另一个URL
  • 4xx表示客户端的错误
  • 5xx表示服务器端的错误

HTTP服务器请求处理

HTTPD(HTTP Daemon)服务器负责处理服务器端的请求/响应。最常见的HTTPD服务器是用于Linux的Apache或nginx以及用于Windows的IIS。

  • HTTPD(HTTP Daemon)接收请求。

  • 服务器将请求分解为以下参数:

    • HTTP请求方法(GET、POST、HEAD、PUT和DELETE)。在地址栏中直接输入的URL情况下,这将是GET请求。
    • 域名,在这种情况下为 - google.com。
    • 请求的路径/页面,在这种情况下为 - /(因为未请求特定路径/页面,/是默认路径)。
    • 服务器验证是否在服务器上配置了与google.com对应的虚拟主机。
  • 服务器验证google.com是否可以接受GET请求。

  • 服务器验证客户端是否允许使用此方法(通过IP、认证等)。

  • 如果服务器安装了重写模块(如Apache的mod_rewrite或IIS的URL Rewrite),它将尝试根据已配置的规则匹配请求。如果找到匹配规则,服务器将使用该规则重写请求。

  • 服务器会去提取与请求对应的内容,在我们的案例中,由于“/”是主文件,因此将回退到索引文件(某些情况可以覆盖此行为,但这是最常见的方法)。

  • 服务器根据请求处理程序解析文件。请求处理程序是一个程序(如ASP.NET、PHP、Ruby等),它读取请求并生成HTML响应。如果Google运行在PHP上,服务器将使用PHP解释索引文件,并将输出流传输到客户端。

注意:每个动态网站面临的一个有趣难题是如何存储数据。较小的网站通常会有一个单一的SQL数据库来存储数据,但存储大量数据和/或有许多访问者的网站必须找到一种方法来将数据库分布到多台机器上。解决方案包括分片(基于主键将表分割到多个数据库)、复制和使用简化的一致性语义较弱的数据库。

服务器响应

以下是服务器生成并返回的响应:

HTTP/1.1 200 OK
Cache-Control: private, no-store, no-cache, must-revalidate, post-check=0,
    pre-check=0
Expires: Sat, 01 Jan 2000 00:00:00 GMT
P3P: CP="DSP LAW"
Pragma: no-cache
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
X-Cnection: close
Transfer-Encoding: chunked
Date: Fri, 12 Feb 2010 09:05:55 GMT

2b3
��������T�n�@����[...]

整个响应大小为36KB,其中大部分位于我已截断的字节块末尾。

Content-Encoding头告诉浏览器,响应体使用gzip算法进行了压缩。解压缩后,你将看到预期的HTML内容:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
      lang="en" id="google" class=" no_js">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-language" content="en" />
...

请注意,设置Content-Type为text/html的头。该头指示浏览器将响应内容呈现为HTML,而不是将其作为文件下载。浏览器将使用该头决定如何解释响应,但也会考虑其他因素,例如URL的扩展名。

浏览器背后的运作原理

当服务器将资源(HTML、CSS、JS、图像等)提供给浏览器后,浏览器会执行以下过程:

  • 解析 - HTML、CSS、JS
  • 渲染 - 构建DOM树 → 渲染树 → 渲染树的布局 → 绘制渲染树

浏览器的高级结构

  1. 用户界面: 包括地址栏、前进/后退按钮、书签菜单等。浏览器显示的每个部分,除了你看到的请求页面的窗口。

  2. 浏览器引擎: 在用户界面和渲染引擎之间调度操作。

  3. 渲染引擎: 负责显示请求的内容。例如,渲染引擎解析HTML和CSS,并在屏幕上显示解析后的内容。

  4. 网络模块: 处理网络调用,例如HTTP请求,不同平台使用不同的实现(通过平台无关的接口)。

  5. UI后端: 用于绘制基本的控件,如组合框和窗口。该后端提供了一个通用的接口,不依赖于特定平台。在其底层,它使用操作系统的用户界面方法。

  6. JavaScript引擎: 用于解析和执行JavaScript代码的解释器。

  7. 数据存储: 这是一个持久化层。浏览器可能需要在本地保存数据,如cookies。浏览器还支持如localStorageIndexedDBFileSystem等存储机制。
    ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/b199cce8cc5e4bd2bcecca279f02a290.png

在这里插入图片描述

让我们从最简单的情况开始:一个带有一些文本和单个图像的简单HTML页面。浏览器需要做什么来处理这个简单的页面?

  1. 转换: 浏览器从磁盘或网络读取HTML的原始字节,并根据文件指定的编码(如UTF-8)将其转换为单个字符。

  2. 标记化: 浏览器将字符串转换为由W3C HTML5标准指定的独立标记,例如htmlbody以及其他在“尖括号”中的字符串。每个标记都有特殊的含义和一组规则。

  3. 词法分析: 生成的标记被转换为定义其属性和规则的“对象”。

  4. DOM构建: 最后,由于HTML标记定义了不同标签之间的关系(一些标签包含在其他标签内),生成的对象被链接在一个树形数据结构中,该结构也捕获了原始标记中定义的父子关系:HTML对象是body对象的父级,body是段落对象的父级,等等。

在这里插入图片描述

这个过程的最终输出是文档对象模型(DOM),浏览器使用它来进一步处理页面。

每次浏览器处理HTML标记时,它都必须逐步执行上述所有步骤:将字节转换为字符、识别标记、将标记转换为节点,并构建DOM树。整个过程可能需要一些时间,特别是当我们需要处理大量HTML时。

在这里插入图片描述

如果你打开Chrome开发者工具并在页面加载时记录时间线,你可以看到执行此步骤的实际时间——在上面的例子中,将一段HTML字节转换为DOM树花费了约5毫秒。当然,如果页面较大,如大多数页面一样,这个过程可能会长得多。你会在我们未来关于创建流畅动画的部分看到,如果浏览器需要处理大量HTML,这很容易成为瓶颈。

渲染引擎

渲染引擎是一个软件组件,它接收标记的内容(如HTML、XML、图像文件等)和格式化信息(如CSS、XSL等),并将格式化的内容显示在屏幕上。

浏览器引擎
ChromeBlink(WebKit的分支)
FirefoxGecko
SafariWebkit
OperaBlink(15版以下为Presto)
Internet ExplorerTrident
EdgeBlink(79版以下为EdgeHTML)

WebKit是一个开源的渲染引擎,最初作为Linux平台的引擎启动,后来被Apple修改以支持Mac和Windows。

渲染引擎是单线程的。几乎所有操作,除网络操作外,都是在一个线程中完成的。在Firefox和Safari中,这是浏览器的主线程。在Chrome中,这是标签进程的主线程。网络操作可以由多个并行线程执行。并行连接的数量有限(通常是每个主机名6-13个连接)。

浏览器主线程是一个事件循环。它是一个无限循环,保持进程的活跃。它等待事件(如布局和绘画事件)并处理它们。

注意:像Chrome这样的浏览器运行渲染引擎的多个实例:每个标签页都有一个实例。每个标签页在一个单独的进程中运行。

主流程

渲染引擎将开始从网络层获取所请求文档的内容。通常以8KB块的形式获取。

之后,渲染引擎的基本流程如下:

在这里插入图片描述

渲染引擎将开始解析HTML文档,并将元素转换为DOM树中的节点,称为**“内容树”**。

引擎将解析样式数据,包括外部CSS文件和样式元素中的数据。样式信息与HTML中的视觉指令一起将被用于创建另一棵树:渲染树
渲染树包含具有视觉属性(如颜色和尺寸)的矩形。这些矩形按正确的顺序显示在屏幕上。

渲染树构建完毕后,它将经历**“布局”**过程。这意味着为每个节点分配确切的坐标,以确定它们在屏幕上显示的位置。

下一个阶段是绘制——渲染树将被遍历,每个节点将通过UI后端层进行绘制。

理解这是一个渐进的过程非常重要。为了更好的用户体验,渲染引擎会尽量在可能的情况下尽早显示内容。它不会等待所有HTML解析完毕后才开始构建和布局渲染树。内容的一部分将被解析并显示,而该过程将继续处理从网络中获取的其余内容。

以下是Webkit的流程:

在这里插入图片描述

解析基础

解析: 将文档转换为代码可以使用的结构。解析的结果通常是一个节点树,代表文档的结构。

语法: 解析基于文档遵循的语法规则:它所编写的语言或格式。你可以解析的每种格式都必须具有确定性的语法,包括词汇和语法规则。它被称为上下文无关文法

解析可以分为两个子过程:词法分析和语法分析。

词法分析: 将输入分解为标记的过程。标记是语言的词汇:有效构建块的集合。

语法分析: 应用语言的语法规则。

解析器通常将工作分配给两个组件:词法分析器(有时称为标记器),负责将输入分解为有效标记;解析器,负责通过根据语言语法规则分析文档结构来构建解析树。词法分析器知道如何剥离无关的字符,如空格和换行符。
在这里插入图片描述

解析过程是迭代的。解析器通常会向词法分析器请求一个新标记,并尝试将该标记与语法规则之一匹配。如果规则匹配,则相应于该标记的节点将被添加到解析树中,解析器将请求另一个标记。

如果没有规则匹配,解析器将内部存储该标记,并继续请求标记,直到找到匹配所有内部存储标记的规则。如果没有找到规则,则解析器将引发异常。这意味着文档无效并包含语法错误。

HTML解析器的工作是将HTML标记解析为解析树。HTML的定义格式为DTD(文档类型定义)。这种格式用于定义SGML家族的语言。

该格式包含对所有允许的元素、它们的属性和层次结构的定义。如前所述,HTML DTD并不构成上下文无关文法。

HTML解析算法包括两个阶段:标记化和树构建。

标记化 是词法分析,将输入解析为标记。HTML标记包括开始标签、结束标签、属性名称和属性值。标记器识别标记,将其传递给树构造器,并消耗下一个字符来识别下一个标记,如此反复直到输入结束。

在这里插入图片描述

DOM 树

输出树(“解析树”)是一个 DOM 元素和属性节点的树。DOM 是 Document Object Model(文档对象模型)的缩写。它是 HTML 文档的对象表示形式,也是 HTML 元素对外界(如 JavaScript)的接口。树的根是“Document”对象。

DOM 与标记几乎是 1:1 对应的。例如:

<html>
  <body>
    <p>
      Hello World
    </p>
    <div> <img src="example.png"/></div>
  </body>
</html>

这个标记将被转换为以下 DOM 树:
在这里插入图片描述

为什么 DOM 很慢?

简单来说,DOM 本身并不慢。添加或移除一个 DOM 节点只是几个指针交换,并没有比在 JS 对象上设置属性更多的操作。

然而,布局是慢的。当你以任何方式触碰 DOM 时,你会在整个树上设置一个脏标志,告诉浏览器需要重新计算所有内容的位置。当 JS 将控制权交还给浏览器时,它会调用布局算法(更技术性地说,它调用 CSS 重新计算算法,然后布局、重绘、再合成)来重新绘制屏幕。布局算法相当复杂——查看 CSS 规范可以了解一些规则——这意味着它通常需要做出非局部的决策。

更糟的是,布局是通过访问某些属性同步触发的。这些属性包括 getComputedStyleValue()、getBoundingClientWidth()、.offsetWidth、.offsetHeight 等,使得它们很容易遇到。完整列表见 这里

因此,很多 Angular 和 JQuery 代码非常慢。一次布局会消耗掉你在移动设备上的整个帧预算。当我在 2013 年测量 Google Instant 时,它在一个查询中导致了 13 次布局,并且在移动设备上锁定了屏幕近 2 秒(之后已经加快了)。

React 并没有加快布局速度——如果你想在移动网页浏览器上实现流畅的动画,你需要采用其他技术,如将所有操作限制在 GPU 上执行的帧内。但它确实确保每次更新页面状态时至多进行一次布局。这通常比现状要好得多。

渲染树

在 DOM 树构建的同时,浏览器还构建另一棵树,即渲染树。该树表示将在屏幕上显示的视觉元素的顺序。它是文档的视觉表示形式。这个树的目的是确保内容以正确的顺序绘制。

渲染器知道如何布局和绘制自己及其子元素。每个渲染器表示一个矩形区域,通常对应于一个节点的 CSS 盒子。

渲染树与 DOM 树的关系

渲染器对应于 DOM 元素,但这种关系不是一对一的。非视觉 DOM 元素不会插入到渲染树中。例如,“head” 元素就是如此。此外,将 display 属性设置为 “none” 的元素不会出现在渲染树中(而 visibility 设置为 “hidden” 的元素会出现在树中)。

有些 DOM 元素对应多个视觉对象。这些通常是结构复杂的元素,无法用单个矩形描述。例如,“select” 元素有三个渲染器:一个用于显示区域,一个用于下拉列表框,一个用于按钮。此外,当文本因宽度不足而被拆分成多行时,新行将作为额外的渲染器添加。

一些渲染对象对应于 DOM 节点,但在树中的位置不同。浮动和绝对定位的元素超出流,位于树的不同部分,并映射到真实的帧。一个占位符帧是它们应该在的位置。

在这里插入图片描述

在 WebKit 中,解析样式和创建渲染器的过程称为“附加”。每个 DOM 节点都有一个“attach”方法。附加是同步的,节点插入到 DOM 树时调用新节点的“attach”方法。

构建渲染树需要计算每个渲染对象的视觉属性。这是通过计算每个元素的样式属性来完成的。样式包括来自各种来源的样式表、内联样式元素和 HTML 中的视觉属性(如“bgcolor”属性)。后者会被转换为匹配的 CSS 样式属性。

CSS 解析

浏览器引擎按从右到左的顺序匹配 CSS 选择器。请记住,当浏览器进行选择器匹配时,它有一个元素(即它试图确定样式的元素)和所有的规则及其选择器,并且需要找到哪些规则与该元素匹配。这不同于通常的 jQuery 情况,在 jQuery 中你只有一个选择器,需要找到所有匹配该选择器的元素。

选择器的特异性计算如下:

  • 如果声明来自 style 属性而不是具有选择器的规则,则计数为 1,否则计数为 0 (= a)
  • 计算选择器中的 ID 选择器数量 (= b)
  • 计算选择器中的类选择器、属性选择器和伪类数量 (= c)
  • 计算选择器中的元素名称和伪元素数量 (= d)
  • 忽略通用选择器

将三个数字 a-b-c-d(在一个大基数的数字系统中)连接起来得到特异性。你需要使用的数字基数由 a、b、c 和 d 中的最大计数决定。

示例:

*               /* a=0 b=0 c=0 -> 特异性 =   0 */
LI              /* a=0 b=0 c=1 -> 特异性 =   1 */
UL LI           /* a=0 b=0 c=2 -> 特异性 =   2 */
UL OL+LI        /* a=0 b=0 c=3 -> 特异性 =   3 */
H1 + *[REL=up]  /* a=0 b=1 c=1 -> 特异性 =  11 */
UL OL LI.red    /* a=0 b=1 c=3 -> 特异性 =  13 */
LI.red.level    /* a=0 b=2 c=1 -> 特异性 =  21 */
#x34y           /* a=1 b=0 c=0 -> 特异性 = 100 */
#s12:not(FOO)   /* a=1 b=0 c=1 -> 特异性 = 101 */

为什么 CSSOM 具有树形结构?在计算页面上任何对象的最终样式集时,浏览器从适用于该节点的最通用规则开始(例如,如果它是 body 元素的子元素,那么所有 body 样式都会应用),然后递归地通过应用更具体的规则来细化计算的样式——即规则“级联”。

WebKit 使用一个标志来标记所有顶级样式表(包括 @imports)是否已加载。如果样式在附加时尚未完全加载,会使用占位符并在文档中标记,一旦样式表加载完毕,它们将被重新计算。

布局

当渲染器创建并添加到树中时,它没有位置和尺寸。计算这些值称为布局或重新流动。

HTML 使用基于流的布局模型,这意味着大多数时候可以在一次传递中计算几何信息。之后的“流”元素通常不会影响之前“流”元素的几何信息,因此布局可以从左到右、从上到下地进行。坐标系统相对于根框架。使用顶部和左侧坐标。

布局是递归的过程。它从根渲染器开始,该渲染器对应于 HTML 文档的 html 元素。布局递归地继续遍历一些或所有的框架层次结构,为每个需要它的渲染器计算几何信息。

根渲染器的位置是 (0,0),其尺寸是视口——浏览器窗口的可视部分。所有渲染器都有一个“布局”或“重新流动”方法,每个渲染器调用需要布局的子元素的布局方法。

为了避免对每个小更改执行完整的布局,浏览器使用“脏标志”系统。已更改或添加的渲染器标记自己及其子元素为“脏”:需要布局。有两个标志:“脏”和“子元素是脏”,这意味着尽管渲染器本身可能没有问题,但至少有一个子元素需要布局。

布局通常遵循以下模式:

  • 父渲染器确定自己的宽度。
  • 父渲染器遍历子元素:
    • 放置子渲染器(设置其 x 和 y)。
    • 如果需要,调用子布局——它们是脏的,或者我们在全局布局中,或者由于其他原因——这将计算子元素的高度。
  • 父渲染器使用子元素的累积高度以及边距和填充的高度来设置自己的高度——这将被父渲染器的父元素使用。
  • 将其脏标志设置为假。

还要注意,布局抖动是指在页面加载前,浏览器需要多次重新流动或重绘网页。在 JavaScript 流行之前,网站通常只会重新流动和绘制一次,但现在越来越多的 JavaScript 会在页面加载时运行,这可能会修改 DOM,从而导致额外的重新流动或重绘。根据重新流动的次数和网页的复杂性,可能会导致加载页面时出现显著延迟,尤其是在移动电话或平板电脑等低性能设备上。

绘制

在绘制阶段,遍历渲染树并调用渲染器的 “paint()” 方法在屏幕上显示内容。绘制使用 UI 基础设施组件。

与布局一样,绘制也可以是全局的——整个树都被绘制——或者增量的。在增量绘制中,某些渲染器以不会影响整个树的方式发生变化。更改的渲染器使其在屏幕上的矩形无效。这会导致操作系统将其视为“脏区域”,并生成“绘制”事件。操作系统会巧妙地将多个区域合并为一个。

在重新绘制之前,WebKit 会将旧矩形保存为位图。然后它仅绘制新旧矩形之间的差异。浏览器尽量在响应更改时做最少的操作。因此,对元素颜色的更改将仅导致元素的重新绘制。对元素位置的更改将导致元素、其子元素和可能的兄弟元素的布局和重新绘制。添加 DOM 节点将导致节点的布局和重新绘制。重大更改,如增大“html”元素的字体大小,将导致缓存失效、重新布局和整个树的重新绘制。

有三种不同的定位方案:

  • 普通: 对象根据其在文档中的位置进行定位。这意味着它在渲染树中的位置与在 DOM 树中的位置类似,并根据其盒子类型和尺寸进行布局
  • 浮动: 对象首先像普通流那样布局,然后尽可能向左或向右移动
  • 绝对: 对象在渲染树中的位置与在 DOM 树中的位置不同

定位方案由“position”属性和“float”属性设置。

  • static 和 relative 导致普通流
  • absolute 和 fixed 导致绝对定位

在静态定位中,没有定义位置,使用默认定位。在其他方案中,作者指定位置:top、bottom、left、right。

由 z-index CSS 属性指定。它表示盒子的第三维度:其沿“z 轴”的位置。

盒子被划分为堆叠(称为堆叠上下文)。在每个堆叠中,后面的元素将首先被绘制,前面的元素在上面,更靠近用户。如果发生重叠,最前面的元素将隐藏前面的元素。堆叠按 z-index 属性排序。具有“z-index”属性的盒子形成一个本地堆叠。

趣闻

网络的诞生

蒂姆·伯纳斯-李(Tim Berners-Lee),一位在 CERN 的英国科学家,在 1989 年发明了万维网(WWW)。这个网络最初的构思和开发是为了满足全球大学和研究所之间自动信息共享的需求。

CERN 的第一个网站——也是世界上的第一个网站——专门介绍了万维网项目本身,并托管在伯纳斯-李的 NeXT 计算机上。该网站描述了网络的基本功能;如何访问其他人的文档以及如何设置自己的服务器。NeXT 机器——原始的 Web 服务器——仍在 CERN。作为恢复 第一个网站 的项目的一部分,CERN 在 2013 年将世界上第一个网站恢复到其原始地址。

1993 年 4 月 30 日,CERN 将万维网软件公开。CERN 通过开放许可提供了下一版本,作为最大化传播的更可靠方式。通过这些行动,将运行 Web 服务器所需的软件免费提供,以及一个 基础浏览器 和代码库,使得 Web 得以蓬勃发展。

更多阅读:

当你导航到一个 URL 时,究竟发生了什么

浏览器如何工作:现代浏览器的幕后

你在浏览器中浏览网站时,究竟发生了什么?

发生了什么

那么浏览器如何实际渲染网站

构建对象模型

网络如何工作:网络开发新手的入门指南(实际上适合任何人)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值