代码规范
代码风格
01. 命名
- 函数命名
 
能够表明这个函数的用处。
// bad 
handleSave() {}
// good,可以带上模块名称
handleSaveCompany() {}
- 变量命名
 
能够表明这个变量的用处。
// bad
visible: false
// good
modalVisible: false
- modal命名
 
// bad
*fetch({ payload }, { call, put }) {
}
// good
*fetchBank({ payload }, { call, put }) {
}
- 组件属性命名
 
组件属性如果是函数,使用on前缀
// bad
  <TableList
    search={this.handleSearchCalendar}
  />
// good
  <TableList
    onSearch={this.handleSearchCalendar}
  />
- 组件命名
 
组件命名首字母大写,要能够表面这个组件的功能
// bad
  <dataType
    search={this.handleSearchCalendar}
  />
// good
  <DataType
    onSearch={this.handleSearchCalendar}
  />
02. 注释
// bad
/**
 * 批量生成数据屏蔽规则
 * @param {Array} params - 生成数据屏蔽规则列表
 */
export async function createDataRules(params) {
  return request(`${HZERO_IAM}/v1/doc-types/generate-shield-rule`, {
    method: 'POST',
    body: params,
  });
}
//  good
/**
 * 查询值集
 * @async
 * @function queryCode
 * @param {object} params - 查询条件
 * @param {!string} param.lovCode - 查询条件
 * @returns {object} fetch Promise
 */
export async function queryCode(params = {}) {
  return request(`${HZERO_PLATFORM}/v1/lovs/value`, {
    query: params,
  });
}
03. 表格列宽度
// bad
    const columns = [
      {
        title: '银行代码',
        width: 150,
        dataIndex: 'bankCode',
      },
      {
        title: '银行名称',
        width: 150,
        dataIndex: 'bankName',
      },
    ];
// good
    const columns = [
      {
        title: '银行代码',
        width: 150,
        dataIndex: 'bankCode',
      },
      {
        title: '银行名称',
        dataIndex: 'bankName',
      },
    ];
04. 表格宽度不固定
// bad
  <Table
    style={{ width: 800}}
    bordered
    rowKey="uomId"
    loading={loading}
    columns={columns}
    dataSource={dataSource}
    pagination={paginations}
    onChange={searchPaging}
  />
// good
  <Table
    bordered
    rowKey="uomId"
    loading={loading}
    columns={columns}
    dataSource={dataSource}
    pagination={paginations}
    onChange={searchPaging}
  />
05. 消息提醒
// bad
notification.success({message: '保存成功'});
// good
notification.success();
06. 引用路径
// bad
import autobind from '../../../utils/autobind'
// good 
import autobind from 'utils/autobind
07. 表单效验国际化
// 必输效验
// bad
rules: [
  {
    required: true,
    message: '二级域名不能为空',
  },
]
// good
rules: [
  {
    required: true,
    message: intl.get('hzero.common.validation.notNull', {
    name: intl.get('hptl.portalAssign.model.portalAssign.webUrl').d('二级页面域名'),
  }),
  },
]
// 长度效验
// bad
rules: [
  {
    max: 20,
    message: '长度不能超过20个字符'
  },
]
// good
rules: [
  {
    max: 20,
    message: intl.get('hzero.common.validation.max', {
      max: 20,
    }),
  },
]
08. 依赖引入
// bad
import { Header, Content } from 'components/Page';
import { connect } from 'dva';
// good
import { connect } from 'dva';
import { Header, Content } from 'components/Page';
09. 对象的简洁写法
// bad
const listPropsReceive = {
  dataSource: receiveList,
  editLine: this.editLine,
  pagination,
};
// good
const listPropsReceive = {
  loading,
  pagination,
  dataSource: receiveList,
  editLine: this.editLine,
};
10. 代码复用
// bad
<FormItem
  labelCol={{ span: 10 }}
  wrapperCol={{ span: 14 }}
>
</FormItem>
// good
const formLayout = {
  labelCol={{ span: 10 }}
  wrapperCol={{ span: 14 }
}
<FormItem {...formLayout}>
</FormItem>
11. 表单中的编码字段
新增、编辑或者充当查询条件的编码字段,需要添加 typeCase 属性
// bad
<FormItem
  label="条款代码"
>
  {form.getFieldDecorator('termCode', {
    initialValue: termCode,
  })(
    <Input
      trim
      inputChinese={false}
    />
  )}
</FormItem>
// good
<FormItem
  label="条款代码"
>
  {form.getFieldDecorator('termCode', {
    initialValue: termCode,
  })(
    <Input
      trim
      typeCase="upper"
      inputChinese={false}
    />
  )}
</FormItem>
12. 路径配置
// bad
{
  path: '/hdpm/modelType',
  component: 'ModelType',
}
// good
{
  path: '/hdpm/model-type',
  component: 'ModelType',
}
13. 页面跳转
// bad
goToDataType(record) {
  const { dispatch } = this.props;
  dispatch(
    routerRedux.push({
      pathname: `/hdpm/data-type/${record}`,
    })
  );
}
// good
goToDataType(record) {
  const { history } = this.props;
  history.push(`/hdpm/data-type/${record}`);
}
14. 返回上一级页面
// bad
handleGoBack = () => {
  const { history } = this.props;
  history.goBack();
};
// good
<Header backPath="/hdpm/model-type" />
15. className统一使用less语法
// bad
<div className="model-list"></div>
// good
<div className={styles['model-list']}>
16. 引出组件
// bad
class DataType extends React.Component {
  // ...
}
export DataType;
// good
export default class DataType extends React.Component {
  // ...
}
17. 不要使用纯组件
// bad
export default class DataType extends React.PureComponent {
  // ...
}
// good
export default class DataType extends React.Component {
  // ...
}
18. 函数形式
// bad
handleFetchList = () => {
  // ...
}
// good
@Bind()
handleFetchList() {
  // ...
}
19. 输入组件等不要写 placeholder
// bad
<TextField placeholder='请输入搜索内容'/>
// good
<TextField/>
功能优化
01. model 与 service 分离
// bad
export const service = {
  async queryBanks(params = {}) {
    const {
      page = { current: 1, pageSize: 10 },
      sort = { name: 'bankCode', order: 'asc' },
      body,
    } = params;
    return request(
      `${SERVICE_URL}?page=${page.current - 1}&size=${page.pageSize}&sort=${sort.name},${
        sort.order
      }&${stringify(body)}`
    );
  },
};
    *fetch({ payload }, { call, put }) {
      const response = yield call(service.queryBanks, payload);
      const list = getResponse(response);
      if (list) {
        yield put({
          type: 'updateState',
          payload: {
            list,
          },
        });
      }
    },
// good
// modal
import {
  createNodeGroup,
} from '../../services/hsgp/nodeGroupService';
effects: {
    *createNodeGroup({ payload }, { call }) {
      const res = yield call(createNodeGroup, payload);
      return getResponse(res);
    },
}
// service
export async function createNodeGroup(params) {
  return request(`${HZERO_HSGP}/v1/${params.envId}/node-groups`, {
    method: 'POST',
    body: params,
  });
}
02. Row 组件的下级须为 Col
// bad
<Row>
  <span>
    {intl.get('hzero.hpfm.config.view.label.tenant').d('选择租户')}:
  </span>
  <Lov
    style={{
      width: 300,
    }}
    code="HPFM.TENANT"
    value={selectedTenant.tenantId || 0}
    onChange={this.selectTenant}
  />
</Row>
// good
<Row>
  <Col>
      <span>
        {intl.get('hzero.hpfm.config.view.label.tenant').d('选择租户')}:
      </span>
      <Lov
        style={{
          width: 300,
        }}
        code="HPFM.TENANT"
        value={selectedTenant.tenantId || 0}
        onChange={this.selectTenant}
      />
  </Col>
</Row>
03. 操作按钮添加loading效果
// bad
<Button onClick={this.handleSaveCompany}>保存</Button>
// good
<Button onClick={this.handleSaveCompany} loading={saveCompanyLoading}>保存</Button>
04. 使用getResponse处理请求
// bad
  dispatch({
    type: 'concPermission/deleteHeader',
  }).then(res => {
    if (res && res.failed) {
      ...
    }
  });
// good
// dispatch
  dispatch({
    type: 'concPermission/deleteHeader',
  }).then(res => {
    if (res) {
      ...
    }
  });
// modal
*deleteHeader({ interfaceServerId }, { call }) {
  const res = yield call(deleteHeader, interfaceServerId);
  return getResponse(res);
},
05. 使用原生Modal
// bad
showAttrsDrawer = record => {
  open({
    title: intl.get(`${promptCode}.model.definition.attrs`).d('组件属性'),
    side: 'right',
    footer: null,
    width: 800,
    onOk: () => {},
    children: <AttrsDrawer record={record} />,
  });
};
// good
import { Modal } from 'hzero-ui';
return (
    <Modal
      destroyOnClose
      visible={modalVisible}
      width="1000px"
    >
    ...
    </Modal>
);
06. 函数参数传递
  // bad
  componentDidMount() {
    this.handleSearch({});
  }
 
  handleSearch(fields = {}) {...}
  // good
  componentDidMount() {
    this.handleSearch();
  }
  
  handleSearch(fields = {}) {...}
07. 公共的 render 函数使用
// bad
{
  title: '状态',
  dataIndex: 'enabledFlag',
  render: val => { return ( <span>{enableRender(val)}</span> ) },
}
// good
{
  title: '状态',
  dataIndex: 'enabledFlag',
  render: enableRender,
 },
08. 编辑的模态框,需要单独调用详情接口,添加loading效果 - 优先级低
// 包裹 Spin
<Spin spinning={loading}>
  ...
</Spin>
9. 在父组件中引入prompt后,非子路由的子组件中无需引入
10. 函数声明
// bad 
handleSaveCompany = () => {}
// good 
import autobind from 'utils/autobind';
@autobind
handleSaveCompany() {}
// 或者
constructor(props) {
  super(props);
  this.handleSaveCompany = this.handleSaveCompany.bind(this);
}
// 声明
handleSaveCompany() {}
11. ds文件
使用Choerodon UI开发时,一个页面服务使用一个js文件存放该页面服务的所有DataSet,文件命名格式统一为页面服务名+DS,注意:文件名首字母大写
// bad
  dataTypeDS.js
// good
  DataTypeDS.js
一个服务模块使用stores文件夹存放该服务模块的所有界面的DS文件,stores和models文件夹同级。
性能优化
01. 无用的父级标签,使用React.Fragment代替
// bad
render() {
    return (
      <div>
        <div className={style['sql-execute-left']}>
          <TreeTable />
        </div>
        <div className={style['sql-execute-right']}>
          <ExecutePage />
        </div>
      </div>
    );
  }
// good
render() {
    return (
      <React.Fragment>
        <div className={style['sql-execute-left']}>
          <TreeTable />
        </div>
        <div className={style['sql-execute-right']}>
          <ExecutePage />
        </div>
      </React.Fragment>
    );
  }
02. loading使用方式
// bad
@connect(({ sqlExecute, loading }) => ({
  sqlExecute,
  loading,
}))
// good
@connect(({ sqlExecute, loading }) => ({
  sqlExecute,
  executeSelectedLoading: loading.effects['sqlExecute/fetchExecuteResult'],
}))