成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

埋點統計優化,首屏加載速度提升

開發 前端
統計代碼會影響業務首屏加載嗎?同步引入方式,當然會,我的業務代碼還沒加載,首屏就加載一大段統計的jsdk,在移動端頁面打開要求比較高的苛刻條件下,首屏優化,你可以在埋點統計上做些優化,那么頁面加載會有一個很大的提升,本文是一篇筆者關于埋點優化的筆記,希望看完在項目中有所思考和幫助。

埋點統計在我們業務里經常有遇到,或者很普遍的,我們自己網站也會加入第三方統計,我們會看到動態加載方式去加載jsdk,也就是你常常看到的insertBefore操作,我們很少考慮到為什么這么做,直接同步加載不行嗎?

統計代碼會影響業務首屏加載嗎?同步引入方式,當然會,我的業務代碼還沒加載,首屏就加載一大段統計的jsdk,在移動端頁面打開要求比較高的苛刻條件下,首屏優化,你可以在埋點統計上做些優化,那么頁面加載會有一個很大的提升,本文是一篇筆者關于埋點優化的筆記,希望看完在項目中有所思考和幫助。

最近遇到一個問題,先看一段代碼。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>埋點</title>
<script>
window.formateJson = (data) => JSON.stringify(data, null, 2);
</script>
<script async defer>
(function (win, head, attr, script) {
console.log("---111---");
win[attr] = win[attr] || [];
const scriptDom = document.createElement(script);
scriptDom.async = true;
scriptDom.defer = true;
scriptDom.src = "./js/tj.js";
scriptDom.onload = function () {
win[attr].push({
id: "maic",
});
win[attr].push({
id: "Tom",
});
console.log("---2222---");
console.log(formateJson(win[attr]));
};
setTimeout(() => {
console.log("setTimeout---444---");
head.parentNode.insertBefore(scriptDom, head);
}, 1000);
})(window, document.getElementsByTagName("head")[0], "actd", "script");
</script>
<script async defer src="./js/app.js"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>

我們會發現,打印的順序結果是下面這樣的:

---111---
app.js:2 ---333--- start load app.js
app.js:4 [
{
"id": "pink"
}
]
(index):30 setTimeout---444---
(index):26 ---2222---
(index):27 [
{
"id": "pink"
},
{
"id": "maic"
},
{
"id": "Tom"
}
]

冥思苦想,我們發現最后actd的結果是:

[
{
"id": "pink"
},
{
"id": "maic"
},
{
"id": "Tom"
}
]

其實我本意想要的結果是先添加maic,Tom,最后添加pink,需求就是,必須先在這個ts.js執行后,預先添加基礎數據,然后在其他業務app.js添加其他數據,所以此時,無論如何都滿足不了我的需求。

試下想,為什么沒有按照我預期的要求走,問題就是出現在這個onload方法上。

onload事件

于是查詢資料尋得,onload事件是會等引入的外部資源加載完畢后才會觸發。

外部資源加載完畢是什么意思?

舉個栗子,我在引入的index2.html引入index2.js,然后在引入腳本上寫一個onload事件測試loadIndex2方法是否在我延時加載后進行調用的。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
function loadIndex2() {
console.log("script loader...");
}
</script>
<script src="./js/index2.js" onload="loadIndex2()"></script>
</body>
</html>

index2.js中寫入一段代碼:

var startTime = Date.now()
const count = 1000;
let wait = 10000;
/// 設置延時
const time = wait * count;
for (let i = 0; i < time; i++) { }

var endTime = Date.now()
console.log(startTime, endTime)
console.log(`延遲了:${Math.ceil((endTime - startTime) / 1000)}s后執行的`)

最后看下打印結果。

圖片

所以可以證實,onload是會等資源下載完了后,才會立即觸發。

所以我們回頭來看。

在瀏覽器的事件循環中,同步任務主線程肯定優先會先順序執行。

從打開印---111---,

然后到onload此時不會立即執行。

遇到定時器,定時器設置了1s后會執行,是個宏任務,會放入隊列中,此時不會立即執行。

然后接著會執行 <script async defer src="./js/app.js"></script>腳本。

所以此時,執行該腳本后,我們可以看到會先執行push方法。

所以我們看到pink就最先被推入數組中,當該腳本執行完畢后,此時會去執行定時器。

定時器里我們看到我們插入方式insertBefore,當插入時成功時,此時會調用onload方法,所以此時就會添加maic與Tom。

很明顯,我們此時的需求不滿足我們的要求,而且一個onload方法已經成了攔路虎。

那么我去掉onload試試,因為onload方法只會在腳本加載完畢后去執行,他只會等執行定時器后,成功插入腳本后才會真正執行,而此時其他腳本已經優先它的執行了。

那該怎么解決這個問題呢?

我把onload去掉試試,于是我改成了下面這樣:

<script async defer>
(function (win, head, attr, script) {
console.log("---111---");
win[attr] = win[attr] || [];
const scriptDom = document.createElement(script);
scriptDom.async = true;
scriptDom.defer = true;
scriptDom.src = "./js/tj.js";
win[attr].push({
id: "maic",
});
win[attr].push({
id: "Tom",
});
console.log("---2222---");
console.log(formateJson(win[attr]));
setTimeout(() => {
console.log("setTimeout---444---");
head.parentNode.insertBefore(scriptDom, head);
}, 1000);
})
(window, document.getElementsByTagName("head")
[0], "actd", "script");
</script>

去掉onload后,我確實達到了我想要的結果。

最后的結果是:

[
{
"id": "maic"
},
{
"id": "Tom"
},
{
"id": "pink"
}
]

但是你會發現:

圖片

我先保證了window.actd添加了我預定提前添加的基礎信息,但此時,這個腳本并沒有真正添加到dom中,我們執行完同步任務后,就會執行app.js,當1s后,我才真正執行了這個插入的腳本,而且我統計腳本你會發現此時是先執行了app.js再加載tj.js的。

當執行setTimeout時,我們會發現先執行了內部腳本,然后才執行打印。

<script async defer>
(function (win, head, attr, script) {
console.log("---111---");
win[attr] = win[attr] || [];
const scriptDom = document.createElement(script);
scriptDom.async = true;
scriptDom.defer = true;
scriptDom.src = "./js/tj.js";
win[attr].push({
id: "maic",
});
win[attr].push({
id: "Tom",
});
console.log("---2222---");
console.log(formateJson(win[attr]));
setTimeout(() => {
console.log("setTimeout---444444---");
window.actd.push({
id: "setTimeout",
});
head.parentNode.insertBefore(scriptDom, head);
console.log(formateJson(window.actd));
}, 1000);
})(window, document.getElementsByTagName("head")[0], "actd", "script");
</script>

最后的結果,可以看到是這樣的:

[
{
"id": "maic"
},
{
"id": "Tom"
},
{
"id": "pink"
},
{
"id": "setTimeout"
}
]

看到這里不知道你心里有沒有一個疑問,為什么在動態插入腳本時,我要用一個定時器1s鐘?為什么我需要用insertBefore這種方式插入腳本?,我同步方式引入不行嗎?不要定時器又會有什么樣的結果?

我們通常在接入第三方統計時,貌似都是一個這樣一個insertBefore插入的jsdk方式(但是一般我們都是同步方式引入jsdk)。

沒有使用定時器(3237ms)

<script async defer>
(function (win, head, attr, script) {
...
console.log("setTimeout---444444---");
window.actd.push({
id: "setTimeout",
});
head.parentNode.insertBefore(scriptDom, head);
console.log(formateJson(window.actd));
})(window, document.getElementsByTagName("head")[0], "actd", "script");
</script>

圖片

結果:

[
{
"id": "maic"
},
{
"id": "Tom"
},
{
"id": "setTimeout"
},
{
"id": "pink"
},
]

使用用定時器的(1622ms)

<script async defer>
(function (win, head, attr, script) {
...
setTimeout(() => {
console.log("setTimeout---444444---");
window.actd.push({
id: "setTimeout",
});
head.parentNode.insertBefore(scriptDom, head);
console.log(formateJson(window.actd));
}, 1000);
})(window, document.getElementsByTagName("head")[0], "actd", "script");
</script>

圖片

當我們用瀏覽器的Performance去比較兩組數據時,我們會發現總長時間,使用定時器的性能大概比沒有使用定時器的性能時間上大概要少50%,在summary中所有數據均有顯示的提升。

不經感嘆,就一個定時器這一點點的改動,對整個應用提升有這么大的提升,我領導說,快應用在線加載時,之前因為這個統計js的加載明顯阻塞了業務頁面打開速度,做了這個優化后,打開應用顯著提升不少。

我們再繼續上一個問題,為什么不同步加載?

我把代碼改造一下,去除了一些無關緊要的代碼:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>js執行的順序問題</title>
<script>
window.formateJson = (data) => JSON.stringify(data, null, 2);
</script>
<script async defer src="./js/tj.js"></script>
<script async defer>
(function (win, head, attr, script) {
win[attr] = win[attr] || [];
win[attr].push({
id: "maic",
});
win[attr].push({
id: "Tom",
});
console.log("---2222---");
console.log(formateJson(win[attr]));
})(window, document.getElementsByTagName("head")[0], "actd", "script");
</script>
<script async defer src="./js/app.js"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>

結果

[
{
"id": "maic"
},
{
"id": "Tom"
},
{
"id": "pink"
}
]

嘿,需求是達到了,因為我的業務app.js加的數據是最后一條,說明業務功能上是ok的,但是我們看下分析數據。

首先肯定是加載順序會發生變化,會先加載tj.js然后再加載業務app.js,你會發現同步加載這種方式有個弊端,假設tj.js很大,那么是會阻塞影響頁面首屏打開速度的,所以在之前采用異步,定時器方式,首屏加載會有顯著提升。

