2015年12月23日 星期三

[HTML] HTML5 針對外部來源的 script 上的 async 和 defer

ps:async 和 defere 需要 script 有 src 時才會有效


1. 正常的 script 宣告如下

<script src="wait1.js"><script>
在以前習慣把 <script> 都放在 html 結構的最後面
主要原因是 browser 在 html parsing 碰到 script 時會暫停 html parsing ,會先下載 script 和執行 script 後才會繼續往下做 html parsing。

所以想看到一個網頁呈現必須等 script 處理完,才能看到網頁,而大部份實務上的應用, script 的執行幾乎都是在 HTML ready 後,才需要執行。

2. async 

<script src="wait1.js" async><script>
async 在碰到 script 時,會開始下載 script 但不會暫停 HTML parsing ,直到 script 下載完,才會暫停 HTML parsing,把 script 執行完,才會繼續做 HTML parsing。

但如果引用多個外部的 script 都是使用 async 時,async 並不保證順序性,誰先下載完,誰就會先行執行。

3. defer

defer 碰到 script 時不會暫停 html parsing 並同時下載 script 直到 HTML parsing 結束後才執行 script。defer 跟 async 不同的是把 script 執行 delay 到 HTML parsing 結束後才處理。

不過這個在 w3c 的文件中並沒有說明,當有多個 defer 的外部 script 時,他的執行順序會是如何?是否有保證為宣告順序?因為有些 javascript 是有前後相依性的。例如 bootstrap.js 就必須先執行 jquery.js 才能執行 boostratp.js (有檢查有沒有 jquery),

Ref:
http://www.w3.org/TR/html5/scripting-1.html#attr-script-defer
https://html.spec.whatwg.org/multipage/scripting.html#attr-script-defer
http://www.growingwiththeweb.com/2014/02/async-vs-defer-attributes.html (這篇是有說 defer 有保證順序性,但在測試時 chrome 有,但firefox 並非如此)






舉個實例

瀏覽器
Chrome 47.0.2526.106
Firefox 43.0.2
(由於跟瀏覽器版本支援度有關係,所以放上版號)
wait1.js
alert(1);
wait2.js
alert(2);
wait3.js
alert(3);

實驗 1
<script src="wait1.js" ></script>
hello world 
chrome 和 firefox 都是先 alert(1) 才會出現 hello world

實驗 2
<script src="wait1.js" async></script>
hello world
chrome 和 firefox 都是先出現 hello world 才 alert(1)

實驗 3
<script src="wait1.js" defer></script>
hello world
chrome 和 firefox 都是先出現 hello world 才 alert(1)

實驗 4
<script src="wait1.js" async></script>
<script src="wait2.js" async></script>
<script src="wait3.js" async></script>
hello world
chrom 和 firefox 會先出現 hello world 但是 alert 就會有 1>2>3、2>1>3、3>2>1 的情形

實驗 5
<script src="wait1.js" defer></script>
<script src="wait2.js" defer></script>
<script src="wait3.js" defer></script>
hello world
chrom 會先出現 hello world > alert(1)>alert(2)>alert(3) (有保證順序)
firefox 會先出現 hello world 但是 alert 就會有 1>2>3、2>1>3、3>2>1 的情形 (沒有保證順序)

結論

  • 不管有無使用 async 和 defer ,建議還是把 script 放在最後面宣告,並按相依性先後順序宣告,即使沒有支援 async 和 defer 或支援度較差的瀏覽器也能正確執行。
  • async 和 defer 都沒有順序上的保證 (雖然 chrome 有,不過 spec 上沒有),所以適合使用在 js 之間沒有前後相依性的情況。
  • async 特性比較適合使用在 Javascript 純粹增加 module、class、function 的,不關聯其他 javascript 或是 HTML 使用。
  • defer 實務上對使用體驗是最佳的,但主要也是在順序性上並沒有保證(w3c 建立的 spec),所以使用上也要考慮一下。

延伸


剛好 IThome 現代化網站技術分享上老木大師分享到
http://mei.homin.com.tw/Keynote_the_secrets_of_web_design_performance.html#!p=61
原理
https://developer.mozilla.org/en-US/docs/Games/Techniques/Async_scripts

可以自己動手做 async,但效果跟 async 差不多就是了,不過可以讓不支援 async 的瀏覽器使用
(ps. 這只是 prototype 實務上還要多實驗)

asyncjs.js
(function() {
    var loader = document.getElementsByTagName("script")[0];
    var scripts = loader.getAttribute("data-src");
    scripts = scripts.split("&");
    scripts.forEach(function(element, index, array){
        var script = document.createElement("script");
        script.src = element;
        document.head.appendChild(script);
    })
})();
index.html
<script src="asyncjs.js" data-src="wait1.js&wait2.js&wait3.js"></script>
hello world
實驗
<script src="asyncjs.js" data-src="wait1.js&wait2.js&wait3.js"></script>
hello world
結果是  hello world 先,alert 就會有 1>2>3、2>1>3、3>2>1 的情形
<script src="asyncjs.js" data-src="wait1.js&wait2.js&wait3.js"></script>
<script> alert("blocking") </script>
hello world
blocking、1、2、3 這幾個順序就不一定了,最後才出現 hello world


Defer ?
還沒想到怎麼實作



Reference:

http://peter.sh/experiments/asynchronous-and-deferred-javascript-execution-explained/
http://www.w3.org/TR/html5/scripting-1.html#attr-script-defer
https://html.spec.whatwg.org/multipage/scripting.html#attr-script-defer
http://www.growingwiththeweb.com/2014/02/async-vs-defer-attributes.html

沒有留言:

張貼留言