js加载对浏览器渲染的影响
				
									
					
					
						|  | 
							freeflydom 2025年7月7日 11:14
								本文热度 1594 | 
					
				 
				一、浏览器渲染基础:关键渲染路径解析
当浏览器加载网页时,遵循以下关键步骤:
- HTML解析 → 2. DOM树构建 → 3. CSSOM构建 → 4. 渲染树构建 → 5. 布局 → 6. 绘制
JavaScript在其中的作用:
graph LR
    A[HTML解析] --> B[遇到JS]
    B -->|同步JS| C[阻塞DOM构建]
    C --> D[执行JS]
    D --> E[继续DOM构建]
    B -->|CSS| F[阻塞渲染]
关键点:在DOM树构建过程中遇到JavaScript时:
- 如果是外部JS文件:浏览器必须等待JS下载并执行完成
- 如果是内联JS:浏览器立即执行代码
二、JavaScript加载的阻塞行为验证
2.1 实验:同步JS的阻塞效应
<!DOCTYPE html>
<html>
<head>
  <title>阻塞测试</title>
  <script>
    
    const start = Date.now();
    while (Date.now() - start < 3000) {}
  </script>
</head>
<body>
  
  <h1>3秒后你会看到我</h1>
</body>
</html>
实验结果:页面空白3秒后才显示内容,证明同步
2.2 外部JS文件的阻塞情况
<script src="heavy-script.js"></script>
问题核心:
- 网络时间:下载JS文件所需的时间
- 执行时间:JS解析和执行时间
三、解决方案:打破JS阻塞的四种策略
3.1 async属性:异步加载(适用于独立脚本)
<script src="analytics.js" async></script>
特性:
- 异步下载,不阻塞HTML解析
- 下载完成后立即执行,可能中断渲染
- 执行顺序无法保证
3.2 defer属性:延迟执行(推荐方案)
<script src="main.js" defer></script>
特性:
- 异步下载,不阻塞HTML解析
- 执行推迟到DOMContentLoaded事件之前
- 保持多个脚本的执行顺序
3.3 动态加载:灵活控制
function loadScript(src, callback) {
  const script = document.createElement('script');
  script.src = src;
  script.onload = callback;
  document.head.appendChild(script);
}
优势:完全控制加载时机,可实现按需加载
3.4 模块化加载(ES Modules)
<script type="module">
  import { init } from './app.js';
  init();
</script>
特性:
- 默认具有defer行为
- 支持模块依赖解析
- 现代浏览器原生支持
四、性能优化实战:对比实验数据
| 加载方式 | 渲染开始时间 | DOMContentLoaded | 完全加载时间 | FCP(ms) | TTI(ms) | 
|---|
| 同步加载 | 3.2s | 3.5s | 4.1s | 3200 | 4100 | 
| async | 0.8s | 2.2s | 3.0s | 800 | 3000 | 
| defer | 0.8s | 1.9s | 2.8s | 800 | 2800 | 
| 动态加载 | 0.8s | 1.4s | 2.5s | 800 | 2500 | 
测试环境:1MB JS文件 + 中等复杂度页面,模拟3G网络
五、避免阻塞的关键实践
5.1 最佳资源加载顺序
<head>
  
  <link rel="stylesheet" href="critical.css">
  
  
  <script src="analytics.js" async></script>
  
  
  <script src="main.js" defer></script>
</head>
5.2 优化JS执行时间
function processInChunks() {
  const chunkSize = 100;
  let index = 0;
  
  function processChunk() {
    const end = Math.min(index + chunkSize, data.length);
    
    for (; index < end; index++) {
      
    }
    
    if (index < data.length) {
      
      requestIdleCallback(processChunk);
    }
  }
  
  processChunk();
}
5.3 现代浏览器预加载扫描器优化
<link rel="preload" href="critical.js" as="script">
<link rel="preconnect" href="https://cdn.example.com">
六、特殊情况与边界处理
6.1 document.write的陷阱
document.write('<script src="dangerous.js"></script>');
风险:在DOMContentLoaded之后使用会清空页面
6.2 CSS对JS执行的潜在阻塞
graph TD
    JS[JavaScript执行] -->|需要CSSOM| CSS[CSS加载]
    CSS -->|未完成| Block[阻塞JS执行]
    Block -->|CSSOM就绪| Continue[继续执行JS]
七、性能监测工具实战
Chrome DevTools监测:
const observer = new PerformanceObserver(list => {
  for (const entry of list.getEntries()) {
    console.log('长任务:', entry);
  }
});
observer.observe({ entryTypes: ['longtask'] });
关键指标:
- FCP (First Contentful Paint):首次内容渲染
- TTI (Time to Interactive):可交互时间
- Long Tasks:超过50ms的任务
小结
- 基本原则: - 关键路径JS:使用<script defer>
- 非关键JS:使用<script async>或动态加载
 
- 性能优化进阶: - 
import('./module')
  .then(module => module.init())
  .catch(err => console.error('加载失败', err));
 
- 现代框架最佳实践: - React:React.lazy+Suspense
- Vue:异步组件
- Angular:路由懒加载
 
最终性能公式:
页面响应速度 = (关键资源大小/网络速度) + 最长任务时间
每次网络请求都是潜在的阻塞点,每毫秒执行时间都会影响用户体验。
转自https://juejin.cn/post/7523037165254934571
该文章在 2025/7/7 11:14:21 编辑过