代码规范
代码风格
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() {}
性能优化
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 命名规范
- ds文件
使用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
- 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相关
- 所以px单位不需要使用rem,使用px即可。