早前写过一篇文章 《也许跟大家不太一样,我是这么用TypeScript来写前端的》 引发了不少的讨论,其实核心还是在于前端函数式和面向对象之间的争议问题:“面向对象在前端有没有必要?”
把前端写成了后端模样
上文中有提到,我们没有像目前流行的一样,使用 Hooks、interface(或Type),而是大量的选择了面向对象的思维:“甚至是照搬了用Java和SpringBoot在写后端时的一些设计”,相信看过早前文章或者 Github代码仓库 的朋友都注意到了,大量的 继承、Service、Entity 这些概念的引入,都像极了写后端时候的样子。
这里有朋友说怎么没上依赖注入,这里简单解释一下:早前确实考虑过也尝试过使用一些第三方的依赖注入框架,后来在做跨端兼容时发现,微信小程序对于 reflect-metadata 这个库的兼容性很差劲(其实对于TypeScript的兼容都很差,目前我们都是使用的自定义 tsc 编译来解决的一些奇奇怪怪的问题)。也不知道现在的状态如何了,后续如果有机会或者有时间,可以尝试下一些其他的方案。
这里简单解释下为什么我们选择了大量的面向对象:
前端也需要抽象思维
通过面向对象的一些抽象设计,我们完成了一些对于各种参数传递的数据类型约束,通过继承来实现了一些公共业务逻辑的封装,通过泛型、数据类型转换、前后置方法等设计完成了一些在标准场合下有特定需求的业务场景的实现。比如:)
- 通过继承和封装实现一些CURD的标准网络请求方法
- 通过泛型和装饰器实现了一些数据类的转换(这点在 基于装饰器-我是这么处理TypeScript项目数据转换的 中有提过)
- 通过反射和装饰器实现一些通用配置(比如表格/表单/搜索等)的保存和读取,在这两篇文章中也有提过: 装饰器和表单校验 装饰器和表格
前端数据类型转换
数据转换在日常开发过程中是很常见也是很有必要的,如上面关于数据转换文章里提到过的一样。
在与后端开发或联调过程中,如果在开发流程不够规范的情况下,经常会出现字段属性的突然变更、数据类型不一致(比如时间转换、布尔值转换、枚举转换等),之前我们说过,reflect-metadata 和 class-transformer 这两个库在早期开发过程中给我们提供了很大的帮助。
有多开心呢?举个例子:
有了自定义属性别名,我们不用再Mock数据,甚至我们可以提前自行编写实体类、字段名等。先写完业务,如果后端有按规范来还好,如果没有,一个别名解决掉这个问题。
后端承诺的数据或者数据类型不是我们要的:在必要的时候,我们也可以自定义转换的方法,来与后端一些不合理的数据进行对接。
纠正一下上一篇文章
《也许跟大家不太一样,我是这么用TypeScript来写前端的》 这一篇中,我们虽然吐槽了 Hooks 类型体操,但其实我们的项目中也有相关的影子,比如:
- 我们提供了关于 表格 表单 选择器 等相关的 Hooks,可以认为是对 控制器 这一层的一个弥补,让写列表和表单、选择器等功能时能感觉到香。
- 我们虽然没有使用大量的类型体操,但我们使用了泛型等来提供灵活的类型自定义,使用了联合类型等特性来实现一些 Props 参数的组合灯
- 我们也有一些interface做为数据类型的实现,比如
@TableField()
@EntityConfig
@SearchField()
@FormField()
等装饰器的参数等
看了上篇文章的一些评论的感想
每一种设计都应该有一些场景的支撑,之前有个朋友评论说关于如何选择的问题,这里想说一下我自己个人的看法:
Web1.0时代,我们总是用很精简的代码去实现需求,那时候需要考虑很多外部的问题,比如流量。后来来到Web2.0时代,大量的UGC和SNS平台的出现,推动了整个互联网的发展,那时候的jQuery撑起了半边天。到现在不管Web3.0的时代是不是已经到来,jQuery的影子却在慢慢的退出视线。
但是,
每一种技术或思想的出现,都会伴着很多其他的问题出现,比如 用原始的JavaScript写,不使用什么第三方的库,产生的无用代码可能会更少;使用jQuery以及一堆的插件,可以做出很棒的效果,但在往前端的传输过程中,传输的无用代码也会更多;使用现代Web前端框架,可以更好的工程化、结构化、系统化,但入门的门槛又高了,学习的成本也高了。
所以我们都只是一直在做选择,而已。
我们选择了编程语言,选择了操作系统,选择了数据库,选择了技术框架,而已。
有时候选择不一定是对的,但可能在当时的场景中,当时的选择,也不见得一定是错的。
就这样
That's all
之前贴过的 Github仓库 欢迎有兴趣的朋友查看,也可以互相交流。
文中提到几个开源地址:
你也可以查看我的专栏: 用TypeScript写前端