浏览器在下载好HTML、CSS、JS等文件后,是如何将这些内容组装成绚丽的页面呈现给用户呢?不同的浏览器渲染过程实际上并不相同,但是依旧存在相一致的部分。
一、浏览器渲染页面
从浏览器地址栏的请求链接开始,浏览器通过DNS解析查到域名映射的IP地址,成功之后浏览器端向此IP地址取得连接。成功连接之后,浏览器端将请求头信息通过HTTP协议向此IP地址所在服务器发起请求。服务器接收到请求之后等待处理,最后向浏览器端发回响应,此时在HTTP协议下,浏览器从服务器接收到text/html类型的代码,浏览器开始显示此HTML,并获取其中内嵌资源文件,然后浏览器再发起请求来获取这些资源,并在浏览器的html中显示。
浏览器解析的大概的工作流程可以分为以下几个步骤:
1) 用户输入网址(假设是HTML页面,第一次访问,无缓存情况),浏览器向服务器发出HTTP请求,服务器返回HTML文件
优化方案:善用缓存,减少HTTP请求,减轻服务器压力
2) 浏览器载入HTML代码,发现head内有一个link引用外部css文件,则浏览器立即发送css文件请求,获取浏览器返回的css文件
优化方案:css文件合并,减少HTTP请求
3) 浏览器继续载入HTML中body部分的代码,并且css文件已经拿到手了,可以开始渲染页面了
优化方案:css文件要放在最上面,避免网页重新渲染
4) 浏览器在代码中发现一个img标签引用了一张图片,向服务器发出请求,此时浏览器不会等到图片下载完,而是继续渲染后面的代码
优化方案:图片文件合并,减少HTTP请求
5) 服务器返回图片文件,由于占用了一定面积,影响后面段落的布局,因此浏览器需要回过头来重新渲染这部分代码
优化方案:最好图片都设置尺寸,避免重新渲染
6) 浏览器发现一个包含一行JavaScript的script标签,会立即运行该js代码
优化方案:script最好放在页面最下面
7) js脚本执行了语句,它令浏览器隐藏掉代码中的某个div,突然就少了一个元素,浏览器不得不重新渲染这部分代码
优化方案:页面初始化样式不要使用js控制
8) 浏览器完成渲染,展示HTML页面
二、重绘和回流
1. 什么是重绘和回流
为什么页面会慢?那是因为浏览器需要花时间去渲染,尤其是当它发现某个部分发生了点变化影响了布局,需要倒回去重新渲染,该过程称为回流(reflow
)。
reflow
几乎是无法避免的,现在界面上流行的一些效果,比如树状目录的折叠、展开(实质上是元素的显示与隐藏)等,都将引起浏览器的reflow
。鼠标滑过、点击等等,只要这些行为引起了页面上某些元素的占位面积、定位方式、边距等属性的变化,都会引起它内部、周围甚至整个页面的重新渲染。通常我们都无法预估浏览器到底会reflow
哪一部分的代码,它们都彼此相互影响着。
如果只是改变某个元素的背景色、文字颜色、边框颜色等等不影响它周围或内部布局的属性,将只会引起浏览器的重绘(repaint
)。
repaint
的速度明显快于reflow
(在IE下需要换一下说法,reflow
要比repaint
更缓慢)。
repaint操
作触发时,元素的外观被改变,且在没有改变布局的情况下发生,如改变outline
、visibility
、background-color
,不会影响到dom结构渲染。
reflow
与repaint
区别就是它会影响到dom的结构渲染,同时它会触发repaint
,它会改变它本身与所有父辈元素,这种开销是非常昂贵的,导致性能下降是必然的,页面元素越多效果越明显。
注意:回流必将引起重绘,重绘不一定引起回流。
2. 如何减少重绘和回流
reflow
是不可避免的,只能将reflow
对性能的影响减少到最小
1) 不要一条一条地修改DOM样式,通过设置
style
属性改变节点样式的话,每设置一次都会导致一次reflow
。所以最好通过设置class
的方式,这样可以将多次改变样式属性的操作合并成一次操作2) 让要操作的元素进行“离线处理”,处理完后一起更新
使用
DocumentFragment
进行缓存操作,引发一次重绘与回流1
2
3
4
5
6
7
8
9var fragment = document.createDocumentFragment();
fragment.appendChild(document.createTextNode('Hello Zhang Xiao!'));
fragment.appendChild(document.createElement('br'));
fragment.appendChild(document.createTextNode('Hello Leon!'));
document.body.appendChild(fragment);使用
display: none
技术,只引发两次重绘与回流原理:由于
display
属性为none
的元素不在渲染树中,对隐藏的元素操作不会引发其他元素的重排。如果要对一个元素进行复杂的操作时,可以先隐藏它,操作完成后再显示。这样在隐藏和显示时触发2次重排。
3) 设置元素的
position
为absolute
或fixed
元素脱离标准文档流,也从DOM树结构中脱离出来,在需要reflow
时只需要reflow
自身与下级元素。
- 4) 不要使用table布局
table中某个元素一旦触发reflow
就会导致table里所有的其他元素reflow
。在适合用table的场合,可以设置table-layout
为auto
或fixed
,这样可以让table一行一行的渲染,这种做法也是为了限制reflow
的影响范围。
- 5) 避免使用css的JavaScript表达式,如果css里有
expression
,每次都会重新计算一次。