从今天开始,拿起 TypeScript 做一个轮子的制造者

12,413 阅读9分钟

前言

前端这些年发展非常迅速,社区里涌现了一堆优秀的轮子,比如Vue、React、Angular、jQuery、axios 等,它们解决着不同领域下的问题。使用这些轮子能极大地帮助我们提升生产力,有些人甚至基于这些轮子二次开发了一些轮子,比如 element-ui、ant-design 等组件库。我们在享受这些轮子给我们带来的便利的时候,有时候也需要面临一些问题:某些轮子不能满足我们自己的特殊业务需求,找不到合适的轮子等。因此我们不仅要会使用轮子,也需要有造轮子的能力。

2019 年,前端技术发展越来越迅速,其中 TypeScript 更是受到了越来越多的开发者的青睐,在 GitHub 上搜索 star 数大于 1w 的项目,我们可以看到很多知名的开源项目如 vscode、angular、ant-design、ionic、deno 等都使用 TypeScript 开发,19 年上升最快的 Vue.js 3.0 也正在用 TypeScript 重构,尤老师都忍不住发出了 “真香” 的言论。

TypeScript 的火爆程度大有成为下一代前端开发语言的趋势,TypeScript 作为 JavaScript 语言的超集,它为 JavaScript 添加了可选择的类型标注,大大增强了代码的可读性和可维护性。同时,它提供最新和不断发展的 JavaScript 特性,能让我们建立更健壮的组件。

越来越多的轮子将用 TypeScript 开发和重构,我们如果想造轮子,也应该使用 TypeScript 这把利器。

如何开始

我们造轮子首先会初始化一个项目,但需要配置一大堆东西,比如 package.json.editorconfig.gitignore 等,还包括一些构建工具如 rollupwebpack以及它们的配置。

当我们使用 TypeScript 去写一个项目的时候,还需要配置 TypeScript 的编译配置文件 tsconfig.json以及 tslint.json 文件。

这些茫茫多的配置往往会让一个想从零开始写项目的同学望而却步,如果有一个脚手架工具帮我们生成好这些初始化文件该多好。好在确实有这样的工具,接下来我们的主角 TypeScript library starter 隆重登场。

TypeScript library starter

它是一个开源的 TypeScript 开发基础库的脚手架工具,可以帮助我们快速初始化一个 TypeScript 项目,我们可以去它的官网地址学习和使用它。

使用方式

git clone https://github.com/alexjoverm/typescript-library-starter.git ts-axioscd ts-axios  
npm install

先通过 git clone 把项目代码拉下来到我们的 ts-axios 目录,然后运行 npm install 安装依赖,并且给项目命名,我们仍然使用 ts-axios

安装好依赖后,我们先来预览一下这个项目的目录结构。

目录文件介绍

TypeScript library starter 生成的目录结构如下:

├── CONTRIBUTING.md  
├── LICENSE ├── README.md  
├── code-of-conduct.md  
├── node_modules  
├── package-lock.json  
├── package.json  
├── rollup.config.ts // rollup 配置文件  
├── src // 源码目录  
├── test // 测试目录  
├── tools // 发布到 GitHup pages 以及 发布到 npm 的一些配置脚本工具  
├── tsconfig.json // TypeScript 编译配置文件  
└── tslint.json // TypeScript lint 文件  

优秀工具集成

使用 TypeScript library starter 创建的项目集成了很多优秀的开源工具:

  • 使用 RollupJS 帮助我们打包。
  • 使用 PrettierTSLint 帮助我们格式化代码以及保证代码风格一致性。
  • 使用 TypeDoc 帮助我们自动生成文档并部署到 GitHub pages。
  • 使用 Jest帮助我们做单元测试。
  • 使用 Commitizen帮助我们生成规范化的提交注释。
  • 使用 Semantic release帮助我们管理版本和发布。
  • 使用 husky帮助我们更简单地使用 git hooks。
  • 使用 Conventional changelog帮助我们通过代码提交信息自动生成 change log。

这里我们列举了很多工具,感兴趣的同学们可以点开他们的链接对这些工具做进一步学习。

工欲善其事必先利其器,我们现在已经搭好了一个项目的雏形,接下来就要开始编写轮子的代码了。

轮子的设计

一般轮子的特点,专注于做某一块事情,为了更直观和具体,我们以一个非常知名的前端开源库 axios 为例,站在轮子制造者的角度上,来讨论一下一个优秀的轮子是如何设计出来的。

核心功能

axios 核心专注于发送 http 请求,并且在此基础了扩展了很多实用和好用的 feature,如下:

  • 在浏览器端使用 XMLHttpRequest 对象通讯
  • 在 Node.js 端使用 http request 通讯
  • 支持 Promise API
  • 支持请求和响应的拦截器
  • 支持请求数据和响应数据的转换
  • 支持请求的取消
  • JSON 数据的自动转换
  • 客户端防止 XSRF

对于轮子的使用者,这些 feature 能让我们知道轮子是做什么的,而对于轮子的制造者,这些 feature 就是我们的需求。对于这些需求,我们可以划分一下优先级:优先实现在浏览器端的请求发送逻辑,实现一些基础功能如请求发送和响应,Promise API,请求数据和响应数据的转换,JSON 数据自动转换以及一些异常情况处理;接着再去扩展拦截器、配置化、请求取消等复杂功能,然后扩展一些简单的功能如客户端防止 XSRF、上传等;最后开发 axios 在 Node.js 端的扩展。

我们的编码的思路也就是按优先级的重要程度依次去开发上述功能。

接口设计

一个轮子是否优秀,首先取决于它是否好用,使用是否方便,五六年前,还是 jQuery 的时代,jQuery 就提供了一个非常好用的 $ 设计,抢占了大部分市场。当年百度也出了一个类似的库叫 tangram,创建一个 DOM 对象需要写 Baidu.T.createDOM(xxx),而 jQuery 只需要 $(xxx) 即可,高下立判。

axios 设计也是非常简单,直接 axios(config) 就可以完成一个请求的发送了,为了减少 config 中的配置参数,还提供了 axios.getaxios.post 等接口。为了方便用户拿到响应结果,axios 支持了 Promise API,我们可以通过 axios(config).then(res =>{}) 的方式拿到响应对象。

axios 可以支持非常多的 feature,完全得益于 config 配置参数,对于 feature 的扩展,我们既可以通过扩展 axios 的实例或者静态方法来实现,也可以扩展 config 的字段来实现,原则是怎么方便用户使用怎么来。

生命周期

axios 本质上就是发送一个请求,拿到响应结果,中途可以去对配置参数、请求数据和响应数据处理,同时也支持一些拦截器的调用,因此我们可以画出它的生命周期图示。

图片描述

通过生命周期图示我们可以清晰地了解一个 axios 工作流程。

轮子开发

综上所述, axios 既是一个函数,又是一个接口,即是一个实例对象,也拥有一些静态方法,如果我们使用 TypeScript 开发,该如何设计 axios 的类型?另外,axios 对外提供的非常多的 API 接口、配置参数,我们又该如何使用 TypeScript 定义这些类型呢?根据生命周期图示,我们可以按流程中的行为去拆分功能模块,我们如何使用 TypeScript 做模块拆分和管理的,哪些模块又可以被抽成基础公共模块呢?在实现整个 axios 库的过程中,如何不断做代码的优化、抽象和封装?如何使用策略模式实现配置合并?拦截器的 Promise 链如何设计?如何通过异步分离实现取消功能?

面对上述问题,不知道你心中是否有答案,如果你还没有想到或者对 axios 的实现感兴趣,不妨来学习我的新课 《TypeScript 从零重构 axios 库》,来一起对 axios 库抽丝剥茧,使用 TypeScript 一步步去实现一个完整的 axios 库。

单元测试

一个优秀的轮子,一定会有单元测试,单元测试是轮子的质量保证,甚至还有测试驱动开发的开发模式。很多同学对单元测试一直处于一种听说过,但没用过的状态。其实写单元测试并不难,首先我们要保证所有工具方法、辅助函数的测试完整性;然后要保证 feature 的测试完整性,保证每一个 feature 的各种情况都能测到。一个检测测试完整性的常用手段是看测试的覆盖率,通常我们会要求测试覆盖率达到 90% 以上。

单元测试有一个非常大的好处是你可以放心地对你现有的代码做新 feature 的开发,以及代码的重构,而不用担心代码的改动影响已有功能,我们可以很容易地通过跑单元测试检测我们改动的代码是否有问题。

目前前端测试的框架有很多,我比较推荐 Jest,它是一个几乎零配置的测试框架,非常易于上手,由 facebook 团队维护,质量也能得到保证。新课程就是使用 Jest 来编写 axios 的单元测试,你会学到很多编写单元测试的方法和技巧,对单元测试感兴趣的同学可以来学习咯。

总结

TypeScript 在类型方面的约束,以及提供很多好用的现代 JavaScript 语法特性,非常适合用它去造一个轮子,当然也非常适合开发大型复杂前端项目。前端的趋势会越来越复杂,TypeScript 这个技术栈在未来一定会变成前端必须掌握的开发技能。

至于造轮子,能不能设计好用的轮子,轮子能不能满足复杂的需求,对你的 JavaScript 内功、代码设计能力都是有很高的要求,造轮子的过程就是一个内功修炼的过程。所以,如果你不满足于做一个熟练工,一个螺丝钉,想提升自己的核心竞争力,那么从今天开始,拿起 TypeScript 做一个轮子的制造者。