1、前言
早前的一些文章中提到的我们在前端使用TypeScript装饰器和面向对象设计的方案作为日常的业务开发方式,引来了不少的争议和讨论,虽然我们也使用了Hooks来实现一些视图层公共代码的抽离,但我们依然还是坚持在项目中使用一些强面向对象的设计思维, 今天我们来仔细聊聊。
2、面向对象
我们使用面向对象的开发方式主要体现在以下几个方面:
2.1 公共代码的继承
在日常开发中经常存在一些公共代码或属性,如数据转换、数据拷贝、数据过滤、公共的属性(ID、创建时间)等,我们需要对它们进行抽离,虽然像 interface
type
等都能完成一些公共属性的声明和继承,使用一些方法类可以将一些公共的方法进行封装,但从抽象上来说,放到 class
中也许更合适:
类的对象更抽象
使用
interface
和type
定义数据结构虽然没问题,但实际传递的参数还是个简单类型的对象Object,无法包含一些自定义的方法,而且无法通过数据取得对应的定义类型,无法实现类似反射
获取一些特性数据。
typescript
interface IBase{
id: number
}
interface IUser extends IBase{
nickname: string
}
const user: IUser = {nickname: "Hamm"}
使用
class
定义数据类型,可以解决上面的问题。
typescript
class Base {
id!: number
}
class User extends Base{
nickname!: string
}
const user = new User()
user.id = 1
user.nickname = "Hamm"
// 当然 也可以使用一些构造或自定义方法来快速初始化数据 如
class BaseData{
id!: number
setId(id: number):this{
this.id = id
return this
}
}
class User extends BaseData{
nickname!: string
setNickname(nickname: string): this{
this.nickname = nickname
return this
}
userType!: number
isAdmin():boolean{
if(this.userType === 1){
return true
}
return false
}
}
const user = new User().setId(1).setNickname("Hamm")
上面的代码是不是看起来很多? 其实使用 TypeScript
和 vscode
写的时候,很多都是直接用 .
给选出来的,类的声明也可以使用一些工具来自动生成,如 Json转TS代码工具
无需将方法和属性分开
上面的示例中,传递的 user
对象就是正经的一个用户对象了,包含了用户的一些属性和方法,方便了下面的使用:
typescript
user.isAdmin() // 直接返回是否管理员的 `true` `false`
2.2 相似代码的抽象
相似代码的抽象,在后端会经常用到,在前端的也是如此,大部分业务都相同,有部分相似但又有一些小区别,为了对这种场景进行封装,我们可以使用抽象类来处理。比如 声明了一个基础的 curd
接口请求方法:
typescript
abstract class AbstractCurdApi<D extends BaseData>{
// 获取请求的 baseUrl 每个子类不一样的地方
abstract getBaseUrl(): string
delete(): void{
// 删除
}
add(data: D): D{
// 新增一条
}
getById(id: number): D{
// 查询指定ID的一个数据
}
// 其他curd方法
}
接下来需要使用的子类中就可以将需要自己实现的方法实现后即可继承父类的所有方法。
typescript
class UserApi extends AbstractCurdApi<User>{
getBaseUrl(){
return 'user'
}
}
当然,TypeScript
还有一个 Java
不服的地方,就是它竟然支持将属性抽象掉:
typescript
abstract class AbstractCurdApi<D extends BaseData>{
abstract baseUrl: string
}
2.3 类和属性可以挂载装饰器
因为类和属性都支持挂载装饰器,所以我们可以选择 class
类来封装数据结构,既可以为它实现一些方法,也可以对类和属性进行装饰,然后后续使用这些装饰配置来实现更多的功能。
请接着阅读下面关于装饰器的部分。
3. 装饰器
TypeScript
的装饰器和 Java
的注解类似,以 @
开头,写在属性、方法、类的前面,关于装饰器的基础知识,可以参考官方文档,我们这里只讲如何使用:
typescript
@Class("用户")
@Enable(CURD.ADD,CURD.DELETE)
class User{
@Field("昵称")
@Form({
required:true,
placeholder:" 请输入一个牛逼的名字"
// ...
})
@Table({
copy: true, //表格中显示为一个可复制的单元格
align: "left",
// ...
})
nickname!: string
@Field("性别")
@Dict(SexDictionary)
@Search()
sex!: Sex
}
如上,我们声明了一个 User
类,并装饰了一大堆的配置,比如表格、表单、搜索等等。
其他在使用的组件中,就可以直接传入这个类,然后获取类中配置的一些信息,来渲染不同的表格、表单、搜索等状态。
如此使用之后,可以直接在类中完成集中化的配置。装饰器的玩法还有很多,听舒服的。
4. 最后
文中说到的一些东西,目前都可以在我们的开源仓库中看到所有代码:
Github:github.com/HammCn/AirP...
你也可以查看我的专栏: 用TypeScript写前端