superagent
SuperAgent是轻量级的渐进式ajax API,具有灵活性、可读性和较低的学习曲线。 它也适用于Node.js!
request .post('/api/pet') .send({ name: 'Manny', species: 'cat' }) .set('X-API-Key', 'foobar') .set('Accept', 'application/json') .then(res => { alert('yay got ' + JSON.stringify(res.body)); });
关于在使用
superagent
库的时候,边对着官方文档来使用即可,这里就不再具体分析如何使用superagent,官方已经讲得比较详细了
将通过源码层面,来分析superagent
是如何被设计/开发出来?通过学习它的源码,我们可以得到什么启示?它为什么会是这种使用方式?我们是否也能够模仿它这样子实现一个这种调用机制? 哪些是值得我们学习的等等!!
superagent的组成与关键对象
通过对 两张图的输出结果可以看出:superagent
库其实是一个函数对象(因为函数是一等公民,与传统的对象类似,可拥有方法与属性),在该函数对象上拥有着一系列的与网络请求关联的相关API方法,
superagent过程分析
- 创建一个Request对象:一切从入口出发,request.post()方法入手,但在源码中,却是以另外一种方式来定义这个post方法,通过
request['post'] = () => {}
类似的方式,来定义这个post方法,那么要覆盖到所有的http请求的话,应该怎么做呢?于是就有了 的关于针对不同的请求方式对应实现不同的请求定义
这里的不同的methods
其实是nodejs中所提供的不同方式的请求,这里实现的是将所有的网络请求动作都指向一个函数,并维护好不同的请求之间的一个特殊性,而在这个函数体中,实现的是创建一个Request
函数对象,通过传递的参数来new
出一个请求对象,该对象中所包含的基本元素定义如下:
从 可以看出这个Request继承于Stream对象(该对象又继承于EventEmitter,于是拥有了on/once/emit等方法),并最终将这个Request
对象返回,也就是说利用这个Request来包装完成后续的网络请求;
装嵌各种属性:创建了Request对象之后,接下来通过链式调用的方式调用着
.send().set().set()
等一系列的操作,这里就是大部分骚操作的来源了,通过继承于RequestBase
父类,直接通过调用send方法,将所有的数据存储在成员属性_data
中,而通过set方法,将所有的请求头存储在_header
属性中,然后最终调用的then方法,通过new Promise
的方式,将原本需要通过回调的方式,变成通过链式调用,在该方法中,其实执行的是Request.end
方法,关于这个end方法,下面接着分析;设置监听并最终发起请求: 的end方法,其实是调用的子类Request对象的
end
方法,这个是骚操作的一步,通过传递的fn参数,将回调函数缓存在Request的_callback
,在发起请求之前,经过数据转换、设置监听、触发request事件、设置进度监听等操作,然后最终调用的Request.req
属性来发起end()动作,这里的req
属性,其实是根据http协议来对应动态导入对应的网络请求库(http/http2/https),通过其统一的end()方法,代表结束对流的输入,完成一个请求动作数据回调:在响应到网络请求的时候,通过 中的
Request.req.once('responce')
监听,可接收到网络回调的动作,在获取到响应的数据时,将数据res直接存储到Request.res
对象中,并针对原本的请求方式进行特殊处理,根据请求时发起的数据编码方式,进行对应不同的解析器来处理,根据不同的类型进行分配对应的解析器,然后解析数据,在解析完成后,将解析后的数据进行一个Response
对象来包装,并触发Request的response
方法,然后再根据之前所缓存下来的_callback
回调方法,将包装后的Response
对象作为参数抛出去,完成数据的响应回调处理动作
从superagent可学习到的地方
在学习了关于
superagent
它是如何使用以及它是如何被设计/开发出来了之后,特别是在学习其编写源码的过程中,有不少的地方值得我们借鉴与学习,当然也有个人觉得不是很好的地方,需要进一步完善的!
借鉴之处
- 也是
nodejs
的源码中所体现的,往将相关的属性定义在一对象中,通过对这个对象的属性的赋值与取值使用,来 足于实际的业务场景需求; 链式方法的调用
,不难发现关于在使用superagent
的过程中,是采用的类promise
链条的方式来调用的方法的,这里简单地理解就是借助于this
关键词,通过在一个对象中执行一系列逻辑后,返回当前对象的编程思路,即可以达到这种效果;- 在
链式调用
之后,完成对对象属性的赋值,将值全部缓存在对象的属性中,待触发的契机发生时(这里即是then/end方法的调用),通过契机方法的调用,从之前缓存在对象中的属性取值,完成业务的实现; - 动态化导入,在superagent中,它其实是通过
http
、http2
、https
的包装,来实现具体的网络请求动作的,其底层还是nodejs相关网络库所提供的服务,通过动态化导入的方式,实现的针对不同的网络协议进行对应的逻辑,而且这里的动态化导入,其实是简单地使用require()方法
,通过该方法所导出的模块来进行request
方法的调用; - 将一个对象
thenable化
,最中通过then
方法,来执行这个合并发起动作,并且以promise的方式来调用,其中的原理也比较简单,无非就是借助new Promise
,将业务包裹其中,执行的时候,通过它来将过程thenable化
;
待完善的空间
- 现在是大
ES6+
时代,自己在后续编写这个库的时候,大可借助于ES6+
的语法糖规则,编写可读性更高的代码,特别是借助于class
、extends
等面向类/对象的编程模式,在编写的过程中,能够让读者对库中对象之间的关联关系 一个比较清晰的认知,而不会是一头雾水,每一个学习者应该不当当仅停留在会用库,而不去理解库! - 调用了
未来的方法
,这个特别是在学习有关对象继承上的时候,找寻调用的方法定义过程中比较悲剧,在父类中调用了一个子类才定义的方法,导致在解读的过程中比较难找寻的过程比较难,而且,在定义父类的过程中,没办法做到像java中的abstract
抽象类一样,通过定义的抽象方法,可以让子类必须重载这个方法一样!!