js文件擺放的位置問題

在瀏覽器渲染頁面之前,它需要通過解析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中也使用了一個要在頁面渲染之前就執行的腳本,如下:

try {document.execCommand("BackgroundImageCache", false, true); } catch (e) {}

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

function h(obj){ obj.style.behavior='url(#default#homepage)'; var a = obj.setHomePage('//www.baidu.com/'); }

還有一些比如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標籤的先後順序來加載文件的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章