最近在使用 facebook api 撈資料發現他初始化的 code 挺有趣的,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script>
window.fbAsyncInit = function() {
FB.init({
appId : 'your-app-id',
xfbml : true,
version : 'v2.6'
});
};
(function(d, s, id){
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {return;}
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
</script>

這段 code 大致上就是找到你 html 中出現的第一個 script

然後用 insertbefore 在它的上面插入一句

1
<script src="//connect.facebook.net/en_US/sdk.js"></script>

這樣做的好處是,當他 insert 到 dom 裡面時他會執行這段 code

但同時就算這個 script 超大,

這段 script 也不會阻擋 html 的執行,

而是在背景下載,然後同時 html 繼續 render。

進入 HTML5 的時代

到了 html5 script 有兩個新的 attribute,

分別是 deferasync

google map api 中初始化的方式就是用這2個東西,

1
2
3
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap">
</script>

簡單來說:

async: 在網頁 render 的同時,下載完 script 並且在背景執行。

defer: 在網頁 render 的同時,下載完 script 但等 html 都 render 完才執行,

並且 script 的執行順序是照著 html 的排序。

而兩個屬性都加的情況是以 async 為主。

到了這邊大家可能會好奇剛剛的 insertBefore

asyncdefer 有什麼差呢?

實際測試

1
2
3
4
5
6
7
8
9
10
<script>
(function(d, s, id){
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {return;}
js = d.createElement(s); js.id = id;
js.src = "static/test.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'test-test'));
</script>
<script src="static/test2.js" async></script>
1
2
/// test.js
alert('11111');
1
2
/// test2.js
alert('22222');

結果是:

22222 先跑出來,然後緊接著 11111 才出現。

用檢視器打開看也發現 test.js 比 test2.js 更早下載好

結論

當瀏覽器不支援 html5 時,

insertBefore 的方法同步載入 script。

如果有支援 html5,

並且 script 與 script 之間沒有相依性,

那就使用 async

反之有順序之分就使用 defer

但重點來惹

為什麼要分不同的 script 就是希望將 code 模組化,

but 光是用defer並沒辦法有效改善 script 相依性的問題,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
script(src="hehehe.js" defer)
script(src="hash.js" defer)
script(src="fastClick.js" defer)
script(src="Scroll.js" defer)
script(src="panel.js" defer)
script(src="handlebar.js" defer)
script(src="db.js" defer)
script(src="deferred.js" defer)
script(src="util/sdk.js" defer)
script(src="util/login.js" defer)
script(src="util/base.js" defer)
script(src="util/city.js" defer)
script(src="util/date.js" defer)
script(src="util/cookie.js" defer)
script(src="app.js")

難以維護且容易搞混。

所以就出現了各種牛鬼蛇神開發的工具,

YUI loader

COMMONJS

AMD/CMD

各式各樣的 module loader 推陳出新,

直到現在整合了各個功能的 webpack 一統天下。

大家如果有興趣深入的了解 javascript 模組化的演變,

可以參考這篇 Javascript 模塊化七日談

受益良多RRR

參考資料