关于重构问题——编写可维护性高的代码
一、使用TypeScript:
背景:
RollBar是一个网页检测的网站,该网站统计了2018年前端项目抛出异常的种类,其中有七种是type error,即类型错误。异常最多的类型是读取了undefined变量的属性
而这个问题可以通过TS定义严格的数据类型解决:
type TypeApple = { name: string, count: number }
/** @type {TypeApple} */
const apple = { name: 'foo', count: 100 }
TS和VSCode(一款IDE)结合,也可以实现静态类型检测,只不过使用注释形式,一样支持tsconfig.json
和自定义Typing。
如何使用TS:
npm install –g typescript
tsc –v
1.新建 .ts 后缀的文件 —— 2.编写满足 ts 风格的代码——3.使用tsc编译ts文件(最后得到的还是js文件)
TS主要语法:
1.类型系统:
2.接口
在TypeScript中,接口的作用是对值所拥有的结构进行类型检查,接口是一种结构约束。同时提供了一种方式去抽象代码。
3.类
js本身并不是一种OOP(面向对象编程)的语言,并没有类这样一个概念。JS的对象使用原型链进行继承和扩展。ES6中提供的Class关键字只是一个语法糖,让你用面向对象的方式编写类,底层还是原型链的链接。
继承,以某个类为父类,继承所有的属性和方法,同时可以进行扩展和重写方法:
a.protected 在类自身的内部可以访问,子类可以访问,外部无法访问;
b.private 仅可在类自身的内部进行访问,子类无法访问,外部无法访问。
抽象类。抽象类无法实例化,只能作为其他类的基类。不同于接口,抽象类可以描述类的实现细节。抽象方法在派生类中必须实现。
4.函数:
TS函数参数和返回值的类型指定以及函数重载。
指定了函数的参数类型和返回结果,函数的功能就比较特定了。但是js毕竟是动态类型的函数,js内根据参数类型不同返回不同的类型比较常见,所以ts提供函数重载功能
5.断言:
注意事项:
1.文件声明(开发环境对TS文件的支持):规定namespace+第三方库+管理工具+声明文件
2.项目工程化:
a.使用webpack的文件变动检测机制,自定义启动脚本(当检测到文件发生变化时重新编译和运行入口js);
b.使用ts-node和pm2,更改pm2的启动配置,让pm2使用ts-node直接运行ts的入口文件。
3.tsconfig.json相关:
类似eslint,例如是否允许 使用 any 类型, 是否允许存在未使用的变量等等。
https://www.tslang.cn/docs/handbook/compiler-options.html
https://www.tslang.cn/docs/handbook/error.html
二、细化模块分类:
一般情况下,模块都会有耦合。但如果耦合度过高,往往是因为模块没有细分到位。如果细化模块?举例,假如有一个模块叫Operation
,里面既包含操作相关逻辑,也有操作面板逻辑。随着业务发展,操作面板逻辑越来越多。我们完全可以将操作面板逻辑单独抽成一个模块OperationPanel
。
化繁为简:
//1.If的使用简单粗暴,容易理解。 if ( animalType === 'dog' ) { console.log( 'Wang!' ) } else if ( animalType === 'cat' ) { console.log( 'Miao!' ) } else if ( animalType === 'bird' ) { console.log( 'Jiu!' ) } //2.Switch可以看做是If的简化。 switch ( animalType ) { case 'dog': console.log( 'Wang!' ) break case 'cat': console.log( 'Miao!' ) break case 'bird': console.log( 'Jiu!' ) break } //3.而Map针对性最强,并且最简洁、最易于维护。 const logMap = { dog: () => console.log( 'Wang!' ), cat: () => console.log( 'Miao!' ), bird: () => console.log( 'Jiu!' ), }
logMap[ animalType ]()
三、解耦可视化库和Vue/Vuex:
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 ) } }
“使用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 }
-end-