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));
   });

:stars: 关于在使用superagent库的时候,边对着官方文档来使用即可,这里就不再具体分析如何使用superagent,官方已经讲得比较详细了

:point_down: 将通过源码层面,来分析superagent是如何被设计/开发出来?通过学习它的源码,我们可以得到什么启示?它为什么会是这种使用方式?我们是否也能够模仿它这样子实现一个这种调用机制? :u6709: 哪些是值得我们学习的等等!!

superagent的组成与关键对象

superagent库的组成 superagent对象输出

:stars: 通过对 :point_up: 两张图的输出结果可以看出:superagent库其实是一个函数对象(因为函数是一等公民,与传统的对象类似,可拥有方法与属性),在该函数对象上拥有着一系列的与网络请求关联的相关API方法,

superagent过程分析

  1. 创建一个Request对象:一切从入口出发,request.post()方法入手,但在源码中,却是以另外一种方式来定义这个post方法,通过request['post'] = () => {}类似的方式,来定义这个post方法,那么要覆盖到所有的http请求的话,应该怎么做呢?于是就有了 :point_down: 的关于针对不同的请求方式对应实现不同的请求定义 不同的http请求方法定义

:stars: :point_up: 这里的不同的methods其实是nodejs中所提供的不同方式的请求,这里实现的是将所有的网络请求动作都指向一个函数,并维护好不同的请求之间的一个特殊性,而在这个函数体中,实现的是创建一个Request函数对象,通过传递的参数来new出一个请求对象,该对象中所包含的基本元素定义如下: Request的组成

:stars::point_up: 可以看出这个Request继承于Stream对象(该对象又继承于EventEmitter,于是拥有了on/once/emit等方法),并最终将这个Request对象返回,也就是说利用这个Request来包装完成后续的网络请求;

  1. 装嵌各种属性:创建了Request对象之后,接下来通过链式调用的方式调用着.send().set().set()等一系列的操作,这里就是大部分骚操作的来源了,通过继承于RequestBase父类,直接通过调用send方法,将所有的数据存储在成员属性_data中,而通过set方法,将所有的请求头存储在_header属性中,然后最终调用的then方法,通过new Promise的方式,将原本需要通过回调的方式,变成通过链式调用,在该方法中,其实执行的是Request.end方法,关于这个end方法,下面接着分析; Request的then方法实现

  2. 设置监听并最终发起请求: :point_up: 的end方法,其实是调用的子类Request对象的end方法,这个是骚操作的一步,通过传递的fn参数,将回调函数缓存在Request的_callback,在发起请求之前,经过数据转换、设置监听、触发request事件、设置进度监听等操作,然后最终调用的Request.req属性来发起end()动作,这里的req属性,其实是根据http协议来对应动态导入对应的网络请求库(http/http2/https),通过其统一的end()方法,代表结束对流的输入,完成一个请求动作 end的过程

  3. 数据回调:在响应到网络请求的时候,通过 :point_up: 中的Request.req.once('responce')监听,可接收到网络回调的动作,在获取到响应的数据时,将数据res直接存储到Request.res对象中,并针对原本的请求方式进行特殊处理,根据请求时发起的数据编码方式,进行对应不同的解析器来处理,根据不同的类型进行分配对应的解析器,然后解析数据,在解析完成后,将解析后的数据进行一个Response对象来包装,并触发Request的response方法,然后再根据之前所缓存下来的_callback回调方法,将包装后的Response对象作为参数抛出去,完成数据的响应回调处理动作

从superagent可学习到的地方

在学习了关于superagent它是如何使用以及它是如何被设计/开发出来了之后,特别是在学习其编写源码的过程中,有不少的地方值得我们借鉴与学习,当然也有个人觉得不是很好的地方,需要进一步完善的!

借鉴之处

  1. 也是nodejs的源码中所体现的,往将相关的属性定义在一对象中,通过对这个对象的属性的赋值与取值使用,来 :u6e80: 足于实际的业务场景需求;
  2. 链式方法的调用,不难发现关于在使用superagent的过程中,是采用的类promise链条的方式来调用的方法的,这里简单地理解就是借助于this关键词,通过在一个对象中执行一系列逻辑后,返回当前对象的编程思路,即可以达到这种效果;
  3. 链式调用之后,完成对对象属性的赋值,将值全部缓存在对象的属性中,待触发的契机发生时(这里即是then/end方法的调用),通过契机方法的调用,从之前缓存在对象中的属性取值,完成业务的实现;
  4. 动态化导入,在superagent中,它其实是通过httphttp2https的包装,来实现具体的网络请求动作的,其底层还是nodejs相关网络库所提供的服务,通过动态化导入的方式,实现的针对不同的网络协议进行对应的逻辑,而且这里的动态化导入,其实是简单地使用require()方法,通过该方法所导出的模块来进行request方法的调用;
  5. 将一个对象thenable化,最中通过then方法,来执行这个合并发起动作,并且以promise的方式来调用,其中的原理也比较简单,无非就是借助new Promise,将业务包裹其中,执行的时候,通过它来将过程thenable化

待完善的空间

  1. 现在是大ES6+时代,自己在后续编写这个库的时候,大可借助于ES6+的语法糖规则,编写可读性更高的代码,特别是借助于classextends等面向类/对象的编程模式,在编写的过程中,能够让读者对库中对象之间的关联关系 :u6709: 一个比较清晰的认知,而不会是一头雾水,每一个学习者应该不当当仅停留在会用库,而不去理解库!
  2. 调用了未来的方法,这个特别是在学习有关对象继承上的时候,找寻调用的方法定义过程中比较悲剧,在父类中调用了一个子类才定义的方法,导致在解读的过程中比较难找寻的过程比较难,而且,在定义父类的过程中,没办法做到像java中的abstract抽象类一样,通过定义的抽象方法,可以让子类必须重载这个方法一样!!

results matching ""

    No results matching ""