前端開發規范

參考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進行壓縮,多余的空格會被刪除,這些規則方便后續代碼的維護

  • 按模塊功能寫注釋
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370

推薦閱讀更多精彩內容