干预 document.write()

您最近是否在 Chrome 的开发者控制台中看到类似如下所示的警告,并且想知道它是什么?

(index):34 A Parser-blocking, cross-origin script,
https://paul.kinlan.me/ad-inject.js, is invoked via document.write().
This may be blocked by the browser if the device has poor network connectivity.

可组合性是 Web 的强大优势之一,让我们能够轻松与第三方构建的服务集成,从而构建出色的新产品!可组合性的缺点之一是意味着您需要共同承担改善用户体验的责任。如果集成欠佳,用户体验将受到负面影响。

导致性能不佳的一个已知原因是在页面内使用了 document.write(),尤其是注入脚本的用途。虽然看起来无关紧要,但可能会给用户带来真正的问题。

document.write('<script src="https://example.com/ad-inject.js"></script>');

浏览器必须先通过解析 HTML 标记来构建 DOM 树,然后才能呈现网页。 每当解析器遇到脚本时,都必须停止并执行该脚本,然后才能继续解析 HTML。如果脚本动态注入另一个脚本,解析器会被迫等待更长时间来下载资源,这可能会产生一次或多次网络往返,并延迟首次呈现网页的时间

对于网速较慢(例如使用 2G 网络)的用户,通过 document.write() 动态注入的外部脚本可能会将主页面内容的显示延迟几十秒,或导致页面加载失败或导致用户中途放弃。根据 Chrome 中的插桩,我们了解到,如果网页包含通过 document.write() 插入的第三方脚本,其加载速度通常比使用 2G 网络的其他网页的加载速度慢一倍。

我们通过对 1% 的 Chrome 稳定版用户(仅限使用 2G 连接的用户)开展为期 28 天的现场试验收集了数据。我们发现,在 2G 网络的所有网页加载中,有 7.6% 的网页至少包含一个通过顶级文档中的 document.write() 插入的跨网站、会拦截解析器的脚本。由于阻止这些脚本的加载,我们发现这些加载出现了以下改进:

  • 达到首次内容渲染(让用户知道页面正在有效加载)的网页加载增加了 10%,达到完全解析状态的网页加载增加了 25%,重新加载减少了 10%,这表明用户满意度有所降低。
  • 完成 First Contentful Paint 所用的平均时间缩短了 21%(加快了一秒以上)
  • 解析网页所用时间缩短了 38%,代表时间缩短了将近 6 秒,显著缩短了向用户显示重要信息所需的时间。

考虑到这些数据,从版本 55 开始,当我们检测到这种已知不良模式时,Chrome 会通过更改 document.write() 在 Chrome 中的处理方式,代表所有用户干预(请参阅 Chrome 状态)。 具体而言,当满足以下所有条件时,Chrome 不会执行通过 document.write() 注入的 <script> 元素:

  1. 用户的连接速度较慢,尤其是在使用 2G 网络时。(将来,这种变化可能会扩展到网速较慢(例如 3G 网络慢或 Wi-Fi 速度较慢)的其他用户。)
  2. document.write() 位于顶级文档中。这项干预不适用于 iframe 内的 document.writing 脚本,因为它们不会阻止主页面的呈现。
  3. document.write() 中的脚本阻止了解析器。具有“async”或“defer”属性的脚本仍会执行。
  4. 此脚本并非托管在同一网站上。也就是说,对于具有匹配的 eTLD+1 的脚本(例如,托管在 js.example.org 上的脚本并插入了 www.example.org),Chrome 不会进行干预。
  5. 该脚本不在浏览器的 HTTP 缓存中。缓存中的脚本不会引起网络延迟,并且仍会执行。
  6. 页面请求不是重新加载。如果用户触发了重新加载,Chrome 不会干预,而是照常执行网页。

第三方代码段有时会使用 document.write() 加载脚本。幸运的是,大多数第三方都提供异步加载替代方案,使第三方脚本能够加载,而不会阻止网页上其余内容显示。

如何解决此问题?

这个简单的答案是不使用 document.write() 注入脚本,我们维护着一组已知的异步加载器支持服务,建议您多加检查。

如果您的提供商不在列表中,并且支持异步脚本加载,请告知我们,我们可以更新此页面,以便为所有用户提供帮助。

如果您的提供商不支持将脚本异步加载到网页中,那么我们建议您与提供商联系,并告知我们以及他们将受到什么影响。

如果您的提供程序提供了包含 document.write() 的代码段,您或许可以向脚本元素中添加 async 属性,或者使用 DOM API(如 document.appendChild()parentNode.insertBefore())添加脚本元素。

如何检测网站何时受到影响

是否强制执行限制的标准有很多,那么,您如何知道自己是否受到影响呢?

检测用户何时使用 2G

如需了解这项变更的潜在影响,您首先需要了解有多少用户将使用 2G。您可以使用 Chrome 中提供的 Network Information API 检测用户当前的网络类型和速度,然后将预警发送到您的分析系统或真实用户指标 (RUM) 系统。

if(navigator.connection &&
    navigator.connection.type === 'cellular' &&
    navigator.connection.downlinkMax <= 0.115) {
    // Notify your service to indicate that you might be affected by this restriction.
}

在 Chrome 开发者工具中捕获警告

从 Chrome 53 开始,开发者工具会针对有问题的 document.write() 语句发出警告。具体而言,如果 document.write() 请求满足条件 2 到 5(Chrome 会在发送此警告时忽略连接条件),则警告将如下所示:

文档写入警告。

在 Chrome 开发者工具中看到警告固然很不错,但如何大规模地检测到这一问题呢?您可以检查在干预发生时发送到服务器的 HTTP 标头。

检查脚本资源上的 HTTP 标头

如果通过 document.write 插入的脚本被屏蔽,Chrome 会将以下标头发送到请求的资源:

Intervention: <https://shorturl/relevant/spec>;

如果发现通过 document.write 插入的脚本在不同情况下可能被屏蔽,Chrome 可能会发送以下内容:

Intervention: <https://shorturl/relevant/spec>; level="warning"

干预标头将作为脚本 GET 请求的一部分发送(如果是实际干预,则会异步发送)。

未来将会怎样?

最初的计划是在检测到满足条件时执行此干预。我们最初在 Chrome 53 的开发者控制台中只显示了一条警告。 (Beta 版发布于 2016 年 7 月。我们预计会在 2016 年 9 月向所有用户推出稳定版)。

从 Chrome 54 开始,我们将暂时禁止 2G 用户使用注入的脚本。预计 Chrome 54 将在 2016 年 10 月中旬面向所有用户推出稳定版。请查看 Chrome 状态条目了解更多动态。

随着时间的推移,我们希望在用户的连接速度较慢(即 3G 或 Wi-Fi 速度较慢)时进行干预。请按照此 Chrome 状态条目操作。

想要详细了解一下?

如需了解详情,请参阅下列额外资源: