你最近是否在 Chrome 的「開發人員工具」中看到類似以下的警告,並想知道原因?
(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>
元素:
- 使用者的連線速度緩慢,尤其是使用 2G 網路時。(未來,此變更可能會擴及連線速度緩慢的其他使用者,例如 3G 速度緩慢或 Wi-Fi 速度較慢的使用者)。
document.write()
位於頂層文件中。這項介入措施不適用於 iframe 中的 document.written 指令碼,因為這些指令碼不會阻斷主頁面的轉譯作業。document.write()
中的指令碼會阻斷剖析器。含有「async
」或「defer
」屬性的指令碼仍會執行。- 該指令碼並未託管在同一網站上。換句話說,如果有相符的 eTLD+1,Chrome 就不會介入處理指令碼 (例如在 js.example.org 上代管,並插入至 www.example.org 的指令碼)。
- 指令碼尚未儲存在瀏覽器的 HTTP 快取中。快取中的指令碼不會產生網路延遲,而且會繼續執行。
- 頁面要求並非重新載入。如果使用者觸發重新載入程序並照常執行網頁,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 狀態項目。
想瞭解詳情嗎?
如要進一步瞭解,請參閱下列其他資源: