一、简述
- 先进行DNS域名解析,获取域名对应的IP地址;
- 浏览器与服务器建立tcp连接;
- 浏览器向服务器发送http请求,
- 服务器根据浏览器请求的资源返回;
- 浏览器将拿到的资源进行解析和和加载,最终对页面渲染,绘制页面呈现给用户。
二、详析:
- 先进行DNS域名解析
先查看本地hosts文件,查看有没有当前域名对应的IP地址,诺有的话直接发起请求;没有的话会在本地域名服务器去查找,该查找属于递归查找;如果本地域名没有查找到;会从根域名服务器查找,该过程属于迭代查找,根域名会告诉你从哪个服务器查找,最后查找到对应的IP地址后把对应规则保存到本地的host文件中。
- 进行http请求,三次握手四次挥手建立断开连接(详解)TCP的三次握手四次挥手原理.
服务器处理可能返回304或者200
- 返回304,说明客户端缓存可用,直接使用缓存即可,该过程属于协商缓存;
- 返回200,的话,同时返回对应的数据。
- 浏览器解析和渲染
(1)浏览器解析HTML源码,然后创建一个DOM树;
(2)浏览器解析CSS代码,然后创建一个CSSOM(CSS树);
(3)根据DOM树和CSSOM来构建一个渲染树(render tree);
(4)生成布局(flow),即将所有渲染树的所有节点进行平面合成;
(5)将布局(paint)绘制在屏幕上。
第四步和第五步是最耗时的部分,这两步合起来,就是我们通常所说的渲染。
触发重排/回流条件
- 页面初始渲染,这是开销最大的一次重排
- 添加/删除可见的DOM元素
- 改变元素位置
- 改变元素尺寸,比如边距、填充、边框、宽度和高度等
- 改变元素内容,比如文字数量,图片大小等
- 改变元素字体大小
- 改变浏览器窗口尺寸,比如resize事件发生时
- 激活CSS伪类(例如::hover)
- 设置 style 属性的值,因为通过设置style属性改变结点样式的话,每一次设置都会触发一次reflow
- 查询某些属性或调用某些计算方法:offsetWidth、offsetHeight等,除此之外,当我们调用 getComputedStyle方法,或者IE里的 currentStyle 时,也会触发重排,原理是一样的,都为求一个“即时性”和“准确性”。
减少重排次数
1.样式集中改变
2.分离读写操作
DOM 的多个读操作(或多个写操作),应该放在一起。不要两个读操作之间,加入一个写操作。——>利用浏览器的渲染队列机制。
// bad 强制刷新 触发四次重排+重绘
div.style.left = div.offsetLeft + 1 + 'px';
div.style.top = div.offsetTop + 1 + 'px';
div.style.right = div.offsetRight + 1 + 'px';
div.style.bottom = div.offsetBottom + 1 + 'px';
// good 缓存布局信息 相当于读写分离 触发一次重排+重绘
var curLeft = div.offsetLeft;
var curTop = div.offsetTop;
var curRight = div.offsetRight;
var curBottom = div.offsetBottom;
div.style.left = curLeft + 1 + 'px';
div.style.top = curTop + 1 + 'px';
div.style.right = curRight + 1 + 'px';
div.style.bottom = curBottom + 1 + 'px';
3.将 DOM 离线
- 使用 display:none
- 通过 documentFragment 创建一个 dom 碎片,在它上面批量操作 dom,操作完成之后,再添加到文档中,这样只会触发一次重排;
4.使用 absolute 或 fixed 脱离文档流
使用绝对定位会使的该元素单独成为渲染树中 body 的一个子元素,重排开销比较小,不会对其它节点造成太多影响。当你在这些节点上放置这个元素时,一些其它在这个区域内的节点可能需要重绘,但是不需要重排;
5.优化动画
- 可以把动画效果应用到 position属性为 absolute 或 fixed 的元素上,这样对其他元素影响较小;
- 启用GPU加速 GPU 硬件加速是指应用 GPU 的图形性能对浏览器中的一些图形操作交给 GPU 来完成,因为 GPU 是专门为处理图形而设计,所以它在速度和能耗上更有效率。
注:
- 如果想加速http请求的话,可以使用缓存服务器CDN: CDN实现原理详解.
<1> 用户输入url地址后,本地DNS会解析url地址,不过会把最终解析权交给cname指向的CDN的DNS服务器;
<2> DNS服务器会返回给浏览器一个全局负载均衡IP;
<3>用户会根据全局负载均衡IP去请求全局负载均衡服务器;
<4>全局负载均衡服务器会根据用户的IP地址,url地址,会告诉用户一个区域负载均衡设备,让用户去请求它;
<5>区域负载均衡服务器会为用户选择一个离用户较近的最优的缓存服务器,并把ip地址给到用户;
<6>用户向缓存服务器发送请求,如果请求不到想要的资源的话,会一层层向上一级查找,直到查找到为止。
- 客户端自上而下执行代码的过程中:
<1> 其中遇到CSS加载的时候,CSS不会阻塞DOM树的解析,但是会阻塞DOM树的渲染,并且CSS会阻塞下面的JS的执行;
<2> 然后是JS加载,JS加载会影响DOM的解析,之所以会影响,是因为JS可能会删除添加节点,如果先解析后加载的话,DOM树还得重新解析,性能比较差。如果不想阻塞DOM树的解析的话,可以给script添加一个defer或者async的标签。
defer: 不会阻塞DOM解析,等DOM解析完之后在运行,在DOMContentloaed之前;
async: 不会阻塞DOM解析,等该资源下载完成之后立刻运行。
三、渲染树(render tree)和DOM树的关系
(1)在DOM树构建的同时,浏览器会构建渲染树。(为了提高用户体验,浏览器并不会等到所有HTML文档加载完成之后才建立渲染树并渲染,而是会在从网络层获取html文档的同时把已经接收到的局部内容先渲染出来);
(2)DOM树完全和html标签一一对应,而渲染树会忽略(即不包含)不需要渲染的元素(如head,样式为display:none的元素等);
(3)渲染树中每一个节点都存储着对应的CSS属性。
四、reflow(重排,回流)和repaint(重绘)
reflow: 浏览器要花时间去渲染,当它发现了某个部分发生了变化并且影响了布局,就需要倒回去重新渲染;
repaint: 如果只是改变了某个元素的背景颜色或文字颜色等,不影响元素周围或内部布局,就只会引起浏览器的repaint,重画其中一部分。
reflow比repaint更花费时间,也就更影响性能,所以在写代码时应尽量避免过多的reflow。