Web前端开发规范(with React)

Web 前端开发规范(with React)

不管有多少人共同参与同一项目,一定要确保每一行代码都像是同一个人编写的

一、概述

  • 提高团队协作效率、便于后期优化维护、输出高质量的代码
  • 代码即文档(编写的代码拥有一定程度的自我说明能力)
  • 封装的函数/类、复杂组件、复杂的业务逻辑,需要书写注释,注释要求简练准确。不要写过多注释
  • 书写 ES6+ 的语法

二、环境要求

  • Node.js 12 或更高版本,你可以使用 nvmnvm-windows 在一台电脑中管理多个 Node 版本
  • 使用 Visual Studio Code (VS Code) 进行代码编写
  • 代码提交前要保证没有运行 bug,并且要保证代码符合规范
  • 使用 Chrome 浏览器并安装 React Developer Tools 进行调试
  • 使用 Tab 缩进,规定 Tab 大小为 2 个空格,保证在所有环境下获得一致展现:
1
2
3
4
5
// vscode settings.json
{
"editor.tabSize": 2
// ...
}

三、编码规范

规范依据

具体规则

具体规则请阅读上述两篇文档,在此仅选取部分常见的规则进行强调与补充

JavaScript 常用代码规则

  • 标准变量采用驼峰式命名(考虑与后台交换数据的情况,对象属性可灵活命名)
  • 字符串统一使用单引号
  • 不要定义未使用的变量
  • 定义变量使用 let,定义常量使用 const
  • 全局性、关键性的常量全大写,用下划线连接
  • 变量名不应过短,要能准确完整地描述该变量所表述的事物
不好的变量名 好的变量名
inp input, priceInput
day1, day2, param1 today, tomorrow
id userId, orderId
obj orderData, houseInfos
tId removeMsgTimerId
handler submitHandler, searchHandler
  • 变量名的对仗要明确,如 up/downbegin/endopened/closedvisible/invisiblescource/target
  • 不要使用中文拼音,如 shijianchuo 应改成 timestamp ; 如果是复数的话加 s,或者加上 List,如 orderListmenuItems; 而过去式的加上 ed,如 updated/found 等; 如果正在进行的加上 ing,如 calling
  • 使用临时变量时请结合实际需要进行变量命名

有些喜欢取 tempobj 之类的变量,如果这种临时变量在两行代码内就用完了,接下来的代码就不会再用了,还是可以接受的,如交换数组的两个元素。但是有些人取了个 temp,接下来十几行代码都用到了这个 temp,这个就让人很困惑了。所以应该尽量少用 temp 类的变量

1
2
3
4
5
6
7
8
9
// not good
let temp = 10
let leftPosition = currentPosition + temp,
topPosition = currentPosition - temp

// good
let adjustSpace = 10
let leftPosition = currentPosition + adjustSpace
let topPosition = currentPosition - adjustSpace
  • 禁止嵌套三元表达式
  • 使用箭头函数取代简单的函数
1
2
3
4
5
6
7
8
// not good
let _this = this
setTimeout(function() {
_this.foo = "bar"
}, 2000)

// good
setTimeout(() => this.foo = "bar", 2000)
  • 禁止不必要的分号!!!
1
2
window.alert('hi')   // ✓ ok
window.alert('hi'); // ✗ avoid
  • 不要使用 (, [, or ` 等作为一行的开始,在没有分号的情况下代码压缩后会导致报错
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// ✓ ok
;(function () {
window.alert('ok')
}())
// ✗ avoid
(function () {
window.alert('ok')
}())

// ✓ ok
;[1, 2, 3].forEach(bar)
// ✗ avoid
[1, 2, 3].forEach(bar)

// ✓ ok
;`hello`.indexOf('o')
// ✗ avoid
`hello`.indexOf('o')

// 上面的正确写法是可行的,但是建议使用以下更加规范的写法

// 譬如:
;[1, 2, 3].forEach(bar)
// 建议的写法是:
let nums = [1, 2, 3]
nums.forEach(bar)
  • 关键字后面加空格
1
2
if (condition) { ... }   // ✓ ok
if(condition) { ... } // ✗ avoid
  • 函数声明时括号与函数名间加空格
1
2
3
4
5
function name (arg) { ... }   // ✓ ok
function name(arg) { ... } // ✗ avoid

run(function () { ... }) // ✓ ok
run(function() { ... }) // ✗ avoid
  • 函数调用时标识符与括号间不留间隔
1
2
console.log ('hello') // ✗ avoid
console.log('hello') // ✓ ok
  • 始终使用 === 替代 ==
1
2
3
4
5
if (name === 'John')   // ✓ ok
if (name == 'John') // ✗ avoid

if (name !== 'John') // ✓ ok
if (name != 'John') // ✗ avoid
  • 文件末尾留一空行

React 相关规范

主要使用技术栈
React 常用代码规则

组件

1. 基本规则

  • 一个文件最多只能包含一个 class 组件,可以包含多个 function 组件
  • 使用 JSX 语法
  • 使用 classfunctionHooks 声明组件,不使用 React.createElement

2. 组件名

  • 组件名称和定义该组件的文件名称尽量保持一致,名称尽量简短
1
2
3
4
5
// Good
import MyComponent from './MyComponent'

// Bad
import MyComponent from './MyComponent/index'
  • 应在 classfunction(箭头函数就是 const 关键字) 关键字或 声明后面直接声明组件名称,不要使用 displayName 来命名 React 模块
1
2
3
4
5
6
7
8
9
10
11
// Good
export default class MyComponent extends React.Component {}

const MyField = (props) => {
return <></>
}

// bad
export default React.createClass({
displayName: 'MyComponent',
})
  • 组件名称应全局唯一

很多业务都会有相同的文件命名,此时应在声明组件名称时保持全局唯一。一般是将业务链路名称全拼至组件声明

1
2
3
4
5
6
7
// Account/User/List.jsx

// Good
export default class AccountUserList extends React.Component {}

// Bad
export default class List extends React.Component {}
  • 高阶组件名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Bad
export default function withFoo(WrappedComponent) {
return function WithFoo(props) {
return <WrappedComponent {...props} foo />
}
}

// Good
export default function withFoo(WrappedComponent) {
function WithFoo(props) {
return <WrappedComponent {...props} foo />
}

const wrappedComponentName =
WrappedComponent.displayName || WrappedComponent.name || 'Component'

WithFoo.displayName = `withFoo(${wrappedComponentName})`
return WithFoo
}

1. 组件文件名

  • 组件文件后缀为 jsxtsx
  • 组件文件名为 PascalCase 大驼峰格式
1
2
components/
|- MyComponents.jsx
  • 组件文件夹名称和默认导出一个组件可以被拆分成多个组件编写的应建立目录,目录使用 PascalCase。建立 index.jsx 导出对象
1
2
3
4
5
components/
|- MyComponent/
|----index.jsx
|----MyComponentList.jsx
|----MyComponentListItem.jsx

4. Hooks

  • Function + Hooks 形式的组件优先,尽量少写 Class 组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// Good
import React, { useState, useEffect } from 'react'
import { Modal, Form, Checkbox } from 'antd'

const AutoModal = props => {
const { visible, formItemLayout, customerFlag, csFlag } = props
const [confirmLoading, setConfirmLoading] = useState(false)
const [form] = Form.useForm()
const [csFlagState, setCsFlagState] = useState(csFlag)
const [customerFlagState, setCustomerFlagState] = useState(customerFlag)
const handleFinish = async values => {
try {
setConfirmLoading(true)
await handleOk(values)
} catch (e) {
console.error(e)
} finally {
setConfirmLoading(false)
}
}
const handleOk = () => {
form.submit()
}
const handleCancel = () => {}
const handleCSFlagChange = e => {
setCsFlagState(e.target.checked)
}
const handleCustomerFlagChange = e => {
setCustomerFlagState(e.target.checked)
}
const checkFlag = () => {
if (csFlagState || customerFlagState) {
return Promise.resolve()
}
message.warning('服务客服或服务客户至少选一项')
return Promise.reject(new Error('服务客服或服务客户至少选一项'))
}

useEffect(() => {
// 使用visible防止出现useForm报错
if (visible) {
form.setFieldsValue({
newTskId: modifyObj.tskId,
creIdList: modifyObj.creIdList,
custIdList: modifyObj.custIdList,
})
}
}, [modifyObj, visible, form])

useEffect(() => {
if (visible) {
form.setFieldsValue({
csFlag,
})
setCsFlagState(csFlag)
}
}, [csFlag, visible, form])

useEffect(() => {
if (visible) {
form.setFieldsValue({
customeFlag: customerFlag,
})
setCustomerFlagState(customerFlag)
}
}, [customerFlag, visible, form])

return (
<Modal
forceRender
title={title}
visible={visible}
onOk={handleOk}
confirmLoading={confirmLoading}
onCancel={handleCancel}
destroyOnClose
>
<Form
form={form}
preserve={false}
onFinish={handleFinish}
layout="horizontal"
{...formItemLayout}
>
<Form.Item
{...formItemLayout}
style={{ marginBottom: 0 }}
label={
<Form.Item
noStyle
name="csFlag"
rules={[{ validator: checkFlag }]}
validateTrigger="onSubmit"
initialValue={csFlagState}
valuePropName="checked"
>
<Checkbox onChange={handleCSFlagChange}>{csLabel}</Checkbox>
</Form.Item>
}
></Form.Item>
<Form.Item
{...formItemLayout}
style={{ marginBottom: 0 }}
label={
<Form.Item
noStyle
name="customeFlag"
rules={[{ validator: checkFlag }]}
validateTrigger="onSubmit"
initialValue={customerFlagState}
valuePropName="checked"
>
<Checkbox onChange={handleCustomerFlagChange}>
{customerLabel}
</Checkbox>
</Form.Item>
}
></Form.Item>
</Form>
</Modal>
)
}

export default AutoModal

5. 组件中的命名规则

  • 属性名称: 使用小驼峰命名
  • style 样式: 使用小驼峰命名的样式属性

6. 组件样式

  • 使用 CSS Modules 编写局部样式组件,因为其对象是作为对象在 jsx 中使用,样式应使用小驼峰命名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.myWrapper {
&-Text {
}
}

import styles from 'style.less'

export default class MyComponent extends React.Component {
render() {
return (
<div className={styles.myWrapper}>
<p className={styles.myWrapperText}></p>
</div>
)
}
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!