JavaScript捆绑和性能:最佳实践

现在,在本世纪之交的时候,该批判性地重新评估最近认为正确的事情,并找出它是否在今天已经失去意义。 昨天的最佳做法有时会成为今天的反模式。



这篇文章的作者(我们今天将发表其翻译)将以使用React创建的简单Hello World应用程序为例,探讨捆绑JavaScript项目的三种方法。 他列举的一些示例包括阅读模块构建器的基础知识,例如Webpack ,它似乎是当今最受欢迎的工具。

方法一:绝对将所有东西都捆绑在一起(看起来就像一个大球)


主要思想:不要使用这种方法。

通过这种方法,模块构建器仅用于打包捆绑中的所有内容-依赖项和应用程序代码。 输出结果就像一个大毛线球。 在我的示例中,这包括reactreact-dom和应用程序本身的代码。 包含所有项目代码的单个捆绑包已连接到页面:

 <!-- index.html --> <script src="bundle.[hash].min.js"></script> 

在HTTP / 2发行之前,此模式可以某种方式被认为是可以接受的,因为它的使用减少了浏览器在加载页面资料时执行的HTTP请求的数量。 但是鉴于当今大多数站点都使用HTTP / 2,因此此模式已成为反模式。

怎么了 事实是,当使用HTTP / 2时,许多请求的执行不再像以前那样在系统上产生相同的负载。 结果,将代码打包到单个大包中不再给项目带来明显的优势。

使用这种方法,浏览器缓存的有效组织变得复杂。 例如,在一个简单的应用程序中仅更改一行代码将更改捆绑哈希,并使存储所有项目代码的缓存无效。 结果,所有回访者都必须再次下载整个站点代码,即使该代码与上次访问时下载的代码没有99%的差异。 在这里,由于相同数据从服务器到客户端的反复传输,我们正在处理网络资源的不合理使用。

如今, 超过95%的客户端都支持HTTP / 2。 在2019年, 大多数服务器都实现了该协议。 在此处了解有关在2019年使用HTTP / 2的更多信息。

方法2:将项目代码和第三方库的代码分开包装(代码分离)


主要思想:请使用这种方法。

让我们考虑上一节中讨论的内容,并通过将项目代码与依赖代码分开来改善浏览器缓存的情况。 这解决了上述情况下的问题,当我们稍微更改项目代码,然后将更新发布到生产环境中时。 现在,只有存储项目自己代码的index捆绑包的哈希值发生了变化,而vendor捆绑包的哈希值保持不变。 返回到更新站点的访问者将仅使用这种方法下载修改后的index文件,这将节省一些网络资源。

如果我们谈论Webpack,那么要实施此策略,您将需要有关代码分离的其他设置 。 在他们的帮助下,我们通知捆绑程序相关代码在哪里。 一种简单方便的检测此类代码的方法是,在对应文件的路径中存在node_modules ,因为所有依赖项代码都存储在此文件夹中。

脚本连接代码如下所示:

 <!-- index.html --> <script src="vendor.[hash].min.js"></script> <script src="index.[hash].min.js"></script> 

这是使用HTTP / 2时的瀑布时间线,用于加载页面。


瀑布页面加载时间表

这是朝正确方向迈出的一步。 但是我们可以继续优化捆绑。 如果考虑到所有这些,您可以理解某些项目依赖项的更改频率要低于其他项目。 也许reactreact-dom最不可能改变react并且当一个库被更新时,另一个库也被更新。 结果,我们可以得出结论,这两个库可以分组为一个逻辑片段,可以与其他依赖项分开,而其他依赖项的变化比reactreact-dom更频繁。 意识到这个想法,我们得到以下几点:

 <!-- index.html --> <script src="vendor.react.[hash].min.js"></script> <script src="vendor.others.[hash].min.js"></script> <script src="index.[hash].min.js"></script> 

如果我们继续发展这个想法,我们可以决定网站访问者可能不需要下载所有项目代码即可仅查看一页。 在冬天,我们中的一些人倾向于增加体重-因此包含应用程序代码的index文件会随着时间增长。 在某些时候,可能会证明项目代码有理由进一步分离, 动态加载各个组件,甚至组织各个模块的预加载

在我看来,这种级别的代码分离仍然令人生畏。 它仍然看起来像实验技术(之类的东西,其中微妙的错误很可能会显现出来)。 但是对我来说很明显,强大的代码分离是Web开发行业前进的方向。 也许由于浏览器对JavaScript模块的支持,我们最终将能够完全放弃Webpack之类的捆绑软件,而仅将单个代码模块提供给客户端。 看到所有这些将我们引向何方将很有趣!

方法3:将公共CDN用于某些依赖项的代码


主要思想:不要使用这种方法。

如果您是那些在Web开发中有点过时的人(像我一样),那么您可能会有一种内在的感觉,我们可以连接vendor.react文件,这在“方法编号No. 2“,使用公共CDN资源:

 <!-- index.html --> <script crossorigin src="https://unpkg.com/react@16.12.0/umd/react.production.min.js"></script> <script crossorigin src="https://unpkg.com/react-dom@16.12.0/umd/react-dom.production.min.js"></script> <script src="index.[hash].min.js"></script> 

我注意到使用这种方法时,有必要告诉 Webpack项目收集者,他应该从捆绑中排除reactreact-dom代码。

乍一看,所有这些看起来都很合理,但是这种方法存在一些我建议考虑的缺点。

▍减号1:使用相同依赖文件的不同站点? 不再...


老派的开发人员一直希望,如果所有站点都链接到相同的CDN资源并使用与我们站点相同的React版本,那么如果浏览器的缓存中已经存在React代码,则访问我们站点的访问者会希望,不会浪费时间重新加载它。 这将严重提高在工作模式下显示我们网站页面的速度。 是的,关于此主题的React 文档看起来很有希望。 因此,可以肯定的是,一些开发人员使用此模式。 对不对

尽管这在早期可以很好地实现,但是最近在浏览器中,为了提高安全性,他们开始实现缓存共享机制。 我们正在谈论的一个事实是,即使在理想条件下,当两个站点使用通过相同CDN链接加载的同一库时,也会为每个域独立下载代码,并且出于隐私原因,缓存最终落入沙箱中分配给特定域。 事实证明 ,该机制已经在Safari中实现(显然,自2013年以来就已经存在?!)。 如果我们谈论的是Chrome 77,那么现在要启用缓存分离,您需要使用一个特殊的标志

可以合理地假设,随着在更多浏览器中实现缓存共享,公共可用CDN的使用将减少。

▍减号2:浪费在辅助操作上的系统资源(每个域)


这里的想法是,使用CDN会增加系统的负载,因为即使在发送HTTP请求之前,浏览器也需要解决许多问题:DNS名称解析,TCP连接,SSL握手。 为了连接到该站点,浏览器在任何情况下都必须执行这些操作,但是如果它也被迫连接到CDN,则会增加其负担。

这是一个瀑布图,说明了使用公共CDN资源中的脚本加载页面的过程。


使用公共CDN资源加载页面的瀑布时间线

红色椭圆形突出显示执行查询之前发生操作的区域。 对于一个简单的Hello World应用程序来说,这似乎有点过多。

随着我的简单示例的发展和壮大,有时会想在其中使用自己的字体。 例如-取自Google字体。 这意味着此类延迟的数量只会增加,因为您必须连接到相应的域才能下载字体。 在这里,将站点的所有资源托管在其自己的主域(当然,该主域位于基于Cloudflare或Cloudfront的项目自己的CDN资源的后面)的想法似乎很有吸引力。

如果在我们的示例中,我们切换到从站点的主域下载两个React依赖项,这将导致以下事实:页面加载的瀑布式时间表将变得更加准确。


不使用公共CDN资源的页面的瀑布图加载计划

▍负号3:不同站点使用不同版本的依赖关系


我使用React 32个最大的站点进行了一些研究。 不幸的是,我发现其中只有10%的人使用公开可用的CDN资源来下载React。 然而,事实证明,考虑到所有研究站点都使用的React版本,这并不重要。 在理想情况下,浏览器缓存不会分离,并且所有站点都可以组织并使用来自相同公共CDN的相同脚本版本。 实际上,不同站点使用的React版本存在很大差异。 这破坏了共享浏览器缓存的想法。


不同站点使用的React版本

如果您首先使用React打开一个受欢迎的站点,然后再使用另一个站点,那么事实证明这两个站点使用相同版本的React的机会很小。

在研究过程中,我发现了有关这些React网站的更多有趣信息。 也许它们对您来说也很有趣:

  • 在2/3站点上,Webpack用于构建代码。
  • 87%的网站使用HTTP / 2,这比58%的平均值还高。
  • 大多数项目(约56%)都托管字体本身。

这是我的实验原始数据。

▍减号4:不履行问题


不幸的是,如今,那些使用公共可用CDN资源的用户不仅面临页面加载速度方面的问题,而且还面临其他一些麻烦:

  • 安全问题。 除了那些旨在解决浏览器中的缓存共享问题之外,使用公开可用的CDN资源还存在一些安全问题。 例如,如果黑客入侵了可公开访问的CDN资源,则他们可以非常小心地将恶意JavaScript代码注入库中。 这样的代码将具有在客户端上执行的站点代码的所有特权,它将能够处理已登录站点的用户的数据。
  • 隐私问题。 许多公司从第三方资源的请求中收集用户数据。 从理论上讲,如果在所有站点上都使用了公共CDN资源来下载依赖项代码或字体,则该CDN资源将能够跟踪用户会话及其在Internet上的工作特征。 类似的资源将能够对它们感兴趣的内容进行假设(例如,出于广告目的)。 而这一切-无需使用cookie!
  • 单点故障。 在多个域中分配站点正常运行所需的材料会增加机会,由于这些域之一的问题,客户端将只能下载使页面恢复正常工作所需的部分材料。 坦白地说,可以使用JavaScript解决此类问题,但是为此,网站开发人员将不得不付出额外的努力。

总结


很明显,未来取决于方法二。

使用HTTP / 2(例如Cloudflare或Cloudfront)将自己的CDN资源托管在服务器前面。 将代码分成小段,以便有效使用浏览器缓存。 将来,由于浏览器已开始实现对该技术的支持,因此将站点代码划分成的片段可能会变得更小,达到各个JavaScript模块的大小。

亲爱的读者们! 您是否在Web项目中使用代码分离技术?

Source: https://habr.com/ru/post/zh-CN483682/


All Articles