React Native 代码规范

本规范主要用于React Native项目,原则是提升效率、规避风险。

  • React/JSX代码规范
  • ES6代码规范
  • 项目结构规范

React/JSX代码规范


文件与组件命名

  • 扩展名:使用.js作为js文件的扩展名。如果同一个文件夹下有同名而不同作用的js文件,则通过中缀(小写)进一步区分,例如:HomeView.component.js,HomeView.style.js,HomeView.action.js等。

  • 文件名: 使用驼峰命名法且首字母大写,如HomeView.js。

  • 组件命名: 与文件名(除中缀外)完全一致。如果组件单独放置在目录中,则目录名也一致。

// bad
import Footer from './Component/Footer/FooterView' 

// good
import Footer from './Component/Footer/Footer'

// good
import Footer from './Footer'

组件声明

  • 使用class与extends关键字。不使用React.createClass方法。需要导出的组件直接在class关键字前使用export default。
// bad
export default React.createClass({
});

// good
export default class HomeView extends React.Component {
}

对齐

  • 组件的属性要对齐,如果能一行摆下的props,最好摆在一行(个人建议超过两个就换行)。
// bad
<Foo superLongParam="bar"
     anotherSuperLongParam="baz" />

// good
<Foo
  superLongParam="bar"
  anotherSuperLongParam="baz"
/>

// 如果一行能摆下props,那就摆在一行
<Foo bar="bar" />

// 子组件照常缩进
<Foo
  superLongParam="bar"
  anotherSuperLongParam="baz"
>
  <Spazz />
</Foo>

引号

  • 对于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 />

state/props

  • 对于多个单词组成的pros,使用驼峰命名法。不使用下划线或连接线。
// bad
<Foo
  UserName="hello"
  phone_number={12345678}
/>

// good
<Foo
  userName="hello"
  phoneNumber={12345678}
/>
  • 读取state和props时,使用const与解构,必要时可使用let。不使用var。
// bad
var userName = this.props.userName;
let checked = this.state.checked;

// good
const { userName, age, sex } = this.props;
const { checked } = this.state;

括号

  • 当JSX标签超过一行时,使用括号包裹。单行时,不必有括号。
/// bad
render() {
  return <MyComponent className="long body" foo="bar">
           <MyChild />
         </MyComponent>;
}

// good
render() {
  return (
    <MyComponent className="long body" foo="bar">
      <MyChild />
    </MyComponent>
  );
}

// good, when single line
render() {
  const body = <div>hello</div>;
  return <MyComponent>{body}</MyComponent>;

标签

  • 对于没有子组件的JSX标签,始终自闭合。
// bad
<Foo className="stuff"></Foo>

// good
<Foo className="stuff" />
  • 如果组件有多行属性,则另起一行进行自闭合。
// bad
<Foo
  bar="bar"
  baz="baz" />

// good
<Foo
  bar="bar"
  baz="baz"
/>

方法

  • 为方法命名时,不使用下划线开头(哪怕是想用作私有方法)。
// bad
React.createClass({
  _onClickSubmit() {
    // do stuff
  }

  // other stuff
});

// good
class extends React.Component {
  onClickSubmit() {
    // do stuff
  }

  // other stuff
});

方法声明的顺序

  • 原则上按如下顺序排列React组件的各个方法(生命周期):
1. constructor
2. 静态方法(static methods)
3. getChildContext
4. componentWillMount
5. componentDidMount
6. componentWillReceiveProps
7. shouldComponentUpdate
8. componentWillUpdate
9. componentDidUpdate
10. componentWillUnmount
11. 点击处理或事件处理函数,比如onClickSubmit()、 onChangeDescription()
12. 用在render中的getter方法,比如getSelectReason()、 getFooterContent()
13. 可选的render方法,比如renderNavigation()、renderProfilePicture()
14. render
  • 按如下方式定义propTypes, defaultProps, contextTypes等
import React, { Component, PropTypes } from 'react';

const propTypes = {
  id: PropTypes.number.isRequired,
  url: PropTypes.string.isRequired,
  text: PropTypes.string,
};

const defaultProps = {
  text: 'Hello World',
};

class Link extends 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;

ES6代码规范


变量与常量声明

  • 1.1 变量

尽量使用let来代替var
对于全局变量声明,采用global.xxx = xxx,但应避免声明过多全局变量污染环境

  • 1.2 常量

对于常量应使用const进行声明,命名采用驼峰写法
对于使用 immutable 数据应用const进行声明

// 不好
let someNum = 123;
const AnotherStr = '不变的字符串';
let arr = ['不', '变', '数', '组'];
var ANOTHER_OBJ = {
  '不变对象': true
};

// 好
const someNum = 123;
const anotherStr = '不变的字符串';
const arr = ['不', '变', '数', '组'];
const anotherObj = {
  '不变对象': true
};

字符串

  • 2.1 处理多行字符串,使用模板字符串

以反引号( ` )标示,可读性更强,代码更易编写。
注意排版引起空格的问题,使用场景为声明HTML模板字符串

// 不好
const tmpl = '<div class="content"> \n' +
              '<h1>这是换行了。</h1> \n' +
            '</div>';

// 好
const tmpl = `
<div class="content">
  <h1>这是换行了。</h1>
</div>`;
  • 2.2 处理字符串拼接变量时,使用模板字符串
// 不好
 function sayHi(name) {
   return 'How are you, ' + name + '?';
 }

 // 好
 function sayHi(name) {
   return `How are you, ${name}?`;
 }

解构

  • 3.1 解构语句中不使用圆括号
// 不好
[(a)] = [11]; // a未定义
let { a: (b) } = {}; // 解析出错

// 好
let [a, b] = [11, 22];
  • 3.2 对象解构

对象解构 元素与顺序无关
对象指定默认值时仅对恒等于undefined ( !== null ) 的情况生效

  • 若函数形参为对象时,使用对象解构赋值
// 不好
function someFun(opt) {
  let opt1 = opt.opt1;
  let opt2 = opt.opt2;
  console.log(op1);
}

// 好
function someFun(opt) {
  let { opt1, opt2 } = opt;
  console.log(`$(opt1) 加上 $(opt2)`);
}

function someFun({ opt1, opt2 }) {
  console.log(opt1);
}
  • 若函数有多个返回值时,使用对象解构,不使用数组解构,避免添加顺序的问题
// 不好
function anotherFun() {
  const one = 1, two = 2, three = 3;
  return [one, two, three];
}
const [one, three, two] = anotherFun(); // 顺序乱了
// one = 1, two = 3, three = 2

// 好
function anotherFun() {
  const one = 1, two = 2, three = 3;
  return { one, two, three };
}
const { one, three, two } = anotherFun(); // 不用管顺序
// one = 1, two = 2, three = 3
  • 已声明的变量不能用于解构赋值(语法错误)
// 语法错误
let a;
{ a } = { b: 123};
  • 3.3 数组解构

数组元素与顺序相关

  • 交换变量的值
let x = 1;
let y = 2;

// 不好
let temp;
temp = x;
x = y;
y = temp;

// 好
[x, y] = [y, x]; // 交换变量
  • 将数组成员赋值给变量时,使用数组解构
const arr = [1, 2, 3, 4, 5];

// 不好
const one = arr[0];
const two = arr[1];

// 好
const [one, two] = arr;

数组

  • 4.1 将类数组(array-like)对象与可遍历对象(如Set, Map)转为真正数组

采用Array.from进行转换

// 不好
function foo() {
  let args = Array.prototype.slice.call(arguments);
}

// 好
function foo() {
  let args = Array.from(arguments);
}
  • 4.2 数组去重

结合Set结构与Array.from
使用indexOf,HashTable等形式,不够简洁清晰

// 好
function deduplication(arr) {
  return Array.from(new Set(arr));
}
  • 4.3 数组拷贝

采用数组扩展...形式

const items = [1, 2, 3];

// 不好
const len = items.length;
let copyTemp = [];
for (let i = 0; i < len; i++) {
  copyTemp[i] = items[i];
}

// 好
let copyTemp = [...items];
  • 4.4 将一组数值转为数组

采用Array.of进行转换

// 不好
let arr1 = new Array(2); // [undefined x 2]
let arr2 = new Array(1, 2, 3); // [1, 2, 3]

// 好
let arr1 = Array.of(2);  // [2]
let arr2 = Array.of(1, 2, 3); // [1, 2, 3]

函数

  • 5.1 当要用函数表达式或匿名函数时,使用箭头函数(Arrow Functions)

箭头函数更加简洁,并且绑定了this

// 不好
const foo = function(x) {
  console.log(foo.name); // 返回'' ,函数表达式默认省略name属性
};

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

var testObj = {
  name: 'testObj',
  init() {
    var _this = this; // 保存定义时的this引用
    document.addEventListener('click', function() {
      return _this.doSth();
    }, false);
  },
  doSth() {
    console.log(this.name);
  }
};

// 好
const foo = (x) => {
  console.log(foo.name); // 返回'foo'
};

[1, 2, 3].map( (x) => {
  return x + 1;
});

var testObj = {
  name: 'testObj',
  init() {
    // 箭头函数自动绑定定义时所在的对象
    document.addEventListener('click', () => this.doSth(), false);
  },
  doSth() {
    console.log(this.name);
  }
};
  • 箭头函数书写约定
    函数体只有单行语句时,允许写在同一行并去除花括号
    当函数只有一个参数时,允许去除参数外层的括号
// 好
const foo = x => x + x; // 注意此处会隐性return x + x

const foo = (x) => {
  return x + x; // 若函数体有花括号语句块时须进行显性的return
}; 

[1, 2, 3].map( x => x * x);
  • 用箭头函数返回一个对象,应用括号包裹
// 不好
let test = x => { x: x }; // 花括号会变成语句块,不表示对象

// 好
let test = x => ({ x: x }); // 使用括号可正确return {x:x}
  • 5.2 立即调用函数 IIFE

使用箭头函数

// 不好
(function() {
  console.log('哈');
})();

// 好
(() => {
  console.log('哈');
})();
  • 5.3 函数参数指定默认值

采用函数默认参数赋值语法

// 不好
function foo(opts) {
  opts = opts || {};// 此处有将0,''等假值转换掉为默认值的副作用
}

// 好
function foo(opts = {}) {
  console.log('更加简洁,安全');
}
  • 5.4 对象中的函数方法使用缩写形式

更加简洁
函数方法不要使用箭头函数,避免this指向的混乱

// 不好
const shopObj = {
  des: '对象模块写法',
  foo: function() {
    console.log(this.des);
  }
};

const shopObj = {
  des: '对象模块写法',
  foo: () => {
    console.log(this.des); // 此处会变成undefined.des,因为指向顶层模块的this
  }
};

// 好
const des = '对象模块写法'; // 使用对象属性值简写方式
const shopObj = {
  des,
  foo() {
    console.log(this.des);
  }
};

  • 6.1 类名应使用帕斯卡写法(PascalCased)
// 好
class SomeClass {

}
  • 类名与花括号须保留一个空格间距
    类中的方法定义时,括号 ) 也须与花括号 { 保留一个空格间距
// 不好
class Foo{
  constructor(){
    // constructor
  }
  sayHi()    {
    // 仅保留一个空格间距
  }
}

// 好
class Foo {
  constructor() {
    // constructor
  }
  sayHi() {
    // 仅保留一个空格间距
  }
}
  • 6.2 定义类时,方法的顺序如下:
  1. constructor
  2. public get/set 公用访问器,set只能传一个参数
  3. public methods 公用方法,公用相关命名使用小驼峰式写法(lowerCamelCase)
  4. private get/set 私有访问器,私有相关命名应加上下划线 _ 为前缀
  5. private methods 私有方法
// 好
class SomeClass {
  constructor() {
    // constructor
  }

  get aval() {
    // public getter
  }

  set aval(val) {
    // public setter
  }

  doSth() {
    // 公用方法
  }

  get _aval() {
    // private getter
  }

  set _aval() {
    // private setter
  }

  _doSth() {
    // 私有方法
  }
}
  • 6.3 如果不是class类,不使用new
// 不好
function Foo() {
}
const foo = new Foo();

// 好
class Foo {
}
const foo = new Foo();
  • 6.4 使用真正意思上的类Class写法,不使用prototype进行模拟扩展

Class更加简洁,易维护

// 不好
function Dog(names = []) {
  this._names = [...names];
}
Dog.prototype.bark = function() {
  const currName = this._names[0];
  alert(`one one ${currName}`);
}

// 好
class Dog {
  constructor(names = []) {
    this._names = [...names];
  }
  bark() {
    const currName = this._names[0];
    alert(`one one ${currName}`);
  }
}
  • 6.5 this的注意事项

子类使用super关键字时,this应在调用super之后才能使用,可在方法中return this来实现链式调用写法

class Foo {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
}

// 不好
class SubFoo extends Foo {
  constructor(x, y, z) {
    this.z = z; // 引用错误
    super(x, y);
  }
}

// 好
class SubFoo extends Foo {
  constructor(x, y, z) {
    super(x, y);
    this.z = z; // this 放在 super 后调用
  }
  setHeight(height) {
    this.height = height;
    return this;
  }
}

模块

  • 7.1 使用import / export来做模块加载导出,不使用非标准模块写法
// 不好
const colors  = require('./colors');
module.exports = color.lightRed;

// 好
import { lightRed } from './colors';
export default lightRed;
  • 7.2 import / export 后面采用花括号{ }引入模块的写法时,须在花括号内左右各保留一个空格
// 不好
import {lightRed} from './colors';
import { lightRed} from './colors';

// 好
import { lightRed } from './colors';

React Native项目结构规范


以下目录结构示例中只展示js与静态资源,不包含原生代码。

.
├── index.ios.js
├── index.android.js
└── js
    ├── component           可复用的组件(非完整页面)
    ├── page                完整页面
    ├── config              配置项(常量、接口地址、路由、多语言化等预置数据)
    ├── util                工具类(非UI组件)
    ├── style               全局样式
    └── image               图片

在component和page目录中,可能还有一些内聚度较高的模块再建目录

page/component
├── HomeView.component.js
├── HomeView.style.js
└── MovieView
    ├── MovieList.component.js          
    ├── MovieList.style.js          
    ├── MovieCell.component.js          
    ├── MovieCell.style.js              
    ├── MovieView.component.js          
    └── MovieView.style.js              

本文摘自https://github.com/sunnylqm/react-native-coding-style
基于airbnb的react代码规范与广发证券前端团队的ES6代码规范修改而成。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容