干预 document.write()

您最近是否在 Chrome 的 Developer Console 中看到了如下所示的警告,并想知道这是什么?

(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 稳定版用户中收集了 28 天的现场试验数据,仅限使用 2G 连接的用户。我们发现,2G 上所有网页加载的 7.6% 包含至少一个通过 document.write() 插入顶级文档中的跨网站、解析器阻止脚本。由于阻止了这些脚本的加载,我们发现这些加载得到了以下改进:

  • 达到首次内容渲染(向用户显示网页正在有效加载)的页面加载次数增加了 10%,达到完全解析状态的页面加载次数增加了 25%,重新加载次数减少了 10%,这表明用户的不满减少了。
  • 距离 First Contentful Paint 的平均时间缩短 21%(缩短了 1 秒以上)
  • 将解析网页所需的平均时间缩短了 38%,这相当于将解析时间缩短了近 6 秒,大大缩短了向用户显示重要内容所需的时间。

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

  1. 用户的网速较慢,特别是在使用 2G 网络时。(将来,此变化可能会在网络连接速度较慢(例如 3G 网络速度或 Wi-Fi 速度较慢)时影响其他用户。)
  2. document.write() 位于顶级文档中。这项干预不适用于 iframe 中的 document.Write 脚本,因为它们不会阻止主页面的呈现。
  3. document.write() 中的脚本阻止了解析器。具有“async”或“defer”属性的脚本仍会执行。
  4. 脚本未托管在同一网站上。换句话说,Chrome 不会干预具有匹配 eTLD+1 的脚本(例如,在 www.example.org 上插入 js.example.org 上托管的脚本)。
  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 的 Developer Console 中只显示了一条警告。 (测试版于 2016 年 7 月推出。我们预计将于 2016 年 9 月向所有用户推出稳定版。)

我们将从 Chrome 54 开始,暂时阻止向 2G 用户注入的脚本。Chrome 54 预计将于 2016 年 10 月中旬为所有用户提供稳定版本。如需了解更多更新,请查看 Chrome 状态条目

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

希望了解更多信息?

如需了解详情,请参阅下面列出的其他资源: