跳到主要内容

资源加载✅

浏览器同一时间可以从一个域名下载多少资源,有什么例外吗?​

答案

参考 stackoverflow Max parallel HTTP connections in a browser?

浏览器HTTP/1.1 每域名连接数明
Chrome6
Firefox6
Safari6
Edge6 基于Chromium
IE118 已停止支持
提示

注意这里是针对 HTTP/1.1 协议的限制。HTTP/2 和 HTTP/3 通过多路复用技术可以在单个连接上并行处理多个请求,因此不受此限制。具体参考 Why just one TCP connection

HTTP/1.1 限制原因

  • 服务器负载保护 防止单个客户端占用过多服务器连接
  • 网络拥塞控制 避免过多并发连接导致网络堵塞
  • TCP 连接开销 每个连接都需要三次握手,资源消耗较大

重要例外情况

<!-- 1. 不同子域名可以突破限制 -->
<link rel="stylesheet" href="https://cdn1.example.com/style.css">
<link rel="stylesheet" href="https://cdn2.example.com/theme.css">
<script src="https://static.example.com/app.js"></script>

<!-- 2. HTTP/2 多路复用 -->
<!-- 同一连接可并行传输多个资源 -->
<link rel="preload" href="/critical.css" as="style">
<link rel="preload" href="/main.js" as="script">

<!-- 3. 内联资源不占用连接 -->
<style>
.critical { color: red; }
</style>
<script>
console.log('inline script');
</script>

实际开发优化策略

  • 域名分片 HTTP/1.1 时代通过多个子域名提高并发数
  • 资源合并 减少请求数量,如 CSS/JS 文件合并
  • HTTP/2 升级 利用多路复用特性,避免域名分片反优化
  • 资源优先级 使用 <link rel="preload"> 控制关键资源加载

延伸阅读

CSS 加载是否阻塞 DOM 解析、渲染和 JavaScript 执行?​

答案

详细解释

阻塞对象是否阻塞原因说明
DOM 解析不阻塞HTML 解析不依赖 CSS 属于流式解析
页面渲染阻塞渲染依赖 CSSOM 构建,会造成阻塞,注意如果资源加载失败浏览器任然会用兜底的样式进行渲染,这个现象叫做 内容样式短暂失效 (FOUC)
JavaScript 执行阻塞<script> 标签会等待 CSS 加载完成
<!DOCTYPE html>
<html>
  <head>
    <title>CSS 阻塞验证测试</title>
    <style>
      body {
        font-family: Arial;
      }
      .test-box {
        width: 200px;
        height: 100px;
        background: red;
        margin: 10px 0;
      }
      .log {
        background: #f0f0f0;
        padding: 10px;
        margin: 5px 0;
        border-left: 3px solid #333;
      }
    </style>

    <!-- 模拟慢加载的 CSS -->
    <link rel="stylesheet" href="https://httpbin.org/delay/3" />

    <script>
      // 记录脚本开始执行时间
      const scriptStartTime = performance.now();
      console.log("1. Script in <head> 开始执行:", scriptStartTime);
      const domElements = document.querySelectorAll("*");
      console.log("2. DOM 元素数量:", domElements.length);
      console.log("3. body 元素是否存在:", !!document.body);
    </script>
  </head>
  <body>
    <h1>CSS 阻塞测试</h1>
    <div class="test-box">测试盒子</div>

    <div class="log">
      <strong>观察现象:</strong>
      <ul>
        <li>页面内容会延迟显示(等待 CSS 加载)</li>
        <li>但 DOM 结构已经解析完成</li>
        <li>控制台会显示执行顺序</li>
      </ul>
    </div>

    <script>
      // 尝试获取样式(会等待 CSS 加载)
      if (document.body) {
        const bodyStyle = getComputedStyle(document.body);
        console.log("4. body 字体:", bodyStyle.fontFamily);
      }
      // 验证 DOM 解析不被阻塞
      console.log("5. Body 中的 script 执行时间:", performance.now());
      console.log(
        "6. 此时 DOM 已解析,元素数量:",
        document.querySelectorAll("*").length
      );

      // 监听页面加载完成
      window.addEventListener("load", () => {
        console.log("7. 页面完全加载完成(包括 CSS)");
      });

      // 监听 DOMContentLoaded
      document.addEventListener("DOMContentLoaded", () => {
        console.log("8. DOMContentLoaded 触发");
      });
    </script>

    <script>
      // 更详细的测试:CSS 加载状态检测
      function checkCSSLoaded() {
        const links = document.getElementsByTagName("link");
        for (let link of links) {
          if (link.rel === "stylesheet") {
            console.log("CSS 链接状态:", {
              href: link.href,
              loaded: link.sheet !== null,
            });
          }
        }
      }

      // 定期检查 CSS 加载状态
      const checkInterval = setInterval(() => {
        checkCSSLoaded();

        // 如果所有 CSS 都加载完成,停止检查
        const allLoaded = Array.from(document.getElementsByTagName("link"))
          .filter((link) => link.rel === "stylesheet")
          .every((link) => link.sheet !== null);

        if (allLoaded) {
          console.log("9. 所有 CSS 加载完成,脚本继续执行");
          clearInterval(checkInterval);
        }
      }, 100);
    </script>

  </body>
