1.浏览器渲染流程
浏览器渲染页面的步骤:
-
1.生成DOM树(渲染了所有的HTML标签)
-
转换
-
令牌
-
词法分析
-
DOM构建
-
2.生成CSSOM树(请求回来CSS后,渲染完CSS)
-
3.DOM树+CSSOM树=>Render-tree(渲染树)
-
4.按照render-tree在设备的视口中进行结构和位置的相关计算:布局(Layout)或重排/回流(reflow)
-
5.根据渲染树以及回流得到的几何信息,得到节点的绝对像素:绘制(painting)或栅格化(rasterizing)
2.进程process和线程thread
- 1.电脑端安装很多的应用软件,每当运行一个应用程序,都相当于开辟了一个进程(而对于浏览器来说,每新建一个页卡访问一个页面,都是新开辟一个进程)
- 2.每一个进程中可能还会同时做多件事情,如果同时做多件事情,则会开辟多个线程
- 3.一个进程中,包含零到多个线程
- 4.浏览器是“多线程”的,但是JS渲染或者页面渲染是“单线程的”
- GUI渲染线程(渲染和绘制页面)
- JS引擎线程(运行和渲染JS代码)
- 事件管控和触发线程
- 定时器管控和触发线程
- 异步HTTP请求线程
3.同步编程和异步编程
- 1.同步编程:一次只能处理一件事情,当前这件事情处理完,才能继续处理下一件事情
-
2.异步编程:同时可以进行好几件事情(一般是基于多线程并发完成,JS中的异步编程,有自己一些特殊的处理方式=>队列Queue和事件循环EventLoop)
4.优化方案
-
标签语义化和避免深层次嵌套
-
CSS选择器渲染是从右到左
-
尽早尽快地把CSS下载到客户端(充分利用HTTP多请求并发机制)
-
style
-
link
-
@import
-
放到顶部
-
避免阻塞的JS加载
-
放到底部
-
减少DOM的回流和重绘
5.重绘
元素样式的改变(但宽高、大小、位置等不变) 如:outline,visibility,color、background-color等
6.回流
元素的大小或者位置发生了变化(当前页面布局和几何信息发生变化的时候),触发了重新布局,导致渲染树重新计算布局和渲染
- 如添加或删除可见的DOM元素:元素的位置发生变化;元素的尺寸发生变化,内容发生变化(比如文本变化或图片被另一个不同尺寸的图片所替代);页面一开始渲染的时候(这个无法避免);因为回流是根据视口的大小来计算的位置和大小的,所以浏览器的窗口尺寸变化也会引发回流...
- 注意:回流一定会触发重绘,而重绘比一定会回流
7.避免DOM回流
-
1.以下代码引发几次回流?
- 在老版本的浏览器中,我们分别改变了三次样式(都涉及了位置或者大小的改变),所以触发三次回流和重绘
- 现代浏览器中默认增加了“渲染队列的机制”,以此来减少DOM的回流和重绘;遇到一行修改样式的代码,先放到渲染队列中,继续看下面一行代码是否还为修改样式的,如果是继续增加到渲染队列中...直到下面的代码不再是修改样式的,而是获取样式的代码!此时不再向渲染队列中增加,把之前渲染队列中要修改的样式一次性渲染到页面中,引发一次DOM的回流和重绘
box.style.width = '200px'; box.style.height = '200px'; box.style.margin = '20px';
-
2.解决方案
1.分离读写:一次回流和重绘
box.style.width = '200px';
box.style.height = '200px';
box.style.margin = '20px';
console.log(box.style.width);
console.log(box.offsetHeight);
2.三次回流和重绘
box.style.width = '200px';
console.log(box.style.width);
//=>中断渲染队列,立即渲染一次,引发一次DOM回流和重绘 200px
box.style.height = '200px';
console.log(box.offsetHeight);
box.style.margin = '20px';
3.手动不分离读写的需求:先设置动画,渲染后,在去改变样式,让其有动画效果
box.style.transition = '.3s';
//下面代码加上后 0.3后才会产生动画
let AA = box.offsetHeight;
box.style.width = '200px';
box.style.height = '200px';
4.集中改变样式
box.className = 'active';
box.style.cssText = 'width:200px;height:200px;';
5.在动态操作DOM结构中的优化(例如:数据绑定)
for (let i = 1; i <= 5; i++) {
let liBox = document.createElement('li');
liBox.innerText = `我是第${i}个LI`;
item.appendChild(liBox);
//=>每一次向页面中增加,都会触发一次DOM的回流和重绘(5次)
}
6.文档碎片:临时创建的一个存放文档的容器,我们可以把新创建的LI,存放到容器中,当所有的LI都存储完,我们统一把容器中的内容增加到页面中(只触发一次回流)
let frag = document.createDocumentFragment();
for (let i = 1; i <= 5; i++) {
let liBox = document.createElement('li');
liBox.innerText = `我是第${i}个LI`;
frag.appendChild(liBox);
}
item.appendChild(frag);
7.真实项目中,有一个文档碎片类似的方式,也是把要创建的LI事先存储好,最后统一放到页面中渲染(字符串拼接)
let str = ``;
for (let i = 1; i <= 5; i++) {
str += `<li>我是第${i}个LI</li>`;
}
item.innerHTML = str;