为什么我的 javascript 包下载这么慢?

Why is my javascript bundle so slow to download?

我已将我所有的 js 库捆绑到一个大文件中,以便节省一些 http 请求。

但由于某种原因,下载这个 1.2mb 的包需要 9.29 秒(有时是 +15)。

在这种情况下,捆绑包并未缩小,但即使缩小,783kb 也需要大约 4-7 秒,所以也好不了多少。

但最大的谜团是:如果我快速刷新页面 5-6 次,那么加载时间就会正常(~150 毫秒)。然后每次刷新都是正常的。但如果我等 5 分钟。并且不提出任何要求。然后加载时间又慢了。

而且当我 运行 我的应用程序在本地环境中时,它总是加载得很快。

现在我有两个问题要问大家:

1: 将我所有的库连接到一个文件中是错误的吗?

2:为什么在我的情况下下载 ~1mb 需要将近 10 秒?

请也看看图片,显示请求加载时间和我的 request-headers

1: Is it wrong to concatenate ALL my libraries into one single file?

回答:我们应该将所有的库连接到一个文件中。但这应该是一种适当的方式。就像在将所有文件合并为一个文件时不应该与任何变量冲突一样。你必须为此使用缩小工具 Like Gulp.. https://www.npmjs.com/package/gulp-concat

2: Why does it take almost 10 seconds to download ~1mb in my case?

回答: 可能无法正确缩小您的 js 文件。但它应该工作正常。请按照以下步骤操作:

HTTP 请求是浏览器要求查看您的页面的方式。当您的网页在浏览器中加载时,浏览器会向 Web 服务器发送 HTTP 请求以获取 URL 中的页面。然后,随着 HTML 的传送,浏览器对其进行解析并查找对图像、脚本、CSS、Flash 等的其他请求。

每次看到对新元素的请求时,它都会向服务器发送另一个 HTTP 请求。您的页面包含的图像、脚本、CSS、Flash 等越多,发出的请求就越多,您的页面加载速度就越慢。减少页面上 HTTP 请求数量的最简单方法是不使用很多(或任何)图像、脚本、CSS、Flash 等。但是只有文本的页面很无聊。

如何在不破坏您的设计的情况下减少 HTTP 请求 幸运的是,有多种方法可以减少 HTTP 请求的数量,同时保持高质量、丰富的 Web 设计。

合并文件 – 使用外部样式表和脚本对于防止它们拖慢您的页面加载时间很重要,但不要超过一个 CSS 和一个脚本文件。使用 CSS 精灵 – 当您将大部分或所有图像组合成一个精灵时,您将多个图像请求变成一个。然后你只需使用 background-image CSS 属性 来显示你需要的图像部分。图像映射——图像映射不像以前那样流行,但是当你有连续的图像时,它们可以将多个 HTTP 图像请求减少到一个。使用缓存改善内部页面加载时间通过使用 CSS 精灵和组合 CSS 和脚本文件,您还可以改善内部页面的加载时间。例如,如果您有一个 sprite 图像,其中包含内部页面的元素以及您的着陆页,那么当您的读者访问这些内部页面时,该图像已经下载并在缓存中。因此他们也不需要 HTTP 请求来将这些图像加载到您的内部页面上。

1: "Is it wrong to concatenate ALL my libraries into one single file?"

这里没有正确和错误的答案,这在很大程度上取决于捆绑包中的内容及其使用方式(所以我假设您通常关心加载速度)。一些想法:

总的来说,合并和缩小 JS 几乎总是一个好主意,CSS(图像应该合并到 sprites)。线路上的字节越少,传输速度越快,请求越少,开销越小,哦,成本也越低。但是...

  • 当您说 "ALL my libraries" 时,您是否包括可通过 public CDN 获得的库?如果是这样,我会考虑使用这些替代方案。最终用户很有可能已经从 large/trusted CDN(例如 google)获得了给定文件,如果他们没有,它将被快速下载,与您的网站加载同时进行,并且您无需支付任何费用. (如果您有安全意识,依赖第三方来托管所需的库可能是不可接受的 - 但它与可能节省大量加载时间的权衡 - 取决于您包含的库)。
  • 浏览器限制每个主机名的请求(因此这取决于您发出的请求总数),因此使用多个 subdomain/domain/cdn 以加载您的资产是有意义的(id 保持它到1 或 2 个额外的域)。
  • 根据您组合的库数量以及它们的使用频率,仅在需要它们的页面上加载特定库可能是有意义的(您甚至可以在不需要它们的页面上延迟加载它们,因此它们是已经缓存,但不要阻止渲染其他页面)。仅在单个页面上需要的脚本可以合理地移动到 HTML 或单独的脚本文件,这将提高平均页面加载速度。

2: "Why does it take almost 10 seconds to download ~1mb in my case?"

你自己说的 - "local environment ... always loads fast"。您的问题是数据必须传输的距离,而不是数据的打包程度。通过 localhost 旅行将几乎是瞬时的,但是您加载了很多脚本,通过互联网出去和回来增加了建立连接的延迟。您的传输速度受到浏览器和服务器之间最慢 "link in the chain" 的限制。

为了缩短您的计算机和服务器之间的距离,您应该考虑缓存您的文件并将它们托管在 CDN 后面。来自浏览器的请求被路由到位于请求者本地的 CDN 边缘位置服务器。如果 CDN 之前缓存了请求,它会立即 returned(而且距离更短,速度更快)。如果 CDN 边缘位置尚未缓存文件,它将代表您的最终用户(通过超快速专用网络)向您的服务器(称为 Origin)发出请求,如果 headers 允许为将来的请求缓存文件。

缓存会导致大问题,所以我的建议是使用 cache busting query string。这提供了 CDN 和浏览器级缓存的好处 - 即巨大的速度改进,但仍然允许您轻松更新代码并确保访问者将检索到最新版本。假设您有一个压缩文件 ~/minified.js,您可以将其引用为 ~/minified.js?v=1(name/value 并不重要)。将来您可以替换 ~/minified.js,并将您的标记更新为 ~/minified.js?v=2。这需要您的实际 HTML 未被缓存,或者至少使用短期缓存,但这意味着浏览器会将 v=1v=2 视为 2 个单独的请求,因此 download/cache他们。

其他一些想法:

  • 根据应用程序,将您的 javascript 分成 2 个包可能是可行的。 critical path 脚本,体积小,下载速度快,包含允许页面开始呈现的最低限度。然后是一个更大的延迟加载脚本,其中包含将在页面加载后期的某个时间点下载的所有其他内容。虽然这会增加传输文件的总开销,并且基本上是 side-stepping 问题,但它可以让您的页面更快地开始呈现,从而使它们 "appear" 更快。此外,您可以将关键路径代码嵌入 HTML - 在 return 中为初始有效负载添加几 KB,以便脚本在 HTML 被解析后立即可用。
  • CloudFlare 提供了一个免费的 CDN 包——它是基本的,但可能是合适的(包括免费的 SSL)。