頁面加載性能之禁用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 的方式插入腳本了,項目開發中咱們只要稍微留個心就好,一些遠古的第三方庫可能還會存在這樣的問題。

參考

相關文章
相關標籤/搜索