页面加载性能之禁用document.write

若是你在控制台看到了如下信息,那么这篇文章对你而言应该会有帮助:javascript

(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 应该是入门级的DOM API,但实际项目开发中,却不多使用。若是面试官问你为何不去使用它,你会怎么回答?java

不少人可能会说这个性能低,那么为何低呢?先看个例子:web

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

在浏览器渲染页面以前,会去根据HTML标签解析DOM树。若是解析器遇到了 <script> 标签,则会中止DOM的解析,优先执行脚本。若是这个脚本动态插入了另外一个脚本,则解析器会等待另外一个脚本下载完成到执行完成,延后了页面渲染的时间。面试

对于网络环境差的,好比 2G 的用户,经过 document.write 写入的新的脚本,可能会致使页面渲染的时间延后数十秒,用户可能就所以放弃了继续等待。基于Chrome的数据报告显示,那些经过 document.write 插入第三方脚本的页面在2G网络下比通常页面慢2倍。chrome

Chrome从55版本开始就开始选择性的干预这类写法,当同时知足如下条件的时候,会禁止脚本执行:浏览器

  1. 用户处于低速网络下,好比2G(从此可能会把该条件扩展成3G以及低速wifi)
  2. document.write 位于最高层级的document上(若是位于iframe中,由于不会阻止主页面渲染,因此不会干预)
  3. document.write 中的脚本是解析阻塞型的(若是带了 async 或者 defer 属性,则不会干预)
  4. 脚本与页面不在同一个站点下,Chrome不会干预eTLD+1级别的脚本,如: js.example.com 和 www.example.com
  5. 脚本不在HTTP Cache中,若是在缓存中,依然会执行,由于不会发出HTTP请求
  6. 页面的请求不是重载。若是用户手动从新重载,Chrome仍是会执行脚本

通常第三方库会用这种写法来加载脚本,不过庆幸的是,大部分第三方库都支持async加载,这样就不会阻塞剩余内容的展示了。缓存

如何修复这类问题?

禁用 document.write 插入新的脚本便可,没有比这更好的方案,若是必须使用,则加上 async 属性。服务器

当你的站点受影响以后,如何检测?

有6条之多的规则须要检查,对你来讲可能太过复杂,有没有更好的方法去检测?网络

检测用户是否是2G网络

想要知道多少用户受影响,只须要看看多少用户是2G网络,能够使用 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.
}

捕获开发者工具中的提示

若是在使用 document.write 的时候,只知足了2-5的条件,你会看到下面这种提示:

在这里插入图片描述
在开发者工具中看到这个你能够马上发现问题,但怎么去检测这个影响范围有多广呢?你能够检测发往你服务器请求的HTTP Headers

你可能会尝试着模拟2G网络来强制干预,但其实不必,能够直接开启这项功能,使用 chrome://flags/#disallow-doc-written-script-loads

检查你的资源请求的HTTP headers

若是经过 document.write 的方式插入的脚本被阻止了,浏览器会携带这样的请求头去请求资源:

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

若是只是warning,会携带如下请求头:

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

这些请求头会携带在对资源的GET请求中(在实际干预的状况下异步请求)

总结

对于现代开发者而言是幸运的,由于大部分第三方库的编写者已经再也不使用 document.write 的方式插入脚本了,项目开发中咱们只要稍微留个心就好,一些远古的第三方库可能还会存在这样的问题。

参考

相关文章
相关标签/搜索