本篇规范针对跨平台方案Taro,在React、React-native开发中酌情调整

组件及 JSX 书写规范


基本书写

组件创建

组件以类的形式进行创建,并且单个文件中只能存在单个组件

代码缩进

使用两个空格进行缩进,不要混合使用空格与制表符作为缩进

  1. import Taro, { Component } from '@tarojs/taro'
  2. import { View, Text } from '@tarojs/components'
  3. class MyComponent extends Component {
  4. render () {
  5. return (
  6. <View className='test'> // ✓ 正确
  7. <Text>12</Text> // 错误
  8. </View>
  9. )
  10. }
  11. }

单引号

JSX 属性均使用单引号

  1. import Taro, { Component } from '@tarojs/taro'
  2. import { View, Input } from '@tarojs/components'
  3. class MyComponent extends Component {
  4. render () {
  5. return (
  6. <View className='test'> // ✓ 正确
  7. <Text className="test_text">12</Text> // 错误
  8. </View>
  9. )
  10. }
  11. }

对齐方式

多个属性,多行书写,每个属性占用一行,标签结束另起一行

  1. // bad
  2. <Foo superLongParam='bar'
  3. anotherSuperLongParam='baz' />
  4. // good
  5. <Foo
  6. superLongParam='bar'
  7. anotherSuperLongParam='baz'
  8. />
  9. // 如果组件的属性可以放在一行就保持在当前一行中
  10. <Foo bar='bar' />
  11. // 多行属性采用缩进
  12. <Foo
  13. superLongParam='bar'
  14. anotherSuperLongParam='baz'
  15. >
  16. <Quux />
  17. </Foo>

空格使用

终始在自闭合标签前面添加一个空格

  1. // bad
  2. <Foo/>
  3. // very bad
  4. <Foo />
  5. // bad
  6. <Foo
  7. />
  8. // good
  9. <Foo />

属性书写

属性名称始终使用驼峰命名法

  1. // bad
  2. <Foo
  3. UserName='hello'
  4. phone_number={12345678}
  5. />
  6. // good
  7. <Foo
  8. userName='hello'
  9. phoneNumber={12345678}
  10. />

JSX 与括号

用括号包裹多行 JSX 标签

  1. // bad
  2. render () {
  3. return <MyComponent className='long body' foo='bar'>
  4. <MyChild />
  5. </MyComponent>
  6. }
  7. // good
  8. render () {
  9. return (
  10. <MyComponent className='long body' foo='bar'>
  11. <MyChild />
  12. </MyComponent>
  13. )
  14. }
  15. // good
  16. render () {
  17. const body = <div>hello</div>
  18. return <MyComponent>{body}</MyComponent>
  19. }

标签

当标签没有子元素时,始终使用自闭合标签

  1. // bad
  2. <Foo className='stuff'></Foo>
  3. // good
  4. <Foo className='stuff' />

如果控件有多行属性,关闭标签要另起一行

  1. // bad
  2. <Foo
  3. bar='bar'
  4. baz='baz' />
  5. // good
  6. <Foo
  7. bar='bar'
  8. baz='baz'
  9. />

书写顺序

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

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

通用约束与建议

所有内置组件均需要引入后再使用

  1. import Taro, { Component } from '@tarojs/taro'
  2. import { View } from '@tarojs/components'
  3. class MyComponent extends Component {
  4. render () {
  5. return (
  6. <View className='test'> // ✓ 正确
  7. <Text>12</Text> // 错误
  8. </View>
  9. )
  10. }
  11. }

推荐使用对象解构的方式来使用 state、props

  1. import Taro, { Component } from '@tarojs/taro'
  2. import { View, Input } from '@tarojs/components'
  3. class MyComponent extends Component {
  4. state = {
  5. myTime: 12
  6. }
  7. render () {
  8. const { isEnable } = this.props // ✓ 正确
  9. const { myTime } = this.state // ✓ 正确
  10. return (
  11. <View className='test'>
  12. {isEnable && <Text className='test_text'>{myTime}</Text>}
  13. </View>
  14. )
  15. }
  16. }

不要以 class/id/style 作为自定义组件的属性名

  1. <Hello class='foo' /> // ✗ 错误
  2. <Hello id='foo' /> // ✗ 错误
  3. <Hello style='foo' /> // ✗ 错误

不要使用 HTML 标签

  1. <div className='foo'></div> // ✗ 错误
  2. <span id='foo' /></span> // ✗ 错误

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

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

  1. this.setState({
  2. value: this.state.value + 1
  3. }) // ✗ 错误
  4. this.setState(prevState => ({ value: prevState.value + 1 })) // ✓ 正确

map 循环时请给元素加上 key 属性

  1. list.map(item => {
  2. return (
  3. <View className='list_item' key={item.id}>{item.name}</View>
  4. )
  5. })

尽量避免在 componentDidMount 中调用 this.setState

因为在 componentDidMount 中调用 this.setState 会导致触发更新

  1. import Taro, { Component } from '@tarojs/taro'
  2. import { View, Input } from '@tarojs/components'
  3. class MyComponent extends Component {
  4. state = {
  5. myTime: 12
  6. }
  7. componentDidMount () {
  8. this.setState({ // ✗ 尽量避免,可以在 componentWillMount 中处理
  9. name: 1
  10. })
  11. }
  12. render () {
  13. const { isEnable } = this.props
  14. const { myTime } = this.state
  15. return (
  16. <View className='test'>
  17. {isEnable && <Text className='test_text'>{myTime}</Text>}
  18. </View>
  19. )
  20. }
  21. }

不要在 componentWillUpdate/componentDidUpdate/render 中调用 this.setState

  1. import Taro, { Component } from '@tarojs/taro'
  2. import { View, Input } from '@tarojs/components'
  3. class MyComponent extends Component {
  4. state = {
  5. myTime: 12
  6. }
  7. componentWillUpdate () {
  8. this.setState({ // ✗ 错误
  9. name: 1
  10. })
  11. }
  12. componentDidUpdate () {
  13. this.setState({ // ✗ 错误
  14. name: 1
  15. })
  16. }
  17. render () {
  18. const { isEnable } = this.props
  19. const { myTime } = this.state
  20. this.setState({ // ✗ 错误
  21. name: 11
  22. })
  23. return (
  24. <View className='test'>
  25. {isEnable && <Text className='test_text'>{myTime}</Text>}
  26. </View>
  27. )
  28. }
  29. }

不要定义没有用到的 state

  1. import Taro, { Component } from '@tarojs/taro'
  2. import { View, Input } from '@tarojs/components'
  3. class MyComponent extends Component {
  4. state = {
  5. myTime: 12,
  6. noUsed: true // ✗ 没有用到
  7. }
  8. render () {
  9. const { myTime } = this.state
  10. return (
  11. <View className='test'>
  12. <Text className='test_text'>{myTime}</Text>
  13. </View>
  14. )
  15. }
  16. }

组件最好定义 defaultProps

  1. import Taro, { Component } from '@tarojs/taro'
  2. import { View, Input } from '@tarojs/components'
  3. class MyComponent extends Component {
  4. static defaultProps = {
  5. isEnable: true
  6. }
  7. state = {
  8. myTime: 12
  9. }
  10. render () {
  11. const { isEnable } = this.props
  12. const { myTime } = this.state
  13. return (
  14. <View className='test'>
  15. {isEnable && <Text className='test_text'>{myTime}</Text>}
  16. </View>
  17. )
  18. }
  19. }

render 方法必须有返回值

  1. import Taro, { Component } from '@tarojs/taro'
  2. import { View, Input } from '@tarojs/components'
  3. class MyComponent extends Component {
  4. state = {
  5. myTime: 12
  6. }
  7. render () { // ✗ 没有返回值
  8. const { isEnable } = this.props
  9. const { myTime } = this.state
  10. <View className='test'>
  11. {isEnable && <Text className="test_text">{myTime}</Text>}
  12. </View>
  13. }
  14. }

值为 true 的属性可以省略书写值

  1. <Hello personal />
  2. <Hello personal={false} />

JSX 属性或者表达式书写时需要注意空格

属性书写不带空格,如果属性是一个对象,则对象括号旁边需要带上空格

  1. <Hello name={ firstname } /> // ✗ 错误
  2. <Hello name={ firstname} /> // ✗ 错误
  3. <Hello name={firstname } /> // ✗ 错误
  4. <Hello name={{ firstname: 'John', lastname: 'Doe' }} /> // ✓ 正确

事件绑定均以 on 开头

在 Taro 中所有默认事件如 onClickonTouchStart 等等,均以 on 开头

  1. import Taro, { Component } from '@tarojs/taro'
  2. import { View, Input } from '@tarojs/components'
  3. class MyComponent extends Component {
  4. state = {
  5. myTime: 12
  6. }
  7. clickHandler (e) {
  8. console.log(e)
  9. }
  10. render () {
  11. const { myTime } = this.state
  12. return (
  13. <View className='test' onClick={this.clickHandler}> // ✓ 正确
  14. <Text className='test_text'>{myTime}</Text>
  15. </View>
  16. )
  17. }
  18. }

子组件传入函数时属性名需要以 on 开头

  1. import Taro, { Component } from '@tarojs/taro'
  2. import { View, Input } from '@tarojs/components'
  3. import Tab from '../../components/Tab/Tab'
  4. class MyComponent extends Component {
  5. state = {
  6. myTime: 12
  7. }
  8. clickHandler (e) {
  9. console.log(e)
  10. }
  11. render () {
  12. const { myTime } = this.state
  13. return (
  14. <View className='test'>
  15. <Tab onChange={this.clickHandler} /> // ✓ 正确
  16. <Text className='test_text'>{myTime}</Text>
  17. </View>
  18. )
  19. }
  20. }

Taro 自身限制规范

不能使用 Array#map 之外的方法操作 JSX 数组

Taro 在小程序端实际上把 JSX 转换成了字符串模板,而一个原生 JSX 表达式实际上是一个 React/Nerv 元素(react-element)的构造器,因此在原生 JSX 中你可以随意地对一组 React 元素进行操作。但在 Taro 中你只能使用 map 方法,Taro 转换成小程序中 wx:for

以下代码会被 ESLint 提示警告,同时在 Taro(小程序端)也不会有效:

  1. test.push(<View />)
  2. numbers.forEach(number => {
  3. if (someCase) {
  4. a = <View />
  5. }
  6. })
  7. test.shift(<View />)
  8. components.find(component => {
  9. return component === <View />
  10. })
  11. components.some(component => component.constructor.__proto__ === <View />.constructor)

以下代码不会被警告,也应当在 Taro 任意端中能够运行:

  1. numbers.filter(Boolean).map((number) => {
  2. const element = <View />
  3. return <View />
  4. })

解决方案
先处理好需要遍历的数组,然后再用处理好的数组调用 map 方法。

  1. numbers.filter(isOdd).map((number) => <View />)
  2. for (let index = 0; index < array.length; index++) {
  3. // do you thing with array
  4. }
  5. const element = array.map(item => {
  6. return <View />
  7. })