架构爱好者
学习交流中心

页面加载

从输入URL到整个页面显示在用户面前发生了什么

1. 浏览器查找域名对应的IP地址

IP地址:IP协议为互联网上的每一个网络和每一台主机都分配的一个逻辑地址。通过IP地址才能确定一台主机(服务器)的位置。

域名(DN,Domain Name):IP地址不便于用户记忆和使用,故用域名来代替纯数字的IP地址。

DNS(Domain Name System):每个域名都对应一个或多个提供相同服务的服务器的IP地址,只有知道服务器IP地址才能建立连接,所以需要通过DNS把域名解析成一个IP地址。

说明:

  • 域名和IP不是一一对应的关系,可以把多个提供相同服务的服务器IP设置为同一个域名,但在同一时刻一个域名只能解析出一个IP地址;同时,一个IP地址可以绑定多个域名,数量不限;
  • 这里的知识点,对前端而言只要知道使用CDN存放静态资源这种优化策略的原理是DNS负载均衡:如果一个大型网站的所有请求都由同一个服务器进行处理,是不现实的;并且对用户而言,用户并不关注具体是哪台机器处理了他的请求。因此,DNS可以根据多个服务器中每个服务器的负载量、该机器离用户的地理位置的距离等信息返回其中某个适合的主机的IP地址,这个过程就是DNS负载均衡,又叫做DNS重定向CDN(Content Delivery Network)就是利用DNS的重定向技术,DNS服务器会返回一个跟用户最接近的点的IP地址。

2. 浏览器根据IP地址与服务器建立socket连接

建立连接——三次握手:知道了服务器的IP地址,便可开始与服务器建立连接了,通信连接的建立需要经历以下三个过程:

  • 主机向服务器发送一个建立连接的请求(您好,我想认识你);
  • 服务器接到请求后发送统一连接的信号(好的,很高兴认识你);
  • 主机接到同意连接的信号后,再次向服务器发送了确认信号(我也很高兴认识你),至此,主机便于服务器建立了连接。

说明:

  • TCP协议:三次握手的过程采用TCP协议,其可以保证信息传输的可靠性,三次握手过程中,若一方收不到确认信息,协议会要求重新发送信号。

3. 浏览器与服务器通信:浏览器发出请求、服务器处理请求

当服务器与主机建立了连接之后,主机便开始与服务器进行通信。网页请求是一个主机向服务器请求数据==>服务器返回相应数据的单向的请求过程。

  • 浏览器根据URL生成HTTP请求,请求中包含请求文件的位置、请求文件的方式等信息;
  • 服务器接到请求后,会根据HTTP请求中的信息来决定如何获取相应的HTML文件;
  • 服务器将得到的HTML文件发送给浏览器;
  • 在浏览器还没有完全接受完HTML文件时便开始渲染、显示页面
  • 在执行HTML中代码时,根据需要,浏览器会继续请求图片、CSS、Javascript、视频、音频等文件,过程类似。

针对浏览器渲染、显示页面的过程,说明如下:

  • 浏览器端是一个边解析边渲染的过程。
  • HTML Parser将HTML内容解析为DOM Tree,CSS Parser将CSS内容解析为样式规则(Style Rules);
  • 根据样式规则和DOM Tree来渲染树(Render Tree),在这个渲染树的过程中会发生回流(layout/reflow/relayout),回流就是浏览器计算各个盒模型的位置、大小等属性的过程;
  • 等浏览器确定了盒模型的位置、尺寸等数据后开始绘制页面,这个过程称为重绘(Painting/repaint)。

4. 浏览器与服务器断开连接

断开连接——四次挥手

  • 主机向服务器发送一个断开连接的请求(不早了,我该走了);
  • 服务器接到请求后发送确认收到请求的信号(知道了);
  • 服务器向主机发送断开通知(我也该走了);
  • 主机接到断开通知后断开连接并反馈一个确认信号(嗯,好的),服务器收到确认信号后断开连接。

说明:

  • 为什么服务器在接到断开请求时不立即同意断开:当服务器收到断开连接的请求时,可能仍然有数据未发送完毕,所以服务器先发送确认信号,等所有数据发送完毕后再同意断开;
  • 第四次挥手后,主机发送确认信号后并没有立即断开连接,而是等待了2个报文传送周期,原因是:如果第四次挥手的确认信息丢失,服务器将会重新发送第三次挥手的断开连接的信号,而服务器发觉丢包与重新发送断开连接到达主机的时间正好为2个报文传输周期。

优化相关的讨论

使用CDN。

对图片、css、js等静态资源进行压缩。

合并图片(css sprite)。

要权衡的是在全不合并(全不按需加载,最节约流量,但交互过程中的加载可能体验并不好)和全部合并(全不按需加载,最节约流量,但交互过程最流畅)之间找一个平衡点。不同类型系统的这个点肯定是不一样的,需要先确定好优化的目标才好有针对性地去解决。

通常的项目JS代码量达不到需要特别关照的程度,一般uglify就足够了。

模块化开发是趋势:分而治之是不变的道理。无论是传统网页还是SPA应用,都需要借力模块化来保持代码的鲁棒性(Robust的音译,即健壮性)。解耦、独立,不会互相影响。

异步加载/按需加载本身有些跑偏了:从LAB.js开始,各种各样的加载器都在追求加载性能,异步加载,希望可以加快页面的加载速度。分模块加载,异步加载的好处其实并没有那么明显,模块太多,或者异步加载太多,整体的加载实现反而延长。

合理分组,同步加载,用好浏览器缓存和CDN应该可以解决大部分问题:区分开发运行时和线上运行时。开发时使用模块化、异步加载器大幅提升开发体验。线上按照代码更新频率和作用合理分组,合并压缩代码,同步加载3~5个文件。配置好静态服务器,使用CDN,充分利用浏览器缓存和CDN,静态资源就不会是性能瓶颈了。

注意,浏览器通常会对同域下的静态资源有并行下载最大数的限制。

最后,任何不以场景为前提的设计都是耍流氓,任何太不切实际的理想终将幻灭,任何想永存的技术都是临时方案。

未经允许不得转载:技术杂烩 » 页面加载