目录
Airbnb Javascript Style Guide
Airbnb React/JSX Style Guide
- 基本规范
- Class vs React.createClass vs stateless
- 命名
- 声明模块
- 代码对齐
- 引号
- 空格
- 属性
- Refs
- 括号
- 标签
- 函数/方法
- 模块生命周期
- 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;
}
对象
- 使用字面值创建对象。eslint:
no-new-object
// 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,
};
- 使用对象方法的简写。eslint:
object-shorthand
// 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,
};
- 属性不要有引号eslint:
quote-props
为什么?一般来说我们主观上认为它更容易阅读。它改进了高亮语法,并且更容易被许多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 }
数组
- 使用字面值创建数组。 eslint:
no-array-constructor
// 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];
- 使用 Array.from 把一个类数组对象转换成数组. eslint:
array-callback-return
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 = {}) {
// ...
}
- 不要改变参数 。 eslint:
no-param-reassign
为什么? 操作作为参数传递的对象可能会在原始调用者中造成不必要的变量副作用。
// bad
function f1(obj) {
obj.key = 1;
};
// good
function f2(obj) {
const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
};
- 切勿重新分配参数。 eslint:
no-param-reassign
为什么? 重新分配参数可能会导致 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;
});
- 如果一个函数适合用一行写出并且只有一个参数,那就把花括号、圆括号和 return 都省略掉。如果不是,那就不要省略. eslint:
arrow-parens
,arrow-body-style
jscs:disallowParenthesesAroundArrowParam
,requireShorthandArrowFunctions
为什么?语法糖。在链式调用中可读性很高。
// 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 }
- 模块只有单个export的时候,最好在export后面加上default。eslint:
import/prefer-default-export
// 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
为什么?这强制我们不变的规则。处理有返回值的纯函数比 处理它的副作用更容易。
纯函数的回调值更易读,这比它带来的副作用更重要。
Usemap()
/every()
/filter()
/find()
/findIndex()
/reduce()
/some()
/ ... to iterate over arrays,
andObject.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
orTODO: -- 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;
- 在文件末尾插入一个空行。eslint:
eol-last
- 在使用长方法链时进行缩进。使用放置在前面的点 . 强调这是方法调用而不是新语句。 eslint:
newline-per-chained-call
no-whitespace-before-property
// 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);
- 小括号和中括号里不加空格。eslint:
space-in-parens
,array-bracket-spacing
// 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]);
- 大括号里加空格。eslint:
object-curly-spacing
// 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相关的属性来用作其他的用途。
为什么?对于
style
和className
这样的属性名,我们都会默认它们代表一些特殊的含义,如元素的样式,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 {
}
代码对齐
- 遵循以下的JSX语法缩进/格式. eslint:
react/jsx-closing-bracket-location
// 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' }} />
空格
- 总是在自动关闭的标签前加一个空格,正常情况下也不需要换行. eslint:
no-multi-spaces
,react/jsx-space-before-closing
// bad
<Foo/>
// very bad
<Foo />
// bad
<Foo
/>
// good
<Foo />
- 不要在JSX
{}
引用括号里两边加空格. eslint:react/jsx-curly-spacing
// bad
<Foo bar={ baz } />
// good
<Foo bar={baz} />
属性
- JSX属性名使用骆驼式风格
camelCase
.
// bad
<Foo
UserName="hello"
phone_number={12345678}
/>
// good
<Foo
userName="hello"
phoneNumber={12345678}
/>
- 如果属性值为
true
, 可以直接省略. eslint:react/jsx-boolean-value
// 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" />
- 使用有效正确的 aria
role
属性值 ARIA roles. eslint:jsx-a11y/aria-role
// 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
- 总是在Refs里使用回调函数. eslint:
react/no-string-refs
// bad
<Foo
ref="myRef"
/>
// good
<Foo
ref={ref => { this.myRef = ref; }}
/>
括号
- 将多行的JSX标签写在
()
里. eslint:react/wrap-multilines
// 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>;
}
标签
- 对于没有子元素的标签来说总是自己关闭标签. eslint:
react/self-closing-comp
// bad
<Foo className="stuff"></Foo>
// good
<Foo className="stuff" />
- 如果模块有多行的属性, 关闭标签时新建一行. eslint:
react/jsx-closing-bracket-location
// 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
}
- 在
render
方法中总是确保return
返回值. eslint:react/require-render-return
// bad
render() {
(<div />);
}
// good
render() {
return (<div />);
}
模块生命周期
class extends React.Component
的生命周期函数:
- 可选的
static
方法 constructor
构造函数getChildContext
获取子元素内容componentWillMount
模块渲染前componentDidMount
模块渲染后componentWillReceiveProps
模块将接受新的数据shouldComponentUpdate
判断模块需不需要重新渲染componentWillUpdate
上面的方法返回true
, 模块将重新渲染componentDidUpdate
模块渲染结束componentWillUnmount
模块将从DOM中清除, 做一些清理任务- 点击回调或者事件处理器 如
onClickSubmit()
或onChangeDescription()
render
里的 getter 方法 如getSelectReason()
或getFooterContent()
- 可选的 render 方法 如
renderNavigation()
或renderProfilePicture()
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
displayName
设定模块名称propTypes
设置属性的类型contextTypes
设置上下文类型childContextTypes
设置子元素上下文类型mixins
添加一些mixinsstatics
defaultProps
设置默认的属性值getDefaultProps
获取默认属性值getInitialState
或者初始状态getChildContext
componentWillMount
componentDidMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
componentDidUpdate
componentWillUnmount
- clickHandlers or eventHandlers like
onClickSubmit()
oronChangeDescription()
- getter methods for
render
likegetSelectReason()
orgetFooterContent()
- Optional render methods like
renderNavigation()
orrenderProfilePicture()
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