幹擾 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%,表示使用者不滿。
  • 首次顯示內容所需時間前,平均時間減少 21% (速度超過一秒)
  • 剖析網頁所需的時間縮短了 38%,表示剖析頁面所需的時間可提升近六秒,大幅縮短顯示與使用者重要內容所需的時間。

瞭解這些資料後,Chrome 自 55 版起,在偵測到這個已知的不良行為時,透過變更 Chrome 處理 document.write() 的方式,代表所有使用者介入措施 (請參閱「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 版開發人員控制台中顯示警告訊息。 (Beta 版已於 2016 年 7 月推出。我們預計在 2016 年 9 月向所有使用者提供穩定版)。

自 Chrome 54 版起,我們會暫時禁止 2G 使用者插入插入的指令碼。我們預計於 2016 年 10 月中旬為所有使用者提供穩定版。詳情請參閱 Chrome 狀態項目

隨著時間的推移,我們希望當使用者的網路連線速度較慢 (例如 3G 或 Wi-Fi 速度較慢) 時,我們希望能介入。按照以下 Chrome 狀態項目操作。

想要瞭解詳情嗎?

詳情請參閱下列其他資源: