不管是下面的準則或您自己的準則,只要用同樣的準則即可。如有任何不對之處,請不吝指教。若想新增或貢獻內容,請至 GitHub 來提 issue。
不管有多少個貢獻者,每行程式碼都應該像是同一人所寫。
</li>
或 </body>
)。<!DOCTYPE html>
<html lang="zh-Hant-TW">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Page title - 建議控制在 65 個字元以內, Google 搜尋結果平均可以顯示 35 個字 (中文以 2 個字元計算)</title>
</head>
<body>
<img src="images/company-logo.png" alt="Company">
<h1 class="hello-world">Hello, world!</h1>
</body>
</html>
每個 HTML 頁面開頭使用這個簡單的 doctype,來啟用標準模式。每個瀏覽器將會有更加一致的 render 結果。
<!DOCTYPE html>
<html>
<head>
</head>
</html>
引用自 HTML5 規格:
鼓勵作者指定根 HTML 元素的 lang 屬性,給予文件使用的語言。這有助於語音合成工具判定要使用的發音以及翻譯工具判斷使用的規則等等。
zh-Hant-TW 係根據目前的規範依照「RFC 5646 Tags for Identifying Languages」
<html lang="zh-Hant-TW">
<!-- ... -->
</html>
IE 支援使用 <meta>
標籤來指定 IE 是否應該呈現指定的版本。除非另有其他考量,否則它是最實用指定
IE 瀏覽器使用最新模式 edge mode 的方法
深入閱讀請查閱 stackoverflow 的討論 What's the difference if <meta http-equiv=“X-UA-Compatible” content=“IE=edge”> exists or not?.
<meta http-equiv="x-ua-compatible" content="ie=edge">
明確宣告字元編碼來快速簡單地確保內容 render 正確無誤。
<head>
<meta charset="UTF-8">
</head>
根據 HTML5 規範,引用 JavaScript 與 CSS 檔案時,無需指定 CSS 與 JavaScript 的 type
。因為預設值便是 text/css
和 text/javascript
。
<!-- External CSS -->
<link rel="stylesheet" href="code-guide.css">
<!-- In-document CSS -->
<style>
/* ... */
</style>
<!-- JavaScript -->
<script src="code-guide.js"></script>
在不犧牲實用性的前提下,盡力維持 HTML 的標準與語義。盡量使用簡潔、簡單的 Markup。
HTML 屬性應按照特定順序撰寫,確保程式碼的易讀性。
class
id
, name
data-*
src
, for
, type
, href
, value
title
, alt
aria-*
, role
Class 是為了重用的元素而生,應該排第一位。ID 具體得多,應盡量少用(可用場景像是頁內書籤),所以排第二位。
<a class="..." id="..." data-modal="toggle" href="#">
Example link
</a>
<input class="form-control" type="text">
<img src="..." alt="...">
布林屬性是不需要寫值的屬性。XHTML 要求你宣告數值,但 HTML5 不需要。
深入閱讀請查閱 WhatWG 關於布林屬性一節:
元素有布林屬性存在即代表 true value,反之不存在則代表 false value。
如果一定要附上屬性的數值的話,則無需遵守 WhatWG 的這條規則:
如果有寫屬性的話,其數值必須是空字串或是屬性的標準名稱(需符合 ASCII 不分大小寫的規則),且前後不可有空白。
長話短說,不用寫數值。
<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="...">
Markup 寫在 JavaScript 裡,不僅難找、也更難編輯,性能更是差。盡量避免在 JavaScript 裡撰寫 Markup。
.navbar
.header
.logo
.workspace
.content
.content__header
.content__body
.sidebar
.footer
.open
: drop-down.hover
: mouseover.selected
: multi-select.active
: slideshow, tab.current
: pager:
後面留一個空白。box-shadow
,inset
前方逗號後有留空白)。.5
,不要寫成 0.5
。#fff
。#fff
來取代 #ffffff
。input[type="text"]
。margin: 0;
而不是 margin: 0px;
。對於這裡使用的術語有任何問題嗎?請參考維基百科上關於層疊樣式表條目裡的語法小節。
/* 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: 0 0 15px;
background-color: rgba(0, 0, 0, .5);
box-shadow: 0 1px 2px #ccc, inset 0 1px 0 #fff;
}
相關的屬性宣告應以下列順序分組:
Content: 內容本身最重要,需定義在最前面較清楚(例如:偽元素內容)。
Positioning: 可以從正常文件順序裡移除元素(out of flow)的位置宣告,或是覆寫牽扯到 Box Model 的樣式。
顯示 & Box Model: 決定元件的位置與尺寸 或 與 Box Model 相關。
其它只在元件內部起作用的屬性放後面,因為這不會影響前面。
Typography: 文字樣式呈現。
Visual: 其他視覺樣式呈現。
Animation: 動畫因為較不影響排版,所以放最後。
關於屬性順序的完整列表,請參考 Recess、idiomatic-css #declaration-order。
.declaration-order {
/* 元素內容 Content */
content: ".";
/* 位置 Positioning */
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 2;
float: right;
/* 顯示 & Box Model */
display: block;
flex-direction: column;
flex-grow: 1;
justify-content: space-between;
align-items: center;
overflow: hidden;
box-sizing: border-box;
width: 100px;
height: 100px;
border: 1px solid #e5e5e5;
margin: 10px;
padding: 10px;
vertical-align: middle;
/* 文字樣式 Typography */
font: normal 13px "Helvetica Neue", sans-serif;
font-weight: 600;
font-size: 12px;
line-height: 1.5;
letter-spacing: 2px;
color: #333;
text-align: center;
text-overflow: ellipsis;
/* 可視化 Visual */
background-color: #f5f5f5;
background-image: url(/img/icon-flash.svg);
border-radius: 3px;
border-collapse: collapse;
box-shadow: 0 1px 3px 0 #222;
/* 其他雜項 Misc */
opacity: 1;
visibility: visible;
white-space: nowrap;
/* 動畫效果 Animation */
animation-name: hiddenTip;
animation-duration: .5s;
animation-delay: 3s;
animation-fill-mode: forwards;
transform: translateY(10px);
transition: opacity .3s;
}
將 Media Query 與其最相關的規則放在一起。別把他們放在 CSS 檔案最後面,或是獨立成另外的樣式表。這樣只會讓之後接手的人錯過他們。右邊是個經典範例。
.element { ... }
.element-avatar { ... }
.element-selected { ... }
@media (min-width: 480px) {
.element { ...}
.element-avatar { ... }
.element-selected { ... }
}
使用帶有各家廠商前綴的屬性時,縮排每個屬性、垂直對齊,以便多行編輯。
/* 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 Validator 表示 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-image: 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 標題只會設定 top 與 bottom margin,所以只要更改這兩個值即可。濫用特性縮寫只會寫出更差的程式碼,也會有無謂的覆寫與不預期的副作用。
Mozilla Developer Network 有篇很好的專文,給不熟悉記法與行為的開發者:特性簡寫。
/* Bad example */
.element {
margin: 0 0 10px;
background: red;
background: url("image.jpg");
border-radius: 3px 3px 0 0;
}
/* Good example */
.element {
margin-bottom: 10px;
background-color: red;
background-image: url("image.jpg");
border-top-left-radius: 3px;
border-top-right-radius: 3px;
}
避免無謂的巢狀。可用巢狀不表示都要用巢狀。
只在多元素需要巢狀,或是需要在父元素下增加樣式的場景下使用巢狀。
// Without nesting
.table > thead > tr > th { … }
.table > thead > tr > td { … }
// With nesting
.table > thead > tr {
> th { … }
> td { … }
}
程式碼是由人來撰寫與維護的。
確保程式碼精準描述、有良好的註解,讓別人看起來是很親切。
好的註解傳遞意圖、意境。
註解不要只是重複元件或是 Class 的名稱。
長註解記得使用完整的句子,一般的筆記用簡潔的用語。
/* Bad example */
/* Modal header */
.modal-header {
...
}
/* Good example */
/* Wrapping element for .modal-title and .modal-close */
.modal-header {
...
}
.btn
和 .btn-danger
)。.btn
很好,但 .s
不代表任何元素。.js-*
Class 來標示行為(相對樣式來說),這些類別放在另外的 CSS。/* Bad example */
.t { ... }
.red { ... }
.header { ... }
/* Good example */
.tweet { ... }
.important { ... }
.tweet-header { ... }
[class^="..."]
)。大家都知道這會影響到瀏覽器的性能。延伸閱讀:
/* Bad example */
span { ... }
.page-container #stream .stream-item .tweet .tweet-header .username { ... }
.avatar { ... }
/* Good example */
.avatar { ... }
.tweet-header .username { ... }
.tweet .avatar { ... }
/*
* 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 { ... }
羅列目前較有 z-index 問題如下:
基本 z-index
= order 數字 * 1000,
有異動需更新文件。
使用 webpack v1.10.1 uglify 或是 css-loader v0.28.7 minimize, 中文字元後會多一個 \(比如:
\5FAE\8EDF\6B63\9ED1\9AD4
變成
\\5FAE\8EDF\6B63\9ED1\9AD4
。
因此只使用英文字體名稱,並且在字型名稱中有空格時加上雙引號,避免 bundle 的結果 encode 錯誤。
.selector {
/* 可能造成 encode 錯誤 */
font-family: "微軟正黑體", Helvetica, Verdana, sans-serif;
font-family: \5FAE\8EDF\6B63\9ED1\9AD4, Helvetica, Verdana, sans-serif;
/* 建議的寫法 */
font-family: "Microsoft JhengHei", Helvetica, Verdana, sans-serif;
}
若專案上有使用到cssnano,在使用css Animation的@keyframes時cssnano會自動將@keyframes的名稱重新命名,並依照字母順序排列(如右側範例)。
為了避免不同專案的@keyframes命名衝突,請於:
{ reduceIdents: false }
/* reduceIdents */
/* before */
@keyframes fadeOut {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
.box {
animation-name: fadeOut;
}
/* after */
@keyframes a {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
.box {
animation-name: a;
}
推薦閱讀 Essential Image Optimization 深入瞭解圖片的最佳化技巧
推薦閱讀 Essential Image Optimization 深入瞭解圖片的最佳化技巧
V8 6.0 引擎的新架構 TurboFan 對 ES2015 的語法有更好的效能 資料來源
// 聲明式 (Declarative), V8 引擎處理的效能較佳
if (obj !== undefined) {
return obj.x;
}
/*
... ...........
23 cmpq [r13-0x60],rax
27 jz 72
... ...........
*/
// 晦澀難解 (Obscure),V8 引擎轉譯較多的 bytes code,效能不彰
if (obj) {
return obj.x;
}
/*
... ...........
27 cmpq [r13-0x40],rax
31 jz 128
37 test al,0x1
39 setzl bl
42 movzxbl rbx,rbx
45 cmpl rbx,0x0
48 jnz 185
54 cmpq [r13-0x38],rax
58 jz 128
64 movq rdx,[rax-0x1]
68 testb [rdx+0xc],0x10
72 jnz 128
78 cmpq [r13+0x50],rdx
82 jz 160
160 vmovsd xmm0,[rax+0x7]
165 movq [rbp-0x18],rbx
169 vxorpd xmm1,xmm1,xmm1
173 vucomisd xmm1,xmm0
177 jz 128
179 movq rbx,[rbp-0x18]
183 jmp 88
185 movq [rbp-0x18],rbx
189 cmpq rax,0x0
193 jz 128
195 movq rbx,[rbp-0x18]
199 jmp 88
... ...........
*/
// -----------------------------------------
// 聲明式 (Declarative)
function foo6 (f, ...args) {
return f(...args);
}
// 晦澀難解 (Obscure)
function foo5 (f) {
switch (arguments.length) {
case 1: return f();
case 2: return f(arguments[1]);
case 3: return f(arguments[1], arguments[2]);
default: {
var args = [];
for (var i = 1; i < arguments.length; ++i) {
args[i - 1] = arguments[i];
}
return f.apply(undefined, args);
}
}
}
使用下列設定來設定編輯器,避免掉常見的程式碼不一致和醜陋的 diffs:
考慮將以上偏好應用到專案的 .editorconfig
檔案並撰寫文件說明。舉個例子,參考 Bootstrap 的 .editorconfig
。瞭解更多內容,請參考 editorconfig.org。