虽说是编程规范,但是本文旨在总结我在日常工作中与学习网上同行经验中收集来的一些最佳实践。建议平时编程尽自己最大能力编写最佳实践的代码,有助于提升自己的代码质量,同时也可以让自己在无形中养成追求卓越、追求优雅的气质。
1 优雅的语法
1.1 基于解构的swap方法
使用解构可以非常优雅地实现swap交换变量的值,下面请欣赏:
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 属性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.state1 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 注释首尾留空格
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 test = 'test' var noVar = 'hello, world'
|
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
子类的构造器中一定要调用 super1 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
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 代码块中不要再改变程序执行流程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 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 2 3
| <View> {isLoggedIn ? <LogoutButton /> : <LoginButton />} </View>
|
- 如果组件的条件判断太复杂,则抽出使用函数式组件
抽出使用函数式组件1 2 3 4 5 6 7 8
| const renderMain = () => { return (...返回生成的组件); }
<View> {renderMain()} </View>
|
- 善于使用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> <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 小结
7 引用
- 《谈一谈 Normalize.css》- 会飞的贼xmy