【一年前端必知必会】如何写出简洁清晰的代码
不管进入什么样的公司,项目工程里总有一些业务代码像是在厕所里滚了一圈。即使使用了 ESlint
和 git hook
,也总是难以避免出现这种代码。本质上还是人的原因,一个 coder
对代码缺乏追求,不论使用什么工具都没法阻止他提交 shit code
。所以本篇文章的主题很简单,我们可以不知道设计模式,我们可以不懂各种算法,但是不妨碍我们写出简洁、清晰的代码,就像写一篇论文,辞藻、文献、数据可以没那么充分,但是该有的格式要有。另外,代码风格因人而异,本篇文章仅仅是个人观点,欢迎探讨。
接下来,从变量命名、排版格式、简化代码三个维度来讲讲,如何写出更简洁、清晰的代码。另外,简洁、清晰不是说这个代码写得多牛,设计模式和算法多花,而是——简单清晰,易于理解。
变量命名
这真是一个老生长谈的问题了,每个人在刚工作时都被无数遍警告过变量命名的问题。但是,讲道理,纵观我司一些基础设施代码,封装得很好,但是一看代码风格,惨不忍睹,很难阅读理解,而且这些基础设施的代码还是当时前端大牛封装的,早已成为公司业务的基石,无法变动。
其中,问题之一就是变量命名,代码中很多 x
、p
、G
类似的别称,很难理解到底什么意思。但是 G
它设置为 window
的别名,这其实完全没必要的。
正反面案例:
一般变量小驼峰命名:
// bad
function get_list_data() {
...
}
// good
function getListData() {
...
}
组件、类、构造函数等大驼峰命名:
// bad
class mainTable extends React.Component {
...
}
// good
class MainTable extends React.Component {
...
}
特殊意义变量不要简写:
// bad
const G = window
const n = navigator
const l = location
// good
const patientInfo = {}
const paymentInfo = {}
const getListData = function() {}
全局变量顶层声明,可大写及拼接下划线:
// bad
function useRegExp() {
const regExp = /^123/;
}
// good 避免在函数执行时重复声明
const NUMBER_REGEXP = /^1[3|4|5|7|8|9]\d{9}/;
const EN_CODE_REGEXP = /^abcdefg/
function useRegExp() {
...
}
不是越长语义化越好:
// bad
// 获取支付详情信息
function getPaymentDetailInfomation() {
...
}
// good 命名不要太长,否则会有阅读、书写的心智负担
function getPayDetail() {
...
}
善用语义词:
// 事件以 on 开头
function onGetListData() {
...
}
// ajax 请求以 fetch、get 开头
function fetchListData() {
...
}
// 删除、更新、创建
function deleteRecord() {
...
}
function updateRecord() {
...
}
function createRecord() {
...
}
// 表 boolean,is 开头
const isDialogShow = false
// 表可用时,can 开头
const canUseWechat = () => {}
// 表是否存在时,has 开头
const hasPropsInObject = () => {}
不要动不动使用 handle
:
这里确实是我个人意见了,因为很多人起名困难时动不动就是 handle
开头,并不是什么情况都适用。
// 自动轮询
// bad
function handleQueryLoop() {
...
}
// good
function autoQueryLoop() {
...
}
关于变量命名,先暂且讲这么多吧。
格式与排版
很多前端对样式的关注,就像他的头发一样稀少。一些公司或者项目可能缺少产品和UI,所以很多业务页面,前端拥有很大自由,很多人根本不考虑屏幕尺寸,也不考虑间距、颜色。讲道理,作为前端,不能侮辱自己的职业,建议大家平时学下UI和页面设计,即使以后转行产品也是大有裨益。
好的代码,靠变量命名是带不来的,在业务越来越复杂的时候,一个组件 3000 行,一个函数 500 行是常有的事,但是抽象、封装外,还有更容易被大家优化时忽略的点:那就是代码格式和排版。
写代码就像我们写论文,摘要、标题、脚注、参考文献等等,有它的格式,好的排版能带来愉悦的阅读体验。
优化判断条件
贴两段错误示例,完全符合 ESlint
的校验,但是真的好吗?
- 横向过长
if (number !== 1 || number !== 2 || number !== 3 || number !== 4 || number !== 5) {
...
}
- 纵向太碎
if (
string === 'a' &&
string2 === 'b' && (
string3 === 'c' ||
string4 === 'd'
)
) {
}
上述两种判断方式,没有人看了会高兴,其实,优化起来很简单。
横向过长:
if ([1, 2, 3, 4, 5].includes(number)) {
...
}
纵向太碎:
const isStringTrue = () => {
return (string === 'a' && string === 'b' && (string3 === 'c' || string4 === 'd'))
}
// 不要判断条件里写又臭又长的判断
if (isStringTrue()) {
}
善用空格
合理使用空格,是你代码风格进步的最有效手段之一:
// 数组 , 后空格
const array = [1, 2, 3, 4, 5]
// 对象两侧 , 后空格
const object = { a: '1', b: '2' }
// 部分()两侧、右侧留空格
if () {}
while () {}
for () {}
function() {}
// 运算符两侧空格
const a = b + c
const isTrue = typeof a === 'string' ? a : 1
const func = () => {}
善用缩进
缩进可以说是垃圾代码最恶心的槽点之二,某些人写 jsx
或者 HTML
常常不注重缩进,导致代码看起来七歪八扭:
return (
<div>
<Radio.Group
onChange={({ target: { value } }) => {
setSelectionType(value);
}}
value={selectionType}
>
<Radio
value="checkbox">Checkbox</Radio>
<Radio value="radio">radio</Radio>
</Radio.Group>
<Divider />
<Table
rowSelection={{
type: selectionType,
...rowSelection,
}}
columns={columns}
dataSource={data}
/>
</div>
);
善用换行
还是拿 jsx
代码举例:
// bad
return (
<Button style={{}} className="button-class" type="primary" onClick={start} disabled={!hasSelected} loading={loading}>
Reload
</Button>
);
// good
return (
<Button
style={{}}
className="button-class"
type="primary"
onClick={start}
disabled={!hasSelected}
loading={loading}
>
按钮
</Button>
);
------------------------------------
// bad
import React from 'react'
import A from 'aaa'
import B from 'bbb'
const { a } = A;
const { b } = B;
class Comp extends React.Component {
}
// good
import React from 'react'
import A from 'aaa'
import B from 'bbb'
const { a } = A;
const { b } = B;
class Child extends React.Component {
}
排版其实更依赖于个人的审美能力和代码习惯。好的排版应该是横向不长(不超过100个字符),纵向不细碎,该空格空格,该换行换行。其实只要做到这几点,80%的代码不会出现排版差的情况。
代码示例
接下来就是本文的重点了,是笔者总结的一些实用的代码片段或者代码书写方式,配合前面的变量命名和排版相信大家一定可以写出简洁、清晰的代码。
使用 map 简化枚举值映射
在日常写表格的需求中,经常会自定义列字段的渲染,特别是一些枚举值的映射,很多人写一堆 if else,但其实只需要一个 map 就可以简化我们的代码:
const tableColumns = [
{
title:'状态',
dataIndex: 'status',
render: (value, index, record) => {
const map = new Map([[1, '已完成'], [0, '未完成'], [3, '完成中'], [4, '中途放弃']]);
return map.get(value);
}
}
]
共性代码合并
还是以 table 表格为例,我们经常会和分页器打交道。分页器有两个方法:onPageChange
,onPageSizeChange
,这两个函数可以合为一个:
<Pagination
onChange={current => onPageChange(current, 'current')}
onPageSizeChange={current => onPageChange(current, 'pageSize')}
/>
const onPageChange = (value, type) => {
const { searchValues } = this.state
this.setState({
searchValues: {
...searchValues,
[type]: value,
}
})
}
这里这么写其实有点小问题,但是意思很简单,就是类似的方法可以合并,没必要定义好几个函数,执行相似功能。
使用新的语法
其实前面的案例中我们已经介绍过 includes,这里我们额外啰嗦一下:
includes 替代 || 运算符
// bad
if (a === 1 || a === 2 || a === 3) {
...
}
// good
if ([1, 2, 3].includes(a)) {
...
}
Set 给数组去重
const array = [1, 2, 3, 3, 4, 2, 1, 5, 6, 7, 7]
console.log([...new Set(array)]) // [1, 2, 3, 4, 5, 6, 7]
简易策略模式优化 if else
现在有一个场景,需要将人民币转换为各个外国客户所在国的货币结算,客户有英国、美国、澳大利亚、泰国等,如果使用 if else 来进行封装,每次客户扩展你只能改代码改到死。写一个策略模式就行了:
// 客户列表
const currencyList = ['美元', '澳元', '泰铢', '欧元']
// 汇率
const rateList = {
'美元': 0.144881,
'澳元': 0.216258,
'泰铢': 5.3383,
'欧元': 0.143649
}
// 实际开发中,上述两个数据都是动态从后端获得的
const rateKeys = Object.keys(rateList)
// 策略容器
const stratagy = {}
// 为每种汇率添加一个key,并赋值为一个计算对应价格的方法
rateKeys.forEach((item) => {
// 传入 RMB 价格作为基数
stratagy[item] = (RMPPrice) => {
return RMPPrice * rateList[item]
}
})
function getAbroadPrice(RMBPrice = 100, target = '美元') {
return stratagy[target](RMBPrice)
}
getAbroadPrice(100, '美元') // 14.481...
阻断提升
其实很简单,把空值、错值提到前面来判断,并阻断代码往下执行:
// bad
import { Message } from 'xxx-ui';
if (value) {
// 100 行逻辑
} else {
Message.error('value不能为空')
}
// good
import { Message } from 'xxx-ui';
if (!value) {
Message.error('value不能为空')
return
}
// 100 行逻辑
函数式编程替代for循环
filter;
map;
some;
every;
find;
findIndex;
Object.keys;
Object.values;
Object.entries;
...
善用 ?. 操作符
对象取值防止代码错误影响正常业务,造成生产事故。同时也能简化各种判空、判非。
if (res && res?.length) {
res.map()
...
}
抽离公共部分或静态变量到单独文件
以 Table 为例,一般我们要写很多 columns 配置,如果列特别多,这一个数组就有上百行,不利于组件阅读,可以将其中不必要留在组件的列定义放在单独的js文件里维护,还可以再进行一层封装:
import moment from 'moment'
import { toolTip } from 'xxx-ui'
// 鼠标浮在文字上显示完整信息
export const toolTip = (
value,
position,
isTime = false,
format = 'YYYY-MM-DD HH:mm:ss'
) => {
const finalValue = isTime && value ? moment(value).format(format) : value;
const tip = <span>{finalValue}</span>;
return (
<Tooltip
trigger={tip}
align={position}
>
{finalValue}
</Tooltip>
);
};
export const tableColumns = [
{
title: '序号',
width: 120,
dataIndex: 'serialNumber',
render: value => toolTip(value, 'r'),
},
{
title: '时间',
width: 120,
dataIndex: 'time',
render: value => toolTip(value, 'r', true),
}
]
未完待续。。。
写在最后
代码能力非一朝一夕之功,比起各种封装抽象,各种设计模式、算法,日常业务中多以增删改查为主。把基础的变量命名、排版、简洁写法贯彻后,代码就能简洁清晰很多。
与其远望高山,不如脚踏实地。与君共勉。
转载自:https://juejin.cn/post/7149048040676917256