当我们打开一个网页,浏览器并不会直接把JS文件丢给CPU去跑,而是经过多进程协作,由渲染进程里的JavaScript引擎完成解析、编译与执行。理解这套机制,有助于写出不卡顿、可预测的前端代码。

一、渲染进程与JS引擎的分工
现代浏览器采用多进程架构,每个标签页通常对应一个渲染进程。渲染进程内部包含负责布局绘制的渲染线程、负责网络请求的网络线程,以及我们最关心的主线程。JS引擎(如Chrome的V8)就运行在主线程上,这意味着同一时刻只能执行一段JS代码,其他任务必须排队。
除了JS引擎,渲染进程还维护着事件触发、定时器、网络回调等模块。它们产生的任务会被放入不同的队列,等待主线程空闲时调取。这也是为什么即使JS在等待接口返回,页面其他逻辑也不会完全冻结,只是主线程被某段同步代码占用时才会失去响应。
二、从源码到可执行代码的编译流程
浏览器拿到JS文本后,第一步是词法分析,将字符流拆成token,例如把let、a、=、1识别为不同记号。接着语法分析器依据语言规则构建抽象语法树(AST),若代码存在语法错误,解析阶段就会抛出并停止执行。
生成AST后,V8并不会立刻编译成机器码,而是先由Ignition解释器生成字节码。字节码跨平台且占用内存小。随后在代码运行中,TurboFan编译器会监控热点函数,将其编译为高度优化的机器码。若后续函数传入类型变化导致假设失效,已编译代码会被去优化回退到字节码,保证结果正确。
// 演示解析与执行顺序
console.log('开始');
function demo() {
// 函数声明会被提升
return 1 + 2;
}
console.log(demo());
// 输出:开始 3
// 说明函数声明提升,调用前已存在
三、调用栈与事件循环机制
JS是单线程语言,函数调用通过调用栈管理。每当调用一个函数,引擎就将其执行上下文压入栈顶;函数返回时出栈。如果栈内存在递归过深或同步死循环,调用栈无法清空,主线程被锁死,页面就无法处理用户点击或渲染更新。
事件循环是协调同步代码与异步任务的机制。主线程把当前调用栈清空后,会先处理微任务队列(如Promise回调、queueMicrotask),再取一个宏任务(如setTimeout、UI事件)执行,如此往复。每次宏任务之间,浏览器可能插入渲染步骤,所以长时间宏任务会推迟渲染。
| 任务类型 | 常见来源 | 执行时机 |
|---|---|---|
| 微任务 | Promise.then、MutationObserver | 当前宏任务结束后立即清空 |
| 宏任务 | setTimeout、setInterval、I/O | 每次循环取一个,之后可渲染 |
四、一段卡顿代码与拆解方案
下面代码在主线程同步循环百万次,期间页面无法滚动,定时器也被延后,因为调用栈一直被占用,事件循环没有机会处理其他队列。
// 错误示范:长任务阻塞
function heavy() {
let sum = 0;
for (let i = 0; i < 100000000; i++) {
sum += i;
}
return sum;
}
heavy();
console.log('完成');
// 在这段循环结束前,页面交互全部无响应
改进思路是把大任务切片,利用setTimeout或requestIdleCallback让出主线程,使事件循环可以处理渲染与输入。另一种方式是使用Web Worker,将计算移到独立线程,通过消息通信回传结果,彻底避免阻塞渲染进程主线程。
// 改进:分片执行
function chunk(start, end) {
let sum = 0;
for (let i = start; i < end; i++) {
sum += i;
}
return sum;
}
let part = 0;
function loop() {
chunk(part, part + 1000000);
part += 1000000;
if (part < 100000000) {
setTimeout(loop, 0); // 让出主线程
} else {
console.log('分片完成');
}
}
loop();
五、与渲染节奏的配合
浏览器通常以每秒六十帧为目标刷新页面。若JS执行时间加上样式计算、布局绘制超过十六毫秒,就会出现掉帧。因此关键动画应放在requestAnimationFrame回调里,它会在每次渲染前执行,与浏览器绘制节奏对齐,避免无谓的中间计算。
此外,使用<script>标签时,普通脚本会阻塞解析,可添加defer或async属性调整时机。defer保证顺序且在解析完成后运行,async不保证顺序且仅在下载完毕立即执行。合理编排脚本加载,也是控制JS执行不影响首屏的重要手段。
JavaScript引擎事件循环渲染进程修改时间:2026-08-01 13:54:12