對於統計頁面數據這樣的情景(俗稱埋點),我們常用的方式就是動態創建<img>或<script>,至於原因,一般有以下幾點:
1.埋點一般不用關心請求的結果
2.可以實現跨域請求
3.無需使用ajax就能達到發請求的目的
4.都是原生實現,兼容性好
現就兩種方式做一下對比和總結:
一、用法
1.動態創建<img>
方式1:通過img標籤
function sendByImg(src) {
var img = document.createElement("img");
img.src = src;
}
方式2:通過Image對象
function sendByImage(src) {
var img = new Image();
img.src = src;
}
2.動態創建<script>
只有一種方式:通過<script>標籤
function sendByScript(src){
var script = document.createElement("script");
script.src = src;
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(script);
}
二、區別
區別1:如果要觸發請求,動態創建的<script>必須要插入到DOM中,而動態創建的<img>則不需要
演示代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Img VS Script</title>
</head>
<body>
<h3>
請觀察瀏覽器中的Network和Elements!
</h3>
<script>
function sendByScript(src){
var script = document.createElement("script");
script.src = src;
}
function sendByScriptInsertDOM(src){
var script = document.createElement("script");
script.src = src;
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(script);
}
function sendByImage(src) {
var img = new Image();
img.src = src;
}
function sendByImg(src) {
var img = document.createElement("img");
img.src = src;
}
function sendRequest(src) {
//一、通過script:
//不插入DOM,不會觸發請求
sendByScript(src + '/byScript');
//插到DOM中,會觸發請求
sendByScriptInsertDOM(src + '/byScriptDOM');
//二、通過img:
//不插入DOM,不會觸發請求
sendByImage(src+ '/byImage');
//不插入DOM,不會觸發請求
sendByImg(src+ '/byImg');
}
sendRequest('https://wwww.baidu.com')
</script>
</body>
</html>
用chrome瀏覽器打開此HTML文件,查看network:
可以看到,插入DOM中的<script>觸發來請求,而未插入DOM中則沒有發起請求;動態創建的<img>的兩種方式沒有插入DOM,都觸發請求。
那麼問題來了,爲什麼動態創建的<script>必須要插入DOM中才會觸發請求,而動態創建的<img>則不用?
關於這個問題,在網上查了很久也沒有找到特別強有力的解釋。有人說是因爲這兩種標籤本身的特性決定的:<img>作爲展示性標籤加載的是圖片資源,其對應的地址可以直接訪問就能得到資源;<script>往往加載的是JavaScript代碼,需要網頁這樣的執行環境。個人覺得此解釋有些牽強,但也沒有找到更權威的解釋。如果有更好的觀點,歡迎留言。
區別2: 動態創建的<script>可以對請求結果進行處理,而動態創建的<img>做不到
JSONP的實現原理就是藉助動態創建<script>標籤,並對返回結果進行處理。
至此,我當時在想,那麼藉助jQuery發送JSONP請求,豈不是每發一次就要在頁面上創建一個<script>標籤?觀察結果顯示並沒有。於是去看了下jQuery的源碼:
如圖所示,在<script>加載完成或出錯後將標籤移除。所以,我們在用動態創建<script>方式發送請求也可以參考這種方式。
三、選擇哪種方式?
單純從發送請求的角度看,理論上兩者沒有特別大的差異,但有一點一定要注意:動態創建<img>的方式在瀏覽器禁用圖片模式下不會觸發請求
。
例如,在chrome瀏覽器中設置禁圖模式(設置>高級設置>隱私設置和安全性>網站設置>圖片>顯示全部 去掉),結果:
只有動態創建<script>的方式有請求,動態創建<img>的方式沒有任何請求記錄。
綜上,從擴展性和兼容性上看,動態創建<script>的方式是首選
。
四、總結
對於發送埋點請求這種應用場景,我們有兩種簡單的處理方式:動態創建<script>和<img>兩種方式,兩者最大的差異是:動態創建的<script>必須插入到DOM中才能觸發請求,而動態創建<img>的方式則不需要。但動態創建<img>的方式有個致命缺陷:瀏覽器設置了禁止圖片顯示時,無法觸發請求。所以,對於封裝埋點庫的時候,動態創建<script>的方式是首選,而且可以參考jQuery,在請求記載完成後對<script>進行清除。