把JavaScript文件放在文檔的頭部仍是尾部

更好閱讀體驗,請訪問dreamapple.mejavascript

咱們今天來聊一聊關於JavaScript文件的引入位置的問題;你們在平時的Web開發中有沒有想過這樣一個問題,那就是我應該在文檔的頭部(也就是<head>標籤內部裏面)引入所須要的JavaScript文件仍是應該在尾部(也就是</body>以前)引入所須要的JavaScript文件呢?今天咱們就來深刻的探究一下這個問題。html

首先咱們須要瞭解的一點就是,在瀏覽器渲染頁面以前,它須要經過解析HTML標記而後構建DOM樹。在這個過程當中,若是解析器遇到了一個腳本(script),它就會停下來,而且執行這個腳本,而後纔會繼續解析HTML。若是遇到了一個引用外部資源的腳本(script),它就必須停下來等待這個腳本資源的下載,而這個行爲會致使一個或者多個的網絡往返,而且會延遲頁面的首次渲染時間。html5

還有一點是須要咱們注意的,那就是外部引入的腳本(script)會阻塞瀏覽器的並行下載HTTP/1.1規範代表,瀏覽器在每一個主機下並行下載的組件不超過兩個(也就是說,瀏覽器一次只可以同時從同一個服務器加載兩個腳本);若是你網站的圖片是經過多個服務器提供的,那麼按道理來講,你的網站能夠一次並行下載多張圖片。可是,當咱們網站在加載腳本的時候;瀏覽器不會再啓動任何其它的下載,即便這些組件來自不一樣的服務器。java

看到這裏,也許不少開發者都會想:既然把腳本(script)資源放在head裏面是個很差的主意,而且可能會阻塞瀏覽器渲染頁面;那咱們是否是要把全部的JavaScript文件都放置到文檔的底部呢?這個固然也是太過極端了,由於仍是有一些狀況須要咱們在頭部引用腳本的;究竟是哪些狀況須要咱們這麼作呢,下面咱們來看看一些大公司的作法:web

圖片描述
咱們能夠看到,這個搜索頁面在頭部加載了5個JavaScript文件(箭頭標註的地方),其中兩個JavaScript文件是內聯的(inline),另外三個你們能夠看到script標籤上都添加了async屬性,那這個屬性是幹嗎的呢?咱們會在下面解釋。瀏覽器

圖片描述
咱們能夠看到,百度也在頭部引入了一些JavaScript文件,這些文件引入的方式與Google的作法差很少,都在引入外部資源的script標籤上添加了async屬性,除了第一個JavaScript文件沒有那樣作。服務器

圖片描述
圖片描述

最後一個是facebook的首頁,令我比較出乎意料的是,facebook的首頁的頭部引入了大量的腳本(script),你們能夠看一下截圖網絡

圖片描述
圖片描述
不過基本上facebook的script標籤上面都添加了async屬性,下面咱們先來來講一下script標籤上面這個async屬性的做用。app

這個屬性是HTML5給script新添加的屬性,並且只適用於外部的JavaScript文件,若是在script標籤上添加了這個屬性,那麼代表這個腳本資源就再也不是同步加載的了,而是異步加載的,因此不會阻塞瀏覽器對頁面的渲染。固然這個屬性會存在一些兼容性問題,一些瀏覽器還未實現對這個屬性的支持。框架

咱們能夠看到,雖然這些網站大部分的script標籤(針對引入的外部文件)都添加了async屬性,可是仍是有一些script標籤沒有添加async屬性,那就表示這些資源是同步加載執行的,在這裏你可能會問,那這些資源爲何不使用異步加載呢?緣由很大程度上是由於,這些腳本須要在瀏覽器渲染頁面以前就執行的;好比Yahoo在Best Practices for Speeding Up Your Web Site中就指出,若是你的腳本中使用了document.write在頁面中插入內容的話,那就不可以將這條腳本放置到文檔的底部了。相似的還有weibo,weibo的head中也使用了一個要在頁面渲染以前就執行的腳本,以下:

<script type="text/javascript">
    try {document.execCommand("BackgroundImageCache", false, true);
        } catch (e) {}
</script>

還有百度首頁的head中也有兩條須要在頁面渲染以前就執行的JavaScript文件:

<script data-compress="strip">
function h(obj){
    obj.style.behavior='url(#default#homepage)';
    var a = obj.setHomePage('//www.baidu.com/');
}
</script>
<script>window._ASYNC_START=new Date().getTime();</script>

還有一些好比Google和Baidu他們搜索頁面同步加載的那些JavaScrip文件一些是爲了在頁面渲染以前作一些全局的處理(好比Google)添加了全局變量google

圖片描述
還有的就是單純的知足本身業務上的一些需求了,好比百度同步加載的那個JavaScript文件:

圖片描述

因此說,除了上面這些狀況外,其它的狀況下咱們的腳本資源都須要放在文檔的底部;固然這裏還有一些須要咱們注意的問題,首先,腳本加載的順序很重要,好比若是你的腳本須要使用jQuery庫,那麼你就應該在加載你的腳本以前先加載jQuery庫。其次,有些腳本是須要等到某些元素加載完成以後才能夠執行的,那麼你能夠將你的腳本緊挨在那個元素的後面;還有一些元素是經過腳本動態建立的,因此它們也須要放在合適的位置。好比微博的:

圖片描述

若是使用過一些框架的腳手架你就會發現,這些框架打包後的那個index.html裏面引入的外部JavaScript資源都是放在文檔的底部的,而且它們也是按照順序來的,vendor.js文件(項目使用的框架,庫打包造成的文件)先引入,而後纔是app.js文件(咱們寫的代碼文件打包造成的),這就說明了引入腳本文件的順序也是很重要的。

到如今爲止,咱們已經討論了不少關於把JavaScript文件放在文檔的頭部仍是尾部的緣由,那麼下面咱們能夠總結出一些加載JavaScript文件的最佳實踐;

  • 對於必需要在DOM加載以前運行的JavaScript腳本,咱們須要把這些腳本放置在頁面的head中,而不是經過外部引用的方式,由於外部的引用增長了網絡的請求次數;而且咱們要確保內斂的這些JavaScript腳本是很小的,最好是壓縮過的,而且執行的速度很快,不會形成瀏覽器渲染的阻塞。

  • 對於支持使用script標籤的asyncdefer屬性的瀏覽器,咱們可使用這兩個屬性;其中須要注意的點就是,async表示的意思是異步加載JavaScript文件,它的下載過程能夠在HTML的解析過程當中進行,加載完成以後當即執行這個文件的代碼,執行文件代碼的過程當中會阻塞HTML的解析,它不保證文件加載的順序。defer表示的意思是在HTML文檔解析以後在執行加載完成的JavaScript文件,JavaScript文件的下載過程能夠在HTML的解析過程當中進行,它是按照script標籤的前後順序來加載文件的。更多詳細的解釋能夠參考async vs defer attributes

到這裏爲止,整篇文章就算是結束了,若是你還想進一步的瞭解關於JavaScript文件加載的一些知識,能夠看看這篇文章

參考的一些資料:

相關文章
相關標籤/搜索