ESLint 代碼檢查規範

目錄

Airbnb Javascript Style Guide

  1. 引用
  2. 對象
  3. 數組
  4. 函數
  5. 箭頭函數
  6. 類和構造器
  7. 模塊
  8. Iterators and Generators
  9. 變量
  10. 比較運算符
  11. 註釋
  12. 空格

Airbnb React/JSX Style Guide

  1. 基本規範
  2. Class vs React.createClass vs stateless
  3. 命名
  4. 聲明模塊
  5. 代碼對齊
  6. 引號
  7. 空格
  8. 屬性
  9. Refs
  10. 括號
  11. 標籤
  12. 函數/方法
  13. 模塊生命週期
  14. isMounted

Airbnb Javascript Style Guide

引用

  • 對全部的引用使用 const ,不要使用 var. eslint: prefer-const, no-const-assign
    這能確保你沒法對引用從新賦值,也不會致使出現 bug 或難以理解
// bad
  var a = 1;
  var b = 2;

// good
 const a = 1;
 const b = 2;
  • 若是必定須要可變更的引用,使用 let 代替 var. eslint: no-var jscs: disallowVar
    由於 let 是塊級做用域,而 var 是函數做用域。
// bad
  var count = 1;
  if (true) {
    count += 1;
  }

  // good, use the let.
  let count = 1;
  if (true) {
    count += 1;
  }

⬆ 回到頂部javascript

對象

// bad
const item = new Object();

// good
const item = {};
  • 使用動態屬性名稱建立對象時,請使用計算屬性名稱

爲何? 它們容許你在一個位置定義對象的全部屬性html

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,
};
// bad
const atom = {
  value: 1,

  addValue: function (value) {
    return atom.value + value;
  },
};

// good
const atom = {
  value: 1,

  addValue(value) {
    return atom.value + value;
  },
};
  • 使用對象屬性值的簡寫。eslint: object-shorthand
    這樣更短更有描述性。
const lukeSkywalker = 'Luke Skywalker';

  // bad
  const obj = {
    lukeSkywalker: lukeSkywalker,
  };

  // good
  const obj = {
    lukeSkywalker,
  };
  • 把 你聲明的對象 的 簡寫屬性 和 非簡寫屬性 分組

爲何? 更容易知道哪些屬性用了簡寫java

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,
};

爲何?通常來講咱們主觀上認爲它更容易閱讀。它改進了高亮語法,而且更容易被許多JS引擎優化node

// 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

爲何? 這些方法可能被 有問題的對象屬性 隱藏 - 考慮{hasOwnProperty: false} - 或者, 對象多是一個空對象(Object.create(null)react

// bad
console.log(object.hasOwnProperty(key));

// good
console.log(Object.prototype.hasOwnProperty.call(object, key));

// best
const has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope.
/* or */
const has = require('has');
…
console.log(has.call(object, key));
  • 淺拷貝對象的時候最好是使用 … 操做符而不是 Object.assign
// very bad
const original = { a: 1, b: 2 };
const copy = Object.assign(original, { c: 3 }); // this mutates `original`  ಠ_ಠ
delete copy.a; // so does this

// bad
const original = { a: 1, b: 2 };
const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }

// 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 }

⬆ 回到頂部git

數組

// bad
const items = new Array();

// good
const items = [];
  • 數組添加元素時,使用push 代替 直接賦值
const someStack = [];

// bad
someStack[someStack.length] = 'abracadabra';

// good
someStack.push('abracadabra');
  • 使用拓展運算符 … 複製數組。
// bad
const items = new Array();

// good
const items = [];

// bad
const len = items.length;
const itemsCopy = [];
let i;

for (i = 0; i < len; i++) {
  itemsCopy[i] = items[i];
}

// good
const itemsCopy = [...items];
const foo = document.querySelectorAll('.foo');
const nodes = Array.from(foo);

⬆ 回到頂部es6

函數

  • 使用命名函數表達式,而不是函數聲明。eslint: func-stylegithub

    爲何?函數聲明是可被提高的, 這意味着太容易在 函數被定義以前 就引用它了。這會損害可讀性和可維護性。若是你發現一個函數的定義是複雜的以至於它干擾到其他文件,,那麼該提取成獨立的模塊。不要忘記命名錶達式 - 匿名函數會使得更難在 Error's call stack 中找到問題(Discussion)ajax

// bad
const foo = function () {
};

// bad
function foo() {
}

// good
const foo = function bar() {
};
  • 用括號包裝函數表達式成爲 當即調用函數表達式(Wrap immediately invoked function expressions in parentheses.)。eslint: wrap-iife

爲何?一個當即調用的表達式是一個單一的單元express

// 當即調用的函數表達式 (IIFE)
(() => {
   console.log('Welcome to the Internet. Please follow me.');
})();
  • 永遠不要在一個非函數代碼塊(if、while 等)中聲明一個函數。把那個函數賦給一個變量。瀏覽器容許你這麼作,但它們的解析表現不一致. eslint: no-loop-func

  • 注意:ECMA-262 將 block 定義爲語句列表 。 函數聲明不是語句,Read ECMA-262's note on this issue.

// bad
if (currentUser) {
  function test() {
    console.log('Nope.');
  }
}

// good
let test;
if (currentUser) {
  test = () => {
    console.log('Yup.');
  };
}
  • 不要把參數命名爲arguments ,這將形成優先覆蓋原來每一個給定做用域的arguments
// bad
function nope(name, options, arguments) {
  // ...stuff...
}

// good
function yup(name, options, args) {
  // ...stuff...
}
  • 不要使用 arguments。能夠選擇 rest 語法 … 替代。 eslint: prefer-rest-params

    爲何?使用 … 能明確你要傳入的參數。另外 rest 參數是一個真正的數組,而 arguments 是一個類數組。

// bad
  function concatenateAll() {
    const args = Array.prototype.slice.call(arguments);
    return args.join('');
  }

  // good
  function concatenateAll(...args) {
    return args.join('');
  }
  • 使用默認參數語法,而不是改變函數參數
// really bad
function handleThings(opts) {
  // No! We shouldn't mutate function arguments.
  // Double bad: if opts is falsy it'll be set to an object which may
  // be what you want but it can introduce subtle bugs.
  opts = opts || {};
  // ...
}

// still bad
function handleThings(opts) {
  if (opts === void 0) {
    opts = {};
  }
  // ...
}

// good
function handleThings(opts = {}) {
  // ...
}
  • 老是把默認參數放在最後
// bad
function handleThings(opts = {}, name) {
  // ...
}

// good
function handleThings(name, opts = {}) {
  // ...
}

爲何? 操做做爲參數傳遞的對象可能會在原始調用者中形成沒必要要的變量反作用。

// bad
function f1(obj) {
  obj.key = 1;
};

// good
function f2(obj) {
  const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
};

爲何? 從新分配參數可能會致使 unexpected behavior, 特別是仇敵訪問arguments對象的時候。它也可能致使優化問題,特別是在V8。

// bad
function f1(a) {
  a = 1;
}

function f2(a) {
  if (!a) { a = 1; }
}

// good
function f3(a) {
  const b = a || 1;
}

function f4(a = 1) {
}

⬆ 回到頂部

箭頭函數

  • 當你必須使用函數表達式(或傳遞一個匿名函數)時,使用箭頭函數符號。eslint: prefer-arrow-callback, arrow-spacing

    爲何?由於箭頭函數創造了新的一個 this 執行環境(譯註:參考 Arrow functions - JavaScript | MDN 和 ES6 arrow functions, syntax and lexical scoping),一般狀況下都能知足你的需求,並且這樣的寫法更爲簡潔。
    爲何不?若是你有一個至關複雜的函數,你或許能夠把邏輯部分轉移到一個函數聲明上。

// bad
  [1, 2, 3].map(function (x) {
    return x * x;
  });

  // good
  [1, 2, 3].map((x) => {
    return x * x;
  });
// bad
[1, 2, 3].map(number => {
  const nextNumber = number + 1;
  `A string containing the ${nextNumber}.`;
});

// good
[1, 2, 3].map(number => `A string containing the ${number}.`);

// good
[1, 2, 3].map((number) => {
  const nextNumber = number + 1;
  return `A string containing the ${nextNumber}.`;
});

// good
[1, 2, 3].map((number, index) => ({
  [index]: number
}));
  • 以防表達式過長,利用括號讓代碼可讀性更好

    爲何? 它能更清晰的展現函數的starts 和 ends.

// bad
['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call(
    httpMagicObjectWithAVeryLongName,
    httpMethod
  )
);

// good
['get', 'post', 'put'].map(httpMethod => (
  Object.prototype.hasOwnProperty.call(
    httpMagicObjectWithAVeryLongName,
    httpMethod
  )
));
  • 避免混淆箭頭函數語法(=>)和比較運算符(<=, >=). eslint: no-confusing-arrow
// bad
const itemHeight = item => item.height >= 256 ? item.largeSize : item.smallSize;

// 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

    爲何? 由於 class 語法更爲簡潔更易讀。

// 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 繼承。

    爲何?由於 extends 是一個內建的原型繼承方法而且不會破壞 instanceof。

// bad
const inherits = require('inherits');
function PeekableQueue(contents) {
  Queue.apply(this, contents);
}
inherits(PeekableQueue, Queue);
PeekableQueue.prototype.peek = function () {
  return this._queue[0];
}

// good
class PeekableQueue extends Queue {
  peek() {
    return this._queue[0];
  }
}
  • 方法能夠返回 this 來幫助鏈式調用
// bad
Jedi.prototype.jump = function () {
  this.jumping = true;
  return true;
};

Jedi.prototype.setHeight = function (height) {
  this.height = height;
};

const luke = new Jedi();
luke.jump(); // => true
luke.setHeight(20); // => undefined

// good
class Jedi {
  jump() {
    this.jumping = true;
    return this;
  }

  setHeight(height) {
    this.height = height;
    return this;
  }
}

const luke = new Jedi();

luke.jump()
  .setHeight(20);
  • 避免重複的類成員

爲何? 重複聲明類成員,那隻會承認最後一個。 - having duplicates is almost certainly a bug.

// bad
class Foo {
  bar() { return 1; }
  bar() { return 2; }
}

// good
class Foo {
  bar() { return 1; }
}

// good
class Foo {
  bar() { return 2; }
}

⬆ 回到頂部

模塊

  • 老是使用import/export

    Why? Modules are the future, let's start using the future now.

// bad
const AirbnbStyleGuide = require('./AirbnbStyleGuide');
module.exports = AirbnbStyleGuide.es6;

// ok
import AirbnbStyleGuide from './AirbnbStyleGuide';
export default AirbnbStyleGuide.es6;

// best
import { es6 } from './AirbnbStyleGuide';
export default es6;
  • import的時候不要使用通配符

    爲何?這樣能確保你只有一個默認 export。

// bad
  import * as AirbnbStyleGuide from './AirbnbStyleGuide';

  // good
  import AirbnbStyleGuide from './AirbnbStyleGuide';
  • 不要從 import 中直接 export

    爲何?雖然一行代碼簡潔明瞭,但讓 import 和 export 各司其職讓事情能保持一致。

// bad
  // filename es6.js
  export { es6 as default } from './airbnbStyleGuide';

  // good
  // filename es6.js
  import { es6 } from './AirbnbStyleGuide';
  export default es6;
  • import同一路徑的,就一次性引入。eslint: no-duplicate-imports

    爲何?從同一路徑導入多個行可能會使代碼難以維護。

// bad
  import foo from 'foo';
  // … some other imports … //
  import { named1, named2 } from 'foo';

  // good
  import foo, { named1, named2 } from 'foo';

  // good
  import foo, {
    named1,
    named2,
  } from 'foo';
  • 不要export可變的綁定。eslint: import/no-mutable-exports

    爲何? 應該避免突變,通常來講,但特別是當輸出可變綁定。 雖然在某些特殊狀況下可能須要此技術,但通常來講,只應導出常量引用。

// bad
  let foo = 3;
  export { foo }

  // good
  const foo = 3;
  export { foo }
// bad
  export function foo() {}

  // good
  export default function foo() {}
  • 將全部導入都放在非import語句之上。 eslint: import/first

    爲何? 保證他們一直在頂部能夠預防 surprising behavior.

// bad
  import foo from 'foo';
  foo.init();

  import bar from 'bar';

  // good
  import foo from 'foo';
  import bar from 'bar';

  foo.init();

⬆ 回到頂部

Iterators and Generators

  • 不要使用 iterators,使用javascript的高階函數例 替代for-in, for-of 。 eslint: no-iterator no-restricted-syntax

    爲何?這強制咱們不變的規則。處理有返回值的純函數比 處理它的反作用更容易。
    純函數的回調值更易讀,這比它帶來的反作用更重要。
    Use map() / every() / filter() / find() / findIndex() / reduce() / some() / ... to iterate over arrays,
    and Object.keys() / Object.values() / Object.entries() to produce arrays so you can iterate over objects.

const numbers = [1, 2, 3, 4, 5];

  // bad
  let sum = 0;
  for (let num of numbers) {
    sum += num;
  }

  sum === 15;

  // good
  let sum = 0;
  numbers.forEach((num) => sum += num);
  sum === 15;

  // best (use the functional force)
  const sum = numbers.reduce((total, num) => total + num, 0);
  sum === 15;
  • 如今還不要使用 generators

    爲何?由於它們如今還無法很好地編譯到 ES5。 (目前Chrome 和 Node.js 的穩定版本都已支持 generators)

⬆ 回到頂部

變量

  • 一直使用 const 來聲明變量。 若是不這樣作就會產生全局變量。咱們須要避免全局命名空間的污染。eslint: generator-star-spacing
// bad
superPower = new SuperPower();

// good
const superPower = new SuperPower();
  • 使用 const 聲明每個變量。eslint: one-var

    爲何?增長新變量將變的更加容易,並且你永遠不用再擔憂調換錯 ;, , 或引入僅標點符號差別。你還可使用debugger遍歷每一個聲明,而不是一次性跳過全部聲明。

// bad
const items = getItems(),
    goSportsTeam = true,
    dragonball = 'z';

// bad
// (compare to above, and try to spot the mistake)
const items = getItems(),
    goSportsTeam = true;
    dragonball = 'z';

// good
const items = getItems();
const goSportsTeam = true;
const dragonball = 'z';
  • 將全部的 const 和 let 分組

    爲何?當你須要把已賦值變量賦值給未賦值變量時很是有用。

// bad
  let i, len, dragonball,
      items = getItems(),
      goSportsTeam = true;

  // 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;
  • 在你須要的地方給變量賦值,但請把它們放在一個合理的位置

    爲何?let 和 const 是塊級做用域而不是函數做用域。

// bad - unnecessary function call
function checkName(hasName) {
  const name = getName();

  if (hasName === 'test') {
    return false;
  }

  if (name === 'test') {
    this.setName('');
    return false;
  }

  return name;
}

// good
function checkName(hasName) {
  if (hasName === 'test') {
    return false;
  }

  const name = getName();

  if (name === 'test') {
    this.setName('');
    return false;
  }

  return name;
}
  • 不要連接變量賦值

    爲何? 連接變量分配建立隱式全局變量。

// bad
(function example() {
  // JavaScript interprets this as
  // let a = ( b = ( c = 1 ) );
  // The let keyword only applies to variable a; variables b and c become
  // global variables.
  let a = b = c = 1;
}());

console.log(a); // undefined
console.log(b); // 1
console.log(c); // 1

// good
(function example() {
  let a = 1;
  let b = a;
  let c = a;
}());

console.log(a); // undefined
console.log(b); // undefined
console.log(c); // undefined

// the same applies for `const`
  • 避免使用一元增量和減量(++, --). eslint no-plusplus

    爲何? 根據eslint文檔,一元增量和減量語句受到自動分號插入的影響,而且可能會致使應用程序中的值遞增或遞減的無聲錯誤。 使用num + = 1而不是num ++或num ++這樣的語句來改變你的值也更具表現力。 不容許一元增量和減量語句也會阻止您無心地預先遞增/預減量值,這也會致使程序出現unexpected behavior。

// bad

  let array = [1, 2, 3];
  let num = 1;
  num++;
  --num;

  let sum = 0;
  let truthyCount = 0;
  for(let i = 0; i < array.length; i++){
    let value = array[i];
    sum += value;
    if (value) {
      truthyCount++;
    }
  }

  // good

  let array = [1, 2, 3];
  let num = 1;
  num += 1;
  num -= 1;

  const sum = array.reduce((a, b) => a + b, 0);
  const truthyCount = array.filter(Boolean).length;

⬆ 回到頂部

比較運算符

  • 優先使用 === 和 !== 而不是 == 和 !=。 eslint: eqeqeq
  • 條件表達式例如 if 語句經過抽象方法 ToBoolean 強制計算它們的表達式而且老是遵照下面的規則:
    • 對象 被計算爲 true
    • Undefined 被計算爲 false
    • Null 被計算爲 false
    • 布爾值 被計算爲 布爾的值
    • 數字 若是是 +0、-0、或 NaN 被計算爲 false, 不然爲 true
    • 字符串 若是是空字符串 被計算爲 false,不然爲 true
if ([0] && []) {
  // true
  // an array (even an empty one) is an object, objects will evaluate to true
}
  • 判斷布爾值的時候能夠用簡短寫法,可是比較字符串和數字的時候就明確寫
// bad
if (isValid === true) {
  // ...stuff...
}

// good
if (isValid) {
  // ...stuff...
}

// bad
if (name) {
  // ...stuff...
}

// good
if (name !== '') {
  // ...stuff...
}

// bad
if (collection.length) {
  // ...stuff...
}

// good
if (collection.length > 0) {
  // ...stuff...
}

⬆ 回到頂部

註釋

  • 使用 /** ... */ 做爲多行註釋。包含描述、指定全部參數和返回值的類型和值
// bad
// make() returns a new element
// based on the passed in tag name
//
// @param {String} tag
// @return {Element} element
function make(tag) {

  // ...stuff...

  return element;
}

// good
/**
 * make() returns a new element
 * based on the passed in tag name
 *
 * @param {String} tag
 * @return {Element} element
 */
function make(tag) {

  // ...stuff...

  return element;
}
  • 使用 // 做爲單行註釋。在註釋對象上面另起一行使用單行註釋。在註釋前插入空行
// bad
const a = 0;// 我是註釋
 
// bad
//我是註釋
const a = 0;
 
// good
// 我是註釋
const a = 0;
  • 給註釋增長 FIXME 或 TODO 的前綴
    幫助其餘開發者快速瞭解這是一個須要複查的問題,或是給須要實現的功能提供一個解決方式。這將有別於常見的註釋,由於它們是可操做的。使用 FIXME: -- need to figure this out or TODO: -- need to implement.

  • 使用 // FIXME: 標註問題
class Calculator {
  constructor() {
    // FIXME: shouldn't use a global here
    total = 0;
  }
}
  • 使用 // TODO: 標註問題的解決方式。
class Calculator {
  constructor() {
    // TODO: total should be configurable by an options param
    this.total = 0;
  }
}

⬆ 回到頂部

Whitespace

  • 使用 2 個空格做爲縮進。 eslint: indent
  • 花括號前要放一個空格。eslint: space-before-blocks
  • 在控制語句(if、while 等)的小括號前放一個空格;在函數調用及聲明中,不在函數的參數列表前加空格。eslint: keyword-spacing
  • 操做符兩側要有空格。eslint: space-infix-ops
// bad
const x=y+5;

// good
const x = y + 5;
// bad
$('#items').find('.selected').highlight().end().find('.open').updateCount();

// bad
$('#items').
  find('.selected').
    highlight().
    end().
  find('.open').
    updateCount();

// good
$('#items')
  .find('.selected')
    .highlight()
    .end()
  .find('.open')
    .updateCount();

// bad
const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true)
    .attr('width', (radius + margin) * 2).append('svg:g')
    .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
    .call(tron.led);

// good
const leds = stage.selectAll('.led')
    .data(data)
  .enter().append('svg:svg')
    .classed('led', true)
    .attr('width', (radius + margin) * 2)
  .append('svg:g')
    .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
    .call(tron.led);

// good
const leds = stage.selectAll('.led').data(data);
// bad
function bar( foo ) {
  return foo;
}

// good
function bar(foo) {
  return foo;
}

// bad
if ( foo ) {
  console.log(foo);
}

// good
if (foo) {
  console.log(foo);
}
 
// bad
const foo = [ 1, 2, 3 ];
console.log(foo[ 0 ]);

// good
const foo = [1, 2, 3];
console.log(foo[0]);
// bad
const foo = {clark: 'kent'};

// good
const foo = { clark: 'kent' };
  • 避免一行代碼超過100個字符,包括空格。eslint: max-len

    爲何? 確保可讀性和可維護性。

javascript // bad 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 const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy; // good $.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' }, }) .done(() => console.log('Congratulations!')) .fail(() => console.log('You have failed this city.'));
⬆ 回到頂部

Airbnb React/JSX Style Guide

基本規範

  • 每一個文件只寫一個模塊.
  • 可是多個無狀態模塊能夠放在單個文件中. eslint: react/no-multi-comp.
  • 推薦使用JSX語法.
  • 不要使用 React.createElement,除非從一個非JSX的文件中初始化你的app.

⬆ 回到頂部

建立模塊

Class vs React.createClass vs stateless

// bad
const Listing = React.createClass({
    // ...
    render() {
        return <div>{this.state.hello}</div>;
    }
});
// good
class Listing extends React.Component {
// ...
    render() {
        return <div>{this.state.hello}</div>;
    }
}

若是你的模塊沒有狀態或是沒有引用refs, 推薦使用普通函數(非箭頭函數)而不是類:

// bad
class Listing extends React.Component {
    render() {
        return <div>{this.props.hello}</div>;
    }
}
// bad (relying on function name inference is discouraged)
const Listing = ({ hello }) => (
    <div>{hello}</div>
);
// good
function Listing({ hello }) {
    return <div>{hello}</div>;
}

⬆ 回到頂部

命名

  • 擴展名: React模塊使用 .jsx 擴展名.
  • 文件名: 文件名使用駝峯式. 如, ReservationCard.jsx.
  • 引用命名: React模塊名使用駝峯式命名,實例使用駱駝式命名. eslint: react/jsx-pascal-case
// bad
import reservationCard from './ReservationCard';
// good
import ReservationCard from './ReservationCard';
// bad
const ReservationItem = <ReservationCard />;
// good
const reservationItem = <ReservationCard />;
  • 模塊命名: 模塊使用當前文件名同樣的名稱. 好比 ReservationCard.jsx 應該包含名爲 ReservationCard的模塊. 可是,若是整個文件夾是一個模塊,使用 index.js做爲入口文件,而後直接使用 index.js 或者文件夾名做爲模塊的名稱:
// bad
import Footer from './Footer/Footer';
// bad
import Footer from './Footer/index';
// good
import Footer from './Footer';
  • 高階模塊命名: 對於生成一個新的模塊,其中的模塊名 displayName 應該爲高階模塊名和傳入模塊名的組合. 例如, 高階模塊 withFoo(), 當傳入一個 Bar 模塊的時候, 生成的模塊名 displayName 應該爲 withFoo(Bar).

    爲何?一個模塊的 displayName 可能會在開發者工具或者錯誤信息中使用到,所以有一個能清楚的表達這層關係的值能幫助咱們更好的理解模塊發生了什麼,更好的Debug.

// bad
export default function withFoo(WrappedComponent) {
    return function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
    }
}
// good
export default function withFoo(WrappedComponent) {
    function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
    }
const wrappedComponentName = WrappedComponent.displayName
|| WrappedComponent.name
|| 'Component';
WithFoo.displayName = `withFoo(${wrappedComponentName})`;
    return WithFoo;
}
  • 屬性命名: 避免使用DOM相關的屬性來用做其餘的用途。

    爲何?對於styleclassName這樣的屬性名,咱們都會默認它們表明一些特殊的含義,如元素的樣式,CSS class的名稱。在你的應用中使用這些屬性來表示其餘的含義會使你的代碼更難閱讀,更難維護,而且可能會引發bug。

// bad
<MyComponent style="fancy" />
// good
<MyComponent variant="fancy" />

聲明模塊

  • 不要使用 displayName 來命名React模塊,而是使用引用來命名模塊, 如 class 名稱.
// bad
export default React.createClass({
    displayName: 'ReservationCard',
    // stuff goes here
});
// good
export default class ReservationCard extends React.Component {
}

⬆ 回到頂部

代碼對齊

// bad
<Foo superLongParam="bar"
anotherSuperLongParam="baz" />
// good, 有多行屬性的話, 新建一行關閉標籤
<Foo
    superLongParam="bar"
    anotherSuperLongParam="baz"
/>
// 若能在一行中顯示, 直接寫成一行
<Foo bar="bar" />
// 子元素按照常規方式縮進
<Foo
    superLongParam="bar"
    anotherSuperLongParam="baz"
>
    <Quux />
</Foo>

引號

  • 對於JSX屬性值老是使用雙引號("), 其餘均使用單引號. eslint: jsx-quotes

    爲何? JSX屬性 不能包括轉譯的引號, 所以在雙引號裏包括像 "don't" 的屬性值更容易輸入.
    HTML屬性也是用雙引號,因此JSX屬性也遵循一樣的語法.

// bad
<Foo bar='bar' />
// good
<Foo bar="bar" />
// bad
<Foo style={{ left: "20px" }} />
// good
<Foo style={{ left: '20px' }} />

⬆ 回到頂部

空格

// bad
<Foo/>
// very bad
<Foo />
// bad
<Foo
/>
// good
<Foo />
// bad
<Foo bar={ baz } />
// good
<Foo bar={baz} />

⬆ 回到頂部

屬性

  • JSX屬性名使用駱駝式風格camelCase.
// bad
<Foo
    UserName="hello"
    phone_number={12345678}
/>
// good
<Foo
    userName="hello"
    phoneNumber={12345678}
/>
// bad
<Foo
    hidden={true}
/>
// good
<Foo
    hidden
/>
  • <img> 標籤老是添加 alt 屬性. 若是圖片以presentation(感受是以相似PPT方式顯示?)方式顯示,alt 可爲空, 或者<img> 要包含role="presentation". eslint: jsx-a11y/img-has-alt
// bad
<img src="hello.jpg" />
// good
<img src="hello.jpg" alt="Me waving hello" />
// good
<img src="hello.jpg" alt="" />
// good
<img src="hello.jpg" role="presentation" />
  • 不要在 alt 值裏使用如 "image", "photo", or "picture"包括圖片含義這樣的詞, 中文也同樣. eslint: jsx-a11y/img-redundant-alt

    爲何? 屏幕助讀器已經把 img 標籤標註爲圖片了, 因此沒有必要再在 alt 裏說明了.

// bad
<img src="hello.jpg" alt="Picture of me waving hello" />
// good
<img src="hello.jpg" alt="Me waving hello" />
// bad - not an ARIA role
<div role="datepicker" />
// bad - abstract ARIA role
<div role="range" />
// good
<div role="button" />
  • 不要在標籤上使用 accessKey 屬性. eslint: jsx-a11y/no-access-key

    爲何? 屏幕助讀器在鍵盤快捷鍵與鍵盤命令時形成的不統一性會致使閱讀性更加複雜.

// bad
<div accessKey="h" />
// good
<div />
  • 避免使用數組的index來做爲屬性key的值,推薦使用惟一ID. (爲何?)
// bad
{todos.map((todo, index) =>
    <Todo
        {...todo}
        key={index}
    />
)}
// good
{todos.map(todo => (
    <Todo
        {...todo}
        key={todo.id}
    />
))}

⬆ 回到頂部

refs

// bad
<Foo
    ref="myRef"
/>
// good
<Foo
    ref={ref => { this.myRef = ref; }}
/>

⬆ 回到頂部

括號

// bad
render() {
    return <MyComponent className="long body" foo="bar">
        <MyChild />
    </MyComponent>;
}
// good
render() {
    return (
        <MyComponent className="long body" foo="bar">
            <MyChild />
        </MyComponent>
    );
}
// good, 單行能夠不須要
render() {
    const body = <div>hello</div>;
    return <MyComponent>{body}</MyComponent>;
}

⬆ 回到頂部

標籤

// bad
<Foo className="stuff"></Foo>
// good
<Foo className="stuff" />
// bad
<Foo
bar="bar"
baz="baz" />
// good
<Foo
bar="bar"
baz="baz"
/>

⬆ 回到頂部

函數

  • 使用箭頭函數來獲取本地變量.
function ItemList(props) {
    return (
        <ul>
            {props.items.map((item, index) => (
                <Item
                    key={item.key}
                    onClick={() => doSomethingWith(item.name, index)}
                />
            ))}
        </ul>
    );
}
  • 當在 render() 裏使用事件處理方法時,提早在構造函數裏把 this 綁定上去. eslint: react/jsx-no-bind

    爲何? 在每次 render 過程當中, 再調用 bind 都會新建一個新的函數,浪費資源.

// bad
class extends React.Component {
    onClickDiv() {
        // do stuff
    }
    render() {
        return <div onClick={this.onClickDiv.bind(this)} />
    }
}
// good
class extends React.Component {
    constructor(props) {
        super(props);
        this.onClickDiv = this.onClickDiv.bind(this);
    }
    onClickDiv() {
        // do stuff
    }
    render() {
        return <div onClick={this.onClickDiv} />
    }
}
  • 在React模塊中,不要給所謂的私有函數添加 _ 前綴,本質上它並非私有的.

    爲何?_ 下劃線前綴在某些語言中一般被用來表示私有變量或者函數。可是不像其餘的一些語言,在JS中沒有原生支持所謂的私有變量,全部的變量函數都是共有的。儘管你的意圖是使它私有化,在以前加上下劃線並不會使這些變量私有化,而且全部的屬性(包括有下劃線前綴及沒有前綴的)都應該被視爲是共有的。瞭解更多詳情請查看Issue #1024, 和 #490

// bad
React.createClass({
    _onClickSubmit() {
        // do stuff
    },
    // other stuff
});
// good
class extends React.Component {
    onClickSubmit() {
        // do stuff
    }
    // other stuff
}
// bad
render() {
    (<div />);
}
// good
render() {
    return (<div />);
}

⬆ 回到頂部

模塊生命週期

  • class extends React.Component 的生命週期函數:
  1. 可選的 static 方法
  2. constructor 構造函數
  3. getChildContext 獲取子元素內容
  4. componentWillMount 模塊渲染前
  5. componentDidMount 模塊渲染後
  6. componentWillReceiveProps 模塊將接受新的數據
  7. shouldComponentUpdate 判斷模塊需不須要從新渲染
  8. componentWillUpdate 上面的方法返回 true, 模塊將從新渲染
  9. componentDidUpdate 模塊渲染結束
  10. componentWillUnmount 模塊將從DOM中清除, 作一些清理任務
  11. 點擊回調或者事件處理器onClickSubmit()onChangeDescription()
  12. render 裏的 getter 方法getSelectReason()getFooterContent()
  13. 可選的 render 方法renderNavigation()renderProfilePicture()
  14. render render() 方法
  • 如何定義 propTypes, defaultProps, contextTypes, 等等其餘屬性...
import React, { PropTypes } from 'react';
const propTypes = {
    id: PropTypes.number.isRequired,
    url: PropTypes.string.isRequired,
    text: PropTypes.string,
};
const defaultProps = {
    text: 'Hello World',
};
class Link extends React.Component {
    static methodsAreOk() {
        return true;
    }
    render() {
        return <a href={this.props.url} data-id={this.props.id}>{this.props.text}</a>
    }
}
Link.propTypes = propTypes;
Link.defaultProps = defaultProps;
export default Link;
  • React.createClass 的生命週期函數,與使用class稍有不一樣: eslint: react/sort-comp
  1. displayName 設定模塊名稱
  2. propTypes 設置屬性的類型
  3. contextTypes 設置上下文類型
  4. childContextTypes 設置子元素上下文類型
  5. mixins 添加一些mixins
  6. statics
  7. defaultProps 設置默認的屬性值
  8. getDefaultProps 獲取默認屬性值
  9. getInitialState 或者初始狀態
  10. getChildContext
  11. componentWillMount
  12. componentDidMount
  13. componentWillReceiveProps
  14. shouldComponentUpdate
  15. componentWillUpdate
  16. componentDidUpdate
  17. componentWillUnmount
  18. clickHandlers or eventHandlers like onClickSubmit() or onChangeDescription()
  19. getter methods for render like getSelectReason() or getFooterContent()
  20. Optional render methods like renderNavigation() or renderProfilePicture()
  21. render

⬆ 回到頂部

ismounted

  • 不要再使用 isMounted. eslint: react/no-is-mounted

    爲何? [isMounted 反人類設計模式:()][anti-pattern], 在 ES6 classes 中沒法使用, 官方將在將來的版本里刪除此方法.
    [anti-pattern]: https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html

⬆ 回到頂部

Enjoy Yourself~

Any feedback or complain or requests of you, please send email to Feedback

相關文章
相關標籤/搜索