浏览器加载
在浏览器中,JavaScript 执行大量任务可能导致页面卡顿,但这与渲染帧的生成有关,不是直接阻止了渲染帧的渲染,而是由于 JavaScript 的执行阻塞了浏览器主线程,导致页面无法响应用户输入、渲染更新等操作,从而造成了卡顿现象。
浏览器的渲染是多进程的,其中渲染进程主要负责页面的渲染和绘制。但是,JavaScript 的执行是在浏览器的主线程上进行的,这意味着如果 JavaScript 执行时间过长,会阻塞主线程的执行,影响了页面的渲染和响应。
当浏览器遇到需要执行 JavaScript 任务时,会停止当前的渲染工作,转而执行 JavaScript 任务。一旦 JavaScript 任务执行完成,浏览器会继续渲染页面。因此,如果 JavaScript 任务过于耗时,会导致页面在此期间无法正常渲染,用户就会感觉到页面卡顿或者无响应。
为了避免页面卡顿,可以采取以下几种策略:
- 分片执行任务:将长时间执行的 JavaScript 任务分解成多个较小的任务,通过定时器或者异步操作分片执行,以避免长时间占用主线程。
- Web Worker:将耗时的任务放到 Web Worker 中执行,这样可以在单独的线程中运行 JavaScript 代码,不会阻塞主线程,从而保证页面的流畅度。
- 优化代码:优化 JavaScript 代码,减少不必要的计算和循环,尽量减少执行时间。
- 利用空闲时间:在页面空闲时执行耗时任务,比如使用 requestIdleCallback API。
通过以上方式,可以有效地减少 JavaScript 执行对页面渲染的影响,提升用户体验。
浏览器原理-渲染帧
在浏览器中,渲染帧(Rendering Frame)是指在屏幕上显示内容的基本单元。当浏览器渲染引擎处理网页内容并将其转换为可视化的图像时,会以帧为单位进行操作。每一帧代表了浏览器在屏幕上渲染一次的过程,包括绘制页面内容、执行 JavaScript、处理用户输入等。
在典型的浏览器渲染流程中,一个渲染帧的处理过程大致包括以下几个步骤:
- 构建文档树(DOM Tree)和样式树(CSSOM Tree):浏览器首先解析 HTML 和 CSS 文件,并构建对应的文档树和样式树,用于确定页面结构和样式信息。
- 生成渲染树(Render Tree):将文档树和样式树合并生成渲染树,渲染树中包含了需要在屏幕上绘制的所有元素及其样式信息。
- 布局(Layout):根据渲染树的信息,浏览器进行布局计算,确定每个元素在屏幕上的位置和大小。
- 绘制(Painting):将布局后的元素转换为实际的像素信息,即进行绘制操作。
- 合成(Composite):将绘制好的元素组合在一起,形成最终的渲染结果。
以上这些步骤构成了一次渲染帧的完整过程。浏览器通常会以每秒 60 帧
的速度进行渲染,也就是每秒将页面渲染成 60 次。这样做是为了保证页面的流畅性和响应速度。
优化渲染帧的处理是提高网页性能的关键之一。通过减少页面重绘、优化布局计算、合理利用硬件加速等手段,可以提高渲染帧的处理效率,从而提升用户体验。
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=11,IE=10,IE=9,IE=8">
<meta name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0">
<meta name="apple-mobile-web-app-title" content="">
<link rel="stylesheet" id="wpsm_tabs_r_bootstrap-front-css" href="" type="text/css"/>
<script type="text/javascript" src=""></script>
<link rel="shortcut icon" href="favicon.ico">
<meta name="keywords" content="">
<meta name="description" content="">
<title></title>
<style>
.btn{
width: 200px;
display: block;
text-align: center;
height: 20px;
line-height: 20px;
border-radius: 20px;
color:white;
background-color: #0a90eb;
}
</style>
</head>
<body>
<a href="javascript:;" class="btn">按钮</a>
<script>
const datas = new Array(100000).fill(0).map((item, index) => index);
const btn = document.querySelector('.btn');
btn.onclick = function () {
const consumer = (data, index) => {
const div = document.createElement('div');
div.textContent = index;
document.body.appendChild(div);
};
const chunkSplitor = task => {
setTimeout(() => {
task(time => time < 16);
}, 30);
};
performChunk(datas, consumer, chunkSplitor);
};
function performChunk(datas, consumer, chunkSplitor) {
if (typeof datas === 'number') datas = new Array(datas);
if (!datas.length) return;
if (!chunkSplitor && globalThis.requestIdleCallback) {
chunkSplitor = task => {
requestIdleCallback(idle => {
task(() => idle.timeRemaining() > 0);
});
};
}
let i = 0; // 目前应该取出的任务下标
// 执行一块任务
function _run() {
if (i === datas.length) return;
chunkSplitor(hasTime => {
console.log(hasTime);
const now = Date.now();
while (hasTime(Date.now() - now) && i < datas.length) {
// 在这一帧还有空闲时间,且任务还没执行完
consumer(datas[i], i);
i++;
}
_run();
});
}
_run();
}
</script>
</body>
</html>