学前端必读的从输入url到页面渲染全过程
从输入 URL 到页面展示这中间到底发生了什么?
,这是一道非常经典的面试题,这一过程涉及到了计算机网络、操作系统、Web等一系列知识,如果对这一过程有非常好的了解,对以后的开发甚至是程序的优化都是非常有益的。现将过程梳理如下:
1. 解析URL
分析所需要使用的传输协议和请求的资源路径
。如果url中的协议或主机名不合法,将会把地址栏中输入的内容传递给搜索引擎,如果没有问题,浏览器会检查url中是否出现了非法字符,如果存在,对非法字符(空格、汉字等双字节字符)进行转义后在进行下一过程。
编码和解码:
encodeURI()/decodeURI()
:encodeURI()函数只会把参数中的空格编码为%20,汉字进行编码,其余特殊字符不会转换encodeURIComponent()/decodeURIComponent()
:由于这个方法对:/都进行了编码,所以不能用它来对网址进行编码,适合对URL中的参数进行编码escape()/unescape()
:将字符的unicode编码转化为16进制序列,不对ASCII字母和数字进行编码,也不会对' * @ - _ + . / '这些ASCII符号进行编码,用于服务器与服务器端传输多
let url = "http://www.baidu.com/test /中国"
console.log(encodeURI(url)); // http://www.baidu.com/test%20/%E4%B8%AD%E5%9B%BD
let url1 = `https://www.baidu.com/from=${encodeURIComponent('http://wwws.udiab.com')}`
console.log(url1); // https://www.baidu.com/from=http%3A%2F%2Fwwws.udiab.com
console.log(escape(url)); // http%3A//www.baidu.com/test%20/%u4E2D%u56FD
1.1 URL地址格式
传统格式:scheme://host:port/path?query#fragment
。例:http://www.urltest.cn/system/user…
- scheme(必写):
协议
。http
(超文本传输协议)、https
(安全超文本传输协议)、ftp
(文件传输协议,用于将文件下载或上传至网站)、file
(计算机上的文件) - host(必写):
域名或IP地址
- port(可省略):
端口号
,http默认80,https默认443 - path:
路径
,例如 /system/user - query:
参数
,例如 username=falcon&age=18 - fragment:
锚点(哈希hash)
,用于定位页面的某个位置
Restful格式:可以通过不同的请求方式(get、post、put、delete)来实现不同的效果
- (GET) http://www.webtest.com/users
- (POST) http://www.webtest.com/users
- (PUT) http:http://www.webtest.com/users/217
- (DELETE) http:http://www.webtest.com/users/217
2. 缓存判断
浏览器缓存就是浏览器将用户请求过的资源存储到本地电脑。当浏览器再次访问时就可以直接从本地加载,不需要去服务器请求,能有效减少不必要的数据传输,减轻服务器负担,提升网站性能,提高客户端网页打开速度。浏览器缓存一般分为强缓存和协商缓存
- 浏览器在请求某一资源时,会先获取该资源缓存的header信息,判断是否命中强缓存(cache-control、expires信息),命中则直接从缓存中获取资源信息,不会向服务器发起请求;
- 如果没有命中强缓存,浏览器会发送请求(携带该资源缓存的第一次请求返回的header字段信息,Last-Modified/If-Modified-Since、Etag/If-None-Match)到服务器,由服务器根据请求中携带的相关header字段进行对比来判断是否命中协商缓存,命中则返回新的header信息更新缓存中对应的header信息,不返回资源内容,浏览器直接从缓存中获取;否则返回最新的资源内容
2.1 强缓存
expires
,绝对时间字符串,发送请求在这个时间之前有效,之后无效
catch-control
,几个比较常用的字段如下:
max-age=number
,相对字段,利用资源第一次请求时间和Cache-Control设定的有效期,计算出一个资源过期时间,然后再进行比较no-cache
,不使用本地缓存,需要使用协商缓存no-store
,禁止浏览器缓存数据,每次用户都需要向服务器发送请求获取完整资源public
,可以被所有的用户缓存,包括终端用户和CDN等中间代理服务器private
,只能被终端用户浏览器缓存
【注】
- 强缓存如何重新加载缓存过的资源?使用强缓存,不用向服务器发送请求就可以获取到资源,如果在强缓存期间,资源发生了变化,浏览器就一直得不到最新的资源,如何操作:
通过更新页面中引用的资源路径,让浏览器主动放弃缓存,加载新的资源
- 如果二者同时存在,
cache-control的优先级高于expires
2.2 协商缓存
Last-Modified/If-Modified-Since
- 浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,会在response的header上加上Last-Modified(表示资源在服务器上的最后修改时间)字段
- 浏览器再次跟服务器请求这个资源时,在request的header上加上If-Modified-Since(值就是上一次请求返回的Last-Modified值)字段,来判断是否发生变化,没变化返回304,从缓存中加载,也不会重新在response的header上添加Last-Modified;发生变化则直接从服务器加载,并且更新Last-Modified值
Etag/If-None-Match
- 这两个值是由服务器生成的每个资源的唯一标识字符串,只要资源变化值就会发生改变
- 判断过程与上面一组逻辑类似
- 不同的是,当服务器返回304时,由于Etag重新生成过,response的header还是会把这个Etag返回
【注】
为什么需要Etag?
一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变修改时间);某些文件修改非常频繁(例如在秒以下的时间进行修改);某些服务器不能精确得到文件的最后修改时间等这些情况下,利用Etag能够更加精确的控制缓存- 两者可以一起使用,
服务器会优先验证Etag,在一致的情况下,才会继续比对Last-Modified
2.3 用户行为对缓存的影响
3. DNS解析
获取输入url中的域名对应的IP地址。
- 第一步,检查
浏览器缓存
中是否缓存过该域名对应的IP地址; - 第二步,检查
本地的hosts文件(系统缓存)
; - 第三步,
本地域名解析服务器
进行解析; - 第四步,
根域名解析服务器
进行解析; - 第五步,gTLD服务器进行解析
(顶级域名)
; - 第六步,
权威域名服务器
进行解析,最终获得域名IP地址;
3.1 域名层级结构图
- 根域:位于域名空间最顶层,一般用一个点“.”表示
- 顶级域:一般表示一种类型的组织机构或者国家地区。
.net(网络供应商) .com(工商企业) .org(团体组织) .edu(教育机构) .gov(政府部门) .cn(中国国家域名)
- 二级域:用来标明顶级域内一个特定的组织。.com.cn .net.cn .edu.cn
- 子域:二级域下所创建的各级域名,各个组织或用户可以自由申请注册
- 主机:位于域名空间最下层,一台具体的计算机。完整格式域:http://www.sina.com.cn
3.2 递归查询、迭代查询
用户向本地DNS服务器发起请求属于递归请求
;本地DNS服务器向各级域名服务器发起请求属于迭代请求
- 递归查询:以本地DNS服务器为中心,客户端发出请求报文后就一直处于等待状态,直到本地DNS服务器发来最终查询结果
- 迭代查询:DNS服务器如有客户端请求数据则返回正确地址;没有则返回一个指针;按指针继续查询
4. TCP三次握手建立连接
- 第一步,客户端发送SYN包(seq=x)到服务器,等待服务器确认
- 第二步,服务器收到SYN包,确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(seq=y),即SYN+ACK包
- 第三步,客户端收到SYN+ACK包,向服务器发送确认包ACK(ack=y+1)
三次握手完成,客户端和服务器正式开始传递数据
4.1 TCP、UDP
TCP
面向连接的协议,只有建立后才可以传递数据UDP
无连接的协议,可以直接传送数据,传输效率较高,但不能保证数据的完整性
5. 发起http、https请求
5.1 http1.0、http1.1、http2.0区别
空
5.2 http、https
- https需要
CA申请证书
,一般需要交费 - http运行在TCP之上,
明文传输
;https运行在SSL/TLS
之上,SSL/TLS运行在TCP之上,加密传输
- http默认端口
80
,https默认端口443
- https可以有效的防止运营商劫持
5.3 XSS、CSRF
XSS
XSS(Cross-site Scripting)。跨域脚本攻击
,指通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序等。
分类如下:
反射型
。发出请求时,xss代码出现在URL中,作为输入提交到服务器端,服务器端解析后响应,xss代码随响应内容一起传回浏览器,最后浏览器解析执行xss代码。例如:"http://www.a.com/xss/reflect…"存储型
。和反射型的差别是提交的代码会存储在服务器端(数据库、内存、文件系统等),下次请求目标页面时不用再提交XSS代码。例如:留言板xss,用户提交留言到数据库,目标用户查看留言板时,留言的内容会从数据库查询出来并显示,浏览器解析执行,触发xss攻击DOM型
。不需要服务器的参与,触发xss靠的是浏览器端的DOM解析,完全是客户端触发
防御措施:
过滤
。对用户的输入(和URL参数)进行过滤。移除用户输入的和事件相关的属性,如onerror、onclick等;移除用户输入的Style节点、Script节点(一定要特别注意,它是支持跨域的)、Iframe节点编码
。对输出进行html编码。对动态输出到页面的内容进行html编码,使脚本无法再浏览器中执行服务端设置会话Cookie的HTTP Only属性
,这样客户端的JS脚本就不能获取cookie信息了
CSRF
CSRF(Cross-site request forgery)。跨站请求伪造
,攻击者通过伪造用户的浏览器请求,向用户曾经认证访问过的网站发送出去,使目标网站接收并误以为是用户的真实操作而去执行命令。常用于转账、盗号、发送虚假消息等
防御措施:
token验证
。服务器返回给客户端一个token信息,客户端带着token发送请求,如果token不合法,服务器拒绝这个请求隐藏令牌
。将token隐藏在http的header中referer验证
。页面请求来源验证,只接受本站的请求,其他进行拦截
5.4 get、post
GET
- 一般用于获取数据;
- 参数放在url中;
- 浏览器回退或刷新无影响;
- 请求可被缓存;
- 请求的参数放url上,有长度限制;
- 请求的参数只能是ASCII码;
- url对所有人可见,安全性差;
- get产生一个tcp数据包,hearder、data一起发送一次请求,服务器返回200
POST
- 一般用于向后台传递数据、创建数据;
- 参数放在body里;
- 浏览器回退或刷新数据会被需重提交;
- 请求不会被缓存;
- 请求的参数放body上,无长度限制;
- 请求的参数类型无限制,允许二进制数据;
- 请求参数不会被保存在浏览器历史或web服务器日志中,相对更安全;
- post产生两个tcp数据包,先发送header,返回100 continue,再发送data,服务器返回200,但不是绝对的,Firefox只发送一次
5.5 状态码
1XX - 通知
- 100 -- 客户端继续发送请求
- 101 -- 切换协议
2XX - 成功
- 200 -- 请求成功,一般应用与 get 或 post 请求
- 201 -- 请求成功并创建新的资源,
3XX - 重定向
- 301 -- 永久移动,请求的资源已被永久移动到新的 url,返回信息包括新的 url,浏览器会自动定向到新的 url,今后所有的请求都是新的 url
- 302 -- 临时移动,资源只是临时移动,客户端应继续使用旧的 url
- 304 -- 所请求的资源未修改,不会返回任何资源。浏览器请求的时候,会先访问强缓存,没有则访问协商缓存,协商缓存命中,资源未修改,返回 304
4XX - 客户端错误
- 400 -- 客户端请求的语法错误,服务器无法理解(z 字段类型,或对应的值类型不一致;或者没有进行 JSON.toStringfy 的转换)
- 401 -- 请求需要用户的认证
- 403 -- 服务器理解客户端请求,但是拒绝执行
- 404 -- 服务器无法根据客户端的请求找到资源
5XX - 服务器端错误
- 500 -- 服务器内部错误,无法完成资源的请求
- 501 -- 服务器不支持请求功能,无法完成资源请求
- 502 -- 网关或代理服务器向远程服务器发送请求返回无效
5.6 跨域
- 跨域:浏览器不能执行其他网站的脚本,这是由于
同源策略(同协议、同域名、同端口)限制
造成的 - 同源策略限制的行为:cookie,localstorage和IndexDB无法读取;DOM无法获取;Ajax请求不能发送
跨域的几种解决方式:
jsonp
,实现原理是<script>
标签的src可以发跨域请求,不受同源策略限制,缺点是只能实现get一种请求document.domain + iframe跨域
domain属性可返回下载当前文档的服务器域名,此方案仅限主域相同,子域不同的跨域场景跨域资源共享(CORS)
只服务端设置Access-Control-Allow-Origin即可,前端无需设置;若要带cookie请求,前后端都需要设置nginx反向代理跨域
,项目中常用的一种方案html5的postMessage(跨文档消息传输),WebSocket(全双工通信、实时通信)
6. 返回数据
当页面请求发送到服务器端后,服务器端会返回一个html文件作为响应
7. 页面渲染
7.1 加载过程
- HTML会被渲染成
DOM树
。HTML是最先通过网址请求过来的,请求过来之后,HTML本身会由一个字节流转化成一个字符流,浏览器端拿到字符流,之后通过词法分析,将相应的词法分析成相应的token,转化不同的token tag,然后通过token类型append到DOM树 - 遇到link token tag,去请求css,然后对css进行解析,生成
CSSOM树
- DOM树和CSSOM树结合形成
Render Tree
,再进行布局和渲染 - 遇到script tag,然后去请求JS相关的web资源,请求回来的js交给浏览器的v8引擎进行解析
7.2 加载特点
- html文档解析,对tag依次从上到下解析,
顺序执行
- html中可能会引入很多css,js的web资源,这些资源在浏览器中是
并发加载
的。 - DOM树和CSSOM树通常是并行构建的, 所以
CSS加载不会阻塞DOM的解析
;Render树依赖DOM树和CSSOM树进行,所以CSS加载会阻塞DOM的渲染
;css会阻塞js文件执行,但不会阻塞js文件下载
,因为GUI渲染线程与JavaScript线程互斥,JS有可能影响样式;js会阻塞DOM的解析(把js文件放在最下面),也就会阻塞DOM的渲染
,同时js顺序执行,也会阻塞后续js逻辑的执行
依赖关系
。页面渲染依赖于css的加载;js的执行顺序依赖关系;js逻辑对于dom节点的依赖关系,有些js需要去获取dom节点引入方式
。直接引入,不会阻塞页面渲染;defer不会阻塞页面渲染,顺序执行;async不会阻塞页面渲染,先到先执行,不保证顺序;异步动态js,需要的时候引入
【注】css样式置顶;js脚本置底;用link代替import;合理使用js异步加载
资源加载完成后,通过样式计算、布局设置、分层、绘制
等过程,将页面呈现出来
7.3 重绘回流
根据渲染树,浏览器可以计算出网页中有哪些节点,各节点的CSS以及从属关系,发生回流
;根据渲染树以及回流得到的节点信息,计算出每个节点在屏幕中的位置,发生重绘
- 元素的规模尺寸、布局、显隐性发生变化时,发生回流,每个页面至少产生一次回流,第一次加载
- 元素的外观、风格、颜色等发生变化而不影响布局,发生重绘
回流一定发生重绘,重绘不一定发生回流
【避免措施】
样式设置
- 避免使用层级较深的选择器
- 避免使用 css 表达式
- 元素适当的定义高度或最小高度
- 给图片设置尺寸
- 不要使用 table 布局
- 能 css 实现的,尽量不要使用 js 实现
渲染层
- 将需要多次重绘的元素独立为 render layer,如设置 absolute,可以减少重绘范围
- 对于一些动画元素,使用硬件渲染
DOM 优化
- 缓存 DOM
- 减少 DOM 深度及 DOM 数量
- 批量操作 DOM
- 批量操作 CSS 样式
- 在内存中操作 DOM
- DOM 元素离线更新
- DOM 读写分离
- 事件代理
- 防抖和节流
- 及时清理环境
TCP四次挥手断开连接
- 客户端发送一个FIN(seq=u)数据包到服务器,用来关闭客户端到服务器的数据连接
- 服务器接受FIN数据包,发送ACK(seq=u+1)数据包到客户端
- 服务器关闭与客户端的连接并发送一个FIN(seq=w)数据包到客户端,请求关闭连接
- 客户端发送ACK(seq=w+1)数据包到服务器,服务器在收到ACK数据包后进行CLOSE状态,客户端在一定时间没有收到服务器的回复证明其关闭后,也进入关闭状态
链接:https://juejin.cn/post/7252869090549268538
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。