导读:本期聚焦于小伙伴创作的《浏览器如何执行JS代码?从解析到渲染的完整流程解析》,敬请观看详情。一段JS脚本从被请求到产生页面变化,中间经历了词法分析、语法树构建、字节码编译和主线程调度。很多人以为script标签里的代码是自上而下瞬间跑完的,其实浏览器会把它拆成微任务与宏任务,再配合渲染节奏执行。V8先把源码转成抽象语法树,再由解释器生成字节码,热点函数交给编译器优化。理解调用栈、消息队列和requestAnimationFrame的协作,才能解释为什么长任务会阻塞交互、定时器为何不准。本文用一段卡顿代码说明执行机制与拆解办法。

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

浏览器如何执行JS代码?从解析到渲染的完整流程解析

一、渲染进程与JS引擎的分工

现代浏览器采用多进程架构,每个标签页通常对应一个渲染进程。渲染进程内部包含负责布局绘制的渲染线程、负责网络请求的网络线程,以及我们最关心的主线程。JS引擎(如Chrome的V8)就运行在主线程上,这意味着同一时刻只能执行一段JS代码,其他任务必须排队。

除了JS引擎,渲染进程还维护着事件触发、定时器、网络回调等模块。它们产生的任务会被放入不同的队列,等待主线程空闲时调取。这也是为什么即使JS在等待接口返回,页面其他逻辑也不会完全冻结,只是主线程被某段同步代码占用时才会失去响应。

二、从源码到可执行代码的编译流程

浏览器拿到JS文本后,第一步是词法分析,将字符流拆成token,例如把leta=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

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。