资讯专栏INFORMATION COLUMN

从输入URL到页面可交互的过程探究之一:从服务端到客户端

Gu_Yan / 2719人阅读

摘要:大多数情况,为了安全考虑,浏览器会强制使用同源策略,意味着一个源无法访问另一个源的数据。如果想要从加载一个文件,它就需要在实行同源策略的浏览中发起一个跨域资源请求。

原文:https://alistapart.com/articl...

最近发现国外有一个系列,专门探究从输入URL到页面可交互的详细过程,是一份干货十足的好资料。笔者决定分为四篇文章对其进行有删减地翻译,只希望能对大家有所帮助,毕竟这是前端必备的知识点,也是容易忽略掉某些细节的知识点。事先声明,这个系列完全由笔者手翻,如有翻译不当的地方,恳请读者给出改进意见!

接下来开始第一篇——从服务端到客户端

在浏览器执行任何工作之前,它需要先知道访问的是哪里。有几种方法可以实现访问:在地址栏中输入URL、点击(或触碰)一个页面上或其他app中的超链接、或者点击你的收藏。无论是哪种情况,都会触发一个动作——导航。导航永远是网页中交互的第一步,因为它触发了如下一系列事件的连锁反应直至网页被加载。

初始化请求

一旦URL被提供给浏览器去加载,以下这些事情就会悄悄在背后发生:

检查HSTS

首先,浏览器需要判断这个URL是否明确为HTTP(不安全)协议。如果它是一个HTTP的请求,那么浏览器则需要检查这个域名是否在HSTS的清单中(HTTP Strict Transport Security——严格安全传输)。这个清单包含了一个预加载好的名单以及你之前访问过的使用HSTS的网站名单,它们都是存放在浏览器中的。如果你请求的HTTP开头的host处于在HSTS清单中,那这个请求会被强制转为HTTPS开头的URL而非HTTP。这就是为什么你会发现当你试图在一个现代浏览器中输入http://www.bing.com 会被转为https://www.bing.com。

检查SERVICE WORKERS

接着,浏览器需要判断service worker是否可以用来处理请求——这对于那些离线的没有网络连接的用户来说至关重要。Service workers相对来讲是比较新的浏览器特性。它通过对网络请求的拦截来提供离线应用的能力,这些请求都可以被保留在脚本控制的缓存中。这是很有用的,因为它使网站能够更好地控制何时使用缓存的项目。这些缓存是跟域名绑定的,这意味着每个域都可以有自己的缓存黑盒,并与其他域的缓存隔离开。

当一个页面被访问时,可以注册一个Service worker,这个动作是由一个工作线程来完成的,它可以把service worker的注册和URL映射记录在本地数据库中。要判断一个service worker是否被安装,只需在这个本地数据中查找是否有对应的URL。如果为service worker查到了对应的URL,它就会被允许处理请求的回应。而如果浏览器支持Navigation Preload的新特性,且开发者使用了它,那么浏览器会同时去发起首次导航请求。这是有好处的,因为它避免了浏览器因为service worker启动过慢而对页面渲染的影响。

当浏览器发现没有service worker来处理初始化请求时,就会继续网络请求层。

检查网络缓存

浏览器会通过网络请求层检查缓存中是否存在全新的响应。这经常是由响应头中的Cache-Control字段决定的,字段中设置的max-age值可以决定缓存多久会刷新,而no-store字段可以表明是否应该被缓存。可想而知,如果浏览器在缓存中找不到任何东西,那么就需要进行网络请求了。而如果在缓存中有一个全新的响应,它就会被立即返回以用于页面加载。如果存在一个不够“新”的资源,那么浏览器会把这个请求转为一个附带条件的校验请求,也就是请求头带上If-Modified-Since或者If-None-Match去告诉服务端当前浏览器存的是哪个版本的缓存。服务端则可以返回HTTP 304状态码(没有更改)告诉浏览器这个缓存是最新的,不带响应正文;或者返回HTTP 200状态码告诉浏览器这个缓存资源已经过期了,并直接返回最新的资源

检查网络连接

如果现在有一个和主机、端口建立起连接的请求,那么它会被浏览器复用而不是重新去建立一个,否则,浏览器会走网络层以了解是否需要执行DNS(域名系统)查询。这个动作的具体流程是,先寻找本地的DNS缓存(存储在你的设备上),然后根据DNS缓存是否过期来决定是否访问远程域名服务器(它们由互联网服务提供商ISP分配主机地址),域名服务器最终会返回准确的IP地址给浏览器进行连接。

某些情况下,浏览器能够预先知道哪些域名会被访问,从而先准备好对这些域名的连接。一个网页可以通过在link标签中使用资源提示(resource hints),比如rel="preconnect" 来提示浏览器提前准备好连接。在如下场景中,资源提示是很有用的,比如一个用户在必应的搜索结果页,而通常的预期中,前几条搜索结果是最有可能被用户访问的。此时,提前准备好对那些域名的连接可以在那些网页被点击之后节省掉DNS查询和域名连接的消耗。

建立起连接

浏览器现在可以与服务器建立起连接了,且服务端知道自己需要从客户端接收和发送消息了。如果我们是使用TLS,我们需要执行一次TLS握手流程以验证服务器提供的证书。

发送请求给服务器

第一个通过这个连接发起的请求叫做顶级页面请求。通常情况,这个请求的资源会是一个HTLML文件,从服务器返回到客户端

处理响应

当响应以数据流的形式到达客户端后,客户端就开始进行解析了。首先,浏览器会检查响应头。HTTP头部是以键值对的形式作为HTTP响应的一部分。如果响应头指示要进行重定向(比如,通过Location字段),浏览器就会再一次进行导航并回到最初的那一步,检查是否需要执行HSTS的升级(为HTTPS)。

如果服务器的响应数据被压缩或分块了,浏览器会尝试对它进行解压和合并。

待响应被解读完成后,浏览器还会并行地将其写入网络缓存中。

接着,浏览器会搞清发送过来的文件的MIME类型,这样它才能以适当的方式去加载这份文件。比如,一份图片文件会原封不动地被加载进来,但HTML文件则会被执行解析和渲染。如果HTML解析器被调用了,那么它会扫描出那些可能要下载的资源文件的URL,以便浏览器在页面渲染之前就可以开始去下载。这一部分的更多细节会在系列文章的下一篇中具体展开。

截至目前,被请求的导航URL已经输入到了浏览器的历史中,这样它就可以被用于浏览器导航的前进和后退功能了。

这里有一张更详细的流程图,它可以让你对目前讨论的内容有个总体的概览:

如你所知的,页面会继续发起请求,因为页面上还有很多对整体体验很重要的子资源,比如图片,脚本,和样式表。另外,这些子资源中引用到的其他资源,比如背景图片(CSS中引用的),或者其他由fetch()import()AJAX请求发起的资源。如果没有这些的话,我们将只能看到一个原始的无交互的空页面。

再谈缓存

刚刚已经提到,浏览器会管理网络缓存,以便在多种场景下能对下载好的资源的重复利用。这对那些长久不更新的资源尤其有用,比如logo和第三方的脚本文件。我们应该尽可能地利用好这些缓存,因为这有利于减少对外的网络请求数,取而代之的是本地的可复用的缓存资源。

响应头中的Cache-Control字段控制着浏览器的缓存逻辑。某些情况下,你可以谨慎地告诉浏览器完全不要进行缓存,比如使用Cache-Control: no-store,因为这个资源在预期中是一直在变化的。另一种情况下,当给定URL的响应内容永远不会变化时,我们可以设置Cache-Control: immutable以便浏览器可以永远地缓存它。实际应用中,当我们使用不同的URL来指向不同版本的同一份资源时,我们就可以采用这种做法,而非对同一个URL的资源进行更改,因为被缓存的版本会一直被使用且不会去发送请求。

Origin模型

Origin是由协议,主机名和端口共同组成的。例如,https://www.bing.com:443 这个origin是由https的协议, www.bing.com 的主机名和443的端口组成的。只要其中任何一个部分有差异,那么在两者进行比较时,都会被认为是不同源的。比如https://images.bing.com:443 和 http://www.bing.com:80 就是不同源的。

