• 代码规范


    代码风格

    01. 命名

    能够表明这个函数的用处。

    // bad 
    handleSave() {}
    
    // good,可以带上模块名称
    handleSaveCompany() {}
    
    

    能够表明这个变量的用处。

    
    // bad
    visible: false
    
    // good
    modalVisible: false
    
    // 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() {}
    
    

    性能优化

    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'],
    }))
    

    Choerodon-UI 开发规范

    0.1 命名规范

    使用Choerodon UI开发时,一个页面服务使用一个js文件存放该页面服务的所有DataSet,文件命名格式统一为页面服务名+DS,注意:文件名首字母大写

    // bad
      dataTypeDS.js
    
    // good
      DataTypeDS.js
    

    一个服务模块使用stores文件夹存放该服务模块的所有界面的DS文件,stores和models文件夹同级。

    页面文件夹命名采用驼峰式写法

    // bad
      dataType.js
      data-type.js
    
    // good
      DataType.js
    

    0.2 DataSet

    stores文件夹中的文件是dataset的配置文件,暴露一个plain object或者返回值为plain object的函数。

    // ds.js
    
    // bad
    const tableDs = new DataSet({
      dataKey: 'content',
      // ...
    })
    
    // good
    const tableDs = {
      dataKey: 'content',
      // ...
    }
    

    0.3 CSS/Less相关