最佳原則
堅持這些原則。無論多少,找出不對的地方。如果你想要爲這個編碼指導做貢獻,請訪問新開一個issue.
無論人數多少,代碼都應該同出一門。
項目命名
項目名全部採用小寫方式, 以中劃線分隔。 比如: my-project-name
目錄名
目錄名參照上一條規則,有複數結構時,要採用複數命名法,比如說: scripts, styles, p_w_picpaths, data-models
JavaScript文件命名
所有js文件名,多個單詞組成時,採用中劃線連接方式,比如說: 賬號模型文件 account-model.js
CSS,SCSS文件命名
多個單詞組成時,採用中劃線連接方式,比如說:retina-sprites.scss
HTML文件命名
多個單詞組成時,採用中劃線連接方式,比如說: error-report.html
HTML
語法
使用四個空格的 soft tabs — 這是保證代碼在各種環境下顯示一致的唯一方式。
嵌套的節點應該縮進(四個空格)。
在屬性上,使用雙引號,不要使用單引號。
不要在自動閉合標籤結尾處使用斜線 - HTML5 規範 指出他們是可選的。
不要忽略可選的關閉標籤(例如,
</li>
和</body>
)。
<!DOCTYPE html><html> <head> <title>Page title</title> </head><body> <img src="p_w_picpaths/company-logo.png" alt="Company"> <h1 class="hello-world">Hello, world!</h1></body></html>
HTML5 doctype
在每個 HTML 頁面開頭使用這個簡單地 doctype 來啓用標準模式,使其每個瀏覽器中儘可能一致的展現。
雖然doctype不區分大小寫,但是按照慣例,doctype大寫 關於html屬性,大寫還是小寫的一片文章
<!DOCTYPE html><html> <head> </head></html>
語言屬性
針對HTML5 :
作者應在html的跟元素上加上這個文件的語言。這會給語音工具和翻譯工具幫助,告訴它們應當怎麼去發音和翻譯。
閱讀更多有關 lang
屬性 在這個文章中.
通過Sitepoint 得到一個語言代碼列表.
Sitepoint只是給出了語言代碼的大類,比如說中文就只給出了ZH,但是沒有區分香港,臺灣,大陸等。而微軟給出的一份細分了zh-cn,zh-hk,zh-tw, Head to Microsoft for a detail list of language codes.
<html lang="en-us"> <!-- ... --></html>
IE 兼容模式
Internet Explorer 支持使用一個文檔屬性標籤 <meta>
來指出這個頁面應當支持的IE的版本。除非另有規定,最好用最新的支持的模式。
更多信息, 閱讀這個 Stack Overflow 文章.
不同doctype在不同瀏覽器下的不同渲染模式,詭異模式總結的很到位.
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
字符編碼
通過聲明一個明確的字符編碼,讓瀏覽器輕鬆、快速的確定適合網頁內容的渲染方式。
<head> <meta charset="UTF-8"></head>
引入 CSS 和 JavaScript
根據 HTML5 規範, 通常在引入 CSS 和 JavaScript 時不需要指明type
,因爲 text/css
和 text/javascript
分別是他們的默認值。
HTML5 規範鏈接
<!-- External CSS --><link rel="stylesheet" href="code-guide.css"><!-- In-document CSS --><style> /* ... */</style><!-- JavaScript --><script src="code-guide.js"></script>
實用高於完美
儘量遵循 HTML 標準和語義,但是不應該以浪費實用性作爲代價。任何時候都要用盡量小的複雜度和儘量少的標籤來解決問題。
屬性順序
HTML 屬性應該按照特定的順序出現以保證易讀性。
id
class
name
data-*
src
,for
,type
,href
title
,alt
aria-*
,role
Classes 是爲高可複用組件設計的,理論上他們應處在第一位。Ids 更加具體而且應該儘量少使用(例如, 頁內書籤),所以他們處在第二位。但爲了突出id的重要性, 把id放到了第一位。
<a id="..." class="..." data-modal="toggle" href="#"> Example link</a><input class="form-control" type="text"><img src="..." alt="...">
Boolean 屬性
Boolean 屬性指不需要聲明取值的屬性。XHTML 需要每個屬性聲明取值,但是 HTML5 並不需要。
瞭解更多內容,參考 WhatWG section on boolean attributes:
一個元素中 Boolean 屬性的存在表示取值 true,不存在則表示取值 false。
如果你必須爲屬性添加並不需要的取值,參照 WhatWG 的指引:
如果屬性存在,他的取值必須是空字符串或者 [...] 屬性的規範名稱,不要在首尾包含空白字符。
簡而言之,不要爲 Boolean 屬性添加取值。
<input type="text" disabled><input type="checkbox" value="1" checked><select> <option value="1" selected>1</option></select>
減少標籤數量
在編寫 HTML 代碼時,需要儘量避免多餘的父節點。很多時候,需要通過迭代和重構來使 HTML 變得更少。 參考下面的示例:
<!-- Not so great --><span class="avatar"> <img src="..."></span><!-- Better --><img class="avatar" src="...">
JavaScript 生成標籤
在 JavaScript 文件中生成標籤讓內容變得更難查找,更難編輯,性能更差。應該儘量避免這種情況的出現。
CSS
語法
使用四個空格的 soft tabs — 這是保證代碼在各種環境下顯示一致的唯一方式。
使用組合選擇器時,保持每個獨立的選擇器佔用一行。
爲了代碼的易讀性,在每個聲明的左括號前增加一個空格。
聲明塊的右括號應該另起一行。
每條聲明
:
後應該插入一個空格。每條聲明應該只佔用一行來保證錯誤報告更加準確。
所有聲明應該以分號結尾。雖然最後一條聲明後的分號是可選的,但是如果沒有他,你的代碼會更容易出錯。
逗號分隔的取值,都應該在逗號之後增加一個空格。比如說box-shadow
不要在顏色值
rgb()
rgba()
hsl()
hsla()
和rect()
中增加空格,並且不要帶有取值前面不必要的 0 (比如,使用 .5 替代 0.5)。This helps differentiate multiple color values (comma, no space) from multiple property values (comma with space).所有的十六進制值都應該使用小寫字母,例如
#fff
。因爲小寫字母有更多樣的外形,在瀏覽文檔時,他們能夠更輕鬆的被區分開來。儘可能使用短的十六進制數值,例如使用
#fff
替代#ffffff
。爲選擇器中的屬性取值添加引號,例如
input[type="text"]
。 他們只在某些情況下可有可無,所以都使用引號可以增加一致性。不要爲 0 指明單位,比如使用
margin: 0;
而不是margin: 0px;
。
對這裏提到的規則有問題嗎?參考 Wikipedia 中的 CSS 語法部分。
/* Bad CSS */.selector, .selector-secondary, .selector[type=text] { padding: 15px; margin: 0px 0px 15px; background-color: rgba(0, 0, 0, 0.5); box-shadow: 0 1px 2px #CCC, inset 0 1px 0 #FFFFFF}/* Good CSS */.selector,.selector-secondary,.selector[type="text"] { padding: 15px; margin-bottom: 15px; background-color: rgba(0,0,0,.5); box-shadow: 0 1px 2px #ccc, inset 0 1px 0 #fff;}
聲明順序
相關的屬性聲明應該以下面的順序分組處理:
Positioning
Box model 盒模型
Typographic 排版
Visual 外觀
Positioning 處在第一位,因爲他可以使一個元素脫離正常文本流,並且覆蓋盒模型相關的樣式。盒模型緊跟其後,因爲他決定了一個組件的大小和位置。
其他屬性只在組件 內部 起作用或者不會對前面兩種情況的結果產生影響,所以他們排在後面。
關於完整的屬性以及他們的順序,請參考 Recess。
.declaration-order { /* Positioning */ position: absolute; top: 0; right: 0; bottom: 0; left: 0; z-index: 100; /* Box-model */ display: block; float: right; width: 100px; height: 100px; /* Typography */ font: normal 13px "Helvetica Neue", sans-serif; line-height: 1.5; color: #333; text-align: center; /* Visual */ background-color: #f5f5f5; border: 1px solid #e5e5e5; border-radius: 3px; /* Misc */ opacity: 1;}
// 爲了方便查閱, 我把Recess的order貼了一份過來, 引入時間2014-05-05// css property ordervar order = [ 'position' , 'top' , 'right' , 'bottom' , 'left' , 'z-index' , 'display' , 'float' , 'width' , 'height' , 'max-width' , 'max-height' , 'min-width' , 'min-height' , 'padding' , 'padding-top' , 'padding-right' , 'padding-bottom' , 'padding-left' , 'margin' , 'margin-top' , 'margin-right' , 'margin-bottom' , 'margin-left' , 'margin-collapse' , 'margin-top-collapse' , 'margin-right-collapse' , 'margin-bottom-collapse' , 'margin-left-collapse' , 'overflow' , 'overflow-x' , 'overflow-y' , 'clip' , 'clear' , 'font' , 'font-family' , 'font-size' , 'font-smoothing' , 'osx-font-smoothing' , 'font-style' , 'font-weight' , 'hyphens' , 'src' , 'line-height' , 'letter-spacing' , 'word-spacing' , 'color' , 'text-align' , 'text-decoration' , 'text-indent' , 'text-overflow' , 'text-rendering' , 'text-size-adjust' , 'text-shadow' , 'text-transform' , 'word-break' , 'word-wrap' , 'white-space' , 'vertical-align' , 'list-style' , 'list-style-type' , 'list-style-position' , 'list-style-p_w_picpath' , 'pointer-events' , 'cursor' , 'background' , 'background-p_w_upload' , 'background-color' , 'background-p_w_picpath' , 'background-position' , 'background-repeat' , 'background-size' , 'border' , 'border-collapse' , 'border-top' , 'border-right' , 'border-bottom' , 'border-left' , 'border-color' , 'border-p_w_picpath' , 'border-top-color' , 'border-right-color' , 'border-bottom-color' , 'border-left-color' , 'border-spacing' , 'border-style' , 'border-top-style' , 'border-right-style' , 'border-bottom-style' , 'border-left-style' , 'border-width' , 'border-top-width' , 'border-right-width' , 'border-bottom-width' , 'border-left-width' , 'border-radius' , 'border-top-right-radius' , 'border-bottom-right-radius' , 'border-bottom-left-radius' , 'border-top-left-radius' , 'border-radius-topright' , 'border-radius-bottomright' , 'border-radius-bottomleft' , 'border-radius-topleft' , 'content' , 'quotes' , 'outline' , 'outline-offset' , 'opacity' , 'filter' , 'visibility' , 'size' , 'zoom' , 'transform' , 'box-align' , 'box-flex' , 'box-orient' , 'box-pack' , 'box-shadow' , 'box-sizing' , 'table-layout' , 'animation' , 'animation-delay' , 'animation-duration' , 'animation-iteration-count' , 'animation-name' , 'animation-play-state' , 'animation-timing-function' , 'animation-fill-mode' , 'transition' , 'transition-delay' , 'transition-duration' , 'transition-property' , 'transition-timing-function' , 'background-clip' , 'backface-visibility' , 'resize' , 'appearance' , 'user-select' , 'interpolation-mode' , 'direction' , 'marks' , 'page' , 'set-link-source' , 'unicode-bidi' , 'speak']
不要使用 @import
與 <link>
標籤相比, @import
更慢, 增加了額外的頁面請求,可能還會導致不可預見的問題。避免使用它們,改用以下的方法:
多用幾個
<link>
標籤將你的css編譯到一個文件裏
用Rails, Jekyll,以及其他環境提供的特性來間接這些css
更多信息, 閱讀 Steve Souders 的文章.
<!-- Use link elements --><link rel="stylesheet" href="core.css"><!-- Avoid @imports --><style> @import url("more.css");</style>
媒體查詢位置
儘量將媒體查詢的位置靠近他們相關的規則。不要將他們一起放到一個獨立的樣式文件中,或者丟在文檔的最底部。這樣做只會讓大家以後更容易忘記他們。這裏是一個典型的案例。
.element { ... }.element-avatar { ... }.element-selected { ... }@media (min-width: 480px) { .element { ...} .element-avatar { ... } .element-selected { ... }}
前綴屬性
當使用廠商前綴屬性時,通過縮進使取值垂直對齊以便多行編輯。
在 Textmate 中,使用 Text → Edit Each Line in Selection(A)。 在 Sublime Text 2 中, 使用 Selection → Add Previous Line (↑) 和 Selection → Add Next Line (↓)。
/* Prefixed properties */.selector { -webkit-box-shadow: 0 1px 2px rgba(0,0,0,.15); box-shadow: 0 1px 2px rgba(0,0,0,.15);}
單條聲明的聲明塊
在一個聲明塊中只包含一條聲明的情況下,爲了易讀性和快速編輯可以考慮移除其中的換行。所有包含多條聲明的聲明塊應該分爲多行。
這樣做的關鍵因素是錯誤檢測 - 例如,一個 CSS 驗證程序顯示你在 183 行有一個語法錯誤,如果是一個單條聲明的行,那就是他了。在多個聲明的情況下,你必須爲哪裏出錯了費下腦子。
/* Single declarations on one line */.span1 { width: 60px; }.span2 { width: 140px; }.span3 { width: 220px; }/* Multiple declarations, one per line */.sprite { display: inline-block; width: 16px; height: 15px; background-p_w_picpath: url(../img/sprite.png);}.icon { background-position: 0 0; }.icon-home { background-position: 0 -20px; }.icon-account { background-position: 0 -40px; }
屬性簡寫
堅持限制屬性取值簡寫的使用,屬性簡寫需要你必須顯式設置所有取值。常見的屬性簡寫濫用包括:
padding
margin
font
background
border
border-radius
大多數情況下,我們並不需要設置屬性簡寫中包含的所有值。例如,HTML 頭部只設置上下的 margin,所以如果需要,只設置這兩個值。過度使用屬性簡寫往往會導致更混亂的代碼,其中包含不必要的重寫和意想不到的副作用。
Mozilla Developer Network 有一篇對不熟悉屬性簡寫及其行爲的人來說很棒的關於 shorthand properties 的文章。
/* Bad example */.element { margin: 0 0 10px; background: red; background: url("p_w_picpath.jpg"); border-radius: 3px 3px 0 0;}/* Good example */.element { margin-bottom: 10px; background-color: red; background-p_w_picpath: url("p_w_picpath.jpg"); border-top-left-radius: 3px; border-top-right-radius: 3px;}
LESS 和 SASS 中的嵌套
避免不必要的嵌套。可以進行嵌套,不意味着你應該這樣做。只有在需要給父元素增加樣式並且同時存在多個子元素時才需要考慮嵌套。
// Without nesting.table > thead > tr > th { … }.table > thead > tr > td { … }// With nesting.table > thead > tr { > th { … } > td { … }}
代碼註釋
代碼是由人來編寫和維護的。保證你的代碼是描述性的,包含好的註釋,並且容易被他人理解。好的代碼註釋傳達上下文和目標。不要簡單地重申組件或者 class 名稱。
Be sure to write in complete sentences or larger comments and succinct phrases for general notes.
/* Bad example *//* Modal header */.modal-header { ...}/* Good example *//* Wrapping element for .modal-title and .modal-close */.modal-header { ...}
Class 命名
保持 Class 命名爲全小寫,可以使用短劃線(不要使用下劃線和 camelCase 命名)。短劃線應該作爲相關類的自然間斷。(例如,
.btn
和.btn-danger
)。避免過度使用簡寫。
.btn
可以很好地描述 button,但是.s
不能代表任何元素。Class 的命名應該儘量短,也要儘量明確。
使用有意義的名稱;使用結構化或者作用目標相關,而不是抽象的名稱。
命名時使用最近的父節點或者父 class 作爲前綴。
使用
.js-*
classes 來表示行爲(相對於樣式),但是不要在 CSS 中包含這些 classes。
/* Bad example */.t { ... }.red { ... }.header { ... }/* Good example */.tweet { ... }.important { ... }.tweet-header { ... }
選擇器
使用 classes 而不是通用元素標籤來優化渲染性能。
避免在經常出現的組件中使用一些屬性選擇器 (例如,
[class^="..."]
)。瀏覽器性能會受到這些情況的影響。減少選擇器的長度,每個組合選擇器選擇器的條目應該儘量控制在 3 個以內。
只在必要的情況下使用後代選擇器 (例如,沒有使用帶前綴 classes 的情況).
擴展閱讀:
/* Bad example */span { ... }.page-container #stream .stream-item .tweet .tweet-header .username { ... }.avatar { ... }/* Good example */.avatar { ... }.tweet-header .username { ... }.tweet .avatar { ... }
代碼組織
以組件爲單位組織代碼。
制定一個一致的註釋層級結構。
使用一致的空白來分割代碼塊,這樣做在查看大的文檔時更有優勢。
當使用多個 CSS 文件時,通過組件而不是頁面來區分他們。頁面會被重新排列,而組件移動就可以了。
/* * Component section heading */.element { ... }/* * Component section heading * * Sometimes you need to include optional context for the entire component. Do that up here if it's important enough. */.element { ... }/* Contextual sub-component or modifer */.element-heading { ... }
編輯器配置
根據以下的設置來配置你的編輯器,來避免常見的代碼不一致和醜陋的 diffs。
使用四個空格的 soft-tabs。
在保存時刪除尾部的空白字符。
設置文件編碼爲 UTF-8。
在文件結尾添加一個空白行。
參照文檔,將這些設置應用到項目的 .editorconfig
文件。 例如,Bootstrap 中的.editorconfig
文件。 通過 關於 EditorConfig 瞭解更多內容。
JavaScript
縮進,分號,單行長度
一律使用4個空格
連續縮進 同樣適用4個空格,跟上一行對齊
Statement 之後一律以分號結束, 不可以省略
單行長度,理論上不要超過80列,不過如果編輯器開啓 soft wrap 的話可以不考慮單行長度
接上一條,如果需要換行,存在操作符的情況,一定在操作符後換行,然後換的行縮進4個空格
這裏要注意,如果是多次換行的話就沒有必要繼續縮進了,比如說右邊第二段這種就是最佳格式。
if (typeof qqfind === "undefined" || typeof qqfind.cdnrejected === "undefined" || qqfind.cdnrejected !== true) { url = "http://pub.idqqimg.com/qqfind/js/location4.js";} else { url = "http://find.qq.com/js/location4.js";}
空行
方法之間加
單行或多行註釋前加
邏輯塊之間加空行增加可讀性
變量命名
標準變量採用駝峯標識
使用的ID的地方一定全大寫
使用的URL的地方一定全大寫, 比如說 reportURL
涉及Android的,一律大寫第一個字母
涉及iOS的,一律小寫第一個,大寫後兩個字母
常量採用大寫字母,下劃線連接的方式
構造函數,大寫第一個字母
var thisIsMyName;var goodID;var AndroidVersion;var iOSVersion;var MAX_COUNT = 10;function Person(name) { this.name = name}
字符常量
一般情況下統一使用 '' 單引號
null的使用場景
初始化一個將來可能被聲明爲一個對象的變量。
與一個可能是對象或者非對象的初始化變量相比。
傳入一個對象待定的函數。
作爲一個對象待定的函數的返回值。
不適合null的使用場景
不要使用null來測試一個一個變量是否存在。
不要用null來測試一個沒聲明的變量。
undefined使用場景
永遠不要直接使用undefined進行變量判斷
使用字符串 "undefined" 對變量進行判斷
// Badvar person;console.log(person === undefined); //true// Goodconsole.log(typeof person); // "undefined"
Object Literals
// Badvar team = new Team();team.title = "AlloyTeam";team.count = 25;// Good semi colon 採用 Followed by space 的形式var team = { title: "AlloyTeam", count: 25};
Array Literals
// Badvar colors = new Array("red", "green", "blue");var numbers = new Array(1, 2, 3, 4);// Goodvar colors = [ "red", "green", "blue" ];var numbers = [ 1, 2, 3, 4 ];
單行註釋
雙斜線後,必須跟註釋內容保留一個空格
可獨佔一行, 前邊不許有空行, 縮進與下一行代碼保持一致
可位於一個代碼行的末尾,注意這裏的格式
// Goodif (condition) { // if you made it here, then all security checks passed allowed();}var zhangsan = "zhangsan"; // 雙斜線距離分號四個空格,雙斜線後始終保留一個空格
多行註釋格式
最少三行, 格式如右
前邊留空一行
何時使用
難於理解的代碼段
可能存在錯誤的代碼段
瀏覽器特殊的HACK代碼
想吐槽的產品邏輯, 合作同事
業務邏輯強相關的代碼
/* * 註釋內容與星標前保留一個空格 */
文檔註釋
各類標籤 @param @method 等 參考 http://usejsdoc.org/
格式如右
用在哪裏
所有的方法
所有的構造函數
所有的全局變量
/** * here boy, look here , here is girl * @method lookGril * @param {Object} balabalabala * @return {Object} balabalabala */
括號對齊
標準示例 括號前後有空格, 花括號起始 不另換行,結尾新起一行
花括號必須要, 即使內容只有一行, 決不允許右邊第二種情況
涉及 if for while do...while try...catch...finally 的地方都必須使用花括號
// Goodif (condition) { doSomething();}if (condition) doSomething(); doSomethingElse();
if else else前後留有空格
if (condition) { doSomething();} else { doSomethingElse();}
switch
採用右邊的格式, switch和括號之間有空格, case需要縮進, break之後跟下一個case中間留一個blank line
花括號必須要, 即使內容只有一行, 決不允許右邊第二種情switch 的 falling through 一定要有註釋特別說明, no default 的情況也需要註釋特別說明況
switch (condition) { case "first": // code break; case "third": // code break; default: // code}switch (condition) { // obvious fall through // 這裏爲啥JSHint默認就會放過,因爲 case "first" 內無內容 case "first": case "second": // code break; case "third": // code /* falls through */ // 這裏爲啥要加這樣的註釋, 明確告知JSHint放過此處告警 default: // code}switch(condition) { case "first": // code break; case "second": // code break; // no default}
for
普通for循環, 分號後留有一個空格, 判斷條件等內的操作符兩邊不留空格, 前置條件如果有多個,逗號後留一個空格
for-in 一定要有 hasOwnProperty 的判斷, 否則 JSLint 或者 JSHint 都會有一個 warn
var values = [ 1, 2, 3, 4, 5, 6, 7 ], i, len;for (i=0, len=values.length; i<len; i++) { process(values[i]);}var prop;for (prop in object) { // 注意這裏一定要有 hasOwnProperty 的判斷, 否則 JSLint 或者 JSHint 都會有一個 warn ! if (object.hasOwnProperty(prop)) { console.log("Property name is " + prop); console.log("Property value is " + object[prop]); }}
變量聲明
所有函數內變量聲明放在函數內頭部,只使用一個 var(多了JSLint報錯), 一個變量一行, 在行末跟註釋, 註釋啊,註釋啊,親
function doSomethingWithItems(items) { var value = 10, // 註釋啊,註釋啊,親 result = value + 10, // 註釋啊,註釋啊 i, // 註釋啊,註釋啊,親 len; // 註釋啊,註釋啊,親 for (i=0, len=items.length; i < len; i++) { doSomething(items[i]); }}
函數聲明
一定先聲明再使用, 不要利用 JavaScript engine的hoist特性, 違反了這個規則 JSLint 和 JSHint都會報 warn
function declaration 和 function expression 的不同,function expression 的()前後必須有空格,而function declaration 在有函數名的時候不需要空格, 沒有函數名的時候需要空格。
函數調用括號前後不需要空格
立即執行函數的寫法, 最外層必須包一層括號
"use strict" 決不允許全局使用, 必須放在函數的第一行, 可以用自執行函數包含大的代碼段, 如果 "use strict" 在函數外使用, JSLint 和 JSHint 均會報錯
function doSomething(item) { // do something}var doSomething = function (item) { // do something}// GooddoSomething(item);// Bad: Looks like a block statementdoSomething (item);// Goodvar value = (function() { // function body return { message: "Hi" }}());// Good(function() { "use strict"; function doSomething() { // code } function doSomethingElse() { // code }})();
雜項
完全避免 == != 的使用, 用嚴格比較條件 === !==
eval 非特殊業務, 禁用!!!
with 非特殊業務, 禁用!!!
http://alloyteam.github.io/code-guide/#css-media-queries