更好閱讀體驗,請訪問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
標籤的async
和defer
屬性的瀏覽器,咱們可使用這兩個屬性;其中須要注意的點就是,async
表示的意思是異步加載JavaScript文件,它的下載過程能夠在HTML的解析過程當中進行,加載完成以後當即執行這個文件的代碼,執行文件代碼的過程當中會阻塞HTML的解析,它不保證文件加載的順序。defer
表示的意思是在HTML文檔解析以後在執行加載完成的JavaScript文件,JavaScript文件的下載過程能夠在HTML的解析過程當中進行,它是按照script
標籤的前後順序來加載文件的。更多詳細的解釋能夠參考async vs defer attributes
到這裏爲止,整篇文章就算是結束了,若是你還想進一步的瞭解關於JavaScript文件加載的一些知識,能夠看看這篇文章
參考的一些資料: