页面加载流程:
-
DNS查询
-
TCP连接
-
发送HTTP请求
-
服务器处理HTTP请求并返回HTTP报文
-
浏览器解析并render页面
-
HTTP连接断开
1. DNS查询
浏览器查看浏览器缓存-->系统缓存-->查找本地host文件-->本地DNS服务器缓存,如果缓存中有,则直接在屏幕中显示页面内容。若没有,则进行DNS解析,获取相应的IP地址,本地域名服务器会向 根域名服务器发送一个请求。如果根域名服务器也不存在该域名时,本地域名会向顶级域名服务器(TLD)发送一个请求,即根域名服务器-->顶级域名服务器-->权威域名服务器依次类推下去。直到最后本地域名服务器得到URL对应的IP地址并把它缓存到本地,供下次查询使用。
2.TCP连接
DNS解析返回域名的IP之后,接下来就是浏览器要和该IP建立TCP连接了。
三次握手:
3. 发送HTTP请求
TCP连接已建立,意味着桥已经搭好了,下一步就该传输HTTP消息了,即客户端与服务器进行通信。网页请求是一个单向请求的过程,即是一个客户端浏览器向服务器请求数据,服务器返回相应的数据的过程。浏览器根据 URL 内容生成 HTTP 请求
http请求
请求:请求行(方法 路径 版本) 请求头 空行 请求体(POST方法时存在)
请求头:
:authority: static.leetcode-cn.com
:method: GET
:path: /cn-frontendx-assets/production/_next/static/css/4539c5e3547b78e8.css
:scheme: https
accept: */*
accept-encoding: gzip, deflate, br
accept-language: zh-CN,zh;q=0.9
cache-control: no-cache
origin: https://leetcode.cn
pragma: no-cache
referer: https://leetcode.cn/
sec-ch-ua: ".Not/A)Brand";v="99", "Google Chrome";v="103", "Chromium";v="103"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: cross-site
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36
请求方法
GET 获取资源。用来请求已被URI识别的资源。GET方法用作请求数据,但不会对数据产生任何影响
HEAD 获取报文头部 跟GET方法类似,但HEAD方法不返回报文的主体,只返回一些相关信息,比如确认URI的有效性、资源更新的日期等信息
POST 传输实体主体。用来给服务器发送数据,比如要表单提交的数据,文件更新的数据等。
PUT 传输文件。用来传输文件,请求报文主体中包含文件内容,然后保存到请求URI指定了资源的存放位置
DELETE 删除文件。用来删除文件,即删除按请求URI下的指定资源
OPTIONS 用于获取目的资源所支持的通信选项。用于获取请求URL所支持的方法。(响应报文包含一个 Allow 首部字段,该字段的值表明了服务器支持的所有 HTTP 方法。一般用于CORS
常用标准请求头字段
Accept 设置接受的内容类型
Accept-Charset 设置接受的字符编码
Accept-Encoding 设置接受的编码格式
Accept-Datetime 设置接受的版本时间
Accept-Language 设置接受的语言
Authorization 设置HTTP身份验证的凭证
Cache-Control 设置请求响应链上所有的缓存机制必须遵守的指令
Connection 设置当前连接和hop-by-hop协议请求字段列表的控制选项
Content-Length 设置请求体的字节长度
Content-MD5 设置基于MD5算法对请求体内容进行Base64二进制编码
Cookie 设置服务器使用Set-Cookie发送的http cookie
Date 设置消息发送的日期和时间
Expect 标识客户端需要的特殊浏览器行为
Forwarded 披露客户端通过http代理连接web服务的源信息
From 设置发送请求的用户的email地址
Host 设置服务器域名和TCP端口号,如果使用的是服务请求标准端口号,端口号可以省略
If-Match 设置客户端的ETag,当时客户端ETag和服务器生成的ETag一致才执行,适用于更新自从上次更新之后没有改变的资源
If-Modified-Since 设置更新时间,从更新时间到服务端接受请求这段时间内如果资源没有改变,允许服务端返回304 Not Modified
If-None-Match 设置客户端ETag,如果和服务端接受请求生成的ETage相同,允许服务端返回304 Not Modified
If-Range 设置客户端ETag,如果和服务端接受请求生成的ETage相同,返回缺失的实体部分;否则返回整个新的实体
If-Unmodified-Since 设置更新时间,只有从更新时间到服务端接受请求这段时间内实体没有改变,服务端才会发送响应
Max-Forwards 限制代理或网关转发消息的次数
Origin 标识跨域资源请求(请求服务端设置Access-Control-Allow-Origin响应字段)
Pragma 设置特殊实现字段,可能会对请求响应链有多种影响
Proxy-Authorization 为连接代理授权认证信息
Range 请求部分实体,设置请求实体的字节数范围,具体可以参见HTTP/1.1中的Byte serving
Referer 设置前一个页面的地址,并且前一个页面中的连接指向当前请求,意思就是如果当前请求是在A页面中发送的,那么referer就是A
TE 设置用户代理期望接受的传输编码格式,和响应头中的Transfer-Encoding字段一样
Upgrade 请求服务端升级协议
User-Agent 用户代理的字符串值
Via 通知服务器代理请求
Warning 实体可能会发生的问题的通用警告
4. 服务器处理HTTP请求并返回HTTP报文
HTTP 请求到达服务器,服务器进行对应的处理。最后要把数据传给浏览器,也就是返回网络响应
响应:响应行(版本 状态码 原因) 响应头 空行 响应体
Cache-Control: no-cache
Connection: keep-alive /* 表示建立持久链接 */
Content-Encoding: gzip
Content-Type: text/html;charset=utf-8
Date: Wed, 04 Dec 2019 12:29:13 GMT /* 响应时间 */
Age:/* 响应持续时间 */
Server: apache /* 服务器应用程序软件的名称和版本 */
Set-Cookie: rsv_i=xxx; path=/; domain=.baidu.com /* 服务器下发cookie */
响应体 Body:判断Connection字段, 如果请求头或响应头中包含Connection: Keep-Alive,表示建立了持久连接,这样TCP连接会一直保持,之后请求统一站点的资源会复用这个连接,否则断开TCP连接,请求-响应流程结束
响应状态码
1**(信息类):表示接收到请求并且继续处理
100——Continue客户必须继续发出请求
101——客户要求服务器根据请求转换HTTP协议版本
2**(响应成功):表示请求被成功接收、理解和接受
200——OK表明该请求被成功地完成,所请求的资源发送回客户端
201—— Created 提示知道新文件的URL
202——Accepted接受和处理、但处理未完成
3**(重定向类):为了完成指定的动作,必须接受进一步处理
300——请求的资源可在多处得到
301——Moved Permanently本网页被永久性转移到另一个URL
302——请求的网页被转移到一个新的地址,但客户访问仍继续通过原始URL地址,重定向,新的URL会在response中的Location中返回,浏览器将会使用新的URL发出新的Request。
304——Not Modified自从上次请求后,请求的网页未修改过,服务器返回此响应时,不会返回网页内容,代表上次的文档已经被缓存了,还可以继续使用
4**(客户端错误类):请求包含错误语法或不能正确执行
400——客户端请求有语法错误,不能被服务器所理解
403——Forbidden 禁止访问,服务器收到请求,但是拒绝提供服务
404——Not Found一个404错误表明可连接服务器,但服务器无法取得所请求的网页,请求资源不存在。eg:输入了错误的URL
5**(服务端错误类):服务器不能正确执行一个正确的请求
HTTP 500 Internal Server Error - 服务器遇到错误,无法完成请求
HTTP 502 - 网关错误
HTTP 503:Service Unavailable 由于超载或停机维护,服务器目前无法使用,一段时间后可能恢复正常
常用标准响应头
Access-Control-Allow-Origin 指定哪些站点可以参与跨站资源共享
Accept-Patch 指定服务器支持的补丁文档格式,适用于http的patch方法
Accept-Ranges 服务器通过byte serving支持的部分内容范围类型
Age 对象在代理缓存中暂存的秒数
Allow 设置特定资源的有效行为,适用方法不被允许的http 405错误
Alt-Svc 服务器使用"Alt-Svc"(Alternative Servicesde的缩写)头标识资源可以通过不同的网络位置或者不同的网络协议获取
Cache-Control 告诉服务端到客户端所有的缓存机制是否可以缓存这个对象,单位是秒
Connection 设置当前连接和hop-by-hop协议请求字段列表的控制选项
Content-Disposition 告诉客户端弹出一个文件下载框,并且可以指定下载文件名
Content-Encoding 设置数据使用的编码类型
Content-Language 为封闭内容设置自然语言或者目标用户语言
Content-Length 响应体的字节长度
Content-Location 设置返回数据的另一个位置
Content-MD5 设置基于MD5算法对响应体内容进行Base64二进制编码
Content-Range 标识响应体内容属于完整消息体中的那一部
Content-Type 设置响应体的MIME类型
Date 设置消息发送的日期和时间
ETag 特定版本资源的标识符,通常是消息摘要
Expires 设置响应体的过期时间
Last-Modified 设置请求对象最后一次的修改日期
Link 设置与其他资源的类型关系
Location 在重定向中或者创建新资源时使用
P3P 以P3P:CP="your_compact_policy"的格式设置支持P3P(Platform for Privacy Preferences Project)策略,大部分浏览器没有完全支持P3P策略,许多站点设置假的策略内容欺骗支持P3P策略的浏览器以获取第三方cookie的授权
Pragma 设置特殊实现字段,可能会对请求响应链有多种影响
Proxy-Authenticate 设置访问代理的请求权限
Public-Key-Pins 设置站点的授权TLS证书
Refresh "重定向或者新资源创建时使用,在页面的头部有个扩展可以实现相似的功能,并且大部分浏览器都支持
Retry-After 如果实体暂时不可用,可以设置这个值让客户端重试,可以使用时间段(单位是秒)或者HTTP时间
Server 服务器名称
Set-Cookie 设置HTTP Cookie
Status 设置HTTP响应状态
Strict-Transport-Security 一种HSTS策略通知HTTP客户端缓存HTTPS策略多长时间以及是否应用到子域
Trailer 标识给定的header字段将展示在后续的chunked编码的消息中
Transfer-Encoding 设置传输实体的编码格式,目前支持的格式: chunked, compress, deflate, gzip, identity
Upgrade 请求客户端升级协议
Vary 通知下级代理如何匹配未来的请求头已让其决定缓存的响应是否可用而不是重新从源主机请求新的
Via 通知客户端代理,通过其要发送什么响应
Warning 实体可能会发生的问题的通用警告
WWW-Authenticate 标识访问请求实体的身份验证方案
X-Frame-Options 点击劫持保护:
deny frame 中不渲染
sameorigin 如果源不匹配不渲染
allow-from 允许指定位置访问
allowall 不标准,允许任意位置访问
5. 浏览器解析并render页面
一个页面一般包含HTML、CSS、 JS、 图片等文件
浏览器的渲染主要步骤
DOM树的构建:渲染引擎解析HTML文档,并将HTML文档包含的元素转化为一个个DOM,并构建一个DOM树,若如果遇到< link href ="..">和< script src ="..">这种外链加载 CSS 和 JS 的标签,浏览器会启用别的线程下载这些静态资源。在 head 中遇到 JS 文件时,HTML 的解析会停下来,等 JS 文件下载结束并且执行完,HTML 的解析工作再接着来,防止 JS 修改已经完成的解析结果,JS的解析会阻塞DOM的解析。
CSSOM:引擎开始解析来自CSS文件或直接嵌在HTML页面中的CSS样式数据,这些样式信息包含了颜色,大小,位置等属性。DOM的解析和CSS的解析是互不影响的,两者是并行的,但是由于render tree的生成是依赖DOM Tree和CSSOM Tree的,因此CSS必然会阻塞DOM的渲染。更为严谨一点的说,CSS会阻塞render tree的生成,进而会阻塞DOM的渲染。
渲染树:将 DOM 和 CSSOM 整合成 RenderTree ,渲染引擎会给每个DOM元素安排精确的样式,大小,坐标等,并根据坐标显示在屏幕上。
为了更好的用户体验,渲染引擎会尽量在屏幕上尽快显示内容。在开始构建和布局渲染树之前,它不会等到所有 HTML 都被解析。部分内容将被解析和显示,而该过程将继续处理来自网络的其余内容。
渲染引擎是单线程工作的,除了网络操作,其他所有的都是单线程的。在Firefox和Safari,它们自己就是主线程,而Chrome就是每个tab处理主线程。网络操作则由多个并列线程去执行,但数量也是受限的,一般在2-6个。浏览器的主线程是一个无限的事件循环,而且一直保持进程alive,一直等着各种事件(例如绘画事件,布局事件),并处理他们。
优化
1. 将 CSS 放在 HTML 头部:这样会让浏览器尽早拿到 CSS 尽早生成 CSSOM ,然后在解析 HTML 之后可一次性生成最终的RenderTree ,渲染一次即可。如果 CSS 放在 HTML 底部,会出现渲染卡顿的情况,影响性能和体验。
2. 将 JS 放在 HTML 底部:JS 放在底部可以保证让浏览器优先渲染完现有的 HTML 内容,让用户先看到内容,体验好。另外, JS 执行如果涉及 DOM 操作,得等待 DOM 解析完成才行, JS 放在底部执行时, HTML 肯定都解析成了 DOM 结构。 JS 如果放在 HTML 顶部, JS 执行的时候 HTML 还没来得及转换为 DOM 结构,可能会报错。
3. script 放在 body 里
当JS 文件放在 head 中属于同步加载,会阻塞 DOM 树的构建,进而影响页面的加载。,由于 DOM 是自上而下解析的,因此 JS 不会阻塞 DOM 的解析,而且这时候可以在 JS 中操作 DOM
4. script设置 defer 属性
通过给 script 标签设置 defer 属性,将脚本文件设置为延迟加载,当浏览器遇到带有 defer 属性的 script 标签时,会再开启一个线程去下载 JS 文件,同时继续解析 HTML,等 HTML 全部解析完、DOM 加载完成之后,再去执行加载好的 JS 文件。只适用于引用外部 JS 文件,并且可以确保所有加了 defer 属性的脚本会按顺序执行
5. script设置 async 属性
async 属性和 defer 属性类似,也是会开启一个线程去下载js文件,但和 defer 不同的是,aysnc 会在 JS 加载完成后立刻执行,而不是会等到 DOM 加载完成之后再执行,所以还是有可能会造成阻塞。同样的也是只适用于外部 JS 文件,如果有多个设置了 aysnc 的 JS 文件,不能像 defer 那样保证按照顺序执行
浏览器的组件构成
- 用户界面 User Interface: UI组件包括地址栏,前进/后退按钮,书签菜单等。
- 浏览器引擎 Browser Engine: 用来查询及操作渲染引擎的接口;
- 渲染引擎 Rendering engine : 负责显示请求的内容。例如,如果是HTML页面,它将解析HTML,CSS,并将解析的内容显示在屏幕上。不同的浏览器使用不同的渲染引擎:IE使用Trident Firefox使用Gecko Safari使用WebKit Chrome和Opera(版本15开始)使用Blink。它是基于Webkit开发的。
- 网络 Networking: 负责网络调用,例如HTTP请求。为不同的平台使用不同的实现接口。
- UI后端 UI backend: 主要用来绘画基本的UI元素,例如下拉框对话框,Windows等。这个UI后台暴露一些通用的接口,并不依赖平台的具有通用接口。
- JS解析器 JavaScript interpreter. 用来解析和运行JavaScript code。
- 数据存储 Data storage. 数据持久化的那一层。浏览器可能需要存储各种各样的数据,例如Cookie。浏览器也得支持我们常用的LocalStorage, IndexedDB,WebSQL以及FileSystem。
6. HTTP连接断开