客户端从服务器获取到需要渲染的源代码后,浏览器会开辟一个GUI渲染线程,自上而下解析代码,最后绘制对应的页面。
自上而下渲染解析代码的过程是"同步",有些操作是异步的
同一个源,不同浏览器, HTTP网络请求线程最多开辟4~7, http并发数
1、关于css资源的加载
- style标签中的代码 =====》”同步“ 交给GUI渲染线程解析
- link外链的样式 不会阻碍GUI渲染线程
- ”异步“开辟一个新的HTTP网络请求线程;
- 不等资源文件请求回来,GUI渲染线程继续往下渲染;
- GUI渲染线程同步操作都完成后,再把HTTP网络线程请求回来的资源文件进行解析渲染
- 遇到@import "导入样式文件"
- ”同步“开辟一个新的HTTP网络请求线程
- 资源文件没有请求回来之前,GUI渲染线程会”阻塞“,不允许其继续向下渲染
2、关于js资源的加载: 默认都是同步,必须基于HTTP网络线程请求回来,并且交给”JS渲染线程“渲染解析完成后,GUI渲染线程才能继续向下渲染 ===》 阻碍GUI渲染
js 异步的属性:
- async属性:首先开辟一个HTTP网络线程去请求加载资源文件,与此同时GUi渲染线程继续向下执行【把默认的同步改为”异步“】,但是资源文件请求回来后,会中断GUI渲染线程,会把请求回来的JS进行解析;
- defer属性:跟async类似,都是开辟新的HTTP网络请求线程去请求加载资源文件,于此同时GUI渲染线程还会继续渲染【”异步“】,不同的是defer与link类似,是在GUI同步的代码渲染完成后,才会渲染解析请求的资源文件
3、遇到图片、音频、视频
默认都是异步的,也会发送新的HTTP网络线程去请求对应的资源,不会阻碍GUI渲染线程;当GUI渲染完成后,才会把请求回来的资源解析渲染。
webkit浏览器预测解析:chrome的预加载扫描器html-perload-scanner通过扫描节点中的”src“,"link" 等属性,找到外部资源后进行资源预加载,避免了资源加载等待的时间,同样实现了提前加载以及加载和执行分离;
4、页面渲染的步骤:
- 生成DOM TREE(DOM树):自上而下渲染页面 ,整理好整个页面的DOM结构;
- 生成CSSOM TREE(样式树):当把所有的样式资源请求回来后,按照引入css的顺序,依照渲染样式代码,生成样式树;
- 生成RENDER TREE(渲染树):把生成的DOM树和样式树结合在一起,生成渲染树(设置display:none;不进行渲染)(根据RENDER TREE 绘制页面的过程: layout (回流/布局/重排) + 分层处理 + Painting绘制(重绘)(还包含图片的绘制)
- Layout 回流/布局/重排:根据生成的渲染树,计算它们在设备视口(viewport)内的确切位置和大小;
- 分层处理:按照层级定位进行分层处理,每一个层级都会详细规划出具体的绘制步骤;
- Painting: 按照每一个层级计算处理的绘制步骤,开始绘制页面。
5、前端性能优化: 【CRP】:关键渲染路径
生成DOM TREE:
- 减少DOM的层级嵌套;
- 不要使用”非标准“ 标签;
生成CSSOM TREE:
- 尽可能不用@import(阻碍GUI渲染线程);
- 如果css代码较少,尽可能使用内联样式(尤其是移动端开发);
- 如果使用link,尽可能把所有的样式资源合并为一个css,且压缩(减少HTTP请求数量,以及渲染css样式树的时候,也不需要再计算依赖关系)
- css选择器的链短一些(css选择器渲染从右到左)
对于其他资源的优化:
- 对于, 尽可能放置在页面的底部(防止其阻塞GUI的渲染)对于部分js需要使用async或者defer
- async是不管js的依赖关系的,哪一个资源先获取到,就先把这个资源代码渲染执行;
- defer 不一样,是等待所有的都请求回来,按照依赖关系依次渲染执行。
- 对于图片
- 懒加载: 第一次加载页面的时候不要加载请求图片,哪怕它是异步的,也占据了http并发的数量,导致其他资源延后加载;
- 图片的BASE64: 不用去请求加载图片,BASE64 基本上代表的就是图片,渲染速度很快(慎用,webpack工程化中可以使用,因为是基于file-loader可以自动base64)
+Layout/Painting :重要的优化手段: 减少DOM的回流和重绘 - 第一次加载页面必然会有一次回流和重绘;
- 触发回流操作后,必然会触发重绘;如果只是单纯的重绘,则不会引发回流;性能优化点:重点在回流上。