我們常用的script標簽,有兩個和性能、js文件下載執行相關的屬性:defer和async
defer的含義【摘自https://developer.mozilla.org/En/HTML/Element/Script】
This Boolean attribute is set to indicate to a browser that the script is meant to be executed after the document has been parsed.
async的含義【摘自https://developer.mozilla.org/En/HTML/Element/Script】
Set this Boolean attribute to indicate that the browser should, if possible, execute the script asynchronously.
Defer
對於defer,我們可以先思考一個情況。一個頁面如果有N個外鏈的腳本,放在head中,那麼,當加載腳本時會阻塞頁面的渲染,也就是常說的空白。在簡單的開發環境中,我們可能只要將源代碼中的外鏈腳本位置換一下就ok了。可是面對越來越復雜的開發環境,前端同事如果要後台開發同事調整一下腳本的位置,可能會花費大量的溝通成本和開發成本。我在去年的一個項目中就遇到過此類情況,當然也很感謝當時的後台開發同事的配合,他們都辛辛苦苦的調整了腳本的位置,解決了空白的問題。
那麼可以讓這個成本降到最低嗎?那麼我們可以使用defer這個屬性。
如果一個script加了defer屬性,即使放在head裡面,它也會在html頁面解析完畢之後再去執行,也就是類似於把這個script放在了頁面底部。
關於defer有兩個demo:
without defer
with defer
簡單介紹一下這個demo,一共引用了3個js和1個css,為了能更好的展示defer的效果,第二個js-2.php是延遲了3秒返回的。1.js會在頁面中生成一個值為1的input框,2.php會生成值為2的input框,3.js會生成值為3的input框。一方面我們需要觀察頁面渲染的時間,另一方面我們也要看一下js是否順序執行了。
下圖是without_defer.html的效果,從瀑布圖可以看出,domready和onload的時間都在6s左右,因為需要等待2.php的返回才能渲染頁面。如果你訪問上面的例子,可以看出,頁面要等6s的時間才會呈現出來,6s之前都是空白。
那麼如果我們為每個js都加上defer屬性,請看下面兩張圖
第一張是在加載過程中截取的,可以看到一旦有了defer屬性,雖然有資源2.php需要等待,但是仍然會繼續渲染頁面,加載後續的js和css等資源文件。對比上面的情況,可以看到domready的時間明顯提前,如果你訪問demo地址,會發現頁面會照常渲染出來,只不過2.php裡面的內容會延遲執行。
從上面的對比可以看出,對於defer,我們可以認為是將外鏈的js放在了頁面底部。js的加載不會阻塞頁面的渲染和資源的加載。不過defer會按照原本的js的順序執行,所以如果前後有依賴關系的js可以放心使用。
Async
對於async,這個是html5中新增的屬性,它的作用是能夠異步的加載和執行腳本,不因為加載腳本而阻塞頁面的加載。一旦加載到就會立刻執行。那async和defer有什麼不同之處呢?我們還是先看async的兩個demo
without async
with async
demo的效果和上面描述的一樣。
下圖是without async的瀑布圖,和沒有defer的情況是一樣的。domready和load的時間都因為一個js的延遲而延遲了。
我們再看一下有async屬性的情況,和defer一樣,會等待的資源不會阻塞其余資源的加載,也不會影響頁面的加載。但是有一點需要注意下,在有async的情況下,js一旦下載好了就會執行,所以很有可能不是按照原本的順序來執行的。如果js前後有依賴性,用async,就很有可能出錯。
Difference
這篇文章中總結了defer和async的相同點和區別。
Both async and defer scripts begin to download immediately without pausing the parser and both support an optional onload handler to address the common need to perform initialization which depends on the script. The difference between async and defer centers around when the script is executed. Each async script executes at the first opportunity after it is finished downloading and before the window’s load event. This means it’s possible (and likely) that async scripts are not executed in the order in which they occur in the page. The defer scripts, on the other hand, are guaranteed to be executed in the order they occur in the page. That execution starts after parsing is completely finished, but before the document’s DOMContentLoaded event.
Wrapping it up
在上述的基礎上,我根據實際使用的情況總結了一下defer和async的特征。
相同點:
加載文件時不阻塞頁面渲染
對於inline的script無效
使用這兩個屬性的腳本中不能調用document.write方法
有腳本的onload的事件回調
區別點:
html的版本html4.0中定義了defer;html5.0中定義了async
浏覽器
Feature Chrome Firefox (Gecko) Internet Explorer Opera Safari
Basic support 1.0 1.0 (1.7 or earlier) (Supported) (Supported) (Supported)
async attribute (Supported) 3.6 (1.9.2) 10 – (Supported)
defer attribute (Supported) 3.5 (1.9.1) 4 – (Supported)
執行時刻
每一個async屬性的腳本都在它下載結束之後立刻執行,同時會在window的load事件之前執行。所以就有可能出現腳本執行順序被打亂的情況;每一個defer屬性的腳本都是在頁面解析完畢之後,按照原本的順序執行,同時會在document的DOMContentLoaded之前執行。
摘自【http://dev.w3.org/html5/spec/Overview.html#attr-script-async】
There are three possible modes that can be selected using these attributes. If the async attribute is present, then the script will be executed asynchronously, as soon as it is available. If the async attribute is not present but the defer attribute is present, then the script is executed when the page has finished parsing. If neither attribute is present, then the script is fetched and executed immediately, before the user agent continues parsing the page.
簡單的來說,使用這兩個屬性會有三種可能的情況
如果async為true,那麼腳本在下載完成後異步執行。
如果async為false,defer為true,那麼腳本會在頁面解析完畢之後執行。
如果async和defer都為false,那麼腳本會在頁面解析中,停止頁面解析,立刻下載並且執行,
最後給一點個人的建議,無論使用defer還是async屬性,都需要首先將頁面中的js文件進行整理,哪些文件之間有依賴性,哪些文件可以延遲加載等等,做好js代碼的合並和拆分,然後再根據頁面需要使用這兩個屬性。
本文another鏈接:http://feifeipan.sinaapp.com/?p=51
=====華麗麗的分割線=========
對於znxds提出的IE下的工作,我針對FF和IE6、IE7、IE8下面做了比較。
demo
Firefox中,inline的defer是沒有效果的;outer的defer會在頁面最底部執行。
IE8.0中,inline和outer的defer是起作用的,都會延遲到頁面底部,排在其他非defer的js後面執行
IE7.0的情況,和IE8.0一致。
IE6.0中,關於defer inline js,要區分是在head中還是在body中。在head中defer inline js會在遇到body之後優先執行,而在body中的defer inline js會在body結束之前執行;關於defer outer js, 依然是在頁面最後執行。
所以可以看出,defer的outer js在各種浏覽器中表現一致;defer的inline js在IE6中比較特殊,head和body中的順序不一樣,IE7和IE8會延遲到頁面底部執行,在Firefox中無效。