表示http请求的状态,初始值为0。如果服务器没有明确指定状态码,则会设置为默认值,即200。
代表响应的数据类型并允许我们手动设置。 如果为空,则默认为文本类型,可以具有以下值:
返回响应的正文,返回的类型由上面确定。
Ajax请求默认会携带同源请求,但跨域请求不会。 将xhr属性设置为true将允许跨域携带。
事件回调
xhr.onreadystatechange = callback;
当属性改变时会被触发。
xhr.onloadstart = callback;
它会在发送ajax请求之前触发(==1之后,==2之前)。
xhr.onprogress = function(event){
console.log(event.loaded / event.total);
}
回调函数可以获取总资源大小和已加载资源大小。 这两个值可以用来计算加载进度。
xhr.onload = callback;
当资源及其依赖的资源完成加载时会触发该事件,通常我们会在事件中处理返回值。
异常处理
xhr.onerror = callback;
ajax资源加载失败时触发。
xhr.ontimeout = callback;
当进度因预定时间到期而终止时触发,可以使用属性设置超时。
6、Ajax的封装
很长一段时间以来,人们都使用提供的ajax包来进行网络请求,包括$.ajax、$.get、$.post等,这些方法现在我觉得还是很实用的。
$.ajax({
dataType: 'json', // 设置返回值类型
contentType: 'application/json', // 设置参数类型
headers: {'Content-Type','application/json'},// 设置请求头
xhrFields: { withCredentials: true }, // 跨域携带 cookie
data: JSON.stringify({a: [{b:1, a:1}]}), // 传递参数
error:function(xhr,status){ // 错误处理
console.log(xhr,status);
},
success: function (data,status) { // 获取结果
console.log(data,status);
}
})
$.ajax只接收一个参数,该参数接收一系列配置。 它封装了一个jqXHR对象。 如果有兴趣,可以阅读-ajax源码:
常用配置:
网址
当前页面地址。 将请求发送到的地址。
类型
Type:请求方式(“POST”或“GET”),默认为“GET”。 注意:也可以使用其他 HTTP 请求方法,例如 PUT 和 PUT,但仅部分浏览器支持。
类型:设置请求超时(毫秒)。 该设置会覆盖全局设置。
类型:请求成功后的回调函数。
jsonp
覆盖 jsonp 请求中回调函数的名称。 该值用于替换 GET 或 POST 请求中 URL 参数的“”部分,例如“=?”。
错误
类型: ,请求失败时调用该函数。
注:源代码中错误的判定:
isSuccess = status >= 200 && status < 300 || status === 304;
除这些状态码外,返回值将进入错误回调。
"xml": 返回 XML 文档,可用 jQuery 处理。
"html": 返回纯文本 HTML 信息;包含的 script 标签会在插入 dom 时执行。
"script": 返回纯文本 JavaScript 代码。不会自动缓存结果。除非设置了 "cache" 参数。注意:在远程请求时 (不在同一个域下),所有 POST 请求都将转为 GET 请求。(因为将使用 DOM 的 script 标签来加载)
"json": 返回 JSON 数据 。
"jsonp": JSONP 格式。使用 JSONP 形式调用函数时,如 "myurl?callback=?" jQuery 将自动替换 ? 为正确的函数名,以执行回调函数。
"text": 返回纯文本字符串
数据
类型:使用 JSON.。
类型:请求完成后的回调函数(请求成功或失败后调用)。
异步
类型:默认值:true。 默认情况下,所有请求都是异步的。 如果需要发送同步请求,请将此选项设置为 false。
类型:默认值:“/x-www-form-”。 向服务器发送信息时的内容编码类型。
一般情况下,这样组织的键值对是没有问题的。 我们这里所说的一般是没有嵌套类型的JSON,即简单的JSON,看起来像这样:
{
a: 1,
b: 2,
c: 3
}
但在一些复杂的情况下就会出现问题。 例如,在 Ajax 中,您想要传递一个复杂的 json 对象,它是一个嵌入的对象数组。 该数组包含对象。 你可以像这样传递它: /x-www-form- 没有办法将复杂的 JSON 组织成键值对格式。
{
data: {
a: [{
x: 2
}]
}
}
复杂的 json 对象可以按如下方式传递:
$.ajax({
dataType: 'json',
contentType: 'application/json',
data: JSON.stringify({a: [{b:1, a:1}]})
})
7. 替代
近年来,前端MV*发展壮大,人们使用它的人越来越少。 我们不可能仅仅针对我们使用的Ajax API来介绍它。 不可避免地,我们需要寻找新的技术解决方案。
游宇熙在他的文档中推荐大家使用axios来进行网络请求。 axios基于对原生XHR非常全面的封装,使用起来也非常优雅。 另外,axios还提供了node环境下的支持,可以说是网络请求的首选解决方案。
未来肯定会有更好的套餐。 他们有非常全面的考虑和详细的文档。 我们在这里不再赘述。 我们将重点关注较低级别。
Fetch API 是一个强大的本机 API,用于访问和操作 HTTP 管道。
此功能之前是使用 . Fetch 提供了一个更好的替代方案,可以很容易地被其他技术使用,例如 Fetch 还提供了一个单一的逻辑位置来定义其他 HTTP 相关的概念,例如 CORS 和 HTTP 的扩展。
可见fetch是作为替代品出现的。
使用 fetch,您不需要加载额外的外部资源。 但浏览器尚未完全支持它,因此您仍然需要一个。
8. fetch的使用
基本的获取请求:
const options = {
method: "POST", // 请求参数
headers: { "Content-Type": "application/json"}, // 设置请求头
body: JSON.stringify({name:'123'}), // 请求参数
credentials: "same-origin", // cookie 设置
mode: "cors", // 跨域
}
fetch('http://www.xxx.com')
.then(function(response) {
return response.json();
})
.then(function(myJson) {
console.log(myJson); // 响应数据
})
.catch(function(err){
console.log(err); // 异常处理
})
Fetch API 提供了一个全局的 fetch() 方法和几个辅助对象来发起网络请求。
拿来()
fetch()方法用于发起获取资源的请求。 它返回一个对象,该对象将在请求响应后被检索。
你可以通过()构造函数创建自己的对象,相当于/的头信息,允许你查询这些头信息,或者对不同的结果执行不同的操作。
var myHeaders = new Headers();
myHeaders.append("Content-Type", "text/plain");
可以通过()构造函数创建一个对象,该对象可以作为fetch函数的第二个参数。
fetch() 处理后返回一个实例,也可以手动创建一个实例。
9. 获取源码分析
由于fetch是一个非常底层的API,我们无法进一步探究它的底层,但我们可以通过它来探索它的基本原理并找出它的陷阱。
代码结构
从代码中可以看出,它主要封装了Fetch API提供的四大对象:
获取包
代码很清楚:
异常处理
可以发现调用有3种可能:
请求超时
请求失败
注意:与服务器建立引言时,从服务器接收到404、500等异常状态码时,无法触发。 只有当网络故障或请求被阻止时才会被标记。 比如跨域、url不存在、网络异常等都会被触发。
因此,当使用fetch接收异常状态码时,会进入then而不是catch。 这些错误的请求通常需要手动处理。
手动终止
您可以在参数中传入一个对象,并为该对象添加 abort 事件监听器。 当xhr. 变为4(响应内容解析完成),移除该对象的abort事件监听器。
这意味着可以调用 .abort 在获取请求结束之前中止该请求。 在浏览器中,您可以使用 () 构造函数创建控制器,然后使用 . 属性。
这是一项实验性功能,在某些浏览器中仍在开发中。
封装
对象中维护了一个地图对象。 构造函数可以传入对象、数组和普通对象类型,并维护map中的所有值。
我之前看到过fetch函数中调用的方法。 这是它的实现:
可见的遍历就是对其内部map的遍历。
另外,它还提供了、、get、set等方法,这些方法都是对其内部的地图对象进行操作。
目的
对象接收到的两个参数就是fetch函数接收到的两个参数。 第一个参数可以直接传递 URL 或构造的对象。 第二个参数是控制不同配置的对象。
您可以传入诸如、、、模式等属性。
这里注意:传入的参数作为构造函数的参数来构造对象。
处理
fetch函数中还有如下代码:
if (request.credentials === 'include') {
xhr.withCredentials = true
} else if (request.credentials === 'omit') {
xhr.withCredentials = false
}
默认类型为same-,可以携带同源请求。
然后发现这里的实现与MDN-using Fetch()不一致,查了很多资料:
mdn:默认情况下,fetch不会发送或接收任何
于是我尝试使用和使用 fetch来承载,发现不用设置就默认承载同源。 这与文档的描述不一致。 查阅了很多资料,据说fetch默认是不会携带的。 下面是在浏览器中使用 fetch进行请求的情况:
然后我发现MDN-Fetch-()已经指出新版本浏览器的默认值已经改为same-,而旧版本仍然是省略。
确实,这里关于 MDN-Using Fetch 的文档没有及时更新,这是有误导性的:
目的
该对象是fetch调用成功后的返回值:
回顾一下 fetch 中 ` 的操作:
xhr.onload = function () {
var options = {
status: xhr.status,
statusText: xhr.statusText,
headers: parseHeaders(xhr.getAllResponseHeaders() || '')
}
options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL')
var body = 'response' in xhr ? xhr.response : xhr.responseText
resolve(new Response(body, options))
}
构造函数:
可以看到,构造函数中主要是对 、 、 、 url 等进行处理并挂载到对象上。
构造函数中没有明确的处理,最终交给了函数,没有主动声明属性。 代码最终调用了Body函数。 事实上,该函数是通过Body函数挂载到body上的。 我们先看一下函数。 :
可以看到函数根据xhr的类型给不同的参数赋值。 (斑点,,...)。 这些参数在 Body 方法中以不同的方式使用。 让我们仔细看看 Body 函数还执行哪些其他操作:
Body函数还挂载了对象、text、json和blob的四个函数。 这些函数中的操作都是将获得的不同类型的返回值返回。
这也说明,fetch执行后,不能直接在其中获取返回值,而必须调用text()、json()等函数来获取返回值。
这里还有一点需要说明一下:几个函数的逻辑类似如下:
var rejected = consumed(this)
if (rejected) {
return rejected
}
功能:
function consumed(body) {
if (body.bodyUsed) {
return Promise.reject(new TypeError('Already read'))
}
body.bodyUsed = true
}
每次调用text()、json()等函数时,该变量都会变为true,表示已读取到返回值。 下次读取时,会直接抛出('read')。 这也遵循了 fetch的原理:
因为对象是按原样设置的,所以它们只能读取一次。
10. fetch 的陷阱
VUE文档对fetch有如下描述:
使用fetch还有很多需要注意的地方,这也是为什么现阶段大家还是更喜欢axios的原因。 当然,这种情况将来可能会改变。
由于fetch是一个非常底层的API,所以还没有进行太多的封装,还有很多问题需要处理:
11.fetch的封装
请求参数处理
支持传入不同的参数类型:
function stringify(url, data) {
var dataString = url.indexOf('?') == -1 ? '?' : '&';
for (var key in data) {
dataString += key + '=' + data[key] + '&';
};
return dataString;
}
if (request.formData) {
request.body = request.data;
} else if (/^get$/i.test(request.method)) {
request.url = `${request.url}${stringify(request.url, request.data)}`;
} else if (request.form) {
request.headers.set('Content-Type', 'application/x-www-form-urlencoded;charset=UTF-8');
request.body = stringify(request.data);
} else {
request.headers.set('Content-Type', 'application/json;charset=UTF-8');
request.body = JSON.stringify(request.data);
}
携带
fetch在新浏览器中已经开始默认携带同源,但在旧浏览器中不会默认携带。 我们需要统一设置一下:
request.credentials = 'same-origin'; // 同源携带
request.credentials = 'include'; // 可跨域携带
异常处理
当收到表示错误的 HTTP 状态代码时,即使 HTTP 响应状态代码是 404 或 500, fetch() 的返回也不会被标记为错误。相反,它会将状态标记为 (但会设置 ok仅当出现网络故障或请求被阻止时,返回值属性为 false)。
因此,我们需要统一处理fetch异常:
.then(response => {
if (response.ok) {
return Promise.resolve(response);
}else{
const error = new Error(`请求失败! 状态码: ${response.status}, 失败信息: ${response.statusText}`);
error.response = response;
return Promise.reject(error);
}
});
返回值处理
调用不同的函数会收到不同的返回值类型。 必须提前确定类型,并且获取返回值的方法不能多次调用:
.then(response => {
let contentType = response.headers.get('content-type');
if (contentType.includes('application/json')) {
return response.json();
} else {
return response.text();
}
});
jsonp
Fetch本身并没有提供对jsonp的支持,而jsonp本身也不是一个很好的解决跨域问题的方法。 推荐使用cors或者nginx解决跨域问题。 详细内容请参见以下章节。
fetch已经封装好了,可以愉快的使用了。
嗯,axios确实很有用。
12. 跨域总结