浏览器底层渲染机制:
当我们从服务器获取代码后,浏览器如何把代码渲染位页面及相关效果的
-
CRP(关键渲染路径)性能优化法则:
了解浏览器底层处理的具体步骤,针对每一个步骤进行优化
-
JS中的同步和异步编程:
-
同步编程:上一件事情没有处理完,下一件事情无法处理
-
异步编程:上一件事情即便没有处理完,也无需等待,可以继续处理后面的事情
-
-
进程和线程:一个进程中可能包含多个线程
-
进程:一般代表一个程序(或者浏览器打开一个页面就开辟一个进程)
-
线程:程序具体干事的人
-
-
浏览器是多线程的,基于浏览器打开页面,会有不同的线程同时去做多件事情
- GUI渲染线程:用来渲染和解析HTML/CSS以及 绘制页面
- JS引擎线程:用来渲染和解析JS
- HTTP网络线程:用来从服务器获取相关资源文件的(同源下,最多同时开辟5-7个HTTP线程)
- 定时器监听线程:监听定时器是否到时间(计时的)
- 事件监听线程:监听事件是否触发 。。。
浏览器底层渲染机制:
-
步骤一:生成DOM树(DOM TREE)
- 当从服务器获取HTML后,浏览器会分配“GUI”渲染线程自上而下解析代码
-
遇到link:分配一个新的HTTP线程去获取对应的CSS资源,GUI继续向下渲染(异步)
-
遇到style:无需获取资源,但是GUI也不会立即渲染CSS代码,防止渲染顺序错乱;会等待DOM结构渲染完成,访问的link资源也获取到了,按照之前的书写顺序,一次渲染样式
-
遇到@import:也需要去服务器获取资源(基于HTTP线程),但是这个我操作会把“GUI线程挂起”,无法继续向下渲染,直到CSS资源获取到后,GUI才继续向下渲染(同步:阻碍GUI渲染)
-
遇到img:和link一样,也是异步操作,分配新的HTTP去获取图片资源,GUI继续渲染
-
遇到script:因为JS中要涉及DOM的操作,所以遇script,默认会阻碍GUI的继续渲染; 先分配HTTP线程去获取JS资源,资源获取后,在分配JS引擎线程把JS代码先渲染了,都渲染完了,GUI在继续向下渲染
-
- 当从服务器获取HTML后,浏览器会分配“GUI”渲染线程自上而下解析代码
-
步骤二:生成CSSOM树(CSSOM TREE)
- DOM树生成后,等待CSS资源都获取到了,此时按照CSS书写的顺序,依次渲染和解析CSS代码(GU渲染线程)
- 生成CSSOM树:计算出每个节点具备的样式(含某些样式是继承过来的,样式都是自己写的)
-
步骤三:合成渲染树(RENDER TREE)
- 把DOM树和CSSOM树合并在一起,生成渲染树
-
步骤四:Layout布局 & 回流/重排
- 按照当前可视窗口大小,计算每个节点在可视窗口中的位置和大小
-
步骤五:分层
- 计算每一层(每个文档流)中各个节点的具体绘画制规则
-
步骤六:Painting绘制 & 重绘
- 按照计算好的规制一层层绘制
CRP优化技巧:
1.我们最好把所有的CSS合并压缩为一个,只请求一次把所有样式获取即可;分多次请求,因为HTTP的并发限制和可能出现的网络拥堵等问题,导致并不如请求一次快
- CSS合并位一个
- JS合并位一个
- 雪碧图
2.尽可能不用@import导入式,因为他会阻碍GUI渲染;如果CSS代码不是很多,使用style内嵌式更好(尤其是移动端开发);但是如果代码很多还是使用Link外链式(但是最好把link放在head中);
3.图片懒加载一定要处理:不要在第一次渲染页面的时候,不要让图片资源的请求去占用有限的HTTP线程以及宽带资源,有限本着CSS和JS 资源获取,当页面渲染完成后,再去根据图片是否出现在视口中,来加载真是图片
-
关于SCRIPT的优化
- 最好把script放在body的末尾,等待DOM结构加载完成,再去获取和解析JS,此时就可以获取页面中的DOM 元素了
- 利用事件监听去处理
- window.addEventListener(DOMContenLoaded.function(){})
- DOM结构加载完成就会触发,所以触发的时候会比window.onload早很多
- window.onload=function(){}
- 等待页面中的所有资源加载完成
- 也可以给script设置async或者defer异步属性
- async:异步获取,同步渲染
- 遇到script async ,分配新的HTTP线程去获取资源,GUI会继续渲染,获取之后立刻结束GUI渲染,让JS引擎去渲染解析JS;JS代码渲染完,再去执行GUI渲染
- 特点是只要JS代码获取到后就会立刻执行,不管书写的先后顺序,适用于JS之间,不存在依赖
- async:异步获取,同步渲染
- defer:获取异步,渲染异步
- 遇到script defer,分配新的HTTP线程去获取资源,GUI继续渲染,DOM结构树渲染完成后,defer的JS资源获取之后,按照之前编写的JS顺序 依次渲染解析JS代码
- 特点是必须等待GUI以及所有设置的JS代码都获取到了,在按照之前的顺序,依次渲染和解析,即实现了资源的异步获取,也保证了JS代码之间的依赖关系
-
加快DOM TREE的构建
- 减少HTML的层级嵌套
- 使用复合WC3的规范语义化标签
加快CSSOM TREE大的构建
- 选择器层级嵌套不要过深(前缀不要过长)(选择器的渲染顺序从左到右)
- 减少CSS表达式的使用
操作DOM比较消耗性能
- 大部分的性能都消耗在DOM的重拍(回流RReflow)和重绘(Repaint)
- 页面第一次渲染,必然会出现依次Layout(回流)和painting(重绘),第一次渲染完成后:
- 重排(回流)
- 如果浏览器的视口大小发生改变,或者页面中的元素的位置大小发生改变
- DOM结构发生变化(删除,新增或者挪动位置)
- 浏览器都需要重新计算节点在视口中的最新位置也就是重新Layout,完成之后在重新分层和重新绘制
- 此操作非常消耗性能,所以我们应该尽量减少重排的次数
- 重绘:视口/元素的位置大小都不变,只是修改了一些基础样式(例如:背景颜色,文字颜色,透明度)
- 此时无需重排,只需要重绘
- 重绘操作时必不可少的,只要想让页面第一次渲染完还可以在改变,必然需要重绘;而且触发一次回流也必然经历重绘
- 重排(回流)
- 如果基于JS操作DOM,那么前端性能优化必做的一件事情:减少DOM的重排
- 基于vue/react/angular等框架进行开发,我们是基于数据驱动视图渲染,规避了直接操纵DOM,我们只需要操作数据,框架内部帮助我们操作DOM(他们做了很多减少操纵DOM的操作)
- 读写分离(获取和设置不写在一起)
- 新版本浏览器中存在“渲染队列机制”:当前上下文代码执行过程中,遇到修改元素样式的操作,并不会立刻去修改样式,而是把其挪到渲染队列当中,代码继续向下执行;当代码执行完成后,此时会把渲染队列当中所有有关于修改样式的操作,同意执行一次,触发一次重排
- 但是在此过程当中遇到获取元素样式的操作(四个),则书信渲染队列;也就是把目前队列当中的操作执行一次,引发一次重排
- 减少重排
-
我们应该把获取样式的操作和修改样式的操作分开,获取样式放在后面
-
样式批量设置
- box.style.cssText=
width:100px;height:100px - box.className=
active--->.active{width:100px;height:222px}
- box.style.cssText=
-
批量新增元素
- 基于模板字符串实现批量新增
let ste = ""; for(let i - 1;i<=10;i++){ str +=`<div>${i}<div>` } document.body.innerHTML+=str;//会导致body原始结构中绑定的事件全部消失- 导致绑定的事件全部消失,适合于原始容器中没有任何内容,我们把新的内容插进去
-