客户端渲染
什么是客户端渲染?
服务端会把静态资源给浏览器,浏览器运行一遍js,生成页面 页面的内容,在html源文件找不到(因为js没运行,就没的HTML DOM节点)
服务端渲染
什么是服务端渲染? 服务器会把完整的html给浏览器,浏览器不必跑一遍js才生成html页面 页面的内容,在html源文件找的到
服务端渲染不是性能万金油,把浏览器干的活都给服务器了,服务器表示鸭梨山大
浏览器渲染的过程
解析HTML(发出外部资源请求【DOM树】)->
计算样式(加载css与DOM和合并生成render树,伪元素也在这个环节被构建到DOM树中【CSSOM树】)->
计算图层布局(元素的相对位置、大小信息【布局渲染树】) ->
绘制图层(把每一个页面图层转换为像素,对所有媒体文件进行解码)->
整合图层,得到页面(合并各个图层,将数据由CPU输出给GPU绘制于屏幕上【绘制渲染树】)
CSS渲染优化点
CSS引擎查找样式规则,是从右到左匹配的
- 避免使用通配符(会遍历每一个元素)
- 关注可以通过继承实现的属性,避免重复匹配重复定义
- 少用标签选择器(#app li{} -> .app-li {})
- 减少嵌套。后代选择器开销最高,深度最好不超过3层
关于阻塞的优化
HTML、CSS、JS都具有阻塞渲染的特性
浏览器在构建CSSOM的过程中,不会渲染任何已处理的内容,哪怕DOM已经OK了
所以CSS是阻塞渲染的资源,需要尽早(css放在head中)尽快(CDN静态资源服务)的下载到客户端
JS引擎独立于渲染引擎,HTML解析器遇到script标签时,暂停渲染过程,将控制权交给JS引擎,JS引擎对内联代码直接执行,外部JS则去下载,在执行,等JS引擎运行完毕,浏览器才会把控制权交给渲染引擎
- 一般情况
<script src="index.js"></script>
阻塞浏览器,需要等index.js加载和执行完毕 - async
<script async src="index.js"></script>
加载是异步的,加载后,脚本立即执行 - defer
<script defer src="index.js"></script>
加载是异步的,执行也是推迟的,执行在整个文档解析完成,DOMContentLoaded事件即将被触发时
回流与重绘
回流
- 回流(重排):对DOM的修改引起了DOM几何尺寸的变化,比如大小、宽高、是否隐藏等,浏览器需要重新计算元素的几何属性,然后再绘制出来,这个过程称为回流
- 重绘:修改了DOM的样式,但没有影响几何属性,比如修改了颜色,浏览器重绘即可 重绘不一定回流,回流一定重绘
这两个都应该减少,即减少DOM操作
DocumentFragment则是这个思想,它不是真实DOM树的一部分,对它修改不会引起回流和重绘,操作完DocumentFragment再统一做一次DOM的操作
引起回流:
1.改变几何属性width、padding、margin、left、boder等
2.改变DOM树结构,节点的增减和移动(开销还凑合)
3.获取offsetTop、scollTop、clientTop等,因为需要即时计算得到,会回流
4.getComputedStyle,也是即时计算得到,会回流
避免回流:
1.不要在循环里,多次获取offsetTop值,可以缓存在外面,计算完再一次性设置
2.不要逐条修改样式,可以合并作为一个类名,统一修改
3.将DOM离线,设置为display:none,再操作(频繁操作的话可以使用)
浏览器并不是在每一次修改都会回流,浏览器维护了一个flush队列,只有在不得已的时候才会回流(比如获取即时性属性)
宏任务与微任务
宏任务:setTimeout、setInterval、setImmediate、script整体代码、I/O操作、UI渲染 微任务:process.nextTick、Promise、MutataionObserver等
异步任务中进行DOM更新,需要包装为微任务