ESLint 代码检查规范

目录

Airbnb Javascript Style Guide

  1. 引用
  2. 对象
  3. 数组
  4. 函数
  5. 箭头函数
  6. 类和构造器
  7. 模块
  8. [Iterators and Generators](#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;
  }

⬆ 回到顶部

对象

// bad
const item = new Object();

// good
const item = {};
  • 使用动态属性名称创建对象时,请使用计算属性名称

为什么? 它们允许你在一个位置定义对象的所有属性

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,
  };
  • 把 你声明的对象 的 简写属性 和 非简写属性 分组

为什么? 更容易知道哪些属性用了简写

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引擎优化

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

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

⬆ 回到顶部

数组

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

⬆ 回到顶部

函数

  • 使用命名函数表达式,而不是函数声明。eslint: func-style

为什么?函数声明是可被提升的, 这意味着太容易在 函数被定义之前 就引用它了。这会损害可读性和可维护性。如果你发现一个函数的定义是复杂的以致于它干扰到其余文件,,那么该提取成独立的模块。不要忘记命名表达式 - 匿名函数会使得更难在 Error's call stack 中找到问题(Discussion)

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

// bad
function foo() {
}

// good
const foo = function bar() {
};
  • 用括号包装函数表达式成为 立即调用函数表达式(Wrap immediately invoked function expressions in parentheses.)。eslint: wrap-iife

为什么?一个立即调用的表达式是一个单一的单元

// 立即调用的函数表达式 (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) {
}

⬆ 回到顶部

箭头函数

为什么?因为箭头函数创造了新的一个 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;

为什么?从同一路径导入多个行可能会使代码难以维护。

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

为什么? 应该避免突变,一般来说,但特别是当输出可变绑定。 虽然在某些特殊情况下可能需要此技术,但一般来说,只应导出常量引用。

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

为什么?这强制我们不变的规则。处理有返回值的纯函数比 处理它的副作用更容易。
纯函数的回调值更易读,这比它带来的副作用更重要。
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

为什么? 确保可读性和可维护性。

   // 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.'));
   ```
**[⬆ 回到顶部](#目录)**
<br />

# Airbnb React/JSX Style Guide
## 基本规范
- 每个文件只写一个模块.
- 但是多个[无状态模块](https://facebook.github.io/react/docs/reusable-components.html#stateless-functions)可以放在单个文件中. eslint: [`react/no-multi-comp`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-multi-comp.md#ignorestateless).
- 推荐使用JSX语法.
- 不要使用 `React.createElement`,除非从一个非JSX的文件中初始化你的app.


**[⬆ 回到顶部](#目录)**
## 创建模块
Class vs React.createClass vs stateless
- 如果你的模块有内部状态或者是`refs`, 推荐使用 `class extends React.Component` 而不是 `React.createClass` ,除非你有充足的理由来使用这些方法. 
eslint: [`react/prefer-es6-class`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-es6-class.md) [`react/prefer-stateless-function`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-stateless-function.md)

```jsx
// 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" />

为什么? 屏幕助读器在键盘快捷键与键盘命令时造成的不统一性会导致阅读性更加复杂.

// 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 反人类设计模式:()][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

posted @ 2016-12-09 11:13  Sameen  阅读(1919)  评论(0编辑  收藏  举报