# Ajax

# 介绍

网络请求是网页开发的必经环节,其中有一些问题我们需要重点关注:

  1. 请求能否正常返回,如果非正常返回或发生错误该如何处理
  2. 请求是否跨域,如何解决跨域
  3. 我们是否能监控所有请求,保证接口和页面显示的稳定
  4. 调用网络请求的方式难以统一,甚至存在冗余的函数定义 解决这些问题就是封装该模块的初衷

# 功能

  1. 能够满足任意请求目标,常用浏览器环境下的网络请求
  2. 能够解决跨域问题
  3. 能够监控请求,上报错误和异常情况
  4. 以统一的调用方式,返回处理方式,标准化解决请求异常

# 特点

  1. 简单,易上手
  2. 灵活,支持广
  3. 返回处理标准统一,上报使得可追踪,出现问题易定位

# 更新日志

2020.11.5

  • jFetch的formdata提交优化
  • 新增顺序和同时加载多个 js 的方法

# 安装

默认存在

# 使用

# 通用请求

【jFetch】

基于浏览器原生的 fetch API,涉及参数的详细介绍如下

参数 类型 含义
method string 请求类型,GET(默认值)/HEAD/POST/PUT/DELETE/OPTIONS/PATCH
mode string 用于指定处理特定类型的请求,same-origin/cors(默认值)/no-cors
cache string 请求与内核 HTTP cache 交互的模式,default(默认值)/no-cache/no-store/reload/force-cache/only-if-cached
credentials string 请求的凭据指定,include/same-origin(默认值)/omit
redirect string 重定向时的选项,follow(默认值)/error/manual
referrer string 请求来源的 header,client(默认值)/no-referrer/空

各参数相关的详细说明:

# mode

  • same-origin 只处理同源请求
  • cors 表示同域或者带有恰当 CORS 响应头(服务端支持 cors)的跨域下可请求成功. 其他请求将被拒绝
  • no-cors 常用于跨域请求服务器不带 CORS 响应头场景

# cache

  • default — 默认情形,内核会以下面方式使用 HTTP cache:

如果命中 HTTP cache 并且资源是 fresh 的,会直接使用 cache。 如果命中 HTTP cache 并且资源是 stale 的,内核会发起条件请求,如果服务器回应资源未有修改,则直接使用 cache,否则会下载资源和更新 cache。 如果没有命中 HTTP cache,内核会发起普通请求,下载资源和更新 cache。

  • no-store — 内核忽略 HTTP cache,直接向服务器请求资源,资源下载成功后也不会更新 HTTP cache。
  • reload — 内核忽略 HTTP cache,直接向服务器请求资源,但资源下载成功后会更新 HTTP cache。
  • no-cache — 内核会以下面方式使用 HTTP cache:

如果命中 HTTP cache 并且资源是 fresh/stale 的,内核会发起条件请求,如果服务器回应资源未有修改,则直接使用 cache,否则会下载资源和更新 cache。 如果没有命中 HTTP cache,内核会发起普通请求,下载资源和更新 cache。

  • force-cache — 内核会以下面方式使用 HTTP cache:

如果命中 HTTP cache 并且资源是 fresh/stale 的,会直接使用 cache。 如果没有命中 HTTP cache,内核会发起普通请求,下载资源和更新 cache。

  • only-if-cached — 内核会以下面方式使用 HTTP cache:

如果命中 HTTP cache 并且资源是 fresh/stale 的,会直接使用 cache。 如果没有命中,会返回错误。 这个模式只允许在 Request.mode = "same-origin"时使用。

# credentials

  • include:任何时候都带上凭据;
  • same-origin:只在同源时允许带凭据(默认);
  • omit:任何时候都不带

# referrer

  • 可以设定为'',则不发送 referrer
  • 或者设定为同源下的其他链接
  • 默认是当前链接
返回 类型 含义
请求状态 Promise 在 then 中执行成功的回调,catch 中执行失败回调

以下列举一些常见的使用栗子

# AutoCMS 请求(跨域 Get)

AutoCMS 的返回多是 content-type:application/x-javascript,需做特殊处理

let url = '//lol.qq.com/act/AutoCMS/publish/LCU/SubNav/subnav.js';
Ajax.jFetch({
  url,
})
  .then((raw) => {
    // 针对autoCMS类型,可以进行过如下处理得到JSON数据
    eval(raw);
    let data = window.SubNav;
    delete window.SubNav;
    console.log(data);
  })
  .catch((e) => {
    console.log(e);
  });
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# Post 请求

let postUrl = '';
Ajax.jFetch({
  url: postUrl,
  method: 'POST',
  body: {
    param1: 'xxx',
    param2: 'xxx',
  },
})
  .then((data) => {
    console.log(data);
  })
  .catch((e) => {
    console.log(e);
  });


//form表单提交1,
//提交是payload不是formdata的情况,导致提交不成功时
Ajax.jFetch({
    url: postUrl,
    method: 'POST',
    credentials: 'include',//传登录态
    headers: {// 浏览器原生的form表单,请求体中的数据会以普通表单形式(键值对)发送到后端
        'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: 'key1=value1&key2=value2'
    }).then((data) => {
        console.log(data);
    }).catch((e) => {
        console.log(e);
})

//formData的表单提交2
let formDataObj = new FormData();
formDataObj.append("age", 1);
formDataObj.append("name", 'XXXX');
Ajax.jFetch({
         url: postUrl,
         method: 'POST',
         credentials: 'include',
         body: formDataObj
     }).then((res) => {
         console.log(res);
     }).catch((e) => {
         console.log(e);
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

# 普通 Get 跨域请求

let url = '//lol.ams.game.qq.com/lol/live/v1/anchor';
Ajax.jFetch({
  url,
})
  .then((data) => {
    console.log(data);
  })
  .catch((e) => {
    console.log(e);
  });
1
2
3
4
5
6
7
8
9
10

# JSONP

【jsonp】

可用于专门支持 fetch 无法解决的场景,如跨域且服务端不支持 cors,且服务器支持 jsonp,使用场景如下

# 服务端不支持 cors

此时如果使用 fetch,会有以下两种情况,都不能得到结果:

  1. mode: cors;该设定在跨域情况下,必须服务端支持 cors,能够返回正确的 cors 头,如 Access-Control-Allow-Origin 等。但如服务端不支持 cors 是不能返回正确的 cors 的 header 头的。往往产生如下报错: xxxx has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
  2. mode: no-cors;可以设置改模式来应对服务器不支持 cors 的情况;该模式允许浏览器发送本次跨域请求,但是不能访问响应返回的内容,这也是其 response type 为 opaque 透明的原因。

Response

let url = `//lol.sw.game.qq.com/lol/commact/?proj=a20200415livelink&c=a20200415livelink&a=getMatchScheduleList`;
Ajax.jsonp(url)
  .then((data) => {
    console.log(data);
  })
  .catch((e) => {
    console.log(e);
  });
1
2
3
4
5
6
7
8

# 加载 js

【loadJS】

解决服务器不支持 CORS,且不支持 JSONP 的情况。

JSONP 要求服务器的响应为 json 数据的包装(故称之为 jsonp,即 json padding),形如:callback({"name":"hax","gender":"Male"})

# 直接加载 js 文件

let url = '//lpl.qq.com/web201612/data/LOL_MATCH2_TEAM_LIST.js';
Ajax.loadJS(url)
  .then((data) => {
    console.log(data);
    // undefined
    console.log(TeamList);
    // xxx 全局变量
  })
  .catch((e) => {
    console.log(e);
  });
1
2
3
4
5
6
7
8
9
10
11

由于该 js 文件返回的是全局变量,并没有数据返回,获取数据要么直接访问全局变量,要么可以通过以下操作得到 json 数据

let url = '//lpl.qq.com/web201612/data/LOL_MATCH2_TEAM_LIST.js';
Ajax.loadJS(url, { dataName: 'TeamList' })
  .then((data) => {
    console.log(data);
  })
  .catch((e) => {
    console.log(e);
  });
1
2
3
4
5
6
7
8

# 顺序加载 js 文件

注意如果不传charset参数,则默认是utf-8 当脚本具有依赖关系时使用

let urls = [
  {
    src:'//ossweb-img.qq.com/images/js/milo_bundle/milo.js',
    charset:'utf-8'
  },
  {
    src:'https://ossweb-img.qq.com/images/js/PTT/ping_tcss_tgideas_https_min.js',
    charset:'utf-8'
  }
];
Ajax.seriesLoadScripts(urls)
  .then((data)=>{
    console.log('都加载完了');
    console.log(data);
  })
  .catch((e)=>{
    console.log(e);
  });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 并行加载 js 文件

注意如果不传charset参数,则默认是utf-8
当脚本相互独立运行时推荐使用

let urls = [
  {
    src:'//ossweb-img.qq.com/images/js/milo_bundle/milo.js',
    charset:'utf-8'
  },
  {
    src:'https://ossweb-img.qq.com/images/js/PTT/ping_tcss_tgideas_https_min.js',
    charset:'utf-8'
  }
];
Ajax.parallelLoadScripts(urls)
  .then((data)=>{
    console.log('都加载完了');
    console.log(data);
  })
  .catch((e)=>{
    console.log(e);
  });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# DEMO

DEMO (opens new window)

# 补充

Last Updated: 12/14/2021, 5:05:41 PM