近期看了些社区关于axios
一些增强封装的文章,就想着也动手玩玩(先随便实现个去重请求 feature。打包工具准备选择rollup
,语言则直接使用ts
,选型单纯是扩展技术体系,在实(踩)践(坑)中提升熟练度。
TypeScript
优缺点
社区上对ts
的讨论其实是非常多的,结合个人分析整理的优缺点如下:
优点:
- 在
coding
过程中,IDE 可以提供成员属性、类型等智能提示 - 静态校验(JS 本身只有运行时才会抛出异常),主要走 AST 词法分析那一套,在我们编写逻辑代码时,进行一些引用(比如常见的变量拼写错误低级问题)、句法问题上的分析,错误会标红并予以可能的解决方案说明
interface
、type
这些对成员类型的定义使后续维护者能够更容易分析整个项目的流程
缺点:
- 接入成本比较大(学习成本、工作量成本、小组工作人员意愿成本等)
- 打包过程中额外的编译时间
- 可能一些第三方库没有编写
typings.d.ts
之类的类型声明,导致引入后ts
抛错
安装&初始化项目配置
1 | // 全局安装ts |
建议通过命令直接生成ts
的配置文件,因为其中提供了全量的配置信息并提供了注释声明,我们仅需要在我们的配置行打开注释并设置我们的内容即可:
Rollup
Rollup
个人最早听说的时候是它有个tree-shaking
的功能,就是可以移除代码里的一些dead code
,从而减小你打包出来项目的体积,不过当时我一直使用的是webpack
进行项目构建,且后续webpack
新版本中也新增了类似的功能,就没有使用过该打包工具了。
根据一些社区的说法,Rollup
似乎在构建应用和构建库之间更适合后者,基本给出的理由都是从代码体积、打包后的可读性上来说。比如webpack
会写入依赖构建图谱,里面会有类似__webpack_require__
的工具方法编织的IIFE
等。
当然,在我个人看来理由还是比较牵强…Rollup
和Webpack
都是工程化上的一种选型,技术细节决定了它们最后打包出来的产物不同,并且没有在数据上达到一个量级的差距。所以我认为从开发者做事的角度来说,相关社区配套资源的物料是否丰富才是更大的影响我们选型的因素。
安装&初始化项目配置
1 | // 全局安装rollup |
下面看下我的具体配置,关键点是其中的plugins
,如果缺少了其中的任意一个都会抛出异常:
1 | import resolve from '@rollup/plugin-node-resolve' |
input
没什么好多说的,写个入口文件就完事,跟我们在webpack
里的entry
配置差不多;在output
中要注意的是如果我们导出的是umd
风格的包,就须要在output
中配置name
、globals
以及第一层配置添加external
项。
我所说的output
的细节可以看下react-redux
的配置:
1 | import nodeResolve from '@rollup/plugin-node-resolve' |
plugins
的配置我认为是和webpack
差异最大的地方,在webpack
中的plugins
是用来在tapable
派发的不同的 hooks 中进行特定时机额外操作的,诸如HtmlWebpackPlugin
入口模版配置,ProgressBar
显示打包进度等。
而在Rollup
中的plugins
更像是webpack
里的loader
,它们的功能都是进行模块解读,没有这块辅助,我们的打包流就无法正常工作。
下面说说我在配置中遇到的plugins
问题。
Rollup 的 plugins 配置
最全的配置见官网。
- @rollup/plugin-node-resolve: 用于协助
Rollup
找到外部依赖的模块如axios
、lodash
等,不添加就会报Unresolved dependencies
问题,即无法将第三方依赖打入我们的代码中。 - @rollup/plugin-commonjs: 使用这个插件主要是因为社区里实际上很多库还是打包成
cjs
格式的,当我们想通过 es 的import
方式导入就需要通过该插件进行转化才行,如qs
等。 - @rollup/plugin-json: 提供了
json
格式文件的处理能力,在引入axios
时,如其中的package.json
文件无法读取,命令行会提示安装该插件辅助。 - ‘@rollup/plugin-typescript: 由于我们使用了
ts
,可以直接通过该插件替代tsc
的命令动作。
Axios 封装
由于本文仅实现一个重复请求取消的简单功能,所以大概讲下思路和记录遇到的一些问题。
先说说思路,之前实际上写过一篇axio 源码阅读的文章,里面有一个非常关键的细节就是interceptors
的实现。这个方法可以让我们在请求过程中进行中间件的定制,也就意味着我们可以在中间判断请求是否重复,从而进行取消。
那么取消重复请求问题就可以拆解成两步:是否重复及如何取消。
判断请求是否重复,我们可以通过将请求url
、请求方法method
及请求参数data
(post)或者params
(get)进行字符串序列化(借助第三方库qs
)并生成请求Map
来进行后续过滤。
取消请求的本质是xhr
的abort
方法,当然设置timeout
的情况下超时请求也会被canceled
。
编写完代码逻辑再结合前阵子文章一套完整的代码规范需要什么后,我们有如下目录结构:
打包:rollup -c
生成bundle.js
。
压缩:npm pack
生成.tgz
压缩包,可以直接发布npm
也可以本地安装调试。
遇到的问题
axios
的设计上,它关键的Cancel
方法是直接挂到它默认导出的实例上去的(通过axios.Cancel = require('./cancel/Cancel');
),所以当我们调用axios.create(config)
方法再创建一个新实例时,就会丢失那些原始默认导出实例上的绑定方法,那些方法需要我们手动进行拷贝。Could not find a declaration file for module
:出现在安装我的方法库后,查阅资料后发现是我的ts
缺少对文件的描述说明文件typings.d.ts
。设置方案如下:
1 | // package.json中 |
效果
启一个umi
的demo
,安装本地编写的依赖包,在mock
目录下设置一个1.5s
超时的mock
接口,点击模拟试验一下:
可以发现连续点击五次按钮,仅最后一次正常到了超时才响应timeout
并且canceled
,前4个重复请求都被监测到并且主动abort
,符合预期。