Appearance
浏览器渲染进程包含的线程
- GUI 渲染线程,负责解析HTML,CSS,构建DOM、CSSOM和render tree, 布局和绘制。当页面回流时。
- js引擎线程,一个渲染进程只有一个js引擎线程,负责解析执行js代码
- 事件线程,当事件触发时,会添加到队尾,等待js引擎执行
- 定时器线程,触发后放在队尾
- htto请求线程,请求后放在队尾
简略版xuanran
- 解析html,构建dom树
- 解析css,构建css树
- cssom和dom合并成渲染树
- 根据渲染树计算节点位置,布局
- 调用GUI绘图,上色,合成图层,并渲染到界面上
html、css和js解析构建顺序
- html解析从上到下解析文档,依次构建DOM
- 如果碰到link或者style,异步的进行cssom构建
- 如果碰到js脚本,会先等到前面的cssom构建完成,才会解析js脚本(可以先下载)。js脚本解析执行期间,html会停止解析
- 初始的html解析完成后,会触发documentContentLoad 事件。此时图片资源可能还未下载完成
- load 事件表示初始html以及资源全部load
渲染进程中各种线程的配合
js引擎线程会执行一个有多个字队列的队列,队列中每个子队列是由各种引擎推送的的。刚开始时会GUI线程会解析HTML,当碰到js代码会将js推送到js的引擎队列,这时js引擎会立即执行。中断GUI进程的渲染。在代码中js引擎可能会发起请求、定时器或者新建事件绑定,就是通知其他线程开始工作, 定时线程会在定时完成后将代码转移到js引擎的新子队列中。 事件引擎和网络同理
渲染进程多线程
渲染进程和尽可能的启用并行的多线程的方式
主线程(main thread)
- 解析html css 构建渲染树
- 布局和绘制
- 执行js代码
- 处理事件
- 定时器
合成器线程(compositor thread)
- 分层 layer
- 图层分块
- 合成器
- 处理简单的动画(不需要由主线程调度)
光栅线程(raster thread)
- 根据合成层的数据转换成GPU能够理解的像素数据
工作线线程(work thread)
- 各种worker
主线程
通过触发的方式一直运行。有任务队列、用户事件、进程通信、vsync触发,其中vsync触发会间隔周期不断触发。当没有触发时,主线程会休眠。每次触发就是一个task,通过perfermance可以观察到。raf idle callback也认为是一次task
每次触发,js开始执行当前任务、然后执行微任务、然后判断是否需要render(满足至少大于16.6ms),如果需要执行则执行raf,然后会立马检查是否有新的任需要执行,如果没有执行idlecallback(同时idleback还有限制是20ms执行一次,总是在raf同周期后)。
我们可以得出结论, 每次事件循环执行的任务是不一样的,有时候会执行raf、idle,有些时候会执行layout、paint、有些时候纯执行js。
得出结论是如果当前时间循环和上一次循环大于16ms,该次时间循环旧总是会执行raf、idle同时会有layout、paint。
react 为什么不使用settimeout和idle callback
settimeout 至少4ms才触发一次,idle callback至少一个帧才触发一次,太慢了。
js
const button = document.getElementById("myButton");
const div = document.getElementById("myDiv");
let count = 0;
function update() {
count += 1;
div.textContent = count;
}
function sleep(iterations) {
let result = 0;
for (let i = 0; i < iterations; i++) {
// 模拟一些复杂的数学运算
result += Math.sqrt(i) * Math.sin(i);
// 随机执行一些额外的操作,进一步消耗CPU
if (i % 10000 === 0) {
result = result % 10000000;
}
}
return result;
}
const requestAnimationFrameAction = function () {
// update();
console.log("requestAnimationFrame executed");
requestAnimationFrame(requestAnimationFrameAction);
};
requestAnimationFrame(requestAnimationFrameAction);
const requestIdleCallbackAction = function () {
// update();
console.log("requestIdleCallback executed");
requestIdleCallback(requestIdleCallbackAction);
};
requestIdleCallback(requestIdleCallbackAction);
const setTimeoutAction = function () {
// update();
sleep(5000000);
console.log("setTimeout executed");
setTimeout(setTimeoutAction);
};
setTimeout(setTimeoutAction);
// const channel = new MessageChannel();
// const messageChannelAction = function () {
// update();
// channel.port1.postMessage(null);
// console.log("messageChannel callback executed");
// Promise.resolve().then(() => {
// console.log("Promise callback executed");
// });
// };
// channel.port2.onmessage = messageChannelAction;
// messageChannelAction();
button.addEventListener("click", function onClick() {
update();
requestIdleCallback(() => {
// update();
console.log("Idle callback executed");
});
requestAnimationFrame(() => {
update();
console.log("Animation frame callback executed");
});
setTimeout(() => {
update();
console.log("Timeout callback executed");
});
});