幹擾 document.write()

您最近曾在 Chrome 的 Play 管理中心看到如下的警告,並想知道這是什麼嗎?

(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.

可組合項是網路的一大優勢,能輕鬆與第三方打造的服務整合,以打造優質的新產品!可組合項的其中一個缺點是,對使用者體驗來說須承擔共同的責任。如果整合成果不理想,使用者體驗就會受到負面影響。

效能不佳的其中一個已知原因就是在網頁中使用 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%,表示使用者不滿 10% 的重新載入會降低。
  • 平均時間縮短 21% (速度超過 1 秒),直到首次顯示內容繪製為止
  • 將剖析頁面的平均時間縮短 38%,代表剖析網頁所花的時間已縮短將近六秒,大幅縮短了顯示對使用者所重要內容所需的時間。

瞭解這些資料後,Chrome 自 55 版起,會變更 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 版中,開始在 Play 管理中心顯示一則警告。 (Beta 版已於 2016 年 7 月推出,我們預計在 2016 年 9 月為所有使用者推出穩定版)。

自 Chrome 54 版起,我們將介入封鎖 2G 使用者註入的指令碼,我們預計在 2016 年 10 月中旬為所有使用者提供穩定版本。如需更多最新消息,請參閱 Chrome 狀態項目

我們會持續努力,設法在連線速度較慢 (例如 3G 或 Wi-Fi 速度緩慢或 Wi-Fi ) 時,提供協助。追蹤這個 Chrome 狀態項目

想進一步瞭解?

如要進一步瞭解,請參閱下列其他資源: