你最近是否在 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 時的載入時間通常是其他網頁的兩倍。
我們在 2G 連線的 1% Chrome 穩定版使用者中進行了 28 天的現場試驗,並收集了相關資料。我們發現,在 2G 上,有 7.6% 的網頁載入作業至少包含一個跨網站的剖析器封鎖指令碼,該指令碼是透過頂層文件中的 document.write()
插入。由於封鎖這些指令碼的載入作業,我們發現這些載入作業的改善情形如下:
- 網頁載入次數達到首次顯示內容 (向使用者提供網頁有效載入的視覺確認) 的次數增加 10%、網頁載入次數達到已完全剖析狀態的次數增加 25%,以及重新載入次數減少 10%,這表示使用者體驗有所改善。
- 減少 21% 的平均時間 (比原先快超過 1 秒),直到首次顯示內容
- 剖析網頁的平均時間減少 38%,代表改善了近六秒,大幅縮短顯示使用者重視內容所需的時間。
考量到這項資料,Chrome 從 55 版開始,在偵測到這個已知的錯誤模式時,會代表所有使用者介入,藉此變更 Chrome 中 document.write()
的處理方式 (請參閱 Chrome 狀態)。具體來說,Chrome 會在符合下列「所有」條件時,停止執行透過 document.write()
插入的 <script>
元素:
- 使用者連線速度較慢,尤其是使用 2G 網路時。(日後,這項變更可能會擴及其他連線速度較慢的使用者,例如 3G 或 WiFi 連線速度較慢的使用者)。
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
屬性新增至指令碼元素,或是使用 document.appendChild()
或 parentNode.insertBefore()
等 DOM API 新增指令碼元素。
如何偵測網站受到影響
系統會根據大量條件判斷是否要實施限制,因此您如何得知自己是否受到影響?
偵測使用者使用 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 狀態文章。
想瞭解詳情嗎?
如要進一步瞭解,請參閱下列其他資源: