標籤:

為什麼把 Script 標籤放在 body 結束標籤之後 html 結束標籤之前?

新浪微博、谷歌等,為什麼把Script標籤放在body結束標籤之後html結束標籤之前?

而不是之前推薦的body結束標籤之前?

問題關鍵:放在body結束標籤之前與之後有什麼差別?


Google並沒有把&插入在&之後,而只是沒有寫&和&閉合標籤。 【這樣做是符合標準的。不僅是html5標準,從第一個HTML正式標準HTML 2.0開始,這樣做都是允許的。相反,在&之後插入其他元素,從HTML 2.0起就是不合標準的。】

新浪微博確實有在&之後輸出&

新浪微博所用的doctype是XHTML 1.0,但是其response的content type頭並沒有用XHTML mimetype,所以瀏覽器仍然會按HTML語法進行parsing。【順便說一句,雖然新浪微博用了XHTML 1.0 DTD,但是其實際內容連well-formed也沒有做到,是典型的掛羊頭賣狗肉的XHTML。新浪的前端同學們應該檢討一下啊!當然了,騰訊微博也是用XHTML 1.0的DTD,並且也不是well-formed的,跟新浪微博是難兄難弟啊!如果說一定要較個高下,那企鵝還是略勝一籌——其網頁出現第一個解析錯誤的行數更靠後一點。】

按照HTML5標準中的HTML語法規則,如果在&後再出現&或任何元素的開始標籤,都是parse error,瀏覽器會忽略之前的&,即視作仍舊在body內。所以實際效果和寫在&之前是沒有區別的。

總之,這種寫法雖然也能work,但是並沒有帶來任何額外好處,實際上出現這樣的寫法很可能是誤解了「將script放在頁面最末端」的教條。所以還是不要這樣寫為好。

【補充】

有同志說因為新浪微博使用bigpipe,所以「從直覺觸發,為了避免瀏覽器出現未知異常,應該在首次吐出的頁面框架中閉合body」。但是這個邏輯上說不通。因為在body以外寫script也可能存在其他異常嘛。有什麼理由能讓開發者推斷出後者會更安全呢?實際上在沒有充分測試的前提下,如果要進行推斷,那麼可以推斷出後者的風險更大。

第一,這是不合標準的行為,而且從有HTML標準以來都是不合標準的,因此瀏覽器實現不一致或者在這種情況下有bug的風險顯然更大。

第二,雖然將&寫在&之後,但最終的DOM樹里,&元素還是會成為body的子節點,這一點很容易在firebug等調試器里驗證。既然如此,如果將&寫在&之前會有問題,你又如何保證寫在之後(並在DOM里又變成了和寫在之前一樣的結構)就沒有問題?


這裡是瀏覽器載入一個有 & 標籤的網站發生的事情:

  1. 拉取 HTML 頁面 (e.g. index.html)
  2. 開始解析 HTML
  3. 解析到 & 標籤之後準備獲取 script 文件.
  4. 瀏覽器獲取script文件。同時,html 解析中斷並且阻斷頁面上其他html的解析。
  5. 一段時間後,script下載完成並且執行
  6. 繼續解析HTML文檔的其他部分(解析script之後的html代碼)

第4步導致了不好的用戶體驗,直到script文件全部下載完成之前HTML都不能得到解析。

為什麼會發生阻斷事件?

任何script代碼都能改變HTML的結構,通過document.write() 這種方式或者其他方式。 這就導致了HTML解析必須等待script全部被下載和執行完,HTML才能解析script標籤之後餘下的部分。

然而,大部分的Javascript開發者在載入文檔過程中,不會通過script操作HTML的DOM結構。然而,他們必須等到script全部載入結束,才能看到頁面。舉個例子:

&
&
&
&My Page&
&

跟 async不同, defer scripts在整個文檔里的script都被下載完才順序執行

根據 http://caniuse.com/#feat=script-defer, 90% 的瀏覽器支持這個屬性. 92% 至少部分支持此屬性。

注意:在 IE &<= 9 瀏覽器應用defer屬性可能會導致script不會順序執行。如果你想讓低版本IE支持此屬性,請看 this

結論

應用 async or defer這兩個屬性,擁抱未來。

原答案來自萬能的: stackoverflow


從實際效果來看,應該是沒有區別的。但是放在 html 結束標籤之前應該是不規範的(通不過 HTML 驗證),放在 body 結束之前才對。當然你完全可以選擇無視驗證。具體選擇這麼做的原因,還真不清楚。


IE 8-(8 不確定)有一個 bug,如果 & 在 & 內並且執行了一些 DOM 增減/修改操作(似乎限定於和 & 有父子關係或結構影響的元素),有可能會引起 IE core error(不是 JS error),某些情況會引起 crash。

