《大前端三问》 -- React编程规范

虽说是编程规范,但是本文旨在总结我在日常工作中与学习网上同行经验中收集来的一些最佳实践。建议平时编程尽自己最大能力编写最佳实践的代码,有助于提升自己的代码质量,同时也可以让自己在无形中养成追求卓越、追求优雅的气质。

1 优雅的语法

1.1 基于解构的swap方法

使用解构可以非常优雅地实现swap交换变量的值,下面请欣赏:

使用解构优雅地实现swap
1
[x, y] = [y, x];

1.2 书写顺序

在 Taro 组件中会包含类静态属性、类属性、生命周期等的类成员,其书写顺序最好遵循以下约定(顺序从上至下):

1
2
3
4
5
6
7
8
9
10
11
1. static 静态方法
2. constructor
3. componentWillMount
4. componentDidMount
5. componentWillReceiveProps
6. shouldComponentUpdate
7. componentWillUpdate
8. componentDidUpdate
9. componentWillUnmount
10. 点击回调或者事件回调 比如 onClickSubmit() 或者 onChangeDescription()
11. render

1.3 React

  • 不要在调用 this.setState 时使用 this.state

由于 this.setState 异步的缘故,这样的做法会导致一些错误,可以通过给 this.setState 传入函数来避免

  • map 循环时请给元素加上 key 属性
map 循环时请给元素加上 key 属性
1
list.map(item => <View className='list_item' key={item.id}>{item.name}</View> );

1.3.1 setState

  • 不要在调用 this.setState 时使用 this.state
不要在调用 this.setState 时使用 this.state
1
2
3
4
5
6
this.setState({
value: this.state.value + 1
}) // ✗ 错误
this.setState(prevState => ({ value: prevState.value + 1 })) // ✓ 正确
  • 不要在 componentWillUpdate/componentDidUpdate/render 中调用 this.setState
  • 不要定义没有用到的 state
  • 组件最好定义 defaultProps
  • render 方法必须有返回值
  • 值为 true 的属性可以省略书写值
  • 事件绑定均以 on 开头

2 书写规范

2.1 命名

普通 JS/TS 文件以小写字母命名,多个单词以下划线连接,例如 util.js、util_helper.js

React组件文件命名遵循 Pascal 命名法,首字母大写,例如 ReservationCard.jsx

2.2 文件后缀

普通 JS/TS 文件以 .js 或者 .ts 作为文件后缀
React组件则以 .jsx 或者 .tsx 作为文件后缀,当然这不是强制约束,只是作为一个实践的建议,组件文件依然可以以 .js 或者 .ts 作为文件后缀。

2.3 缩进

2.3.1 采用两个空格进行缩进

采用两个空格进行缩进
1
2
3
4
function hello (name) {
console.log('hi', name) // ✓ 正确
console.log('hello', name) // ✗ 错误
}

2.3.2 除了缩进,不要使用多个空格

除了缩进,不要使用多个空格
1
2
const id = 1234 // ✗ 错误
const id = 1234 // ✓ 正确

2.3.3 代码块中避免多余留白

代码块中避免多余留白
1
2
3
4
5
6
7
8
9
if (user) {
// ✗ 错误
const name = getName()
}
if (user) {
const name = getName() // ✓ 正确
}

2.3.4 关键字后面加空格

关键字后面加空格
1
2
if (condition) { ... } // ✓ 正确
if(condition) { ... } // ✗ 错误

2.3.5 展开运算符与它的表达式间不要留空白

展开运算符与它的表达式间不要留空白
1
2
fn(... args) // ✗ 错误
fn(...args) // ✓ 正确

2.3.6 注释首尾留空格

注释首尾留空格
1
2
3
4
5
//comment // ✗ 错误
// comment // ✓ 正确
/*comment*/ // ✗ 错误
/* comment */ // ✓ 正确

2.3.7 逗号后面加空格

逗号后面加空格
1
2
3
4
5
6
7
// ✓ 正确
const list = [1, 2, 3, 4]
function greet (name, options) { ... }
// ✗ 错误
const list = [1,2,3,4]
function greet (name,options) { ... }

2.3.8 单行代码块两边加空格

单行代码块两边加空格
1
2
function foo () {return true} // ✗ 错误
function foo () { return true } // ✓ 正确

2.3.9 点号操作符须与属性需在同一行

点号操作符须与属性需在同一行
1
2
3
4
5
console.
log('hello') // ✗ 错误
console
.log('hello') // ✓ 正确

2.3.10 字符串

  • 字符串统一使用单引号
字符串统一使用单引号
1
2
3
4
5
console.log('hello there')
// 如果遇到需要转义的情况,请按如下三种写法书写
const x = 'hello "world"'
const y = 'hello \'world\''
const z = `hello 'world'`

3 语法规范

3.1 变量

3.1.1 使用 const/let 定义变量

  • 当前作用域不需要改变的变量使用 const,反之则使用 let
1
2
3
4
5
const a = 'a'
a = 'b' // ✗ 错误,请使用 let 定义
let test = 'test'
var noVar = 'hello, world' // ✗ 错误,请使用 const/let 定义变量

3.1.2 不要省去小数点前面的 0

1
2
const discount = .5 // ✗ 错误
const discount = 0.5 // ✓ 正确

3.2 对象与数组

3.2.1 类名要以大写字母开头

类名要以大写字母开头
1
2
3
4
5
class animal {}
const dog = new animal() // ✗ 错误
class Animal {}
const dog = new Animal() // ✓ 正确

3.2.2 子类的构造器中一定要调用 super

子类的构造器中一定要调用 super
1
2
3
4
5
6
7
8
9
10
11
class Dog {
constructor () {
super() // ✗ 错误
}
}
class Dog extends Mammal {
constructor () {
super() // ✓ 正确
}
}

3.2.3 使用 this 前请确保 super() 已调用

使用 this 前请确保 super() 已调用
1
2
3
4
5
6
class Dog extends Animal {
constructor () {
this.legs = 4 // ✗ 错误
super()
}
}

3.2.4 对象中定义了存值器,一定要对应的定义取值器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const person = {
set name (value) { // ✗ 错误
this._name = value
}
}
const person = {
set name (value) {
this._name = value
},
get name () { // ✓ 正确
return this._name
}
}

3.2.5 使用数组字面量而不是构造器

1
2
const nums = new Array(1, 2, 3) // ✗ 错误
const nums = [1, 2, 3] // ✓ 正确

3.2.6 NaN

  • 检查 NaN 的正确姿势是使用 isNaN()
1
2
if (price === NaN) { } // ✗ 错误
if (isNaN(price)) { } // ✓ 正确

3.3 函数

3.3.1 避免使用 arguments.callee 和 arguments.caller

1
2
3
4
5
6
7
8
9
10
11
function foo (n) {
if (n <= 0) return
arguments.callee(n - 1) // ✗ 错误
}
function foo (n) {
if (n <= 0) return
foo(n - 1)
}

3.3.2 不使用 Generator 函数语法

  • 使用 Promise 或者 async functions 来实现异步编程

3.4 正则

3.4.1 正则中不要使用控制符

1
2
const pattern = /\x1f/ // ✗ 错误
const pattern = /\x20/ // ✓ 正确

3.5 逻辑与循环

3.5.1 始终使用 === 替代 ==

  • 例外: obj == null 可以用来检查 null || undefined
始终使用 === 替代 ==
1
2
3
4
if (name === 'John') // ✓ 正确
if (name == 'John') // ✗ 错误
if (name !== 'John') // ✓ 正确
if (name != 'John') // ✗ 错误

3.6 出错处理

  • 用 throw 抛错时,抛出 Error 对象而不是字符串
用 throw 抛错时,抛出 Error 对象而不是字符串
1
2
throw 'error' // ✗ 错误
throw new Error('error') // ✓ 正确
  • finally 代码块中不要再改变程序执行流程
finally 代码块中不要再改变程序执行流程
1
2
3
4
5
6
7
try {
// ...
} catch (e) {
// ...
} finally {
return 42 // ✗ 错误
}

3.6.1 使用 Promise 一定要捕捉错误

使用 Promise 一定要捕捉错误
1
asyncTask('google.com').catch(err => console.log(err)) // ✓ 正确

4 React组件

4.1 state & props

4.2 prop传递函数

父组件要往子组件传递函数,属性名必须以 on 开头。如:

父组件要往子组件传递函数,属性名必须以 on 开头
1
2
3
4
5
6
7
8
class MyButton extends Component {
render () {
return (
<Button onPress={() => this.props.onUserClick()} />
)
}
}

4.2.1 不要在 state 与 props 上用同名的字段

不要在 state 与 props 上用同名的字段
1
2
3
this.props = { content: 'content' }
this.state = { content: 'content' } // ✗ 错误
this.state = { msg: 'content' } // ✓ 正确

5 JSX最佳实践

5.1 属性值

5.1.1 布尔属性值

属性中的布尔属性,当为true时,可以省略赋值(即属性的默认值为true),比如:

1
2
3
<Label bold={true}>Name</Text>
等价于 ==>
<Label bold>Name</Text>

5.1.2 解构与展开

属性展开特性(Object Rest/Spread Properties for ECMAScript),此特性可以方便地为元素传递js对象的所有属性,常用于浅拷贝对象或修改对象的复制版本而不影响原对象。比如常用:

展开特性
1
<Button {...this.props, backgroundColor: '#ff0'} />

这个操作相当于将父组件传下来的props属性全部赋给button,然后只改变backgroundColor的值。

5.2 布局书写

5.2.1 尽量在一行写多个组件

推荐
1
2
3
<div>
<Main content={...} />
</div>
不推荐
1
<div> <Main content={...} /> </div>

5.2.2 retuen组件使用圆括号

retuen组件使用圆括号
1
2
3
4
5
return (
<View>
<Main content={...} />
</View>
);

5.2.3 多个属性的书写

多个属性书写一行一个属性,使用相同的缩进。
非容器组件最后的结尾/>与最后一个属性一行。

多个属性书写一行一个属性,使用相同的缩进
1
2
3
4
5
6
<Button
text="abc"
onSomething={this.handleSomething} />
<Button
text="123"
onSomething={this.handleSomething} />

5.2.4 条件语句

  1. 多使用短路判断

多使用短路判断特性,&&, ||。因为这样的代码更紧凑更一目了然。

多使用短路判断
1
2
3
4
5
6
7
8
9
10
11
12
13
<View>
{isLoggedIn && <LoginButton />}
</View>
// ---- 上面的写法优于下面这样写 ----
let button = null;
if (isLoggedIn) {
button = <LoginButton />
}
<View>
{button}
</View>
  1. 多种结果时使用三元操作符
使用三元操作符让组件结构更一目了然
1
2
3
<View>
{isLoggedIn ? <LogoutButton /> : <LoginButton />}
</View>
  1. 如果组件的条件判断太复杂,则抽出使用函数式组件
抽出使用函数式组件
1
2
3
4
5
6
7
8
const renderMain = () => {
// ... 复杂判断
return (...返回生成的组件);
}
<View>
{renderMain()}
</View>
  1. 善于使用map等函数式方法循环输出组件

此法常用于列表类等子元素较多的组件。

使用map函数简化代码
1
2
3
<List>
{users.map(user => <UserInfo user={user} />)}
</List>

5.3 巧用Fragment

React只允许返回单个Root元素,比如下面这样返回会报错:

错误的返回
1
2
3
4
5
6
7
8
class UserInfoView extends React.Component {
render() {
return (
<Text>Name</Text> // 报错了,return时只能返回单个元素
<Text>Age</Text>
);
}
}

但有的时候我们不希望组件的层级太深,比如外面已经有一层view了,此封装组件不想再有一层View了,这时可以使用Fragment组件包裹:

使用React.Fragment包裹并列子组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
return (
<React.Fragment>
<Text>Name</Text>
<Text>Age</Text>
</React.Fragment>
);
// 你也可以像下面一样使用短语法,和上面效果是一样的
return (
<>
<Text>Name</Text>
<Text>Age</Text>
</>
);

6 小结

1
2

7 引用

  1. 《谈一谈 Normalize.css》- 会飞的贼xmy
坚持原创技术分享,您的支持将鼓励我继续创作!