JS在HTML文檔引入位置


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

首先我們需要了解的一點就是,在瀏覽器渲染頁面之前,它需要通過解析HTML標記然后構建DOM樹。在這個過程中,如果解析器遇到了一個腳本(script),它就會停下來,並且執行這個腳本,然后才會繼續解析HTML。如果遇到了一個引用外部資源的腳本(script),它就必須停下來等待這個腳本資源的下載,而這個行為會導致一個或者多個的網絡往返,並且會延遲頁面的首次渲染時間。

還有一點是需要我們注意的,那就是外部引入的腳本(script)會阻塞瀏覽器的並行下載,HTTP/1.1規范表明,瀏覽器在每個主機下並行下載的組件不超過兩個(也就是說,瀏覽器一次只能夠同時從同一個服務器加載兩個腳本);如果你網站的圖片是通過多個服務器提供的,那么按道理來說,你的網站可以一次並行下載多張圖片。但是,當我們網站在加載腳本的時候;瀏覽器不會再啟動任何其它的下載,即使這些組件來自不同的服務器。

看到這里,也許很多開發者都會想:既然把腳本(script)資源放在head里面是個不好的主意,並且可能會阻塞瀏覽器渲染頁面;那我們是不是要把所有的JavaScript文件都放置到文檔的底部呢?這個當然也是太過極端了,因為還是有一些情況需要我們在頭部引用腳本的;到底是哪些情況需要我們這么做呢,下面我們來看看一些大公司的做法:

我們可以看到,這個搜索頁面在頭部加載了5個JavaScript文件(箭頭標注的地方),其中兩個JavaScript文件是內聯的(inline),另外三個大家可以看到script標簽上都添加了async屬性,那這個屬性是干嘛的呢?我們會在下面解釋。

我們可以看到,百度也在頭部引入了一些JavaScript文件,這些文件引入的方式與Google的做法差不多,都在引入外部資源的script標簽上添加了async屬性,除了第一個JavaScript文件沒有那樣做。

最后一個是facebook的首頁,令我比較出乎意料的是,facebook的首頁的頭部引入了大量的腳本(script),大家可以看一下截圖

不過基本上facebook的script標簽上面都添加了async屬性,下面我們先來來說一下script標簽上面這個async屬性的作用。

這個屬性是HTML5給script新添加的屬性,而且只適用於外部的JavaScript文件,如果在script標簽上添加了這個屬性,那么表明這個腳本資源就不再是同步加載的了,而是異步加載的,所以不會阻塞瀏覽器對頁面的渲染。當然這個屬性會存在一些兼容性問題,一些瀏覽器還未實現對這個屬性的支持。

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

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

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

  1.  
    <script data-compress="strip">
  2.  
    function h(obj){
  3.  
    obj.style.behavior='url(#default#homepage)';
  4.  
    var a = obj.setHomePage('//www.baidu.com/');
  5.  
    }
  6.  
    </script>
  7.  
    <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文件加載的一些知識,可以看看這篇文章

插播一個提問 我就想知道,知乎為什么不添加使用Markdown編輯答案和寫文章的功能? - 知乎,提問好幾天,木有人回答,不知道大家怎么看待這個問題的?

參考的一些資料:

 

 

.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM