最佳實踐系列:前端代碼標准
@窩窩商城前端(劉軼/李晨/徐利/穆尚)翻譯於2012年 版本0.55 @鄭昀校對
isobar的這個前端代碼標准和最佳實踐文檔,涵蓋了Web應用開發的方方面面,我們翻譯了大部分章節,並做了注解。請仔細閱讀用標記的段落。
關鍵詞列表:
漸進增強;Combo Handler;Quirks Mode;瀏覽器盒子模型;選擇器特殊性;Spacer Image;CSS Sprites;PNG8;
編寫此文檔的目的主要有兩方面,第一,如何保持代碼的一致性;第二,什么才是最佳實踐。我們通過編碼風格和約定保持一致,可以減輕代碼維護的工作量,並降低風險,在將來代碼出現問題的時候,方便我們排查錯誤。我們要保持最佳的編碼習慣和做法,確保優化頁面的加載和性能,並能更好地維護代碼。
普遍性准則
前端開發的核心
1、 結構、表現、行為應該分離。
注:漸進增強(Progressive Enhancement)
名詞定義:漸進增強是從一個功能可用的基本版本出發,然后逐步增加用戶體驗的豐富程度,並在應用增強功能之前先測試對該功能的支持性;最基本的可用性出發,在保證站點頁面在低級瀏覽器中的可用性和可訪問性的基礎上,逐步增加功能及提高用戶體驗。輔助閱讀:http://www.ryanbay.com/?p=97
一般性實踐
縮進
對於所有代碼語言,我們需要通過TAB縮進進行代碼排版(使用空格字符),在文本編輯器擊中TAB應相當於四個空格。
可讀性 vs 壓縮
我們在代碼可讀性和文件大小的取舍中,更傾向於良好的可讀性。
在合適的地方,我們鼓勵使用大量的縮進、換行來維持文件代碼的可讀性。開發者不需要刻意地壓縮文件的體積大小,也不需要混淆使用JavaScript。
我們將使用服務器端或發布過程中自動minify和gzip的所有靜態客戶端文件,如CSS和JavaScript。
注:一般采用服務端的 Combo Handler 來對服務器端上傳的未壓縮、未混淆的JS和CSS文件做統一合並、壓縮和混淆。
標簽
標記定義了結構和輪廓的文檔,並提供了一個結構化內容。
標記並不打算定義的外觀和視覺表現,只用於感知網頁的基本概念,如標題、段落、列表。HTML標記中表現樣式的屬性都應被棄用,樣式的表現應該包含在樣式表里。
HTML5
我們將在適當的時候使用HTML5 Doctype和HTML5特性。
我們將用W3C驗證器測試我們的標記,以確保格式良好。100%有效的代碼並不是我們的目標,但W3C驗證確實有助於編寫更易於維護的網站以及調試代碼。Isobar不保證代碼是100%有效,而是確保跨瀏覽器有統一的瀏覽體驗。
模板
對於HTML5文檔,我們將定制 H5BP。
文檔類型
應該始終使用一個合適的 Doctype 來觸發瀏覽器的標准模式。
我們應該始終避免Quirks Mode(怪異模式或兼容模式)。
HTML5的一個好的方面是,它簡化了所需的代碼量。無意義的屬性已經有所下降,而且已大大簡化了 Doctype 聲明。因此建議文檔類型應聲明為HTML5的形式。
HTML5 Doctype
1.
<!DOCTYPE html>
注:怪異模式(quirks mode)
名詞定義:指在計算機領域中,一些網頁瀏覽器為了維持對較舊的網頁設計的向后兼容性,而使用的一種技術,有別於嚴格遵循萬維網聯盟(W3C)與互聯網工程任務組(IETF)標准而設計的「標准模式」。
為了與可能數量眾多的網頁維持兼容,現代的網頁瀏覽器一般具有多種渲染模式:在「標准模式」(standards mode) 頁面按照 HTML 與 CSS 的定義渲染,而在「quirks 模式」中則嘗試模擬更舊的瀏覽器的行為。
在 quirks 模式和標准模式之間一個突出的不同是對 CSS IE盒模型缺陷的處理。
參考資料:What happens in Quirks Mode?
DOCTYPE聲明 |
說明 |
IE8 |
FF |
Chrome |
<!DOCTYPE HTML> |
HTML5推薦的方式 |
CSS1Compat |
CSS1Compat |
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" |
嚴格模式聲明並給出DTD URL |
CSS1Compat |
CSS1Compat |
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> |
嚴格模式聲明但不給出DTD URL |
CSS1Compat |
CSS1Compat |
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" |
過渡模式並給出DTD URL |
CSS1Compat |
CSS1Compat |
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> |
過渡模式但不給出DTD URL |
BackCompat |
BackCompat |
字符編碼
所有標記都應設置為utf – 8。它應該同時指定在 HTTP 報頭和文檔頭部。
1.
<
meta
http-equiv
=
"Content-Type"
content
=
"text/html; charset=UTF-8"
/>
In HTML5, just do:
1.
<
meta
charset
=
"utf-8"
>
通用標記指南
以下是一般指南,用於構建你的HTML標記。
- 使用實際的P元素分隔符而不是多段BR標簽。
- 在適當的時候,利用DL(定義列表)和 BLOCKQUOTE。
- 使用UL,OL,DL 來表示一個項目列表,不要使用多個DIV嵌套模擬一個項目列表
。
- 使用label 字段來標記表單中每一個字段,它的for屬性把這個label與input field關聯起來,使得用戶可以點擊這個label。設置cursor:pointer給label也是明智之舉
!!
- 不要使用size屬性來控制你輸入框的大小,應該使用CSS來定義
。
- 在一些閉合div后面加上html注釋,以標示你關閉的是什么元素
。有大量嵌套和縮進時這會很有幫助。
- 不要使用 tables來完成你的頁面布局。
- 在適當的時候合理的運用table中的thead 、tbody、tfoot以及th等標簽。
正確的表格:
引號屬性
雖然HTML5規范定義了在那些可以自動識別空格的屬性周圍添加引號作為可選屬性,但是所有的屬性都應該添加引號。
1.
<
p
class
=
"line note"
data-attribute
=
"106"
>This is my paragraph of special text.</
p
>
CSS
Web頁面的第二個組件是包含在樣式表(Cascading Style Sheet,CSS)里的展示信息。
一般性編碼原則
- 通過外部文件來添加CSS。CSS文件里盡可能不要用 # 。CSS總是應該在文檔頭部<head></head>之間。
- 使用 link 標記來引用外部CSS文件,永遠不要使用 @import導入CSS。
- 不要使用內聯樣式(inline styling),因為維護的時候很難跟蹤樣式的規則。
- 確保同一個CSS文件文檔只引用一次。
- 請用一個字體標准化文件,如YUI fonts.css,來定義各個標記的字體在不同瀏覽器中有統一表現
!
- 文檔中僅出現一次的元素,應使用ID,否則,使用class。
- 理解層疊和選擇器特性,這樣你就可以編寫非常簡潔、有效的代碼。
- 編寫CSS代碼時,使用高效的選擇器。如果可能,要避免使用運行效率低下的選擇器
。比如,我們應該避免 * 通配符選擇器,避免不適當的ID選擇器(如
div#myid
)或類選擇器(如table.results
.)。這些之所以特別重要,是因為在Web應用程序中速度是至關重要的,可能有數以千計甚至數以萬計的DOM元素需要被渲染。更多細節請參考: 在MDC上編寫高效的CSS。
瀏覽器盒子模型
深入了解CSS和瀏覽器盒子模型(browser-based box model)是做好CSS布局非常必要的一個條件。
CSS校驗
我們不使用 w3c的css校驗器。
CSS格式化
我們應該保證,至少每一個選擇器應該在單獨的一行。聲明應該是縮進的。
Classes vs. IDs
如果一些元素是文檔中獨一無二的,那么你應該只給他們一個ID屬性。CLASS則是應用在具備同樣屬性樣式的多個元素上。
選擇器的命名規范
無論是ID選擇器還是類選擇器,我們應該遵循“它是什么(what it is)”而不是“它是什么樣子的(what it looks like)”的原則進行命名。例如:一個class命名為 bigBlueText,如果它后來被改為了紅色的小字體,那么該class命名就失去了意義,讓人不好理解。
如果我們命名為 noteText,無論如何改變外觀,它仍然是一個文本框,仍然有意義。
選擇器(Selectors)
CSS Selectors Level 3規格說明書里介紹了一整套CSS選擇器,非常有用。
Pseudo-classes
偽類(Pseudo-classes)能動態化樣式內容。一些偽類從CSS1就存在了,如:visited, :hover
,有些從CSS2就存在了,如:first-child, :lang
。CSS3引入了16個偽類,都非常有用,請深入學習一下它們的用法。
特殊性(Specificity)
瀏覽器會計算一個選擇器的權重,來決定應用哪一個CSS規則。如果兩個選擇器都要應用到一個元素上,那么特殊性更高的選擇器勝出。
ID的特殊性比屬性選擇器的更高;屬性選擇器則比類選擇器高。所以你可以嘗試用id來增加特殊性。
計算特殊性
當你遇到一個又大又復雜的樣式表時,你最好知道怎么計算特殊性,這樣能讓你的選擇器更有效率。
特殊性的值可以看作是一個由四個數組成的一個組合,用 a,b,c,d 來表示它的四個位置。 依次比較 a,b,c,d 這個四個數比較其特殊性的大小。比如,a 值相同,那么 b 值大的組合特殊性會較大,以此類推。注意,W3C 中並不是把它作為一個 4 位數來看待的。
a,b,c,d 值的確定規則:
- 如果 HTML 標簽的 'style' 屬性中該樣式存在,則記 a 為 1;
- 數一下選擇器中 ID 選擇器的個數作為 b 的值。比如,以上樣式中包含 '#c1' 和 '#c2' 的選擇器;
- 其他屬性以及偽類(pseudo-classes)的總數量是 c 的值。比如,上面例子中的 '.con',':hover' 等;
- 元素名和偽元素的數量是 d 的值;比如上面例子中的 ‘div’。
(注:原文為:
- Element, Pseudo Element: d = 1 – (0,0,0,1)
- Class, Pseudo class, Attribute: c = 1 – (0,0,1,0)
- Id: b = 1 – (0,1,0,0)
- Inline Style: a = 1 – (1,0,0,0)
此處沒有直接翻譯。)
下面舉一個實際例子:
使用!important將覆蓋所有特殊性,無論它們值有多高。因此盡量不要用 !important。大多數情況下它都是不必要的。
Pixels vs. Ems
我們使用px計量單位來定義字體大小,因為它對文本提供了絕對控制。在IE6瀏覽器中,如果是用px為單位,那么在頁面放大縮小的時候,就不會根據頁面縮放的大小來調整基礎文本的大小。當今所有主要瀏覽器(包括IE7和IE8)現在支持文本的像素單位 和/或 整版的縮放。在不考慮IE6的情況下,像素大小是首選。此外,沒有單位的行高(line-height)是首選的,因為它不繼承它的父元素一個百分比值,而是基於一個乘數的字體大小。
正確的寫法:
錯誤的寫法:
IE的那些BUG
不可避免的,當其他瀏覽器都工作正常時,IE的各個版本有可能會引入一些荒謬的BUG,搞得你措手不及。最后我們只好用CSS hooks加一些條件判斷單獨設置IE下的表現。更多詳情請閱讀paulirish的文章。
舉例:
如果你使用HTML5(以及HTML5 Boilerplate),那推薦你使用Modernizer Javascript 庫以及下面這個模式:
簡寫(CSS Shorthand)
一般來說,CSS簡寫是首選的,因為簡潔。開發人員應該意識到TRBL(Top Right Bottom Left)縮略詞,按順時針“上、右、下、左”進行定義。例如:
padding-left:10px;padding-right:15px;padding-top:20px;padding-bottom:10px;
可將其簡寫為:padding:20px 15px 10px 10px;
參考閱讀:
- http://qrayg.com/journal/news/css-background-shorthand
- http://sonspring.com/journal/css-redundancy
- http://dustindiaz.com/css-shorthand
圖片
- 當需要使用重復平鋪的圖片時,圖片尺寸大小應該大於 1x1像素
。
- 注:鏈接給出了一個例子,他用了一個1×1的透明PNG圖片作為div的重復背景,樣式為:background:transparent url('images/transparent-bg.png') repeat left;,但發現在IE7和IE8下顯示有問題,最后只好用了一個1×2的透明圖片。
- 不要使用Spacer Images(空白圖片)
!
- 注:spacer image,指的是1×1的透明圖片,用以填充表格的空白區域,以便撐住表格不變形。
- 請大量使用CSS sprites技術
!這樣做可以大大的減少圖片的HTTP請求次數,從而加快網頁的加載時間。
- 所有的切片圖像應具有透明背景(PNG8)。切圖應該緊着圖片邊緣切。
- 但是 logo 圖片文件應該在切圖時保留一個padding,這樣便於其他人加熱鏈。
文本和字體樣式
標題
- 定義標題的默認樣式為h1~h6。推薦做法是,在你的CSS文件頂部聲明這些默認樣式,必要時修改它們使得它們在網站中保持一致性。
- 標題的顯示根據頁面層級的不同和標題的重要性,從頂部開始,自上而下。h1為最大字體,h6為最小字體。
- SEO提示:請務必保證你的頁面在失去CSS支持的時候,依然能夠保證一個清晰的一個頁面結構,因為搜索引擎對一個結構清晰的頁面會特別關照。
鏈接
應確保鏈接的默認樣式與主要文本的樣式有所區分,鼠標懸停狀態的樣式也應該有所區分。
JavaScript
JavaScript是web頁面中的第三個主要的組件。
JavaScript庫
目前,我們主要使用jQuery開發新的web應用程序。
一般的編碼規則
- 99%的代碼應該放在外聯JavaScript文件里。這些外聯JavaScript文件的聲明都應該放在body標記的底部,即</body>標簽之前。
- 不要依賴於User-Agent字符串。一定要做適當的特征檢測。(更多閱讀:Dive Into HTML5: Detection & jQuery.support docs)。
- 不要使用document.write()
!
- 所有的布爾變量應該用“is”開頭
。
- 注意變量和函數名稱的命名邏輯:例如:popUpWindowForAd,而不是mywindow。
- 不要人為的簡寫變量名稱。(除了在做For循環中傳統的 i 變量),變量名應該是足夠長以確保有意義。
- 你的文檔應該遵循NaturalDocs結構。
- 常量或配置變量(如動畫持續時間等),應在文件的頂部聲明。
- 多使用函數,這些函數帶有參數和返回值。函數中只存在抽象的邏輯代碼。通過參數的傳遞使其該函數能夠達到最大化的重用。當功能和代碼邏輯需要改變的時候,可以大大的減少維護代碼的開銷。例如,我們需要做一個彈窗,彈窗內容、尺寸大小、URL鏈接,我們應該將其做為變量傳遞,而不是將這些屬性值寫死。
- 注釋你的代碼!它有助於花費更少的時間解決所遇到的問題。
(讓combo handler負責minify!)
- 內嵌的Javascript代碼塊,請使用<!-- -->包裹,除非你關心Netscape 4。
- 盡可能少地使用全局變量,如果你要使用,請給予一個命名空間
!
合理使用空格
一般來說,使用空格應該遵循長期以來的英語閱讀約定,符號左右應該存在一個空格,我們提倡代碼的可讀性。此外,前括號應該和總是和他的函數名處在同一行。
考慮下面這個例子,一個JavaScript循環…
變量、ID和類
所有JavaScript的變量名,都應該寫成全部字母小寫或者駝峰式大小寫的形式。一個例外是,構造函數需要按照傳統統統大寫。所有CSS的ID和Class應該全部使用小寫。盡可能的避免使用中划線和下划線。
事件委托
事件委托就是在一個頁面上使用一個事件來管理多種類型的事件。這並不是一個新的想法,但對於把握性能來說卻很重要。
事件委托對於web應用程序的性能有如下幾個優點:
- 需要管理的函數變少了
- 占用的內存少了
- JavaScript代碼和DOM結構之間的關聯更少了
- 在改變DOM結構中的innerHTML時,不需要改動事件處理函數
從傳統的事件處理方法轉向事件委托,提高了大型web應用的性能。正因為它如此重要,一些類似於YUI、jQuey的javascript庫也開始將事件委托應用在它們的核心接口中。實現事件委托是很輕松的,卻能帶來性能上很大的提高。尤其表現在你將數十個事件處理函數整合到一個函數里。試一下事件委托,你就不會再使用傳統的事件處理方法了。
出於性能考慮,jQuery中的delegate()要大大優於live()。
調試
即使最好的驗證器,也不可避免地會導致瀏覽器怪異模式的問題。有幾個寶貴的工具,將有助於改進代碼的完整性和加載速度。我們建議開發時先以Firefox和Safari第一,然后Chrome和Opera,最后有條件地單獨調整Internet Explorer。
下面列出了有用的調試器和速度分析…
- Firefox: Firebug, Page Speed, YSlow
- Safari: Web Inspector
- Google Chrome: Developer Tools
- Opera: Dragonfly
- Internet Explorer 6-7: Developer Toolbar
- Internet Explorer 8-10: Developer Tools
良好的Javascript編寫模式
- 編寫可維護的代碼
- 單var模式(即用一個var語句聲明多個變量,並以逗號分隔)
- 不增加內置的原型
- 避免隱含類型轉換
- 避免使用eval()
- 數字轉換請使用parseInt()
- 注意左花括號的位置。
- 構造函數名大寫
- 記得寫注釋
- 避免聲明void
- 如果可能的話,避免位運算符(Bitwise Operator)
性能
我們將繼續關注網絡的性能優化,它仍然同樣的重要。一個網頁可以做到最小的性能消耗及等待時間。以下部分說明了如何對一個網頁進行優化,讓更多的瀏覽者更加滿意。
優化並交付CSS和Javascript
- 遵循雅虎的性能指南
- 使用smush.it對圖像做無損壓縮
!
- 適當時機請緩存headers。
- 靜態資源文件要考慮單獨開辟無 cooike 的子域(避免Request攜帶大量cookies)
!
- 避免內聯的<script>塊
!
- CSS應該定義在文檔的<head>中,javascript 則應在</body>前通過外部鏈接的方式引用。
- CSS和Javascript在應用程序發布前進行壓縮,很多人使用YUI Compressor來進行壓縮。
- CSS和Javascript在服務端都應該被gzip壓縮。
- CSS和Javascript文件應該被合並在一起,目的是在頁面加載過程中減少對HTTP請求的次數
!
- 避免在HTML文檔中的內聯
<script>
塊,他們會阻塞頁面渲染和頁面加載時間!
優化Javascript執行順序
在一個頁面加載中,通常等待執行的腳本有很多。基於用戶的響應,這個順序優先級應如下:
- 會改變頁面可視形態的的Script,優先級應該絕對是第一位的。這包括任何字體調整,布局框變更或IE6 的pngfixes。
- 頁面內容初始化。
- 頁眉、導航欄和頁腳的初始化。
- 事件監聽處理程序。
- 第三方的插件或腳本。
合理利用CSS Sprites技術
CSS Sprites把一些基本的圖像資源合並到一個圖像文件中。通過CSS背景定位的方式來獲取其中的某一個部分。這樣做的目的同樣是為了減少http的請求次數。以下是一個典型的CSS Sprites:
使用CSS Sprites 能有效降低頁面引用資源數,從而加快頁面加載。更多內容請閱讀CSS-tricks.com的技術和概述。
許多開發人員會使用一個垂直排列內部圖片元素的Sprites,這種垂直排列的Sprites應該<= 100px寬(高)。包含圖標通常應該放在文件里的最邊上,如列表項icon小圖標。雅虎使用了一些像這樣的Sprites。
另外一個要考慮的是不要使用過大的Sprites,超過1000px的任意一個方向的Sprites會占用相當大的內存空間。
圖片格式的應用
- JPEG——這將應用在所有的攝影圖像,如主頁或者頻道頁面中的宣傳圖像產品圖像,或任何要求具備非常高的顏色數的圖像。
- PNG24——方便在PhotoShop中輸出高色數和全面支持分級不透明的圖像,相對而言,這是一個相當重的格式,可能會產生千字節容量,而且在IE6中需要執行一個pngfix,在這種情況下我們建議使用DD_belatedPNG script(一個使IE6兼容背景透明的JS)。在很多時候,我們會為IE6后備一個GIF圖像來支持背景透明。因為如果在一個很大的格式為PIN24的Sprites中應用pngfixes 會使所有的pngfixes的速度變得非常的慢,性能消耗代價特別大。因此最好避免在Sprites使用PNG24格式。
- PNG8——256色的顏色可以捕獲一個令人驚訝的多樣性, PNG是一個很大的可壓縮比GIF(使用pngcrush和pngquant的工具,如)。此格式幾乎在所有的瀏覽器允許漸變的不透明度,但在IE6中,那些半透明的像素是100%透明。在許多情況下,這是足夠的。它也不會需要一個pngfix的腳本來運行,所以它的速度進行了優化。
- 透明GIF 89a——GIF 89a中提供了靈活性,透明度和廣泛的瀏覽器的支持,但沒有漸變的不透明度,而且色數不能超過256色。根據我們的經驗,色彩深度為64仍然提供了非常好的品質縮覽圖,並能保持同等的文件大小。所有低色數,如圖標或專題圖形圖像應使用PNG8格式,因為它是這四個格式中最高效的。對於大多數網站上的圖形,PNG8是我們的主要建議使用的圖像格式。
高速緩存
對於靜態內容,只要是合理的,我們都應該在本地緩存文件。應該被緩存的文件包括:
- CSS和JavaScript文件
- 產品圖片
- 專題圖形
- favicon.ico標志
- flash的.swf文件
- 宣傳多媒體影像
如果你的服務器是Apache,使用ExpiresDefault指令設置相對於當前日期的過期時間。這個例子ExpiresDefault指令集的要求過期時間為1年。
Expires: Thu, 15 Apr 2015 20:00:00 GMT
緩存只適用於准確的URL,所以改變文件名將開始一個新的拷貝。許多開發人員在文件發布或構建過程中添加一個版本號或時間戳。每一次構建將開始一個全新的緩存的版本,你不用擔心緩存不會過期。
有節制的使用IFRAME
創建IFRAME比創建任何其他類型的DOM元素(包括腳本和樣式表)耗時多出1-2個數量級。
盡快產生 Window的onload事件是很重要的。這個事件會引發瀏覽器狀態指示到達“完畢”狀態,通知用戶頁面已經下載完畢。當onload事件被延遲,給用戶的感受是頁面變的更慢了。
Window的onload事件在所有的iframe和iframe內所有元素完全下載后才能觸發。在 Safari 和 Chrome瀏覽器內,通過JavaScript動態設置iframe的src屬性可以避免這種阻塞行為。
有人可能希望一個iframe有它自己單獨的並發連接池,但是事實並不是這樣。在所有主流瀏覽器中,連接池是被頁面和iframe共享的。這意味着有可能一個iframe內的資源占據了所有可用的連接並阻塞了主頁面的資源下載。如果iframe內的內容和主頁面一樣重要或者更重要,這是沒有問題的。但如果iframe不太重要,iframe占用連接是不可取的。一個解決辦法是在 更高級別的資源下載之后,動態設置iframe的src屬性動態加載內容
美國10大網站中5個使用了iframe。大多數情況下,它們常用來加載廣告。這是不幸的,但可以理解,因為使用iframe插入廣告內容很容易實現。在許多情況下,iframe是合乎邏輯的 解決方案。但要記住他們會對你的頁面產生性能影響。盡可能避免使用iframe,當必須使用時,要有節制的使用它們。
FLASH
在應用FLASH動畫的地方,請備注上相應的HTML文本,以便於SEO搜索引擎的優化。SWFObject的初始化應該在文檔加載完畢之后。不要使用內嵌的JavaScript輸出SWFObject。
建立於客戶間的成功指標。
這些指標不管從技術角度以及可測試性應該是合理的:
- 在頁面沒有任何緩存的情況下,頁面的完全加載和初始化應該保證在5秒以內
。
- 鼠標經過時的變化(如hover)應該保證在100毫秒以內完成
。
原文鏈接:http://isobar-idev.github.com/code-standards/
翻譯組:@窩窩團前端(劉軼/李晨/徐利/穆尚)
校對:@鄭昀
2)55最佳實踐系列:Logging最佳實踐 (2012-12-15 16:43)
贈圖1枚: