React Native 开发规范

684 阅读4分钟

一、关于命名

(1) 代码中命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束

[反例]_name / $Object / name_ / name$ / Object$

(2)代码中命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式

[反例]DaZhePromotion [打折] / getPingfenByName() [评分] / int 某变量 = 3

(3)类名使用 UpperCamelCase 风格,必须遵从驼峰形式,第一个字母必须大写

/**
 *作者:郭翰林
 * 时间:2018/8/3 0003 17:10
 * 注释:安心管分享页面
 */
export default class AnXinSharePage extends PureComponent {

(4)方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase风格,必须遵从驼峰形式,第一个字母必须小写

  constructor(props) {
    super(props);
    this.isFromNative = this.props.navigation.state.params ? false : true;
    this.params = this.props.navigation.state.params || this.props.screenProps;
    this.state = {
      /**
       * 分数
       */
      score: 200,
      /**
       * 百分比
       */
      percent: "20%",
    };
  }
	
 /**
   * 作者:郭翰林
   * 时间:2018/7/26 0026 16:40
   * 注释:绑定按钮
   */
  componentDidMount() {
    this.props.navigation.setParams({onBack: this.onBack.bind(this)});
    this.props.navigation.setParams({onShare: this.onShare.bind(this)});
  }

(5)常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长,记得加注释便于后人理解

export const FamilyMemberKey = {
  /**
   * 关系
   */
  RELATION: "relation",
  /**
   * 身份证号
   */
  IDCARD: "idCard",
  /**
   * 姓名
   */
  NAME: "name",
  /**
   * 性别
   */
  SEX: "gender",
  /**
   * 地址
   */
  ADDRESS: "address",
  /**
   * 生日
   */
  BIRTHDAY: "birthday",
  /**
   * 英文名
   */
  ENGNAME: "engName",
  /**
   * 手机号
   */
  MOBILE: "mobile",
  /**
   * 身高
   */
  HEIGHT: "height"
}

(6)图片命名、文件夹命名统一小写

二、关于代码格式化

(1)IDE统一设置格式化规范,如下图设置

(2)import 导入,一个import独占一行

import {Image, ImageBackground, NativeModules, ScrollView, StyleSheet, Text, View} from 'react-native';
import React, {PureComponent} from 'react';
import {StyleConfig} from "../../resources/style";
import {SafeAreaView} from "react-navigation";
import Button from 'react-native-button';
import {XAnimatedProgress} from '@xyz/react-native-xprogress';

(3)外部样式格式化,左括号必须强制换行,一个属性独占一行,以强制换行右括号结尾

const styles = StyleSheet.create({
  main: {
    flex: 1,
  },
  messageStyle: {
    marginTop: 65,
    marginHorizontal: 43.5,
  },
  progressViewStyle: {
    marginTop: 39,
    alignItems: "center"
  },
  shareViewStyle: {
    width: StyleConfig.screen_width,
    marginTop: 67,
    marginBottom: 37,
    justifyContent: "center",
    alignItems: "center"
  },
});

(4)少用行内样式,如果是组件属性则应换行展示

  /**
   * 作者:郭翰林
   * 时间:2018/8/3 0003 12:15
   * 注释:绘制分数进度条
   */
  renderProgressView() {
    return (
      <View style={styles.progressViewStyle}>
        <XAnimatedProgress
          style={{}}
          fill={this.state.score}
          rate={1500}
          gradientStartColor="#ffffff"
          gradientEndColor="#ffffff"
          width={185}
          height={220}
          dashOffset={533}
          innerPath="M88,188 C88,188 13,163 13,75.5 C13,13 25.5,13 88,13 C150.5,13 163,13 163,75.5 C163,163 88,188 88,188 Z"
          progressWidth={15}
          shadowOffset={0}
          outerTransformOne="translate(-212.000000, -482.000000)"
          outerTransformTwo="translate(212.000000, 482.000000)"
          innerTransformOne="translate(-217.000000, -487.000000)"
          innerTransformTwo="translate(212.000000, 482.000000)"
          startTransparent={1}
          endTransparent={0.2}
          children={this.renderChildView}/>
      </View>
    );
  }

(5)提交代码之前必须格式化代码和删除未使用的import

三、关于代码注释

(1)类、类方法的注释必须使用 Javadoc 规范,使用/***/格式,不得使用 //xxx 方式

  /**
   * 作者:郭翰林
   * 时间:2018/8/3 0003 11:51
   * 注释:绘制头部信息和进度条
   */
  renderHeaderView() {
    let message;
    if (this.state.score < 300) {
      message = "近乎裸奔行走江湖的勇士,抵抗风险全靠精神意念。";
    } else if (300 <= this.state.score && this.state.score < 550) {
      message = "没心没肺没烦恼,胆大心不细,全凭运气好。";
    } else if (550 <= this.state.score && this.state.score < 700) {
      message = "徘徊在及格线边缘的纠结症患者,完善保障要趁早哦。";
    } else if (700 <= this.state.score && this.state.score < 800) {
      message = "筑起保障再去勇闯四方,像你这样靠谱的人已经不多啦。";
    } else {
      message = "你就是传说中那个能让家人安全感爆棚,兼具责任与使命感的家庭守护卫士。";
    }
    return (
      <View style={styles.messageStyle}>
        <Text style={{fontSize: 24, color: StyleConfig.color_white, textAlign: "center"}}>{message}</Text>
        {this.renderProgressView()}
      </View>
    );
  }

(2)所有的类都必须添加创建者信息,以及类的说明

/**
 * 作者:郭翰林
 * 时间:2018/7/26 0026 8:48
 * 注释:更新家庭成员资料
 */
class UpdateFamilyMemberPage extends PureComponent {

(3)方法内部单行注释,在被注释语句上方另起一行,使用//注释

/**
 * 作者:郭翰林
 * 时间:2018/7/27 0027 20:11
 * 注释:获取用户信息
 * @returns {Promise<void>}
 */
async getUserInfo() {
	this.userInfo = await UserBridge.getUser();
	if (this.userInfo) {
		//获取性别
		if (Platform.OS === "ios") {
			const {gender} = this.userInfo;
			this.setState({gender: gender});
		} else {
			const {linkman: {gender}} = this.userInfo;
			this.setState({gender: gender});
		}
		//是否实名认证
		const {authStatus} = this.userInfo;
		this.setState({authStatus: authStatus});
	}
}

(4)JSX复杂页面绘制应该抽出单独组件,复杂逻辑应添加注释标记

<ScrollView style={{flex: 1, backgroundColor: StyleConfig.color_background}}
			contentContainerStyle={{paddingTop: 0, paddingBottom: 0}}
			automaticallyAdjustContentInsets={false}
			keyboardShouldPersistTaps="handled"
			showsHorizontalScrollIndicator={false}
			showsVerticalScrollIndicator={false}>
	<View>
		{/*个人资料*/}
		<PersonalDataView
				props={this.props} 
				isAdd={false}
				isShowRelationship={this.state.insurantId ? true : false}
				gender={this.state.gender}
				authStatus={this.state.authStatus ? this.state.authStatus : -1}
		/>
		{/*生活习惯*/}
		<LifeHabitView props={this.props}/>
		{/*职业信息*/}
		<JobInfoView props={this.props}/>
		{/*财务信息*/}
		<FinanceInfoView props={this.props}/>
		<View style={{height: 44, alignItems: 'center', marginTop: 30}}>
			<XLargeButton onPress={() => this.filterUpdate()}>保存</XLargeButton>
		</View>
		<View style={{alignItems: 'center', marginTop: 30, marginBottom: 40}}>
			<Text style={styles.textStyle}>个人信息仅用于给你提供新一站服务时使用。</Text>
		</View>
	</View>
</ScrollView>
import {  View,StyleSheet,} from 'react-native';
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import ItemSectionView from "./ItemSectionView";
import Picker from '../../../components/views/picker/index';
import {XListItem} from '../../../components/antd/XList';
import {FamilyMemberKey,OftenDrinking,OftenDriving,OftenExercising,OftenSmoking} from "../constants/FamilyMemberConst";

/**
 * 作者:郭翰林
 * 时间:2018/7/26 0026 18:08
 * 注释:生活习惯页面
 */
export default class LifeHabitView extends PureComponent {
    static propTypes = {
        props: PropTypes.any,
    }

