npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

tt-tio

v0.0.4

Published

Promise based HTTP client for all JavaScript runtimes

Downloads

2

Readme

Tio

基于Promise的,支持所有JavaScript运行时的Http库。Tio是基于axios二次开发的一个项目,它主要有如下功能和特点:

  1. 兼容axiosAPI.
  2. 支持所有JavaScript运行时(小程序、Weex等)。
  3. 支持请求同步。
  4. 请求重定向;在APP的Webview中,可以将网络请求自动重定向到Native。

安装

使用 npm(推荐):

$ npm install tt-tio

使用bower:

$ bower install tt-tio

源码集成请参考:Tio集成-使用CDN或dist目录文件集成

简介

兼容axiosAPI

Tio兼容axios API,使用方法可以参照axios,不过需要将示例中的axio换成tio,如:

const tio = require('tt-tio');

// Make a request for a user with a given ID
tio.get('/user?ID=12345')
  .then(function (response) {
    // handle success
    console.log(response);
  })
  .catch(function (error) {
    // handle error
    console.log(error);
  })

支持所有JavaScript运行时

Axios目前只支持浏览器和Node,而tio目标之一正是为了弥补这个不足。Tio可以通过适配器的方式,支持各种所有JavaScript运行时。下面是Tio的架构图:上层提供标准、平台统一的API,下层针对不同平台提供不同的适配器:

架构图

目前tio支持的平台有头条小程序微信小程序支付宝小程序轻应用Weex以及浏览器和Node, 下面试各个平台的使用方法:

先引入tio:

const tio = require('tt-tio');

下面是各个平台的导入方法:

注意:示例是在npm环境中引入的,如果某个平台的开发工具不支持npm包管理,请使用源码集成

头条小程序

const adapter=require('tt-tio/lib/adapters/mp/tt')
tio.defaults.adapter=adapter;

微信小程序

const adapter=require('tt-tio/lib/adapters/mp/wx')
tio.defaults.adapter=adapter;

支付宝小程序

const adapter=require('tt-tio/lib/adapters/mp/al')
tio.defaults.adapter=adapter;

轻应用

const adapter=require('tt-tio/lib/adapters/hap')
tio.defaults.adapter=adapter;

Weex

const adapter=require('tt-tio/lib/adapters/weex')
tio.defaults.adapter=adapter;

Node

const adapter=require('tt-tio/lib/adapters/http')
tio.defaults.adapter=adapter;

浏览器

const adapter=require('tt-tio/lib/adapters/xhr')
tio.defaults.adapter=adapter;

注意:和axios不同,在浏览器环境中,tio必须显式设置xhrAdapter,这是因为tio为了减小包体积,没有将xhrAdapter作为内置默认的adapter.

现在你就可以使用tio来发起网络请求了,使用方法和axios一致,如:

tio.get('/user?ID=12345')
  .then(function (response) {
    // handle success
    console.log(response);
  })
  .catch(function (error) {
    // handle error
    console.log(error);
  })
  .then(function () {
    // always executed
  });

支持请求同步

我们知道axios的拦截器可以返回一个promise来执行异步任务,但是axios的拦截器没有办法来同步多个请求,什么意思?我们看一个场景:

由于安全原因,我们需要所有的请求都需要在header中设置一个csrfToken,如果csrfToken不存在时,我们需要先请求一个csrfToken,然后再发起网络请求。

可以想到,在这个场景中,为了保证每次请求都有csrfToken,我们需要在每次发起网络请求之前都要先检查一下token;很显然,我们不可能没个请求都这么做一下,那怎么解决这个问题?可以想到的一个方法就是在拦截器中去检查,如果没有再请求;用axios实现的流程大概如下:

var csrfToken="";
tio.interceptors.request.use(function (config) {
   if(!csrfToken){ //csrfToken不存在, 先获取
     return fetchTocken().then((data)=>{
         config.headers.csrfToken=csrfToken=data.token;
         return config;
     })  
   }else{
       config.headers.csrfToken=csrfToken=data.token;
       return config;
   }
});

上面看起来貌似很完美,但是有一个严重的缺陷,那就是如果页面初始化时同时发起多个网络请求时,csrfToken会请求多次。因为每个请求都会进入请求拦截器,而这时csrfToken都为空,所以没个请求都会走到fetchTocken,而我们期望的是,只有第一个去请求csrfToken,在请求的过程中,其它请求都先等待,直到csrfToken请求成功后,其它请求再继续,这种场景和多线程同步问题非常类似,所以这种场景我们可以理解为需要“同步请求”(而不是并发)。为了解决“同步”问题,tio引入了一种机制:可以给拦截器加锁。我们先看看用tio如何解决这个问题:

var csrfToken="";
tio.interceptors.request.use(function (config) {
   if(!csrfToken){ //csrfToken不存在, 先获取
     this.lock(); //锁定请求拦截器,之后,其它请求将在请求拦截器外面等待,  
     return fetchTocken().then((data)=>{
         config.headers.csrfToken=csrfToken=data.token;
         this.unlock(); //解锁请求拦截器
         return config;
     }).catch(()=>this.unlock())  //解锁请求拦截器
   }else{
       config.headers.csrfToken=csrfToken=data.token
       return config;
   }
});

解释:

  1. 请求拦截器被锁定后(调用lock方法),其它请求将不能再进入请求拦截器,此时,其它请求将进入一个等待队列;当请求拦截器解锁后(调用unlock方法),等待队列中的请求才会进入请求拦截器。上面代码中,我们在请求csrfToken前锁定了请求拦截器,所以即使有多个并发请求,其它请求都得进入等待队列,当我们请求到csrfToken后解锁,其它请求恢复执行,这时csrfToken已经存在,所以就不需要再去请求csrfToken。

  2. 如果你想取等待消队列里的所有请求(如在请求csrfToken的过程中遇到错误),可以调用clear(reason) 方法,调用后便会终止请求,进入上层catch方法,reason 将会作为catch的回调参数。如:

      tio.interceptors.request.use(function (config) {
          ... //省略无关代码
          this.clear("error test")
          ...
       });

    发起请求:

      tio.all([getUserAccount(), getUserPermissions()])
         .then(tio.spread(function (acct, perms) {
           ...
         }))
         .catch(e){
           console.log(e); // > "error test"
         }
  3. 请求拦截器和响应拦截器是不同的对象,每个拦截器都包含lock/unlock/clear三个方法,加锁、解锁、清空操作用于当前拦截器,比如你只锁定的是响应拦截器,那么请求拦截器依然没有锁,所有并发请求也都会进入请求拦截器,知道他们在需要进入响应拦截器的时,才会到响应拦截器外排队。

    注意:在执行拦截器回调时,tio会将当前拦截器对象作为this来call拦截器回调,我们拿请求拦截器来举例:

    tio.interceptors.request.use(function (config) {
     this.lock();// 等价于tio.interceptors.request.lock()
    });

    另外注意,拦截器回调函数如果使用箭头函数,则不能在里面使用this来替代拦截器对象。

  4. 在上面的示例中,fetchTocken() 方法不能再使用tio去请求csrfToken,因为在调用fetchTocken()tio的拦截器队列已经锁定了,所以再用它去请求csrfToken的话将会陷入循环等待(死锁),正确的作法很简单,重新创建一个新的tio实例来发起请求即可,如:

    function fetchTocken(){
       return tio.create().get("/token")
    }

除了上面请求csrfToken的场景,请求同步功能在很多场景也都很实用,再举一个常见的例子,比如登录token自动延时:登录成功后会返回一个token,但token会有一个有效期,如果过期则需要重新请求token。

总结

我们总看看Tio发起网络请求的整个流程图:

Tio request flow

请求重定向

在APP内嵌的H5页面中,应该尽可能通过APP发起网络请求;如果使用Webview发起网络请求会有如下问题:

  1. 不能使用我司的TTNet库
  2. cookie 同步困难
  3. 接口安全
  4. 访问控制
  5. 性能
  6. 缓存

详细的分析请查看 为什么在APP内嵌的H5页面中,网络请求应该尽可能通过Native发起?

那如何使用APP发起网络请求呢?

目前我司的APP都有JsBridge,APP内可以实现一个网络请求的JsBridge方法fetch供H5调用;这样的话前端可以在内嵌的H5页面中直接通过JsBridge方法fetch方法来发起请求;如果直接手动调用fetch方法,这样不但太痛苦,而且代码迁移难度会非常大,试想一下:有些H5页面会同时在外部浏览器和APP中打开,对于这些页面我们期望如果在APP中就使用fetch,如果在浏览器中就使用浏览器发起。

那么现在,救星来了,使用tio,这一切将会变得非常简单;我们只需要定义一个能将请求转发到Native的Adapter,然后再APP环境中便使用Native Adapter,在浏览器环境就仍然使用xhr adapter(内置实现);那么如何定义Native Adapter呢?以F项目举例:APP实现的fetch方法和主端的fetch方法一致,Native Adapter代码如下(文件名为fAppAdapter.js):

require('byted-ttfe-jsbridge');
const settle = require('tio/lib/core/settle');
const createError = require('tio/lib/core/createError');

module.exports = function (config) {
    return new Promise(function (resolve, reject) {
        config.header = config.headers;
        config.data = config.body;
        window.ToutiaoJSBridge.call("fetch", config, function (res) {
            if (res.code === 1) {
                var response = {
                    status: res.status,
                    config: config,
                    data: res.response,
                    headers: res.headers
                };
                settle(resolve, reject, response);
            } else {
                reject(createError("Network Error!", config, res.status || 0))
            }
        });
    })
}

使用

const tio = require('tt-tio');
var adapter = require('./fAppAdapter')
tio.defaults.adapter=adapter; 

接下来就可以正常发起请求了;如果要动态判断是否在App中,代码如下:

const tio = require('tt-tio');
var nativeAdapter = require('./fAppAdapter');
var xhrAdapter= require('tt-io/lib/adapters/xhr');
tio.defaults.adapter=utils.isInAPP()?nativeAdapter:xhrAdapter;

注意,如果确定页面只会在APP中打开,但测试的时候需要在浏览器中测试,请不要使用这种方法,因为这样会将两个adapter都打包;取而代之的方法是使用DefinePlugin,根据不同的打包参数来打包不同的代码。

内置的ttAppAdapter

只要APP实现的fetch方法也和主端的fetch方法一致,就可以使用上述的adapter,为了使用方便,tio将上述adapter已经内置了,名为"ttAppAdapter", 所以可以直接使用:

const adapter=require('tt-tio/lib/adapters/ttAppAdapter')
tio.defaults.adapter=adapter;

成功案例

目前F项目前端相关项目都在使用:包括M站、C端APP内嵌H5工程、B端APP内嵌H5工程、小程序、B端后台、中台等。

如果你的项目也正在使用,请告知我们 (Lark搜 杜文),谢谢。

FAQ

我的页面时一个纯Web页面,不会在App中打开,有必要使用tio吗?

如果你需要使用Tio的请求同步功能,则需要使用tio;如果没使用,则无所谓,此时tio和axios功能是一致的。

Tio包有多大,和axios相比呢?

Tio和axios包大小基本持平,如果是浏览器环境下,Tio+xhrAdapter为13.8K,而axios为13.3K,Gzip后两者基本持平,都在5K左右;另外值得注意的是,在小程序环境下,Tio+adapterGzip前平均在12K左右,会小于浏览器环境下。

小程序中能使用tio进行文件上传吗?

不可以,小程序中都会有单独的文件上传API,使用其即可;浏览器中Tio可以支持文件上传是因为使用了浏览器内置对象FormData,而小程序中没有该对象。

Tio的请求取消、超时在各个平台下都支持吗?

请求取消、超时取决于平台原生API是否支持,目前浏览器、Node、各种小程序平台都是支持的,所以这些平台下Tio都是支持的。而APP内嵌取决于appAdapter实现,如果APP jsBridge 没有实现或支持请求取消机制,则Tio的取消功能将无效;