同步加載(1846ms)

圖片

圖片

我們發現tj.js與app.js相隔的時間很少,且我們從火焰圖中分析看到,Summary的數據是1846ms。

綜上比較,雖然同步加載依然比不上使用定時器的加載方式,使用定時器相比較同步加載,依然是領先11%左右。

異步標識async/defer

在上面的代碼中,我們多次看到async和defer標識,在之前文章中筆者有寫過一篇你真的了解esModule嗎,闡述一些關于script標簽中type="moudle", defer,async的幾個標識,今天再次回顧下。

其實從腳本優先級來看,同步的永遠優先最高,當一個script標簽沒有指定任何標識時,此時根據js引擎執行來說,誰放前面,誰就會優先執行,前面沒執行完,后面同步的script就不會執行。

注意到沒有,我在腳本上有加async與defer。

在上面栗子中,我們使用insertBefore方式,這就將該插入的js腳本的優先級降低了。

我們從上面火焰圖中可以分析得處結論,排名先后順序依次如下:

1、setTimeout+insertBefore

執行順序:app.js->tj.js

2、同步腳本加載

執行順序:tj.js->app.js

3、不使用定時器+insertBefore

執行順序:app.js->tj.js

當我們知道在1中,app.js優先于tj.js

因為insertBefore就是一種異步動態加載方式

舉個例子:

<script async defer>
// 執行
console.log(1)
// 2 insertBefore 這里再動態添加js
</script>
<script async defer>
// 執行
console.log(3)
</script>

執行關系就是1,3,2。

關于async與defer誰先執行時,defer的優先級比較低,會等異步標識的async下載完后立馬執行,然后再執行defer的腳本,具體可以參考以前寫的一篇文章你真的了解esModule嗎。

總結

  • 統計腳本,我們可以使用定時器+insertBefore方式可以大大提高首屏的加載速度,這也給我們了一些啟發,首屏加載,非業務代碼,比如埋點統計可以使用該方案做一點小優化加快首屏加載速度。
  • 如果使用insertBefore方式,非常不建議同步方式insertBefore,這種方式還不如同步加載統計腳本。
  • 在特殊場景下,我們需要加載統計腳本,有基礎信息的依賴后,我們也需要在業務代碼使用統計,我們不要在動態加載腳本的同時使用onload,在onload中嘗試添加基礎信息,實際上這種方式并不能滿足你的需求。
  • 一些關于async與defer的特性,記住,執行順序,同步任務會優先執行,async是異步,腳本下載完就執行,defer優先級比較低。
  • 本文示例code example[1]

[1]code example: https://github.com/maicFir/lessonNote/tree/master/javascript/21-js異步執行

責任編輯:武曉燕 來源: Web技術學苑
相關推薦

2023-12-17 14:49:20

前端首屏時間

2025-03-10 00:00:50

2021-07-01 12:10:31

性能優化React

2022-05-14 08:35:12

Webpack前端

2009-09-04 11:34:31

NetBeans優化

2023-11-25 20:16:22

前端

2021-01-08 09:40:40

優化VUE性能

2012-06-08 09:41:18

Web

2013-05-22 09:20:42

Chrome 27瀏覽器

2024-11-01 07:30:00

2017-12-05 13:41:02

SQL數據庫SQL查詢

2024-04-17 08:23:50

WebView技巧優化

2017-08-16 10:57:25

H5HTML開發

2017-12-28 14:54:04

Android代碼埋點全埋點

2024-11-28 10:04:14

2016-09-30 13:11:31

前端后端網頁速度

2010-12-24 08:57:44

Google加速工具page-speed

2024-02-23 08:18:32

首屏產品瀏覽器

2015-10-29 09:40:54

優化頁面加載速度

2023-12-13 18:46:50

FlutterAOP業務層
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 欧美日韩精品在线一区 | 日本电影韩国电影免费观看 | 黑人性hd | 日本激情视频中文字幕 | 成人高清视频在线观看 | 黄色欧美 | www国产精品 | 国产美女一区二区三区 | 久久久视 | 在线视频 欧美日韩 | 岛国av免费看 | 在线一区视频 | 98久久 | 91精品久久久久久久久 | www亚洲精品 | 国产精品成人一区二区 | 欧美日韩成人在线观看 | 成人在线观看黄 | 一区二区精品 | 日韩视频在线一区 | 久久精品视频一区二区三区 | 国产精品一区二区三区四区 | 天天天操操操 | 国产四区 | 天天操欧美 | 欧美片网站免费 | 日韩精品三区 | 亚洲福利一区 | 91精品国产91久久久久游泳池 | 亚洲成人av | 亚洲精品日韩在线 | k8久久久一区二区三区 | 伊人伊人伊人 | 亚洲在线 | 99在线免费观看视频 | 狠狠色狠狠色综合系列 | 国产专区在线 | 国产精品久久亚洲 | 99精品久久久 | 久久精品视频在线观看 | 欧美一卡二卡在线观看 |