參考airbnb規范,該規范是airbnb定義的eslint規范,從github上來看,受歡迎程度很高,選擇這個做為規范標準
ESLint基礎知識
安裝
npm install --save-dev eslint
設置基本配置(生成.eslintrc)
./node_modules/.bin/eslint --init
運行相關檢查
./node_modules/.bin/eslint 待檢查js文件
parserOptions檢驗ecma版本,以及ecma的一些特性
{
"parserOptions": {
"ecmaVersion": 6, //指定ECMAScript支持的版本,6為ES6
"sourceType": "module", //指定來源的類型,有兩種”script”或”module”
"ecmaFeatures": {
"jsx": true//啟動JSX
},
}
}
Parser設定對腳步的解析,默認是使用esprima,還可以設置為babel-eslint(目前項目都會使用es6的一些語法,所以這個參數一般都要設為babel-eslint)
對腳步解析是指把源碼轉成可AST(Abstract Syntax Tree抽象語法樹,是源代碼語法所對應的樹狀結構)
"parser": "babel-eslint" // 要先安裝babel-eslint
Environments指定腳本的運行環境,不同的環境會有不同的預定義全局變量。
"env": {
"browser": true,
"commonjs": true,
"es6": true
}, // 可設定參數很多,具體可以參考官方文檔
extends設置繼承的規則集,可以是單個字符也可以是多個配置的數組
"extends": "eslint:recommended", // 啟用核心配置規則
除了可以使用核心配置外,還可以使用可別人共享的配置,extends屬性值可以省略包名的前綴 eslint-config-,比如使用eslint-config-standard
使用這個規則時,需要先安裝
npm install --save-dev eslint-config-standard eslint-plugin-standard eslint-plugin-promise eslint-plugin-import eslint-plugin-node
在相關配置中使用這個規則
"extends": "standard"
另外雖然可以自己設定相關配置,然后共享出來,這里直接選用airbnb的標準進行代碼檢查
eslint-config-airbnb的標準需要eslint-plugin-jsx-a11y、eslint-plugin-react、eslint-plugin-import依賴,安裝時要一并安裝
npm install --save-dev eslint-config-airbnb eslint-plugin-jsx-a11y eslint-plugin-import eslint-plugin-react
"extends": "airbnb"
globals 設置可以是全局變量的變量("extends": "eslint:recommended"會啟用no-undef禁用未聲明的變量)
// var1和var2是全局變量,var1可以被重寫,var2不可以被重寫
{
"globals": {
"var1": true,
"var2": false
},
}
eslint rules規則
rules: {
rules name: [錯誤級別, 相應規則]
}
rules name:就是對應的限制規則
錯誤級別:
- "off"/0 關閉規則
- "warn"/1 警告
- "error"/2 錯誤
"rules": {
"indent": ["error", 4] // 限制使用4個空格
}
plugins eslint允許使用的第三方插件,在使用第三方插件時,如果需要設置相關規則可以使用插件名/規則ID的形式添加
{
"plugins": [
"plugin1"
],
"rules": {
"eqeqeq": "off",
"curly": "error",
"quotes": ["error", "double"],
"plugin1/rule1": "error"
}
}
部分代碼過濾,不進行相關檢查
// 使用方式如下,如果下面的代碼不加eslint相關過濾信息,在進行eslint檢查時會報no-console的錯誤,加上后不再進行相關檢查,這個方式最好不要使用,除非是在開發過程中臨時調試代碼時使用
var add = function () {
/* eslint-disable */
console.log('add');
/* eslint-enable */
};
// 和注釋一樣,也可以使用//來過濾
alert('foo'); // eslint-disable-line
設置使用eslint時的檢查范圍
使用eslint檢查代碼時,ESLint將自動在要檢測的文件目錄里尋找配置文件,然后緊接著是父級目錄,一直到文件系統的根目錄(當有多個檢查文件時,離檢測的文件最近的.eslintrc文件有最高優先級),在項目開發時,我們一般會希望檢查規則一致,所以通常只會在項目的根目錄設置一個.eslintrc,避免在其他子目錄下添加這個配置文件,如果想要eslint只檢查當前目錄忽略其他目錄,可以在配置中添加root: true,把相關檢測限制到一個特定的項目
home
└── user
├── .eslintrc <- 根目錄配置
└── projectA
├── .eslintrc <- 項目配置
└── lib
├── .eslintrc <- 庫文件配置
└── main.js
// 如果在lib下的配置文件中設置了root:true,在對main.js文件檢查時會忽略項目配置
如果同一目錄下 .eslintrc 和 package.json 同時存在,.eslintrc 優先級高會被使用,package.json 文件將不會被使用,不要在package.json的文件中添加檢測規則文件
設置eslint檢查的忽略文件
在項目根目錄創建一個.eslintignore文件,用來告訴ESLint去忽略特定的文件和目錄,設定規則如下:
- 以#開頭的被當作注釋,不會影響忽略
- 路徑是相對于.eslintignore的位置
- 忽略模式同.gitignore規范
- 以!開頭是否定模式,它將會重新包含一個之前
// 一個模擬的例子,下面例子表示在lib目錄下所有目錄下的js都不進行代碼檢查
src/assets/lib/**/*.js
airbnb規范標準-js部分
標準中分成建議使用和校驗(下面使用[0]代碼建議使用,[1]代表校驗),建議使用并不會報錯,校驗會報錯(判斷依據是我使用airbnb官方實例,在配置好的環境中運行,看是否會報錯)
引用
- 對變量的聲明不要使用var,如果是聲明后不再重現分配的變量要使用const,如果是重現分配的變量要使用let [1]
- 注意let和const的塊級作用域 [0]
對象
- 使用對象直接量創建對象 [1]
// bad
const item = new Object();
// good
const item = {};
- 如果一個對象的key值包含動態屬性,要保證對象的所有屬性都是在一個地方進行定義 [0]
function getKey(k) {
return `a key named ${k}`;
}
// bad
const obj = {
id: 5,
name: 'San Francisco',
};
obj[getKey('enabled')] = true;
// good
const obj = {
id: 5,
name: 'San Francisco',
[getKey('enabled')]: true,
};
// 從上面的代碼我們可以看出,這個建議的是要我們把與對象相關的屬性,都在一個地方定義,便于維護
- 在定義方法時,使用簡寫的方式進行定義 [1]
// bad
const atom = {
value: 1,
addValue: function (value) {
return atom.value + value;
},
};
// good
const atom = {
value: 1,
addValue(value) {
return atom.value + value;
},
};
- 在定義對象屬性時,如果key值和value值同名,要使用簡寫形式 [1]
const lukeSkywalker = 'Luke Skywalker';
// bad
const obj = {
lukeSkywalker: lukeSkywalker,
};
// good
const obj = {
lukeSkywalker,
};
- 在定義對象時,簡寫的屬性和非簡寫的屬性要按順序寫,不要混著寫 [0]
const anakinSkywalker = 'Anakin Skywalker';
const lukeSkywalker = 'Luke Skywalker';
// bad
const obj = {
episodeOne: 1,
twoJediWalkIntoACantina: 2,
lukeSkywalker,
episodeThree: 3,
mayTheFourth: 4,
anakinSkywalker,
};
// good
const obj = {
lukeSkywalker,
anakinSkywalker,
episodeOne: 1,
twoJediWalkIntoACantina: 2,
episodeThree: 3,
mayTheFourth: 4,
};
- 在定義對象時,key值只有在無效識別時添加單引號 [1]
// bad
const bad = {
'foo': 3,
'bar': 4,
'data-blah': 5,
};
// good
const good = {
foo: 3,
bar: 4,
'data-blah': 5,
};
- 不允許直接使用Object.prototype的相關方法,比如hasOwnProperty, propertyIsEnumerable, 和 isPrototypeOf.因為在定義對象時,有可能會覆蓋了這些方法 [1]
const item = {
name: 'rede',
};
// bad
console.log(item.hasOwnProperty('name'));
// good
console.log(Object.prototype.hasOwnProperty.call(item, 'name'));
// best
const has = Object.prototype.hasOwnProperty;
console.log(has.call(item, 'name'));
- 使用對象展開符淺復制對象,不要使用Object.assign [0]
// very bad
// 本意是復制一個對象,在從復制出的對象上刪除某個值,但實際上原始對象的值也會被影響
const original = { a: 1, b: 2 };
const copy = Object.assign(original, { c: 3 });
delete copy.a;
// bad
// 可以達到預期目的,但在寫法上可讀性不好
const original = { a: 1, b: 2 };
const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }
delete copy.a;
// good
const original = { a: 1, b: 2 };
const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }
const { a, ...noA } = copy; // noA => { b: 2, c: 3 }
數組
- 在定義數組時,使用數組直接量,不要使用new Array的形式 [1]
// bad
const items = new Array();
// good
const items = [];
- 在向數組添加元素時,不要直接賦值,通過push添加(注意指的是添加,并不包括修改的情況) [0]
const items = [];
// bad
items[items.length] = 'test';
// good
items.push('test');
- 復制數組時,使用展開操作符 [0]
const items = [1, 2, 4, 8, 16];
// bad
const len = items.length;
const itemsCopy = [];
let i;
for (i = 0; i < len; i += 1) {
itemsCopy[i] = items[i];
}
// good
const itemsCopy = [...items];
- 在把類數組對象轉為數組對象時,使用展開操作符來替代Array.from [0]
const foo = document.querySelectorAll('.foo');
// good
const nodes = Array.from(foo);
// best
const nodes = [...foo];
- 使用Array.from代替展開操作符來處理針對數組的映射操作,可以避免操作創建中間數組 [0]
const foo = [1, 2, 3];
// bad
const baz = [...foo].map(x => x + x);
// good
const baz = Array.from(foo, x => x + x);
- 數組的某些方法會存在回調函數,eslint會強制回調函數使用return,不過airbnb的規范中認為,如果某些數組的回調只是單條語句的那種,是可以省略return的 [0]
主要是一些可以對數組進行迭代的方法,比如every,filter,find,findIndex,map,reduce,some,sort
// airbnb同樣標記這種寫法為good
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
// 下面的寫法也是good,兩種寫法都沒有問題,不過可能下面這種更易讀些
[1, 2, 3].map(x => (x + 1)*x)
// airbnb下面標準的是bad,剛開始我也以為是針對這個規則的說法
// 實際運行下,發現針對這種寫法報的錯誤是no-else-return,就是不允許在return之后使用else,哪怕看起來是另外的邏輯分支
inbox.filter((msg) => {
const { subject, author } = msg;
if (subject === 'Mockingbird') {
return author === 'Harper Lee';
} else {
return false;
}
});
// 后來嘗試了下,結合eslint頁面中的規則,估計這個地方的代碼應該是這樣
// 再次運行監測,會報幾個錯誤,包括提到的array-callback-return,就是這里必須設定return的值
const inbox = ['black', 'white', 'yellow'];
inbox.filter((msg) => {
const { subject, author } = msg;
if (subject === 'Mockingbird') {
return author === 'Harper Lee';
} else {
return;
}
});
// good
const inbox = ['black', 'white', 'yellow'];
inbox.filter((msg) => {
const { subject, author } = msg;
if (subject === 'Mockingbird') {
return author === 'Harper Lee';
}
return false;
});
- 如果數組有多行,在數組的中括號前后要進行換行 [0]
// bad
const arr = [
[0, 1], [2, 3], [4, 5],
];
const numberInArray = [
1, 2,
];
// good
const arr = [[0, 1], [2, 3], [4, 5]];
const numberInArray = [
1,
2,
];
解構
- 優先使用對象解構來訪問對象屬性 [1]
// bad
function getFullName(user) {
const firstName = user.firstName;
const lastName = user.lastName;
return `${firstName} ${lastName}`;
}
// good
function getFullName(user) {
const { firstName, lastName } = user;
return `${firstName} ${lastName}`;
}
// best
function getFullName({ firstName, lastName }) {
return `${firstName} ${lastName}`;
}
- 優先使用數組解構從數組索引創建變量 [0]
const arr = [1, 2, 3, 4];
// bad
const first = arr[0];
const second = arr[1];
// good
const [first, second] = arr;
- 用對象解構去處理多個返回值 [1]
//
const input = {
left: '0px', right: '0px', top: '0px', bottom: '0px',
};
// bad
function processInput(input) {
const left = input.left;
const right = input.right;
const top = input.top;
const bottom = input.bottom;
return [left, right, top, bottom];
}
const [left, __, top] = processInput(input);
// good
function processInput({
left, right, top, bottom,
}) {
return {
left, right, top, bottom,
};
}
const { left, top } = processInput(input);
字符串
- 字符串使用單引號 [1]
// bad
const name = "Capt. Janeway";
// bad
const name = `Capt. Janeway`;
// good
const name = 'Capt. Janeway';
- 當字符串過長時,不要使用字符串連接符寫成多行 [1][0]
// bad
// 此時報的檢查錯誤是no-multi-str,保證字符串不分兩行書寫
const errorMessage = 'This is a super long error that was thrown because \
of Batman. When you stop to think about how Batman had anything to do \
with this, you would get nowhere \
fast.';
// bad
// 此時運行代碼檢查并不會報錯,針對這種寫法應該是建議不要這么寫
const errorMessage = 'This is a super long error that was thrown because ' +
'of Batman. When you stop to think about how Batman had anything to do ' +
'with this, you would get nowhere fast.';
// good
// 要是感覺代碼過長不方便看時,可以在編譯器上做相關設置,設置自動換行
const errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.';
- 當字符串和變量要進行連接時,要使用模版字面量 [1]
// bad
function sayHi(name) {
return 'How are you, ' + name + '?';
}
// bad
// 檢查的錯誤template-curly-spacing,禁止模版字符串前后使用空格
function sayHi(name) {
return `How are you, ${ name }?`;
}
// good
function sayHi(name) {
return `How are you, ${name}?`;
}
針對字符串不要使用eval [1]
在字符串中只在有意義的地方使用轉義字符 [1]
// bad
const foo = '\'this\' is \"quoted\"';
// good
const foo = '\'this\' is "quoted"';
函數
- 在定義函數時,使用指定名詞的函數表達式,而不是匿名表達式 [0]
關于這點建議,最開始其實并不能理解,因為自己平時使用最多的也就是第二種方式,后來參考github這片文章的討論上的討論,感覺這么定義還是有必要的,針對這個我也單獨寫了一篇總結,有興趣可以去看看
// bad
function add (a, b) {
return a + b;
}
// bad
const add = function (a, b) {
return a + b;
}
// good
const add = function myAdd (a, b) {
return a + b;
}
- 立即執行函數要用大括號包裹起來 [1]
// bad
const x = function () { return { y: 1 };}()
...
// not bad
// 隨著現在模塊的普及立即執行函數使用的應該不多了
const x = (function () { return { y: 1 }; }());
- 禁止循環中出現函數,如果需要在循環中定義函數,最好把相關函數使用變量定義好再使用,避免形成閉包 [0]
此處例子是我自己添加非airbnb例子
// bad
// 下面循環中如果把i的定義換成var,那就會形成一個最經典的閉包問題,數組funcs所有的返回值都是10,使用let會避免這個問題
const funcs = [];
for (let i = 0; i < 10; i + 1) {
funcs[i] = function () {
return i;
};
}
...
// 如果使用babel轉上面的代碼
// 在這里可以看出babel進行轉換時會按這規則,把本來定義的函數提取出來單獨定義成一個變量,再進行使用
"use strict";
var funcs = [];
var _loop = function _loop(i) {
funcs[i] = function () {
return i;
};
};
for (var i = 0; i < 10; i + 1) {
_loop(i);
}
// not bad
const funcs = [];
const printI = function printI(i) {
return i;
};
for (let i = 0; i < 10; i + 1) {
funcs[i] = printI(i);
}
...
// babel轉碼后為
// 轉碼前后其實變化不大
"use strict";
var funcs = [];
var printI = function printI(i) {
return i;
};
for (var i = 0; i < 10; i + 1) {
funcs[i] = printI(i);
}
- 禁止在代碼塊中使用函數聲明,不過可以使用函數表達式 [1]
// bad
const currentUser = true;
if (currentUser) {
function test() {
console.log('Nope.');
}
}
// good
const currentUser = true;
let test;
if (currentUser) {
test = () => {
console.log('Nope.');
};
}
- 函數接收的形參不可以使用argumenst [1]
// bad
function foo(name, options, arguments) {
// ...
}
- 函數內部不要使用使用arguments來替代獲取形參,可以使用rest參數獲取多余參數 [1]
//bad
function concatenateAll() {
const args = Array.prototype.slice.call(arguments);
return args.join('');
}
// good
function concatenateAll(...args) {
return args.join('');
}
- 使用默認參數語法,不要去使用存在變化的函數參數 [1]
// bad
// 這個會報no-param-reassign,禁止對 function 的參數進行重新賦值
function handleThings(opts) {
opts = opts || {};
...
}
// bad
// 報的錯誤同上
function handleThings(opts) {
if (opts === 0) {
opts = {};
}
}
// good
function handleThings(opts = {}) {
// ...
}
- 避免對函數默認參數進行不可預期的操作 [1]
// bad
// 檢查時會報no-plusplus,禁用++
// 這個例子應該是要說明,不對默認參數設置不確定的因素,就比如這里的b值,兩次調用返回值都不同,會令人費解
let b = 1;
function count(a = b++) {
console.log(a);
}
count(); // 1
count(); // 2
- 始終將默認參數,放到最后 [0]
// bad
function handleThings(opts = {}, name) {
return { opts, name };
}
// good
function handleThings(name, opts = {}) {
return { opts, name };
}
- 禁用Function構造函數創建函數 [1]
// bad
const x = new Function('a', 'b', 'return a + b');
// good
const x = function backAdd(a, b) {
return a + b;
};
- 強制在函數圓括號前和代碼塊之前使用統一的空格 [1]
// bad
const f = function(){};
const g = function (){};
const h = function() {};
// good
const f = function () {};
const g = function () {};
const h = function () {};
- 禁止對函數參數再賦值 [1]
// bad
// 怎么都想不到為什么要對參數進行賦值,報錯為no-param-reassign
function foo(bar) {
bar = 13;
}
...
// bad
function foo(bar) {
bar.name = 'foo';
}
- 在函數括號內使用一致的換行,如果是多行會要求最后一項帶逗號 [1]
// normal
// 不使用換行
function foo(bar, baz, quux) {
// ...
}
// bad
// 會報function-paren-newline要求使用一致的換行
function foo(bar,
baz,
quux) {
// ...
}
// good
// 最后一行添加了逗號
function foo(
bar,
baz,
quux,
) {
// ...
}
箭頭函數
- 當必須使用匿名函數時(比如回調函數),可以使用箭頭函數 [1]
// bad
[1, 2, 3].map(function (x) {
const y = x + 1;
return x * y;
});
// good
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
- 如果回調函數只是簡單的單行語句,可以省略return [0]
// good
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
// good,提高可讀性
[1, 2, 3].map(x => (x + 1) * x)
- 如果表達式垮多行,將其包裹在括號中,可以提高代碼的可讀性 [0]
// not good
['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call(
httpMagicObjectWithAVeryLongName,
httpMethod,
));
// good
['get', 'post', 'put'].map(httpMethod => (
Object.prototype.hasOwnProperty.call(
httpMagicObjectWithAVeryLongName,
httpMethod,
)
));
- 箭頭函數體只有一個參數時且不使用大括號,可以省略圓括號。其它任何情況,參數都應被圓括號括起來 [0]
// bad
// arrow-parens
[1, 2, 3].map((x) => x * x);
// good
[1, 2, 3].map(x => x * x);
// bad
// 因為此時使用了大括號,箭頭函數后面跟隨了代碼塊
[1, 2, 3].map(x => {
const y = x + 1;
return x * y;
});
- 禁止在可能與比較操作符相混淆的地方使用箭頭函數 [1]
// bad
// 此時的箭頭符號和比較符號看起來很像
const itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize;
// good
const itemHeight = item => (item.height > 256 ? item.largeSize : item.smallSize);
// good
const itemHeight = (item) => {
const { height, largeSize, smallSize } = item;
return height > 256 ? largeSize : smallSize;
};
類和構造函數
- 總是使用class,避免使用prototype [0]
// bad
function Queue(contents = []) {
this.queue = [...contents];
}
Queue.prototype.pop = function () {
const value = this.queue[0];
this.queue.splice(0, 1);
return value;
};
// good
class Queue {
constructor(contents = []) {
this.queue = [...contents];
}
pop() {
const value = this.queue[0];
this.queue.splice(0, 1);
return value;
}
}
- 對類的繼承使用extends [1]
// good
// 這種寫法簡單明了
class student {
constructor({ id } = {}) {
this.name = `stu-${id}`;
}
backName() {
return this.name;
}
}
class classmates extends student {
constructor({ id } = {}) {
super({ id });
this.id = id;
}
id() {
return this.id;
}
}
- 方法可以返回this來進行鏈式調用 [0]
// normal
class student {
constructor({ id } = {}) {
this.name = `stu-${id}`;
}
setAge({ age } = {}) {
this.age = age;
}
setGender({ gender } = {}) {
this.gender = gender;
}
backName() {
return this.name;
}
backInfo() {
return `name: ${this.name}, age: ${this.age}, gender: ${this.gender}`;
}
}
const stu = new student({id: 2333});
stu.setAge({age: 18});
stu.setGender({gender: 'man'});
stu.backInfo();
// good
class student {
constructor({ id } = {}) {
this.name = `stu-${id}`;
}
setAge({ age } = {}) {
this.age = age;
return this;
}
setGender({ gender } = {}) {
this.gender = gender;
return this;
}
backName() {
return this.name;
}
backInfo() {
return `name: ${this.name}, age: ${this.age}, gender: ${this.gender}`;
}
}
const stu = new student({id: 2333});
stu.setAge({age: 18}).setGender({gender: 'man'});
stu.backInfo();
- 定義類時可以定義一個toString方法,只要保證該方法不會產生意外的副作用 [0]
class student {
constructor({ id } = {}) {
this.name = `stu-${id}`;
}
setAge({ age } = {}) {
this.age = age;
return this;
}
setGender({ gender } = {}) {
this.gender = gender;
return this;
}
backName() {
return this.name;
}
backInfo() {
return `name: ${this.name}, age: ${this.age}, gender: ${this.gender}`;
}
toString() {
return this.backName();
}
}
- 如果構造函數只是一個空的構造函數或只是簡單的調用父類,此時構造函數可以省略 [1]
// bad
// 因為此時構造函數constructor沒有任何作用,此時沒必要設置
class Jedi {
constructor() {}
getName() {
return this.name;
}
}
// bad
// 此時只是透傳數據,沒必要使用構造函數
class Rey extends Jedi {
constructor(...args) {
super(...args);
}
}
// godd
class Rey extends Jedi {
constructor(...args) {
super(...args);
this.name = 'Rey';
}
}
- 類的成員禁止重名 [1]
// bad
// 很明顯下面的bar會覆蓋上面的bar方法
class Foo {
bar() { return 1; }
bar() { return 2; }
}
模塊
- 使用模塊import/export輸入輸出代碼塊 [1]
- 使用import時,不要使用通配符,最好明確要導入的代碼塊 [0]
// 另外一個模塊文件,比如是index.js
export const firstName = 'rede';
export const lastName = 'li';
// 引用該模塊
// bad
// 按webpack 4+以上的版本會靜態分析index.js只導入需要的代碼塊,所以明確導入的代碼塊會更有利于減少打包體積
import * as index from './index';
console.log(index.firstName);
// best
// 原例子上還提到了一個good的寫法,不過看了下,感覺這種寫法最好,結合編譯器,還能減小文件代碼
import { firstName } from './index';
console.log(firstName);
- 不要從import中直接導出(export) [0]
// bad
export { firstName as default } from './index';
// good
// 這樣更加明晰
import { firstName } from './index';
export default firstName;
- 同一個路徑的導入只在一個地方導入,禁止重復導入 [1]
// bad
import { firstName } from './index';
import { lastName } from './index';
export default { firstName, lastName };
// good
import { firstName, lastName } from './index';
export default { firstName, lastName };
- 不要export可變綁定 [1]
// bad
let foo = 3;
export { foo };
// good
const foo = 3;
export { foo };
- 如果一個文件只導出一個模塊,默認export優于命名export [1]
// bad
const foo = 3;
export { foo };
// good
const foo = 3;
export default { foo };
- 將所有import導入放在非導入語句上面 [1]
// bad
import { firstName, lastName, backName } from './index';
backName();
import { name } from './test';
export default { firstName, lastName, name };
// good
import { firstName, lastName, backName } from './index';
import { name } from './test';
backName();
export default { firstName, lastName, name };
- 多行導入應該像多行數組那樣進行縮進 [0]
// bad
import { firstName, lastName, year } from './main';
export { firstName, lastName, year };
// good
import {
firstName,
lastName,
year,
} from './main';
export { firstName, lastName, year };
- 禁止在模塊的import中使用Webpack加載語法 [1]
// bad
import fooSass from 'css!sass!foo.scss';
// good
import fooSass from 'foo.scss';
迭代器(Iterators)和生成器(Generators)
- 不要使用iterators,請使用高階函數,例如map、reduce而不是for-in、for-of這樣的循環 [1]
// bad
// no-restricted-syntax這個檢查是禁用了特定語法
const numbers = [1, 2, 3, 4, 5];
let sum = 0;
for (const num of numbers) {
sum += num;
}
export default { sum };
// good
const numbers = [1, 2, 3, 4, 5];
let sum = 0;
numbers.forEach((num) => {
sum += num;
});
export default { sum };
// best
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((total, num) => total + num, 0);
export default { sum };
// bad
// 此時檢查并不會報錯,只是語法建議,不過和下面的good、best進行比較,best寫法更加明晰
const numbers = [1, 2, 3, 4, 5];
const increasedByOne = [];
for (let i = 0; i < numbers.length; i + 1) {
increasedByOne.push(numbers[i] + 1);
}
export default { increasedByOne };
// good
const numbers = [1, 2, 3, 4, 5];
const increasedByOne = [];
numbers.forEach((num) => {
increasedByOne.push(num + 1);
});
export default { increasedByOne };
// best
const numbers = [1, 2, 3, 4, 5];
const increasedByOne = numbers.map(num => num + 1);
export default { increasedByOne };
- 現在不要使用generators(生成器) [0]
// 下面的代碼來源阮一峰老師的《ECMAScript 6 入門》的Generator 函數的語法,在瀏覽器中運行是可以正常運行的,但按照建議規范的建議是因為現在還沒有好的方式轉為ES5,所以不建議使用
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
const hw = helloWorldGenerator();
export default { hw };
- 如果使用了generators,function*是定義generators的專用語法,不可以把function和*分開寫 [1]
屬性
- 使用點語法來訪問對象屬性 [1]
// bad
const luke = {
jedi: true,
age: 28,
};
const isJedi = luke['jedi'];
export default { isJedi };
// good
const luke = {
jedi: true,
age: 28,
};
const isJedi = luke.jedi;
export default { isJedi };
- 當通過變量訪問屬性時要使用中括號 [0]
const luke = {
jedi: true,
age: 28,
};
function getProp(prop) {
return luke[prop];
}
const isJedi = getProp('jedi');
export default { isJedi };
- 求冪運算使用求冪運算符 [1]
// bad
const binary = Math.pow(2, 10);
// good
const binary = 2 ** 10;
變量
- 總是使用const或let聲明變量,避免全局變量污染 [1]
- 每個便利在聲明時,都要使用const或let [1]
// bad
const items = 'rede',
goSportsTeam = true,
dragonball = 'z';
export { items, goSportsTeam, dragonball };
// good
const items = 'rede';
const goSportsTeam = true;
const dragonball = 'z';
export { items, goSportsTeam, dragonball };
- 將所有的const和let的定義分組 [0]
// bad
let i;
const items = getItems();
let dragonball;
const goSportsTeam = true;
let len;
// good
const goSportsTeam = true;
const items = getItems();
let dragonball;
let i;
let length;
- 在需要的地方再對變量進行分配 [0]
// bad
// 這里的checkName中name會調用getName函數,但是name并不是總會返回,如果hasName為test時,并不會返回,所以,應該把對name的定義延后
const getName = function backName() {
return 'rede';
};
function checkName(hasName) {
const name = getName();
if (hasName === 'test') {
return false;
}
return name;
}
// good
const getName = function backName() {
return 'rede';
};
function checkName(hasName) {
if (hasName === 'test') {
return false;
}
const name = getName();
return name;
}
- 禁止使用連續賦值 [1]
// bad
const a = b = c = 1;
// good
const a = 1;
const b = a;
const c = a;
- 避免使用++或--運算符 [1]
// bad
let num = 1;
num++;
--num;
// good
let num = 1;
num += 1;
num -= 1;
- 在=號前后要避免進行換行,如果變量名超過最長限制,要統一換行方式 [0]
// bad
// 此時的換行是沒有必要的
const foo
= 'superLongLongLongLongLongLongLongLongString';
// good
const foo = 'superLongLongLongLongLongLongLongLongString';
// bad
const foo =
superLongLongLongLongLongLongLongLongFunctionName();
// good
const foo = (
superLongLongLongLongLongLongLongLongFunctionName()
);
變量提升
- var聲明會被提升至他們作用域的頂部,但相應的賦值不會提升,let和const的聲明并不會提升,因為其形成了一個暫時性死區
(function example() {
console.log(declaredButNotAssigned); // => undefined
var declaredButNotAssigned = true;
})()
...
function example() {
console.log(declaredButNotAssigned); // => throws a ReferenceError
console.log(typeof declaredButNotAssigned); // => throws a ReferenceError
const declaredButNotAssigned = true;
}
- 匿名函數表達式的變量也會被提升,但函數體不會被提升
function example() {
console.log(anonymous); // => undefined
anonymous(); // => TypeError anonymous is not a function 輸入錯誤,anonymous 不是一個函數
var anonymous = function () {
console.log('anonymous function expression');
};
}
- 命名的函數表達式變量也會被提升,但函數體不會被提升
function example() {
console.log(named); // => undefined
named(); // => TypeError named is not a function,輸入錯誤,named 不是一個函數
superPower(); // => ReferenceError superPower is not defined, ReferenceError(引用錯誤)superPower 未定義
var named = function superPower() {
console.log('Flying');
};
}
- 函數聲明的名詞和函數體都會被提升
function example() {
superPower(); // => Flying
function superPower() {
console.log('Flying');
}
}
比較運算符和等號
使用===和!==,避免使用==和!= [1]
-
諸如if語句之類的條件語句會把其中的值強制進行布爾值轉換,遵循以下簡單規則
- Objects求值為true
- Undefined和Null求值為false
- Numbers如果是+0,-0或NaN求值為false,其他為true
- Strings如果是''求值為false,其他為true
對于布爾值使用簡寫,但對于數字和字符串要進行顯式比較 [0]
const isValid = true;
// bad
// isValid是布爾值
if (isValid === true) {
// ...
}
// good
if (isValid) {
// ...
}
...
const name = 'rede';
// bad
// 比如代碼塊中依賴對name是否為空進行相關邏輯,這時省略對''進行比較是不會影響功能的,但是對代碼的可讀性會產生影響
if (name) {
// ...
}
// good
if (name !== '') {
// ...
}
...
const collection = [];
// bad
if (collection.length) {
// ...
}
// good
if (collection.length > 0) {
// ...
}
- 在case和default中,如果要創建包含詞法聲明的語句塊(let、const、function和class)要使用大括號進行包裹 [1]
// bad
// 詞法聲明在整個switch語句都是可見的,在多個case子句試圖定義相同變量時會報no-redeclare(禁止重新聲明變量)
const num = 1;
switch (num) {
case 1:
let name = 'rede';
break;
case 2:
let name = 'tom';
break;
}
// good
const num = 1;
switch (num) {
case 1: {
const name = 'rede';
break;
}
case 2: {
const name = 'tom';
break;
}
}
- 使用三元操作時,避免進行嵌套 [1]
// bad
// 會降低代碼的易讀性
const bar = 'bar';
const bing = 'bind';
const foo = bar === 'bars' ? (bing === 'bing' ? 'bars bing' : 'bars bind') : 'bar';
export default { foo };
// good
const bar = 'bar';
const bing = 'bind';
const barVal = bar === 'bars' ? 'bars' : 'bar';
const foo = bing === 'bing' ? `${barVal} bing` : `${barVal} bind`;
export default { foo };
- 避免使用一些不必要的三元運算 [1]
// bad
const answer = 1;
const isYes = answer === 1 ? true : false;
export default { isYes };
// good
const answer = 1;
const isYes = answer === 1;
export default { isYes };
- 當多個運算符混合在一個語句中,要添加適當的括號,不要將**和%與+、-,*,/混在一起使用,能提高代碼的可讀性 [1]
// bad
const a = 1;
const b = 0;
const c = 4;
const d = 3;
const foo = a && b < 0 || c > 0 || d + 1 === 0;
const bar = a ** b - 5 % d;
export default { foo, bar };
// good
const a = 1;
const b = 0;
const c = 4;
const d = 3;
const foo = (a && b < 0) || c > 0 || (d + 1 === 0);
const bar = (a ** b) - (5 % d);
export default { foo, bar };
代碼塊
- 使用大括號包裹多行代碼,單行代碼可以強制直接出現不需要換行 [1]
const test = true;
let boo = '';
// bad
if (test)
boo = 'boo';
// good
if (test) boo = 'boo';
export default { boo };
- 如果通過if 和 else使用多行代碼塊,要把else放在if代碼塊閉合括號的同一行 [1]
// bad
const name = 'rede';
let foo = '';
if (name === 'rede') {
foo = 'foo'
}
else {
foo = 'bar';
}
export default { foo };
// good
const name = 'rede';
let foo = '';
if (name === 'rede') {
foo = 'foo';
} else {
foo = 'bar';
}
export default { foo };
- 如果一個if代碼塊使用了return語句,后面的else可以省略,在else if塊中return可分成多個if來return [1]
// bad
function foo (val) {
if (val) {
return 'foo';
} else {
return 'bar';
}
}
export default { foo };
// good
function foo(val) {
if (val) {
return 'foo';
}
return 'bar';
}
export default { foo };
// bad
function foo(val) {
if (val === 'foo') {
return 'foo';
} else if (val === 'bar') {
return 'bar';
} else if (val === 'test') {
return 'test';
}
return 'rede';
}
export default { foo };
// good
function foo(val) {
if (val === 'foo') {
return 'foo';
}
if (val === 'bar') {
return 'bar';
}
if (val === 'test') {
return 'test';
}
return 'rede';
}
export default { foo };
控制語句
- 如果控制語句太長或超過最大行長度,那么每個分組條件可以放單獨一行,但要注意把運算符放在每行的起始處 [0]
// not good
const nv1 = 2;
const nv2 = 3;
const nv3 = 4;
const nv4 = 5;
const nv5 = 6;
const nv6 = 7;
let foo = '';
// not good
if (nv1 === 1 && nv2 === 2 && nv3 === 3 && nv4 === 4 && nv5 === 5 && nv6 === 6) foo = 'foo';
// not good
if (nv1 === 1 && nv2 === 2 && nv3 === 3 &&
nv4 === 4 && nv5 === 5 && nv6 === 6) foo = 'foo';
// good
if (nv1 === 1 && nv2 === 2 && nv3 === 3
&& nv4 === 4 && nv5 === 5 && nv6 === 6) foo = 'foo';
export default { foo };
注釋
- 多行注釋使用/**...*/ [0]
// good
/**
* make() returns a new element
* based on the passed-in tag name
*/
- 單行注釋使用 // ,將單行注釋放在續注釋的語句上方。在注釋之前放置一個空行,除非它位于代碼塊的第一行。 [0]
// bad
const active = true; // is current tab
// good
// is current tab
const active = true;
// bad
function getType() {
console.log('fetching type...');
// set the default type to 'no type'
const type = this.type || 'no type';
return type;
}
// good
function getType() {
console.log('fetching type...');
// set the default type to 'no type'
const type = this.type || 'no type';
return type;
}
// also good
function getType() {
// set the default type to 'no type'
const type = this.type || 'no type';
return type;
}
- 所有注釋符和注釋內容用一個空格隔開,讓它更容易閱讀 [0]
// bad
//is current tab
// good
// is current tab
// bad
/**
*make() returns a new element
*based on the passed-in tag name
*/
// good
/**
* make() returns a new element
* based on the passed-in tag name
*/
給注釋增加FIXME或TODO的前綴,可以幫助其他開發者快速了解這個是否是一個需要重新復查的問題,或是你正在為需要解決問題提出的解決方案,有別于常規注釋 [0]
使用 // FIXME來標識需要修正的問題
使用 // TODO來標識需要實現的問題
還可以使用 // XXX說明注釋處代碼雖然實現了功能,但是實現的方法有待商榷,希望將來能改進,要改進的地方會在說明中簡略說明
空白
- 使用一致的縮進 [1]
這里默認是使用4個空格
- 在大括號前放置一個空格 [1]
// bad
function test(){
console.log('test');
}
// good
function test() {
console.log('test');
}
// bad
dog.set('attr',{
age: '1 year',
breed: 'Bernese Mountain Dog',
});
// good
dog.set('attr', {
age: '1 year',
breed: 'Bernese Mountain Dog',
});
- 在控制語句的小括號前放一個空格,在函數調用及聲明中,不在函數的參數列表前加空格 [1]
const isJedi = true;
// bad
if(isJedi) {
console.log('dd');
}
// good
if (isJedi) {
console.log('dd');
}
// bad
function fight () {
console.log ('Swooosh!');
}
// good
function fight() {
console.log('Swooosh!');
}
- 使用空格把運算符隔開 [1]
// bad
const x=y+5;
// good
const x = y + 5;
- 在文件末尾插入一個空行 [1]
- 長方法鏈式調用時使用縮進,使用一個點開頭,強調該行是一個方法調用,不是一個新的聲明 [1]
估計是怕鏈式調用太長不方便看
// bad
$('#items').find('.selected').highlight().end().find('.open').updateCount();
// good
$('#items').find('.selected').highlight().end()
.find('.open')
.updateCount();
- 在語句塊后和下條語句前留一個空行 [0]
const foo = true;
let bar = '';
// bad
if (foo) {
bar = 'bar';
}
export default { bar };
// good
if (foo) {
bar = 'bar';
}
export default { bar };
- 塊級代碼中禁用多余的空行 [1]
// bad
function bar() {
console.log(foo);
}
// good
function bar() {
console.log(foo);
}
- 不要在圓括號前后加空格 [1]
// bad
if ( foo ) {
console.log(foo);
}
// good
if (foo) {
console.log(foo);
}
- 不要在中括號前后添加空格 [1]
// bad
const foo = [ 1, 2, 3 ];
console.log(foo[ 0 ]);
// good
const foo = [1, 2, 3];
console.log(foo[0]);
- 在大括號前后添加空格 [1]
// bad
const foo = {clark: 'kent'};
// good
const foo = { clark: 'kent' };
- 避免有超過100個字符(包括空格)的代碼行,如果有超過,要適當考慮把代碼進行換行 [1]
保證代碼的可讀性和可維護性
// bad
const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy;
// good
const foo = jsonData
&& jsonData.foo
&& jsonData.foo.bar
&& jsonData.foo.bar.baz
&& jsonData.foo.bar.baz.quux
&& jsonData.foo.bar.baz.quux.xyzzy;
// bad
$.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.'));
// good
$.ajax({
method: 'POST',
url: 'https://airbnb.com/',
data: { name: 'John' },
})
.done(() => console.log('Congratulations!'))
.fail(() => console.log('You have failed this city.'));
逗號
- 在行開頭處不要使用逗號 [1]
// bad
const hero = {
firstName: 'Ada'
, lastName: 'Lovelace'
, birthYear: 1815
, superPower: 'computers'
};
// good
const hero = {
firstName: 'Ada',
lastName: 'Lovelace',
birthYear: 1815,
superPower: 'computers',
};
- 在結尾添加逗號 [1]
// bad
const hero = {
firstName: 'Ada',
lastName: 'Lovelace',
birthYear: 1815,
superPower: 'computers'
};
// good
const hero = {
firstName: 'Ada',
lastName: 'Lovelace',
birthYear: 1815,
superPower: 'computers',
};
因為通過git diff時,能體現出這些差異
// bad - 沒有結尾逗號的 git diff 差異比較
const hero = {
firstName: 'Florence',
- lastName: 'Nightingale'
+ lastName: 'Nightingale',
+ inventorOf: ['coxcomb chart', 'modern nursing']
};
// good - 有結尾逗號的 git diff 差異比較
const hero = {
firstName: 'Florence',
lastName: 'Nightingale',
+ inventorOf: ['coxcomb chart', 'modern nursing'],
};
分號
- 禁止使用自動分號插入,都要加入分號 [1]
類型轉換
- 在聲明語句的開始處就執行強制類型轉換
- 轉為字符串時,禁止原始包裝實例
const reviewScore = 9;
// bad
// 此時typeof totalScore為Object并不是string
const totalScore = new String(reviewScore);
// bad
// 調用的事 reviewScore.valueOf()
const totalScore = reviewScore + '';
// bad
// 不能保證返回一個字符串
const totalScore = reviewScore.toString();
// good
const totalScore = String(reviewScore);
- 轉為數字類型時,禁止使用原始包裝實例,如果使用parseInt要必須有基數 [1]
const inputValue = '4';
// bad
// 不要使用原始包裝實例
const val = new Number(inputValue);
// bad
const val = +inputValue;
// bad
const val = inputValue >> 0;
// good
const val = Number(inputValue);
// bad
// 沒基數
const num = parseInt('071');
// good
const num = parseInt('071', 10);
- 避免使用按位運算符 [1]
// bad
const val = inputValue >> 0;
- 進行布爾值轉換時,避免使用原始包裝實例 [1]
const age = 0;
// bad
const hasAge = new Boolean(age);
// good
const hasAge = Boolean(age);
// best
const hasAge = !!age;
命名規則
- 避免使用單字母名詞 [0]
- 當命名對象,函數和實例時使用駝峰式命名 [1]
// bad
// 一般命名分為駝峰和下劃線拼寫寫法,這里建議使用駝峰式寫法
const this_is_my_object = {};
// good
const thisIsMyObject = {};
- 當命名構造函數或類時使用PascalCase式命名(帕斯卡拼寫法,即首字母大寫) [0]
// bad
function user(options) {
this.name = options.name;
}
const bad = new user({
name: 'nope',
});
// good
class User {
constructor(options) {
this.name = options.name;
}
}
const good = new User({
name: 'yup',
});
- 變量禁止使用下劃線開頭或結尾 [1]
// bad
const __firstName__ = 'Panda';
// good
const firstName = 'Panda';
- 不要存儲this引用 [0]
// bad
function foo() {
const self = this;
return function () {
console.log(self);
};
}
// good
function foo() {
return () => {
console.log(this);
};
}
- 文件名應與其默認導出的名詞完全匹配 [0]
// 文件1
class CheckBox {
// ...
}
export default CheckBox;
// 文件2
export default function fortyTwo() { return 42; }
// 文件3
export default function insideDirectory() {}
// good
import CheckBox from './CheckBox'; // export/import/filename 單詞首字母大寫命名
import fortyTwo from './fortyTwo'; // export/import/filename 駝峰式命名
import insideDirectory from './insideDirectory';
- 當導出一個默認函數時使用駝峰式命名,文件名應該和你的函數名字一致 [0]
function makeStyleGuide() {
// ...
}
export default makeStyleGuide;
...
文件名為makeStyleGuide
- 當導出一個構造函數/類/單例/函數庫/純對象時使用PascalCase式命名 [0]
- 首字母縮寫的詞,應該總是全部大寫,或全部小寫 [0]
// bad
import SmsContainer from './containers/SmsContainer';
// bad
const HttpRequests = [
// ...
];
// good
import SMSContainer from './containers/SMSContainer';
// good
const HTTPRequests = [
// ...
];
// also good
const httpRequests = [
// ...
];
// best
import TextMessageContainer from './containers/TextMessageContainer';
- 如果變量是被導出的,或者只是常量,或者可以確信變量不會改變,可以使用大寫,使用大寫的變量可以幫助使用者確定使用的變量不會改變 [0]
存取器
- 屬性的存取器不是必須
- 避免使用js的getters/setters,因為會導致意想不到的副作用,而且會很難測試維護,可以使用存取器函數,使用getVal及setVal [0]
// bad
class Dragon {
constructor(val) {
this.name = val;
this.year = 0;
}
get age() {
// ...
return this.year;
}
set age(value) {
// ...
this.year = value;
}
}
// good
class Dragon {
constructor(val) {
this.name = val;
this.year = 0;
}
getAge() {
// ...
return this.year;
}
setAge(value) {
// ...
this.year = value;
}
}
- 也可以創建 get() 和 set() 函數, 但要保持一致 [0]
標準庫
- 使用Number.isNaN代替全局isNaN [1]
// bad
// no-restricted-globals禁用特定的全局變量
isNaN('1.2');
// good
Number.isNaN('1.2.3');
- 使用Number.isFinite代替isFinite [1]
// bad
isFinite('2e3');
// good
Number.isFinite('2e3');
airbnb規范標準-css部分
這部分內容是建議
-
格式
- 使用 2 個空格作為縮進。
- 類名建議使用破折號代替駝峰法。如果你使用 BEM,也可以使用下劃線(參見下面的 OOCSS 和 BEM)。
- 不要使用 ID 選擇器。
- 在一個規則聲明中應用了多個選擇器時,每個選擇器獨占一行。
- 在規則聲明的左大括號 { 前加上一個空格。
- 在屬性的冒號 : 后面加上一個空格,前面不加空格。
- 規則聲明的右大括號 } 獨占一行。
- 規則聲明之間用空行分隔開。
-
注釋
- 建議使用行注釋 (在 Sass 中是 //) 代替塊注釋。
- 建議注釋獨占一行。避免行末注釋。
- 給沒有自注釋的代碼寫上詳細說明,比如:
- 為什么用到了 z-index
- 兼容性處理或者針對特定瀏覽器的 hack
不建議使用ID選擇器
ID選擇器會帶來不必要的優先級,而且ID選擇器是不可復用的
- JavaScript鉤子
避免在Css和JavaScript中綁定相同的類,不利于后續維護,因為寫定后就相當于樣式和js文件綁定了,改樣式名詞會造成頁面邏輯出錯。如果要涉及到與樣式有關的操作,添加.js-前綴
定義邊框無樣式時,使用0代替none
不要讓嵌套選擇器深度超過3層
Google HTML/CSS代碼風格
并非按條條寫,只選取了一部分
HTML規則
- 對于圖片和其他媒體文件,樣式表和腳本,請盡量使用https協議,除非相關文件不支持
- 標簽,屬性,屬性值,css選擇器,屬性,和屬性值只使用小寫
- 刪除行尾不必要的空格
- 指定頁面編碼為utf-8
<meta charset="utf-8">
- 添加合理的注釋,說明相關代碼是做什么的,只在關鍵代碼處添加相關注釋,避免過多添加增加HTML和CSS的代碼量
- 使用TODO注釋,說明代碼功能
- 文檔類型首選HTML5標準
<!DOCTYPE html>
- 保證HTML代碼的有效性,可以借助W3C HTML validator分析頁面,修正一些明顯的錯誤
這個工具會分析出靜態文件不符合語意化的地方,不過這個檢查標準比較嚴格,僅供參考
- 根據HTML元素的語義使用相關元素
- 為有意義的多媒體元素提供備選文案
比如一個詳情頁的產品圖,就是頁面中有意義的圖片,要保證這類多媒體元素要有備選文案,一方面是保證元素在加載不出時,頁面不至于報錯,另一方面也是給盲人提供提示文字,但如果是一些類似背景圖片的元素就沒必要添加備選文案
- 將行為和表現分開
嚴格遵循結構,表現和行為的分離,盡量保證三者交互保持最低,確保所有表現都放到樣式表中,所有行為都放到腳本中,要盡量減少外鏈
// bad
<!DOCTYPE html>
<title>HTML sucks</title>
<link rel="stylesheet" href="base.css" media="screen">
<link rel="stylesheet" href="grid.css" media="screen">
<link rel="stylesheet" href="print.css" media="print">
<h1 style="font-size: 1em;">HTML sucks</h1>
<p>I’ve read about this on a few sites but now I’m sure:
<u>HTML is stupid!!1</u>
<center>I can’t believe there’s no way to control the styling of
my website without doing everything all over again!</center>
...
// good
<!DOCTYPE html>
<title>My first CSS-only redesign</title>
<link rel="stylesheet" href="default.css">
<h1>My first CSS-only redesign</h1>
<p>I’ve read about this on a few sites but today I’m actually
doing it: separating concerns and avoiding anything in the HTML of
my website that is presentational.
<p>It’s awesome!
- 省略樣式表和腳本類型的type屬性
- 引用屬性值時,使用雙引號
- 對class命名時要能保證光從名字就能看出是干什么用的,命名也要簡短易懂
CSS規則
- 避免使用CSS類型選擇器
// bad
div.error {}
// good
.error {}
- 寫屬性值時盡量使用縮寫
// bad
padding-bootom: 2em;
padding-left: 1em;
padding-right: 1em;
padding-top: 0;
...
// good
padding: 0 1em 2em;
- 除非必要,否則省略0后面的單位
flex: 0px;
flex: 1 1 0px; /* IE11下會要求添加單位 */
- 省略0開頭小數前面的0
// bad
font-size: 0.8em;
// good
font-size: .8em;
- 十六進制盡可能使用3個字符
// bad
color: #eebbcc;
// good
color: #ebc;
- 用-連接命名,增進對名字的理解和查找
// bad
.demoimage {}
.error_status {}
// good
.video-id {}
- 避免使用css hacks
- 選擇器在大括號前要添加一個空格
// bad
.test{
...
}
// good
.test {
...
}
- 在每個聲明尾部都加上分號
// bad
.test {
display: block;
height: 100px
}
// good
.test {
display: block;
height: 100px;
}
- 在屬性名冒號結束后添加一個空格
// bad
h3 {
font-size:16px;
}
// good
h3 {
font-size: 16px;
}
- 當有多個選擇器時,每個選擇器都要獨立新行
// bad
h1, h2, h3 {
}
// good
h1,
h2,
h3 {
}
- 多個規則之間要隔行
// bad
html {
...
}
body {
...
}
// good
html {
...
}
body {
...
}
這些規則表明看起來會增加css代碼量,但實際上我們現在結合webpack等工具,會對css進行壓縮,多余的空格會被刪除,這些規則方便后續代碼的維護
- 按模塊功能寫注釋