Origin对于浏览器来说是很重要的概念,因为它定义了数据是如何被隔离和保护的。大多数情况,为了安全考虑,浏览器会强制使用同源策略,意味着一个源无法访问另一个源的数据。就像上面提到的两个源——https://images.bing.com:443 和 http://www.bing.com:80 ,它们互相都无法访问对方的缓存(service worker的)。

如果bing.com想要从microsoft.com加载一个Javascript文件,它就需要在实行同源策略的浏览中发起一个跨域资源请求。想要允许这种操作的话,microsoft.com就需要与bing.com通过指定CORS(跨域资源共享)的头部进行合作。

总结

既然你已经明白了资源如何从服务器走到客户端以及之间的所有细节,那么请继续关注网页加载的下一步:从HTML标签转为DOM。

ps:欢迎关注微信公众号——前端漫游指南,会定期发布优质原创文章和译文,关注公众号福利:回复666可以获得精选前端进阶电子书,感谢~

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/106782.html

相关文章

  • android学习之一探究活动

    摘要:此时,活动不在栈顶了。销毁状态当一个活动从返回栈中移除后就变成了销毁状态。在这时该处于栈的顶部,并且接受用户的输入。 什么是 Activity Activity 就是布满整个窗口或者悬浮于其他窗口上的交互界面。在一个应用程序中通常由多个 Activity 构成,都会在 AndroidManifest.xml 中指定一个主的 Activity,如下设置 ...

    lucas 评论0 收藏0
  • 前端每周清单半年盘点之 JavaScript 篇

    摘要:前端每周清单专注前端领域内容,以对外文资料的搜集为主,帮助开发者了解一周前端热点分为新闻热点开发教程工程实践深度阅读开源项目巅峰人生等栏目。背后的故事本文是对于年之间世界发生的大事件的详细介绍,阐述了从提出到角力到流产的前世今生。 前端每周清单专注前端领域内容,以对外文资料的搜集为主,帮助开发者了解一周前端热点;分为新闻热点、开发教程、工程实践、深度阅读、开源项目、巅峰人生等栏目。欢迎...

    Vixb 评论0 收藏0
  • 何勉:第一性原理和精益敏捷规模化实施

    摘要:摘要什么是第一性原理第一性原理如何指导我们的精益敏捷开发阿里资深解决方案架构师畅销书精益产品开发原则方法与实施作者何勉,结合实践案例,详述第一性原理和精益敏捷的规模化实施。前言今天分享的题目是第一性原理和精益敏捷的规模化实施。 摘要: 什么是第一性原理?第一性原理如何指导我们的精益敏捷开发?阿里资深解决方案架构师、畅销书《精益产品开发:原则、方法与实施》作者何勉,结合实践案例,详述第一...

    233jl 评论0 收藏0
  • 前端进阶之路: 前端架构设计(3) - 测试核心

    摘要:而测试驱动开发技术并不只是单纯的测试工作。需求向来就是软件开发过程中感觉最不好明确描述易变的东西。这里说的需求不只是指用户的需求,还包括对代码 可能很多人和我一样, 首次听到前端架构这个词, 第一反应是: 前端还有架构这一说呢? 在后端开发领域, 系统规划和可扩展性非常关键, 因此架构师备受重视, 早在开发工作启动之前, 他们就被邀请加入到项目中, 而且他们会跟客户讨论即将建成的平台的...

    Karuru 评论0 收藏0
  • 前端进阶之路: 前端架构设计(3) - 测试核心

    摘要:而测试驱动开发技术并不只是单纯的测试工作。需求向来就是软件开发过程中感觉最不好明确描述易变的东西。这里说的需求不只是指用户的需求,还包括对代码 可能很多人和我一样, 首次听到前端架构这个词, 第一反应是: 前端还有架构这一说呢? 在后端开发领域, 系统规划和可扩展性非常关键, 因此架构师备受重视, 早在开发工作启动之前, 他们就被邀请加入到项目中, 而且他们会跟客户讨论即将建成的平台的...

    宋华 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<