前段时间重构一个页面,页面中存在经过第三方JavaScript代码插入的动态广告(正常的产品需求),上线后发现第三方的广告资源存在重复请求的问题。因为控制广告插入的JavaScript代码由第三方提供,咱们只负责按照他们要求的方式引入便可,因此对JavaScript代码内容并不了解,在这种状况下开始了艰难的排查过程。虽困难重重,但最终仍是找到了缘由,在此过程当中有些收获,现将排查过程抽象以下:chrome
注:如下过程和截图皆在Chrome浏览器中进行。浏览器
<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元素服务器
既然是解决重复请求的问题,那么重复请求从何而来是咱们要解决的第一问题。
因为请求是从第三方的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
产生重复请求的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?
首先Status是canceled表明什么意思呢?
从其字面意思理解,表明此请求被取消了,即此请求在发给服务器端以前就被浏览器取消了,也就是说此请求根本就没有从浏览器发出去,更不可能到达服务器,因此状态是canceled而不是HTTP状态码也就不难理解了。
那第一次的请求为何会被浏览器取消呢?
用关键词“chrome cancel request”谷歌了一下,在stack overflow上找到了一个比较全面的解答,截图以下:
其中红色标注即为咱们要寻找的答案。
根据截图大概梳理一下,Chrome浏览器会取消请求的几种场景:
至此,整个过程当中的疑问点一一解开了。
如今再回顾此bug,产生的缘由并不高深,但整个排查过程确实值得总结。小结一下:
1.对于第三方库报错,切莫妄图经过通读并熟悉整个库后解决问题,通读代码只会浪费解决问题的时间,弄明白调用关系才是王道
2.Chrome开发者工具中的Network > Initiator表明请求是从哪里触发的,对于定位请求很是有用,尤为是对于一些第三方库中发出的请求
3.请求状态为canceled,表示请求被浏览器取消了,并无从浏览器发出去,更不可能进到服务器
4.Chrome浏览器取消请求的几种情景,见上图
5.element.innerHTML += HTMLStr 表示将原有的子节点和新的节点拼接后再从新赋值,会致使节点元素从新渲染,节点内容中含有iframe时慎用