最近想比较系统的关注一下国外的技术博客。顺带将一些文章翻译下。
这是《From URL to Interactive》系列文章的第一篇《Server to Client》。《From URL to Interactive》是个引子就不译了,文章主要基于windows自带的浏览器Eage为基础介绍现代浏览器对HTML从请求、链接、加载、解析、渲染、交互的过程。分阶段介绍:
From URL to Interactive(一)---从服务器到客户端(Server to Client)
From URL to Interactive(二)---从标签到DOM(Tags to DOM)
From URL to Interactive(三)---从大括号到像素(Braces to Pixels)
From URL to Interactive(四)---从var到及时编译(Var to JIT)
篇原文地址:https://alistapart.com/article/server-to-client
-----------------------------------------------------------------------------------------------------------------------------
在浏览器开始行动之前,它必须首先知道要去哪里。有多种方法可以到达某个地地址:在地址栏中输入一个URL,在页面上点击一个链接或者点击其他应用程序中的链接,或单击收藏夹中的收藏。无论如何,这些都会导致所谓的导航。导航是任何Web交互的第一步,因为它启动了一个有连锁反应的事件,事件最终使得网页被加载到浏览器中。
发起请求
一旦将URL提供给浏览器来加载,就会发生一些事情:
检查是否需要HSTS升级
首先,浏览器需要确定URL是否指定为HTTP(非安全)方案(译者:浏览器支持其他协议,如FTP,或者直接file开头的)。如果是HTTP请求,浏览器需要检查域名是否在HSTS列表中(HTTP安全性传输协议)。该列表包括预加载的域名列表和先前使用HSTS访问过的域名的列表, 两者都存储在浏览器中。如果请求的HTTP主机位于HSTS列表中,则会用HTTPS协议代替HTTP协议。这就是为什么你会发现,即使你试图在现代浏览器中键入http://www.bing.com,它也会将你发送到https://www.bing.com。
检查Service Workers
接下来,浏览器需要确定 service worker 是否可以处理请求 - 这在用户离线且没有网络连接的情况下尤为重要。Service Worker是浏览器中相对较新的功能。它们通过允许拦截网络请求(包括顶级请求)来启用具有脱机功能的网站,以便可以从脚本控制的缓存中提供响应。
访问页面时可以注册service worker,这里有一个记录了service worker注册和URL映射到本地数据库的过程。确定是否安装了service worker就像在该数据库中查找导航的URL一样简单。如果该给定URL存在service worker,则允许它处理对该请求的响应。如果浏览器中的“导航预加载”功能在可用,并且站点可以使用它,则浏览器将同时向网络询问初始导航请求。这是有益的,因为它允许浏览器不阻止潜在的但较慢的service worker的启动。
在没有service wokrer处理初始请求(或者正在使用导航预加载)的情况下,浏览器将继续询问网络层。
检查网络缓存
浏览器通过网络层检查其缓存中是否有较新的响应。这通常由Cache-Control响应中的标记定义,其中设置a max-age表示缓存项目可以被缓存多久,即缓存结果被视为最新的结果的最长时间,设置no-store则表示是否应该缓存响应。当然,如果浏览器在其网络缓存中找不到任何内容,则需要网络请求。如果缓存中有fresh的响应,则会将其返回以便加载页面。如果找到了资源但它不是fresh的,浏览器可能会转向下一步,将请求转换为有条件的revalidation请求,revalidation请求包含一个If-Modified-Since或If-None-Match标题告诉服务器浏览器中缓存内容的版本。服务器可以通过返回HTTP 304 (Not Modified)且没有body内容来告诉浏览器其副本仍然是最新的,或者通过返回携带新版本的资源的HTTP 200 (OK)响应来告诉浏览器其副本是陈旧的(译者:这里同时将最新的请求响应返回回来了)。
检查连接(connection)
如果先前与被请求的主机和端口建立了连接,则将重用连接而不是建立新连接。如果没有,浏览器会询问网络层以了解它是否需要进行DNS(域名系统)查找。这将涉及查看本地DNS缓存(存储在您的设备上),并且,根据该缓存的fresh程度,决定是否查询远程DNS(它们可以由Internet服务提供商托管),这最终会使得获取到正确的IP地址供浏览器连接。
在某些情况下,浏览器可能能够预测将访问哪些域,并且可以提前与这些域建立连接。该页面可以通过使用resource hints(如rel="preconnect”链接标记)向浏览器提示。使用resource hint有用的一种情况是,如果用户在Bing搜索结果页面上,并且预期最初几个搜索结果最有可能被访问。在这种情况下,提前启动与这些域名的连接可以帮助您在以后单击这些链接时不必支付DNS查找和连接设置的成本。
建立联系(Establish Connection)
现在,浏览器可以与服务器建立连接了,以便让服务器知道它将同时与客户端进行收发数据。如果我们使用了 TLS,我们还需要执行TLS握手协议来验证服务器提供的证书。
发送请求到服务器
通过该连接的第一个请求是最上层的页面请求。通常,这将是一个server提供的HTML文件,会由服务器返回给客户端。
处理Response
当数据流传输到客户端后,浏览器将分析response数据。首先,浏览器检查响应的头信息(header)。HTTP头是作为HTTP响应的一部分发送的name-value对。如果响应的header指示重定向(例如,通过Location字段),则浏览器将重新开始导航过程并退回到“检查是否需要HSTS升级”的第一步。
如果服务器response被压缩(compressed )或有分块(chunked),浏览器将尝试解压缩和拼装它。
在读取响应的同时,浏览器也开始并行地将response写入网络缓存。
接下来,浏览器将尝试了解发送到浏览器的文件的MIME类型,因此它可以适当地解释如何加载文件。例如,图像文件将作为图像加载,而HTML文件则将被解析和渲染呈现。如果要使用HTML解析器来处理这个文件,浏览器则会同时扫描response的内容以查找可能要下载的资源的URL,以便浏览器可以在页面开始渲染之前提前开始这些下载。本系列的下一篇文章将对此进行更详细的介绍。
到目前为止,所请求的URL已经记录到浏览器历史记录中,这使得它可以在浏览器的后退和前进功能中进行导航的原因。
这是一个流程图,它概述了到目前为止所讨论的内容,并提供了更多细节:
如您所知,页面将继续发出请求,因为页面上有许多子资源对整体体验很重要,包括图像,JavaScript和样式表。另外,还有那些被子资源所引用的资源,如启动背景图片(CSS中引用的)或其他资源fetch(),import()或AJAX调用也需要被请求。没有这些,我们只会有一个没有太多交互性的普通页面。正如您在前面的解释和流程图中看到的那样,请求的每个资源都部分受到浏览器缓存策略的影响。
高速缓存
如前所述,浏览器管理着一个网络缓存,允许在许多情况下重用先前下载的资源。这对于大部分不变的资源特别有用,例如来自框架的logos和JavaScript。尽可能利用此缓存非常重要,因为它可以通过重用本地缓存的资源来减少网络请求的数量。反过来,这有助于最大限度地减少所需的其他费力且潜在的操作,从而缩短页面加载时间。
当然,网络缓存的配额会影响缓存的项目数量以及缓存时间。这并不意味着网站在此事上没有发言权。response中的Cache-Control头信息用来控制浏览器的缓存逻辑。在某些情况下,告诉浏览器不要缓存任何东西(例如with Cache-Control: no-store),因为预估到response会每次都不同。在其他情况下,让浏览器无限期地缓存内容是有意义的(Cache-Control: immutable),因为给定URL的response永远不会改变,在这种情况下,使用不同的URL指向同一资源的不同版本而不是让同一个URL的资源进行版本的变动,因为始终会使用缓存的版本。
当然,网络缓存不是浏览器中唯一的缓存类型。可以通过JavaScript利用程序化缓存。具体地,在上面给出的service worker的示例中,service worker可以拦截顶级页面的初始资源请求,然后可以使用其程序缓存的由站点定义的缓存内容。这很有用,因为它使网站能够更好地控制何时使用缓存项。这些缓存是origin-bound的(译者:下面Origin model 详细介绍),这意味着每个域名空间都有自己的沙盒缓存集,用它可以控制实现域名之间的缓存隔离。
原点模型(Origin model)
原点只是一个由方案/协议、主机名和端口组成的三元组。例如,https://www.bing.com:443具有HTTPS协议、www.bing.com主机名和443作为端口。如果与其他来源相比,其中有任何一个元素不同,则认为它们是不同的来源。例如,https://images.bing.com:443和http://www.bing.com:80是不同的来源。
原点是浏览器的一个重要概念,因为它定义了数据如何沙箱化和安全化。在大多数情况下,出于安全目的,浏览器强制执行同源策略(same-origin policy),这意味着一个源无法访问另一个源的数据 - 两者都需要是同一个源。具体来说,在前面介绍的缓存案例中,https://images.bing.com:443和http://www.bing.com:80都无法看到另一个的程序缓存。
如果bing.com想要加载来自microsoft.com的JavaScript文件,它将发出一个跨源资源请求(cross-origin resource request ),浏览器将在该请求上强制实施同源策略。为了允许这种行为,microsoft.com需要通过指定CORS(Cross-Origin Resource Sharing)header信息与bing.com合作,使得bing.com能够从microsoft.com加载JavaScript文件。设置正确的CORS header信息是一种很好的做法,借此浏览器可以适当地处理跨源资源请求。
结论
现在您已经了解了我们如何从服务器访问到客户端 - 以及介于两者之间的所有细节 - 请继续关注以了解加载网页的下一步:我们如何从HTML tags转到DOM。
关于作者
Ali Alabbas
Ali Alabbas是Microsoft Edge网络平台团队的PM,他负责其中的service workers,browser storage和 networking。同时他也是W3C的撰稿人和索引数据库(Indexed Database)规范的编辑。