    constructor(props) {
        super(props);
        this.state = {};
    }

    render() {
        const {getFieldProps} = this.props.props.form;
        return (
            <View style={{flex:1}}>
                <ItemSectionView sectionName={"生活习惯"}/>
                {/*经常开车*/}
                <Picker data={OftenDriving} cols={1}
                        title={"经常开车"}
                        {...getFieldProps(FamilyMemberKey.OFTENDRIVING, {
                            rules: [{required: true}],
                        })}
                >
                    <XListItem arrow={"horizontal"}>经常开车</XListItem>
                </Picker>
                {/*经常运动*/}
                <Picker data={OftenExercising} cols={1}
                        title={"经常运动"}
                        {...getFieldProps(FamilyMemberKey.OFTENEXERCISING, {
                            rules: [{required: true}],
                        })}
                >
                    <XListItem arrow={"horizontal"}>经常运动</XListItem>
                </Picker>
                {/*经常吸烟*/}
                <Picker data={OftenSmoking} cols={1}
                        title={"经常吸烟"}
                        {...getFieldProps(FamilyMemberKey.OFTENSMOKING, {
                            rules: [{required: true}],
                        })}
                >
                    <XListItem arrow={"horizontal"}>经常吸烟</XListItem>
                </Picker>
                {/*经常饮酒*/}
                <Picker data={OftenDrinking} cols={1}
                        title={"经常饮酒"}
                        {...getFieldProps(FamilyMemberKey.OFTENDRINKING, {
                            rules: [{required: true}],
                        })}
                >
                    <XListItem arrow={"horizontal"}>经常饮酒</XListItem>
                </Picker>
            </View>
        );
    }

}

const styles = StyleSheet.create({

});

(5)不允许残留注释代码,注释掉的确认不用的代码应当删除

(6)关于注释模板,复制以下代码到IDE设置->Setting进行如下图设置

注释: $c$
* 时间: $a$ $b$
* @author XXX

在需要使用的注释的地方,使用快捷键调出注释,然后回车,打c回车或者Tab。具体使用如果有不明白的地方,百度“IDE设置Live Templates”

四、关于代码编写规范

(1)Props属性一定要写方法检测

  static propTypes = {
    props: PropTypes.any,
    isAdd: PropTypes.bool,
    isShowRelationship: PropTypes.bool,
    gender: PropTypes.number,
    authStatus: PropTypes.number
  }

(2)如果元素内没有子视图,则标签应该闭合

<LifeHabitView props={this.props}/>  //正确
<LifeHabitView props={this.props}></LifeHabitView>//错误

(3)内部变量使用let,常量不变值使用const,不要再使用var

	//请求接口
	const response = await AnXinServe.checkRepeatIdCard(params);
	if (response) {
		console.log("检测家庭成员" + JSON.stringify(response));
		if (response.msg) {
			this.setState({errorMsg: response.msg.msg});
			this.setState({modalErrorVisiable: true});
			return;
		}
		if (response.familyMemberDTO) {
			let {insurantId, loginId} = response.familyMemberDTO;
			if (insurantId) {
				this.forms = forms;
				this.insurantId = insurantId;
				this.setState({isLoading: false});
				this.showCoverDialog();
				return;
			}
			if (loginId) {
				this.setState({isLoading: false});
				this.showIsMeDialog();
				return;
			}
			this.addMember(params);
		} else {
			this.addMember(params);
		}
	}

(4)尽量采用解构赋值的写法获取参数,不要使用xxxx.xxx.xxx的方式获取参数

 let {insurantId, loginId} = response.familyMemberDTO;

(5)关于一对多选值,不要采用if else和switch方式获取,尽量采用预设Map,然后从Map中获取的方式拿到想要的值。

  _selectDefaultImage(rowData) {
    const relation = rowData.relation;
    const loginId = rowData.loginId;
    if (rowData.gender == 1 && loginId) { //投保人是男
      if (loginId) {
        return defaultImage.husband;
      } else {
        if (relation == 1) {
          return defaultImage.wife
        }
        if (relation == 2) {
          return defaultImage.father
        }
        if (relation == 3) {
          return defaultImage.mother
        }
        if (relation == 4 || relation == 6) {
          return defaultImage.gonggong_yuefu
        }
        if (relation == 5 || relation == 7) {
          return defaultImage.popo_yuemu
        }
        if (relation == 8) {
          return defaultImage.boy_bady
        }
        if (relation == 9) {
          return defaultImage.girl_baby
        }
      }
    } else {
      if (loginId) {
        return defaultImage.wife;
      } else {
        if (relation == 1) {
          return defaultImage.husband
        }
        if (relation == 2) {
          return defaultImage.father
        }
        if (relation == 3) {
          return defaultImage.mother
        }
        if (relation == 4 || relation == 6) {
          return defaultImage.gonggong_yuefu
        }
        if (relation == 5 || relation == 7) {
          return defaultImage.popo_yuemu
        }
        if (relation == 8) {
          return defaultImage.boy_bady
        }
        if (relation == 9) {
          return defaultImage.girl_baby
        }
      }
    }
    return defaultImage.husband;
  }
  /**
   * 作者:郭翰林
   * 时间:2018/8/1 0001 9:49
   * 注释:设置头像对应关系
   */
  setHeaderImageMap() {
    this.headerImageMap.set(0, require('../../resources/images/anXin/user_man.png'));
    this.headerImageMap.set(1, require('../../resources/images/anXin/user_women.png'));
    this.headerImageMap.set(2, require('../../resources/images/anXin/father.png'));
    this.headerImageMap.set(3, require('../../resources/images/anXin/mother.png'));
    this.headerImageMap.set(4, require('../../resources/images/anXin/gongong_or_yuefu.png'));
    this.headerImageMap.set(5, require('../../resources/images/anXin/popo_or_yuemu.png'));
    this.headerImageMap.set(6, require('../../resources/images/anXin/gongong_or_yuefu.png'));
    this.headerImageMap.set(7, require('../../resources/images/anXin/popo_or_yuemu.png'));
    this.headerImageMap.set(8, require('../../resources/images/anXin/boy_baby.png'));
    this.headerImageMap.set(9, require('../../resources/images/anXin/girl_baby.png'));
    this.headerImageMap.set(10, require('../../resources/images/anXin/anxin_car.png'));
    this.headerImageMap.set(11, require('../../resources/images/anXin/anxin_house.png'));
    this.headerImageMap.set(12, require('../../resources/images/anXin/anxin_cash.png'));
    this.headerImageMap.set(-1, require('../../resources/images/anXin/anxin_add.png'));
  }
	
  /**
   * 作者:郭翰林
   * 时间:2018/8/2 0002 9:56
   * 注释:获取显示头像
   */
  getItemHeaderImg(dataItem) {
    //是本人
    if (dataItem.relation < 2 && dataItem.relation !== -1) {
      if (dataItem.gender === 0) {
        //女
        return this.headerImageMap.get(1);
      } else {
        //男
        return this.headerImageMap.get(0);
      }
    } else {
      return this.headerImageMap.get(dataItem.relation);
    }
  }

(6)关于数组合并,不要采用for循环的方式合并,采用ES6推荐的方式合并数组

  /**
   * 作者:郭翰林
   * 时间:2018/7/31 0031 19:29
   * 注释:获取家庭成员列表
   * @returns {Promise<void>}
   */
  async getUserLists() {
    const params = {};
    params.joinEvalution = 1;
    params.loginId = await UserBridge.getLoginId();
    this.setState({isLoading: true});
    const response = await AnXinServe.familyMemberList(params);
    console.log("第三方保单-获取保障对象" + JSON.stringify(response));
    this.setState({isLoading: false});
    if (response) {
      if (response.familyMemberDTOList) {
        let defaultDatas = [
          {relation: 10, name: "车辆"},
          {relation: 11, name: "房屋"},
          {relation: 12, name: "银行卡"},
          {relation: -1, name: "添加新成员"},
        ];
        this.datas = [...response.familyMemberDTOList, ...defaultDatas];
        this.setState({listData: this.transDatas(-1)});
      }
    }
  }

(7)判空使用!==null的形式去判空不要使用变量本身去判空,否则变量为0会引起BUG

  async getUserInfo () {
    this.userInfo = await UserBridge.getUser()
    if (this.userInfo!==null) {
      const {gender} = this.userInfo
      if (gender) {
        this.setState({gender: gender})
      }
    }
  }

(8)state变量应当事先在constructor()中声明,不涉及页面刷新的变量不应写入state中

  constructor (props) {
    super(props)
    this.state = {
      modalMeVisiable: false,
      modalCoverVisiable: false,
      modalErrorVisiable: false,
      isLoading: false,
      gender: 1,
      errorMsg: '',
    }
    this.insurantId = ''
    /**
     * 表单数据
     * @type {{}}
     */
    this.forms = {}
    this.joinEvalution = props.navigation.state.params.joinEvalution
  }

(9)尽量使用箭头函数,不要使用this.XXXX.bind(this)的函数 普通函数中this对象的指向是可变的,但是在箭头函数中,它是固定的

function foo() {
	setTimeout(() => {
	console.log('id:', this.id);
	}, 100);
}
var id = 21;
foo.call({ id: 42 });
// id: 42

箭头函数可以让this指向固定化,这种特性很有利于封装回调函数

  var handler = {
    id: '123456',
    init: function() {
      document.addEventListener('click',
        event => this.doSomething(event.type), false);
    },
    doSomething: function(type) {
      console.log('Handling ' + type + ' for ' + this.id);
    }
  };

上面代码的init方法中,使用了箭头函数,这导致这个箭头函数里面的this,总是指向handler对象。否则,回调函数运行时,this.doSomething这 一行会报错。 this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块 的this。正是因为它没有this,所以也就不能用作构造函数。 (10)使用ES6的语法的导出和导入即export和import (11)关于常量定义不建议在常量类中书写属性方法,如果需要定义方法最好使用类引用Static函数的方式。

import { NativeModules, Platform, StatusBar } from 'react-native'

export default class Common {

  static async getTopOfSafeArea () {
    if (Platform.OS === 'ios') {
      return await NativeModules.CommonUtilBridge.getTopOfSafeArea()
    } else {
      return StatusBar.currentHeight
    }
  }

  static async getTopEdgeOfSafeArea () {
    if (Platform.OS === 'ios') {
      return await NativeModules.CommonUtilBridge.getTopEdgeOfSafeArea()
    } else {
      return StatusBar.currentHeight
    }
  }

  static async getBottomOfSafeArea () {
    if (Platform.OS === 'ios') {
      return await NativeModules.CommonUtilBridge.getBottomOfSafeArea()
    } else {
      return 0
    }
  }

  static async addPolicyToWallet (policyId, callback) {
    if (Platform.OS === 'ios') {
      await NativeModules.CommonUtilBridge.addPolicyToWallet(policyId, callback)
    }
  }

  static async uploadPoilcy (file, callback) {
    await NativeModules.CommonUtilBridge.uploadPolicy(file, callback)
  }
}