這個問題我是在用 yepnope.js 1.0 版本還有一些早期 JS loader 時發現的,它的工作方式是往 & 插入 &

不確定這是不是真實理由。


基於規則 %html.content "HEAD|BODY" HTML 標籤的子元素只能是 HEAD BODY。

但是瀏覽器對HTML(XHTML)均有容錯機制。 錯誤嵌套的標籤、以及位置放置錯誤的標籤都會在paser HTML 過程中嘗試修復。修復後得到合法的HTML後在由布局引擎建立相應的DOM對象。

在&標籤放置於&標籤之後時,源碼被所有瀏覽器【泛指PC上常見的】修復為正常形式,即&&&。

由此,Google 這種利用基於瀏覽器修復【或規範中可以不閉合標籤條款的】機制,處理是可以的。它的意圖是儘可能少輸出內容,由客戶端瀏覽器來輔助它處理HTML,最終目的是為了提速與儘可能加大服務端吞吐量。

至於新浪微博的問題,比較複雜,只能推測下。

新浪微博默認採用【非IE6下】bigpipe機制分塊輸出頁面內容。這需要在首塊時候就吐出頁面框架結構給瀏覽器。那麼這個先吐出的頁面結構是否需要閉合&標籤呢?

從直覺觸發,為了避免瀏覽器出現未知異常,應該在首次吐出的頁面框架中閉合body。那麼,之後分塊輸出的 script 標籤必然會被列印在 &之後,然後依賴瀏覽器修復機制執行代碼。

新浪微博當時的工程師們可能並不知道修復機制,僅僅是為了避免項目風險,經過測試後發現這麼做可行,就這麼做了。

不管怎樣,基於bigpipe機制分塊吐出機制,在body沒有閉合,頁面數據繼續到達的情況下,之後被吐出的script代碼很可能存在操作document.body對象,這樣可能會導致異常,你我做如此實現時候都必須掂量下是否要冒風險不閉合body標籤。

此外,對於賀老祖的一些解釋,偶表示部分不認同。

content type 的問題。

rfc3236 規定了 application/xhtml+xml 這樣的 XHTML mimetype 來表示一個XHTML文件。但是瀏覽器並不一定按照這個 mimetype 來解析,偶從開源瀏覽器代碼中【Gecko/Webkit】只看到 parser HTML 部分的實現,這部分內容對於 XHTML 是無特殊處理的,就是說即使是 XHTML 也會被當做 HTML parser。唯一有一點不同的是,頁面的 DTD 如何寫會被瀏覽器嗅探,用來觸發三種不同的文檔模式【怪異、近乎標準、標準】。至於當前文檔的 mimetype 是什麼,對解析 HTML 或者渲染的影響,沒有得到很明顯的源碼證據。

【當然,基於network層是會區分mimetype 的,它會篩選出它認為是HTML的類型交付HTML parse 處理。事實上IE會比其他瀏覽器做的要更多,對於非二進位類型,會啟用「HTML嗅探機制」,如果首200字元內有特定HTML標記,則會將文檔交由HTML parse 處理】。

賀老祖【】內表述的掛羊頭賣狗肉行為偶就不細說了,僅當做 賀老祖 個人情緒發泄。個人感覺沒必要太學院了,實際適用就好。

恩恩,慣例,最後來個總結。

說了這麼多,兩種方式實際使用上沒有什麼區別,唯一差異在於你是否要利用瀏覽器的自動修復機制。

當然,理論上講,利用了自動修復機制必然會導致性能有損失。

至於性能損失多大,是否要如此用,偶說不好,由看官自行判斷。


請問提問者在哪個地方看到的是「放在body結束標籤之後html結束標籤之前」?麻煩提供下具體頁面……

script確實是放在頁面底部是最好(主要是為了放防止頁面阻塞),但是這個頁面底部應該也是body結束標籤之前。放在body標籤之後,恕個人能力有限,在我看來這應該是不符合相關標準的頁面。


感覺於前端性能而言應該沒差別.

查了一下, 有個差別, 說是放在&之前可以通過驗證, 放在&之後就不行了.

http://stackoverflow.com/questions/3037725/is-it-wrong-to-place-the-script-tag-after-the-body-tag

http://www.quora.com/Why-put-script-tag-below-html


或許是為了規避body內的js錯誤。在某些瀏覽器中,當body內js錯誤,則後續js腳本不再執行,但是body之外的script還是可以繼續執行的。有道雲筆記


放一張StackoverFlow上的神圖,保存下來了。


從實際效果上確實沒什麼區別


推薦閱讀:

如何解決自定義變數名和 jQuery 的 $ 標識的衝突?
jQuery會過時嗎?
一些人瞧不起 jQuery 的理由是什麼?
如何看待 jQuery?

TAG:JavaScript | HTML5 |