原文地址: alistapart.com/article/res… 原文作者:Jeremy Wagner 译者:龚亮 声明:本翻译仅做学习交流使用,转载请注明来源
针对网站上存在的 JavaScript 问题,你已经尝试了所有可能解决它的方法。比如, 尽可能地依赖 Web 平台 , 避免使用 Babel , 使用较小的框架替代方案 ,对应用程序代码做极致化精简。然而,速度还是不够快。当网站无法像设计师和开发人员所期望的那样发挥作用时,不可避免得让我们反思: “我们没有做到什么?” “我们写的代码能做什么?” “我们架构的哪些部分没有达到预期?”
相当一部分性能问题源于我们自己的代码,然而把责任全部归咎于我们自己会赤裸裸地忽略一个事实:相当大一部分性能问题是由外部因素导致的。
便利总是要付出代价的, 我们对 web 的集体偏好也破坏了它 ,特别是 JavaScript,它的使用方式表明了一种快速增长的趋势,即外包我们(第一方)不想做的任何事情。有时这是一个必要的决定。在许多情况下,它具有完美的财务和运营意义。
不要误会, 使用第三方 JavaScript 永远不会便宜 。这是一个魔鬼的交易,供应商使用能解决你问题的解决方案来诱惑你,却没有明确地提醒你,你几乎无法控制解决方案带来的副作用。如果第三方供应商给他们的产品新增了功能或者改变了基础架构,你将首当其冲受到这些变动的影响。那些使用你网站的人会感到沮丧,他们不会努力克服由于糟糕的用户体验而带来的烦恼。除非完全移除供应商提供的解决方案,否则你只能减轻而非完全消除使用它带来的影响。
在本篇文章中,我们所采用的技术方法要比 上一部分 中略有减少。我们将更多地谈论第三方供应商的人性方面,然后我们将介绍一些有关如何解决这类问题的技术途径。
当我们谈论当今 Web 糟糕的状态时,我们中的一些人很快指出了开发人员的便利性在导致此问题中的作用。虽然我同意开发人员的便利性会损害用户体验,但这只是将网站变得迟缓、混乱的便利性之一。
操作上的便利性可能会成为非常棘手的技术债务的先兆,当我们无法独自解决一个普遍存在的问题时,这些便利性就是我们所追求的。它们代表了在缺乏架构灵活性或足够的开发资源的情况下解决问题的第三方解决方案。
每当出现不便之处时,就应该讨论如何以一种全面的方式解决它。所以,让我们从更人性化的角度来谈谈如何处理这种情况。
第三方供应商介入的原因是痛苦。当一个组织的决策者对某个问题感到足够痛苦时,他们会做一件非常人性化的事情,那就是找到最快的方法让痛苦消失。
市场总是会找到解决这些痛点的方法,即使这种方法是不可持续的,甚至毫无用处。Web 可访问性覆盖(旨在自动修复可访问性问题的第三方脚本)是最严重的违法者。首先,你花钱去买一个解决不了任何问题的解决方案。然后,当该“解决方案”损害了你网站的可用性时,你需要付出的就远不止之前那么点了。这并不是抹黑某些第三方供应商提供的工具的有用性,而是说明第三方解决方案的采用是如何发生的,即使那些客观上很糟糕的工具也是如此。
因此,当一个供应商承诺解决我们面临的非常痛苦的问题时,很可能有人会采纳解决方案。如果这个人在公司的地位足够高,且在决策过程中没完全绕过他的话,他就会对其他人施加下行压力,迫使他们购买。相反,当那些处于压力之下的人缺乏足够的资源来创建必要的特性时,也会出现采用第三方解决方案的情况。
无论如何,把你的同事聚集在一起,共同制定一个解决和减轻你面临的问题的计划是值得的。
无论第三方解决方案多么不明智,一旦组织中的人员采用了它,你在强制更改过程中遇到的困难将取决于该解决方案的需求有多迫切。事实上,你不应该试图让解决方案的支持者相信他们的决定是错误的。这样的努力几乎总是适得其反,会让人们觉得受到了攻击,对你所说的话更加抵触。更糟糕的是,这些努力可能会导致人们完全不倾听对方的意见,从而产生激烈的争吵,从而滋生出更严重的问题。
如果有必要的话,我自己也经常这么做,在你的同事之间发牢骚表示同情,但把你的不满放在一边,拿出一个缓解计划,引导你的同事走向更好的结果。具体方法的具体细节将取决于第三方本身和组织的结构,但其实质可能类似于以下一系列问题。
选择第三方解决方案是有原因的,这个问题将帮助你确定采用该解决方案的理由是否合理。有些决策是在所有必要的人都不在的时候做出的。你可能处于这样一种境地:你必须对那个决策的后果做出反应。这个问题的答案会引导你自然而然地跟进。
该问题将帮助你确定解决方案的保质期。它是以临时解决方案的形式引入,在解决了问题后(例如在可访问性覆盖的情况下)将其移除吗?还是以长期解决方案的形式引入,例如A / B测试套件提供的数据?另一种可能性是解决方案永远无法彻底的移除,因为它起着至关重要的作用,就像分析脚本一样。 这就像在游泳池里扔床垫 :扔进去容易,但拖出来几乎是不可能的。
在任何情况下,如果你不询问便无法确定第三方脚本是否会继续存在。实际上,如果你发现解决方案是临时的,则可以制定计划,一旦解决了问题,便最终将其从网站中删除。
当第三方解决方案就位时,在出现问题时必须有人作为联络人。
我已经看到了第三方脚本失控时会发生的事情(太常见了)。例如,由于市场营销人员没有清除旧的标记或完成A / B测试导致标记管理器或A / B测试框架的 JavaScript 缓慢且隐式地增长。正是由于这些原因,你的网站上目前使用的第三方解决方案的责任需要由组织中的特定人员承担。在每种情况下,该责任人承担的责任都会有所不同,但可能包括:
在这种情况下,责任感绝不是同事所承担麻烦的、苛刻的义务,而应该是一种鼓励同事保持优秀思想道德素质的练习。因为如果没有责任感,第三方脚本对你网站的不良影响将被忽略,直到它变成房间里令人不快的食人魔,再也无法忽略。为第三方解决方案分配责任人可以帮助防止这种情况的发生。
如果你能够制定一个缓解计划,并让每个人都参与进来,那么确保负责任地使用第三方解决方案的工作就可以开始了。幸运的是,实际的技术工作将比试图与人争论更容易。所以,如果你已经做到了这一步,你所需要的就是时间和坚持。
这看起来是显而易见的,仅加载必要的内容。从我看到的未使用的第一方 JavaScript 的加载量(更不用说第三方 JavaScript)来看,这显然是一个问题。这就像试图通过将杂物塞入壁橱来打扫房间一样。无论是否实际需要它们,在每个页面上都加载第三方脚本的情况并不少见,因此请联系你的联络人以找出哪些页面需要哪些第三方脚本。
例如,我一个老客户使用一个流行的第三方工具在多个品牌网站上获得一个指定产品的零售商列表。它展示了清晰的价值,但该脚本只需要出现在网站的产品详细信息页面上。实际上,它常常被加载到每个页面。从不属于此脚本的页面中剔除此脚本可以显著提高非产品详细信息页面的性能。
弄清楚哪些页面需要哪些第三方脚本需要你做一些非技术性的工作。实际上,你必须从办公桌前站起来,与被指派负责你正在处理的第三方解决方案的人进行交谈。这对我来说是一项非常困难的工作,但当真诚的合作发生,并最终实现良好的结果时,这是值得的。
这个建议绝对不是秘密。我甚至在本系列的 前一篇文章 中提到过它,在这里我仍然建议:你应该尽可能多地 自行托管第三方资源 。这是否可行取决于所讨论的第三方脚本。
你是从 Google的托管库 , cdnjs 或其它类似的托管库中获取某个框架吗?现在就自我托管那个资源。
Casper找到了一种方法来自我托管他们的优化脚本 ,并显着减少了开始渲染时间。这确实使人们意识到,第三方资源的一个主要危害是,它们仅存在于其他服务器上,这是我们遇到的最糟糕的性能瓶颈之一。
如果你希望自托管分析解决方案或类似类型的脚本,则自托管它的难度更高。你可能会发现,有些第三方脚本根本无法自托管,但这并不意味着不值得花时间去解决。如果你发现自托管不是第三方脚本的选项,不要担心,你还可以尝试其他缓解方法。
如果你不能自托管第三方脚本,那么最好是预先连接到托管它们的服务器。WebPageTest 的连接视图非常出色地向你展示了网站从哪些服务器收集资源,以及与这些服务器建立连接所需的延迟。
预连接是让浏览器在发现它们之前就建立了到第三方服务器的连接,能够有效的减少延迟。解析HTML需要时间,而且解析器经常被样式表和其他脚本阻塞。如果你不能自托管第三方脚本,那么预连接就非常有意义。
正如 Andy Davies 所指出的, 资源预加载 一开始听起来很不可思议,直到你考虑到它可能会 适得其反 。如果你不熟悉预加载,那么它与预连接类似,它指示浏览器更快地获取特定的资源。
预加载的缺点是,虽然它可以确保尽快加载资源,但它会更改该资源的发现顺序。每当我们这样做时,我们都在暗指其他资源不那么重要,包括对渲染甚至核心功能至关重要的资源。
可以肯定的是,你的大多数第三方代码对网站功能的重要性不如你自己的代码。也就是说,如果必须预加载第三方资源,请确保仅对页面呈现起到至关重要的第三方脚本执行此操作。
如果你发现你的网站的初始呈现依赖于第三方脚本,请参考你的缓解计划,看看你可以做些什么来消除或改善你对它的依赖。依靠第三方来实现核心功能从来都不是一个好主意,因为你将大量控制权交给了那些可能并不关心你的最佳利益的人。
最好的请求就是没有请求 。如果你有一个不需要立即加载的第三方脚本,请考虑使用 Intersection Observer 来延迟加载它。这是滚动到 viewport 中时 延迟加载Facebook Like按钮 的示例:
let loadedFbScript = false; const intersectionListener = new IntersectionObserver(entries => { entries.forEach(entry => { if ((entry.isIntersecting || entry.intersectionRatio) && !loadedFbScript) { const scriptEl = document.createElement("script"); scriptEl.defer = true; scriptEl.crossOrigin = "anonymous"; scriptEl.src = "https://connect.facebook.net/en_US/sdk.js#xfbml=1&version=v3.0"; scriptEl.onload = () => { loadedFbScript = true; }; document.body.append(scriptEl); } }); }); intersectionListener.observe(document.querySelector(".fb-like")); 复制代码
在上面的代码片段中,我们首先设置一个变量来跟踪是否加载了Facebook SDK JavaScript。然后,创建一个 IntersectionListener
来检查观察到的元素是否在 viewport 中,以及 Facebook SDK 是否已经加载。如果 SDK JavaScript 没有被加载,对它的引用将被注入到DOM中,这将引发对它的请求。
你不能延迟加载每个第三方脚本。其中一些需要在页面加载时执行。无论如何,请做一些检查工作,看看是否可以延迟加载一部分第三方JavaScript。
当我建议延迟加载第三方脚本时,我从同事那里听到的一个常见问题是,它会延迟第三方提供的任何交互。这是一个合理的问题,因为当你延迟加载任何内容时,资源加载可能会出现明显的延迟。在某种程度上,你可以通过 资源预取 来解决这个问题。这与我们前面讨论过的预加载不同。预取会消耗相当数量的数据,但是预取资源的优先级较低,并且不太可能与关键资源争夺带宽。
密切关注第三方 JavaScript 需要一种近乎高度警惕的责任感。当你意识到技术债务的糟糕表现时,你会很自然地陷入这样一种状态:你会像对待其他技术债务一样对待它。
依靠第三方来进行重构是一项需要你定期执行的工作,比如清理标记管理器和A/B测试、整合第三方解决方案、移除任何不再需要的解决方案,以及应用上面讨论的编码技术。此外,你需要与你的团队合作,以循环的方式解决这个技术债务。这种工作不能被自动化,需要你与实际的人进行面对面、同步的对话。
如果你已经习惯于周期性地执行“cleanup sprints”制度,那么这就是你处理与性能相关的技术债务的时间和空间,不管它涉及到第三方代码还是第一方代码。特性开发是有时间的,但这段时间不应该包含你的全部工作时间。只专注于功能开发的开发团队注定会被技术债务完全消耗掉,这是不可避免的。
因此,在本系列的第四部分(也是最后一部分)中,我们将讨论在流程的上下文中负责地使用JavaScript意味着什么。在这里,我们将探讨如何将您的组织联合起来,使您的网站更快、更容易访问,从而使每个人、任何地方都更容易使用。
如果你觉得这篇内容对你有价值,请点赞,并关注我们的 官网 和我们的微信公众号(WecTeam),每周都有优质文章推送:
img10.360buyimg.com/wq/jfs/t1/4…