</html>

实际开发建议

  1. 关键 css 内联导入
  2. 非关键 CSS 异步加载 <link rel="preload" href="non-critical.css" as="style" onload="this.onload=null;this.rel='stylesheet'">

延伸阅读

@import 是否会阻塞主线程?​

答案

核心概念 @import 不会直接阻塞 JavaScript 主线程的执行,但它会严重阻塞浏览器的关键渲染路径(Critical Rendering Path),并显著破坏页面的加载性能:

  1. 打破并发加载,形成瀑布流(Waterfall):
    • 浏览器内部拥有高效的预加载扫描器(Preload Scanner),能并发快速拉取 HTML 中的多个 <link rel="stylesheet">。
    • 而 @import 嵌套在 CSS 内部,浏览器只有在下载完当前 CSS 并解析到 @import 规则时,才能发现并开始发起新文件的网络请求,导致 CSS 资源从“并发下载”退化为“串行下载”。
  2. 延迟 CSSOM 构建与首屏渲染:
    • CSS 属于渲染阻塞资源(Render-blocking)。在所有通过 @import 引入的外部样式表下载、解析完成且完整的 CSSOM 构建就绪前,浏览器无法生成渲染树(Render Tree),页面会持续处于白屏状态,恶化 FCP(First Contentful Paint)和 LCP。
  3. 可能引发样式闪烁(FOUC):
    • 若在老旧浏览器或特定异步条件下未严格阻断,页面可能会先按部分样式渲染,待 @import 资源返回后再重新重绘,导致视觉抖动与重排。

加载模型对比:

[ 并发模式: 多个 <link> ]
HTML 解析 ─────────►
├─► 下载 styleA.css (并发) ───┐
└─► 下载 styleB.css (并发) ───┴─► 构建 CSSOM ──► 绘制首屏

[ 串行模式: CSS 内部 @import ]
HTML 解析 ─────────►
└─► 下载 main.css ────► 解析到 @import ────► 下载 imported.css ────► 构建 CSSOM ──► 绘制首屏 (严重延迟)

工程最佳实践

  • 在生产环境中避免直接在 CSS 中书写运行时 @import。
  • 构建阶段通过 Sass、Less 或 PostCSS(postcss-import)将 @import 静态内联打包为单个样式文件。

延伸阅读

什么是文档的预解析​

答案

文档预解析用来解决关键资源串行加载导致的页面渲染延迟,通过预加载技术,实现对核心资源的并行加载,提升页面加载速度。

预加载包含如下类型

类型描述
dns-prefetch浏览器提前解析域名,减少后续请求的 DNS 查询延迟。通过 <link rel="dns-prefetch"> 标签实现。
preconnect浏览器提前建立与服务器的连接,以减少后续请求的延迟。通过 <link rel="preconnect"> 标签实现。
preload浏览器在解析 HTML 时,提前加载指定资源(如脚本、样式表、图片等),以便在需要时可以立即使用。通过 <link rel="preload"> 标签实现。
prefetch浏览器在空闲时加载可能会用到的资源,以便在需要时可以立即使用。通过 <link rel="prefetch"> 标签实现。

延伸阅读

你如何对网站的文件和资源进行优化?​

答案

网站文件和资源优化是前端性能优化的核心,主要从减少请求数量、优化文件大小、加速传输和提升缓存效率四个维度进行。

资源压缩与合并

优化类型具体措施工具示例
JavaScript代码压缩、Tree Shaking、代码分割Webpack、Rollup、Terser
CSS压缩、去重、关键CSS提取PurgeCSS、PostCSS、Critical
HTML压缩空格、注释删除HTMLMinifier
图片格式优化、尺寸压缩、懒加载WebP、AVIF、ImageOptim

HTTP 优化策略

<!-- 1. 资源预加载 -->
<link rel="dns-prefetch" href="//cdn.example.com">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preload" href="/critical.css" as="style">
<link rel="prefetch" href="/next-page.js">

