一次排查頁面重複請求的經歷

前段時間重構一個頁面,頁面中存在經過第三方JavaScript代碼插入的動態廣告(正常的產品需求),上線後發現第三方的廣告資源存在重複請求的問題。因爲控制廣告插入的JavaScript代碼由第三方提供,咱們只負責按照他們要求的方式引入便可,因此對JavaScript代碼內容並不瞭解,在這種狀況下開始了艱難的排查過程。雖困難重重,但最終仍是找到了緣由,在此過程當中有些收穫,現將排查過程抽象以下:chrome

注:如下過程和截圖皆在Chrome瀏覽器中進行。瀏覽器

1、代碼

<div id="container">
    <iframe src="/iframe-1" frameborder="0"></iframe>
    <iframe src="/iframe-2" frameborder="0"></iframe>
    <iframe src="/iframe-3" frameborder="0"></iframe>
</div>

<script>
    document.getElementById('container').innerHTML += '<p>上面是iframe</p>';
</script>

代碼大意:頁面上先渲染3個iframe(目前頁面插入廣告仍然以iframe做爲主要實現形式),而後在最後一個iframe後面追加一個p元素服務器

2、現象

1.頁面:渲染正常

圖片描述

2.Network:存在重複的異常請求(Status是canceled)

圖片描述

3、排查過程

1.重複請求從何而來?

既然是解決重複請求的問題,那麼重複請求從何而來是咱們要解決的第一問題。
因爲請求是從第三方的JavaScript代碼中發出的,去讀第三方壓縮後的JavaScript代碼更像無頭蒼蠅。整個過程就像在圍城以外徘徊,心急如焚。後來靜下心來發現Chrome的devtools中一個很關鍵的排查助力神器:Network下的Initiator網絡

圖片描述

此列是什麼意思呢?通俗地說就是觸發請求的位置。
經過對比發現,同一個重複的請求發起的位置並不相同,以/iframe-1爲例:
點擊第一個請求的Initiator,跳轉的位置(標黃位置):
圖片描述app

點擊第二個請求的Initiator,跳轉的位置(標黃位置):
圖片描述工具

經過觀察能夠發現,第一個/iframe-1請求是因爲正常渲染iframe元素自動觸發的,第二個/iframe-1請求是在執行JavaScript代碼(做用拼接DOM節點)時觸發的,然而對於觸發第二個/iframe-1請求的那行JavaScript代碼,其真實意圖僅僅是拼接一個p元素而已,並不指望其餘額外的事情(好比觸發新的請求)發生。另外,對於/iframe-2和/iframe-3的第二次請求的觸發點都是那段拼接DOM節點的JavaScript代碼,至此,產生問題的罪魁禍首已經浮出水面,接下來咱們分析下產生重複請求的緣由。spa

2.爲何會重複請求?

產生重複請求的JavaScript代碼翻譯

document.getElementById('container').innerHTML += '<p>上面是iframe</p>';

翻譯成:code

document.getElementById('container').innerHTML = document.getElementById('container').innerHTML + '<p>上面是iframe</p>';

意思就明瞭多了:先獲取id爲container的div元素的全部內部HTML,將其拼接p元素後,再賦值給container的innerHTML。圖片

這個過程會致使iframe元素的從新渲染,也就會引起iframe對應的請求從新觸發。

因此,同一個請求會觸發兩次的緣由:頁面加載時渲染iframe元素會觸發第一次請求,執行JavaScript代碼致使iframe從新渲染觸發第二次請求。

找到了問題的緣由,解決問題的辦法也就水到渠成了,將

document.getElementById('container').innerHTML += '<p>上面是iframe</p>';

改成:

var div = document.createElement('div');
var text = document.createTextNode('廣告');
div.appendChild(text);

document.getElementById('container').appendChild(div);

問題解決了,不過,還有一個疑問:爲何渲染iframe產生的第一個請求的狀態是canceled?

3.爲何重複的請求的Status是canceled?

首先Status是canceled表明什麼意思呢?
從其字面意思理解,表明此請求被取消了,即此請求在發給服務器端以前就被瀏覽器取消了,也就是說此請求根本就沒有從瀏覽器發出去,更不可能到達服務器,因此狀態是canceled而不是HTTP狀態碼也就不難理解了。

那第一次的請求爲何會被瀏覽器取消呢?
用關鍵詞「chrome cancel request」谷歌了一下,在stack overflow上找到了一個比較全面的解答,截圖以下:
圖片描述

其中紅色標註即爲咱們要尋找的答案。

根據截圖大概梳理一下,Chrome瀏覽器會取消請求的幾種場景:

  • 觸發請求的DOM元素被刪除了(好比img元素尚未加載完就被刪除了)
  • 作了一些沒必要要的數據加載(好比開始加載iframe後改變其src或重寫其內容)
  • 大量的請求指向同一個服務器,而且前面請求的網絡問題代表後續的請求也走不通(DNS查詢錯誤,前面的請求報400)

至此,整個過程當中的疑問點一一解開了。

4、總結

如今再回顧此bug,產生的緣由並不高深,但整個排查過程確實值得總結。小結一下:

1.對於第三方庫報錯,切莫妄圖經過通讀並熟悉整個庫後解決問題,通讀代碼只會浪費解決問題的時間,弄明白調用關係纔是王道

2.Chrome開發者工具中的Network > Initiator表明請求是從哪裏觸發的,對於定位請求很是有用,尤爲是對於一些第三方庫中發出的請求

3.請求狀態爲canceled,表示請求被瀏覽器取消了,並無從瀏覽器發出去,更不可能進到服務器

4.Chrome瀏覽器取消請求的幾種情景,見上圖

5.element.innerHTML += HTMLStr 表示將原有的子節點和新的節點拼接後再從新賦值,會致使節點元素從新渲染,節點內容中含有iframe時慎用

相關文章
相關標籤/搜索