不知不觉已是 2019 年的 7 月,恍惚之间已工作四年。懵懵懂懂的成长,间歇性努力,实话说,对现在自己取得的成果不大满意。不过,好在时不时顿悟,知道适时作出改变。
此后发文会适当记录一些心路历程,与君共勉。
欢迎 Star 和订阅我的博客。
本文要点:
- 什么项目,为何会重构?
- 怎么重构的?
- 重构前后对比
什么项目,为何会重构?
项目是公司主打业务产品之一的可视化子项目,与其他子项目几乎没有耦合,所以可以单独拎出来重构。
具体业务不作描述。技术主要用的是 Vue2 系列和 JavaScript,还有一个自研的可视化工具库。第一个重构原因就是没有引入静态类型,导致查看一个对象结构需要翻来覆去在多个文件中查找。第二是因为之前新增代码模式一般为:“来一个需求加一段代码”,长期积累导致代码结构混乱,可读性差。第三是各个状态模块耦合度高,加大了代码维护难度。
怎么重构的?
一、在 JavaScript 中使用 TypeScript。“什么?在 JS 中使用 TS? 闻所未闻。”在看到 TS 官网手册最后一条 ”Type Checking JavaScript File” 之前,我也这样想。其实,TS 和 VSCode(一款 IDE)结合,也可以实现静态类型检测,只不过使用注释形式,一样支持 tsconfig.json
和自定义 Typing。
type TypeApple = {name: string, count: number}
/** @type {TypeApple} */
const apple = {name: 'foo', count: 100}
二、细化模块分类。一般情况下,模块都会有耦合。但如果耦合度过高,往往是因为模块没有细分到位。如果细化模块?举例,假如有一个模块叫Operation
,里面既包含操作相关逻辑,也有操作面板逻辑。随着业务发展,操作面板逻辑越来越多。我们完全可以将操作面板逻辑单独抽成一个模块OperationPanel
。
三、解耦可视化库和 Vue/Vuex。写业务的时候,很容易因为方便,在 Vue 组件或 Vuex 模块中代码越写越长,越来越难维护。这个项目也不列外。所以重构的时候,单独将可视化库喜爱那个管逻辑抽成模块,并使用类 Vuex 写法 (state, getters, mutations, actions) 进行管理。
class Counter {
// # state
/** @type {number} */
count = 0
// # getters
get countText() { return `Count is: ${ this.count}` }
// # mutations
/** @param {number} count*/
SET_COUNT = count => {this.count = count}
// # actions
/** @param {number} count*/
logCount = (count) => {this.SET_COUNT( count)
console.log(this.countText)
}
}
四、最后一条,编写可维护性高的代码。这里说两个方法。
第一个是“使用 Map
”。处理“一个有多类型的数据”需要使用判断,常见有 3 种方法:If
, Switch
, Map
。If
的使用简单粗暴,容易理解。
if (animalType === 'dog') {console.log( 'Wang!')
} else if (animalType === 'cat') {console.log( 'Miao!')
} else if (animalType === 'bird') {console.log( 'Jiu!')
}
Switch
可以看做是 If
的简化。
switch (animalType) {
case 'dog':
console.log('Wang!')
break
case 'cat':
console.log('Miao!')
break
case 'bird':
console.log('Jiu!')
break
}
而 Map
针对性最强,并且最简洁、最易于维护。
const logMap = {dog: () => console.log('Wang!'),
cat: () => console.log( 'Miao!'),
bird: () => console.log( 'Jiu!'),
}
logMap[animalType]()
具体使用也哪一种因场景而异,但多数场景下,使用 Map
可读性更强。
第二个是“使用 getters 和 mutations”。比如定义一个模块的`:
operationGetters.js`, 里面提供各种用来获取与操作有关的常量和方法。
export const OPERATION_TYPE_A = 0
export const OPERATION_TYPE_B = 1
export const OPERATION_TITLE_MAP = {[ OPERATION_TYPE_A]: 'Title A',
[OPERATION_TYPE_B]: 'Title B',
}
export const getOperationTitleByType = type => OPERATION_TITLE_MAP[type]
定义 mutations
则是定义一个提供相关各种变更数据方法的文件。在维护代码的时候,查找变更方法名即可直接找到更改数据的出处。
export const SET_OPERATION_TITLE = (operation, title) => {operation.title = title}
重构前后对比
代码量减少了快一半,性能显著提升,最重要的是代码可读性、可维护性大大增强,从而淡定从容应对之后的新需求。
感谢你花时间阅读这篇文章。如果你喜欢这篇文章, 欢迎点赞、收藏和分享,让更多的人看到这篇文章,这也是对我最大的鼓励和支持!
欢迎 Star 和订阅我的原创前端技术博客。