<!-- 2. 关键资源优先加载 -->
<style>
/* 关键CSS内联 */
.header { background: #000; }
</style>
<link rel="stylesheet" href="/non-critical.css" media="print"
onload="this.media='all'">

<!-- 3. 脚本优化加载 -->
<script src="/critical.js"></script>
<script src="/non-critical.js" defer></script>
<script src="/analytics.js" async></script>

缓存策略配置

// Service Worker 缓存策略
self.addEventListener('fetch', event => {
if (event.request.destination === 'image') {
// 图片缓存优先策略
event.respondWith(
caches.match(event.request).then(response => {
return response || fetch(event.request)
})
)
}
})

// Webpack 文件名哈希
module.exports = {
output: {
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].chunk.js'
}
}

现代优化技术

  • HTTP/2 推送 主动推送关键资源,减少往返时间
  • 模块联邦 微前端架构下的资源共享
  • ESM 动态导入 按需加载模块,减少初始包体积
  • WebAssembly 计算密集型任务性能优化
// 动态导入示例
const loadChart = async () => {
const { Chart } = await import('./chart.js')
return new Chart()
}

// 图片懒加载
const imageObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target
img.src = img.dataset.src
imageObserver.unobserve(img)
}
})
})

实际开发建议

  • 资源监控 使用 Lighthouse、WebPageTest 定期检测性能
  • 渐进式优化 优先优化影响 FCP、LCP 的关键资源
  • CDN 部署 静态资源使用 CDN 加速全球访问
  • 构建优化 配置合理的 chunk splitting 策略
提示

优化顺序:关键资源内联 → 非关键资源延迟 → 长期缓存策略 → 监控调优

延伸阅读

什么是资源预热,有哪些手段?​

答案

核心概念 资源预热(Resource Warm-up / Pre-warming)是指在用户实际触发交互或页面渲染真正需要某个资源之前,提前进行网络连接建立、域名解析或资源预加载并缓存。其核心目标是将网络往返耗时(RTT)前置化,消灭关键路径上的加载白屏与等待:

  1. 连接层预热:
    • dns-prefetch:提前解析跨域资源服务器的 DNS(如 CDN 域名),消除 ~20-120ms 的 DNS 查询时间。
    • preconnect:比 DNS 预解析更进一步,不仅解析 DNS,还提前完成 TCP 三次握手和 TLS 证书协商,消除完整连接建立耗时。
  2. 资源层预热:
    • preload:声明当前页面即将使用的高优先级核心资源(如字体包、首屏核心 CSS/JS、LCP 大图),让浏览器跳过发现阶段立即以最高优先级并行拉取。
    • prefetch:声明用户后续极可能访问的页面或资源(如猜测下一个路由),在当前页面渲染完毕、浏览器空闲(idle)时以最低优先级抓取并存入 HTTP 缓存。
    • prerender / Speculation Rules API:提前在后台完整渲染下一个页面(包括执行 JS、计算布局),当用户点击跳转时实现 0 毫秒秒开。
  3. 协议与架构层预热:
    • 103 Early Hints:在后端服务器生成主 HTML 响应体期间,提前向客户端发送 HTTP 103 状态码和 Link 头,让浏览器在等待主 HTML 期间提前并发下载 CSS 和关键 JS。
    • Service Worker Cache:通过离线缓存策略预先将 App Shell、通用图标与 SDK 存入 CacheStorage。

典型代码配置:

<head>
<!-- 1. 连接层预热:提前与 API 服务器建立 TLS 连接 -->
<link rel="preconnect" href="https://api.example.com" crossorigin>
<link rel="dns-prefetch" href="https://cdn.example.com">

<!-- 2. 当前页关键资源预加载:提前加载首屏字体与 LCP 英雄图 -->
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/images/hero.webp" as="image">

<!-- 3. 下一页未来资源预获取:空闲时下载下个路由的分包 -->
<link rel="prefetch" href="/js/detail-page.chunk.js" as="script">

<!-- 4. 现代标准:投机规则 API (Speculation Rules) 进行预渲染 -->
<script type="speculationrules">
{
"prerender": [
{
"source": "list",
"urls": ["/next-article"]
}
]
}
</script>
</head>

面试官视角

  • 考查对浏览器网络链路和现代性能标准(如 Speculation Rules / Early Hints)的了解深度。
  • 关键区分点:能否清晰对比 preload(当前页必须,强制高优先级)与 prefetch(未来页可能用,空闲低优先级)的语义区别。

延伸阅读