ajax与XMLHttpRequest
Ajax(Asynchronous JavaScript and XML)是一种用于创建动态Web应用程序的技术。它使用JavaScript和XMLHttpRequest对象来实现异步数据交互,从而避免了页面的刷新和重载,提高了用户体验。
Ajax本身不是一种技术,而是一种将一些现有技术结合起来使用的方法,包括:HTML 或 XHTML、CSS、JavaScript、DOM、XML、XSLT、以及最重要的 XMLHttpRequest 对象。
尽管 Ajax 中的 X 代表 XML,但是 JSON 才是首选,因为它更加轻量,而且是用 JavaScript 编写的。在 Ajax 模型中,JSON 和 XML 都被用来包装信息。
Ajax技术可以实现以下功能:
- 异步加载数据:通过Ajax技术,可以在不刷新页面的情况下,向服务器发送请求并获取数据,然后将数据更新到页面中。
- 动态更新页面:使用Ajax技术,可以动态更新页面的部分内容,而不需要刷新整个页面。
- 表单验证:通过Ajax技术,可以在用户提交表单之前,使用JavaScript验证表单数据的有效性,并向用户提供及时的反馈。
- 自动完成:使用Ajax技术,可以实现自动完成功能,即在用户输入关键字时,自动向服务器发送请求并获取匹配的数据。
- 实时聊天:通过Ajax技术,可以实现实时聊天功能,即在不刷新页面的情况下,向服务器发送消息并接收其他用户的消息。
XMLHttpRequest的异步模式和同步模式
XMLHttpRequest是一种用于创建异步Web应用程序的JavaScript API。它可以向服务器发送HTTP请求并接收响应,从而实现异步数据交互。XMLHttpRequest最初是由Microsoft开发的,后来成为W3C标准的一部分,现在被广泛应用于Web开发中。
XMLHttpRequest 对象的 open() 方法的第三个参数async的值决定的。如果该参数的值为 false,则该 XMLHttpRequest请求以同步模式进行,否则该过程将以异步模式完成。
异步请求:
var request = (url, callback) => {
var xhr = new XMLHttpRequest();
xhr.addEventListener('load', function() {
callback(this.responseText);
})
xhr.open('GET', url);
xhr.send();
}
request('https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/Synchronous_and_Asynchronous_Requests', (text) => {
console.log(`text:${text}`);
});
同步请求:
const request = (url) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, false);
xhr.send();
return xhr.responseText;
}
const text = request('https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/Synchronous_and_Asynchronous_Requests')
console.log(`text:${text}`);
XMLHttpRequest如何判断请求成功
在使用XMLHttpRequest对象发送请求后,可以通过readyState和status属性来判断请求是否成功或失败。
readyState属性表示XMLHttpRequest对象的状态,它有以下几个取值:
- 0:未初始化。XMLHttpRequest对象已经创建,但尚未调用open方法。
- 1:正在加载。XMLHttpRequest对象已经调用open方法,但尚未调用send方法。
- 2:已加载。XMLHttpRequest对象已经调用send方法,但尚未接收到响应。
- 3:交互中。XMLHttpRequest对象正在接收响应数据。
- 4:完成。XMLHttpRequest对象已经接收到全部响应数据。
status属性表示HTTP状态码,它有以下几个取值:
- 200:请求成功。
- 404:请求的资源不存在。
- 500:服务器内部错误。
- 其他状态码:其他HTTP状态码表示请求失败。
因此,可以通过判断readyState和status属性的值来判断请求是否成功或失败。当readyState的值为4且status的值为200时,表示请求成功;当status的值为其他状态码时,表示请求失败。可以在XMLHttpRequest对象的onreadystatechange事件中进行判断,代码示例如下:
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
// 请求成功
} else {
// 请求失败
}
};
xhr.open('GET', 'example.com', true);
xhr.send();
XMLHttpRequest设置请求头
setRequestHeader()是XMLHttpRequest对象的一个方法,用于设置HTTP请求头。该方法接受两个参数,第一个参数是请求头的名称,第二个参数是请求头的值。可以使用该方法设置多个请求头。
例如,以下代码设置了两个请求头:
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Authorization', 'Bearer ' + token);
第一个请求头是Content-Type,表示请求体的类型为JSON格式。第二个请求头是Authorization,表示使用Bearer Token进行身份验证。
需要注意的是,如果请求头名称已经存在,则该方法会覆盖原有的请求头值。如果需要设置多个相同名称的请求头,则可以使用逗号分隔多个值,例如:
xhr.setRequestHeader('Accept-Encoding', 'gzip, deflate');
这里设置了Accept-Encoding请求头,表示客户端支持的压缩算法为gzip和deflate。
总之,setRequestHeader()方法是XMLHttpRequest对象的一个重要方法,用于设置HTTP请求头。可以使用该方法设置多个请求头,以满足不同的HTTP请求需求。
XMLHttpRequest跨域请求
在JavaScript中,使用XMLHttpRequest对象发送跨域请求需要注意以下几点:
-
跨域请求需要服务器端支持CORS(跨域资源共享)。
CORS是一种机制,允许Web应用程序从不同的域访问其资源。服务器端需要设置响应头,允许指定的域名访问资源。例如,设置
Access-Control-Allow-Origin响应头,允许指定的域名访问资源。 -
发送跨域请求时,需要设置
withCredentials属性为true。withCredentials属性是XMLHttpRequest对象的一个属性,用于指示是否发送跨域请求时携带cookie等认证信息。如果需要发送cookie等认证信息,则需要将withCredentials属性设置为true。 -
发送跨域请求时,需要使用
XMLHttpRequest对象的open()方法,指定请求方法、URL和是否异步。xhr.open('GET', 'https://example.com/api/data', true); -
发送跨域请求时,需要使用
XMLHttpRequest对象的send()方法,发送请求数据。xhr.send();
下面是一个完整的跨域请求的示例:
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.open('GET', 'https://example.com/api/data', true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(xhr.responseText);
}
};
xhr.send();
在这个示例中,我们创建了一个XMLHttpRequest对象,并设置了withCredentials属性为true,表示发送跨域请求时携带cookie等认证信息。然后,使用open()方法指定请求方法、URL和是否异步,使用onreadystatechange事件监听请求状态的变化,最后使用send()方法发送请求。当请求完成时,如果响应状态码为200,则输出响应内容。
监测XMLHttpRequest进度
var oReq = new XMLHttpRequest();
// progress 事件在使用 file: 协议的情况下是无效的
// 检索的数据量发生了变化
oReq.addEventListener("progress", updateProgress);
// 传输完成,所有数据保存在 response 中
oReq.addEventListener("load" , transferComplete);
// 请求失败
oReq.addEventListener("error", transferFailed);
// 请求成功
oReq.addEventListener("abort", transferCanceled);
// 使用 loadend 事件可以侦测到所有的三种加载结束条件(abort、load,或 error)
req.addEventListener("loadend", loadEnd);
oReq.open();
// 服务端到客户端的传输进程(下载)
function updateProgress (oEvent) {
if (oEvent.lengthComputable) {
var percentComplete = oEvent.loaded / oEvent.total * 100;
// ...
} else {
// 总大小未知时不能计算进程信息
}
}
function transferComplete(evt) {
console.log("The transfer is complete.");
}
function transferFailed(evt) {
console.log("An error occurred while transferring the file.");
}
function transferCanceled(evt) {
console.log("The transfer has been canceled by the user.");
}
function loadEnd(evt) {
console.log("The transfer finished (although we don't know if it succeeded or not).");
}
如何中断XMLHttpRequest请求
要中断XMLHttpRequest请求,可以使用abort()方法。该方法将会立即终止请求,并触发XMLHttpRequest对象的abort事件。以下是一个示例,当用户点击"取消"按钮时,中断XMLHttpRequest请求:
const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/data');
xhr.send();
// 监听"取消"按钮的点击事件
document.getElementById('cancelBtn').addEventListener('click', function() {
xhr.abort();
});
// 监听XMLHttpRequest对象的abort事件
xhr.addEventListener('abort', function() {
console.log('请求已中断');
});
在这个示例中,我们首先创建了一个XMLHttpRequest对象,并发送了一个GET请求。然后,我们监听了"取消"按钮的点击事件,当用户点击"取消"按钮时,调用xhr.abort()方法中断请求。最后,我们监听了XMLHttpRequest对象的abort事件,当请求被中断时,会在控制台输出"请求已中断"。
需要注意的是,如果请求已经被发送并且服务器已经开始响应,调用abort()方法将无法中断请求。在这种情况下,浏览器将继续接收响应,并将其丢弃。
中断XMLHttpRequest请求的使用场景通常是在用户需要取消或中止正在进行的请求时。例如:
- 用户在上传大文件时,如果发现上传速度过慢或者上传的文件不正确,可以通过中断请求来取消上传过程,避免浪费时间和带宽资源。
- 在使用Ajax加载数据时,如果用户在数据加载过程中关闭了页面或者进行了其他操作,可以通过中断请求来停止数据加载过程,避免浪费服务器资源和网络带宽。
- 在使用轮询技术实现实时数据更新时,如果用户不再需要实时更新或者需要更新的数据已经被获取到,可以通过中断请求来停止轮询过程,避免浪费服务器资源和网络带宽。
总之,中断XMLHttpRequest请求可以帮助我们更好地管理网络请求,避免浪费资源和提高用户体验。
XMLHttpRequest的缺点
XMLHttpRequest是Ajax技术的核心,它也存在一些缺点,包括:
- 受同源策略限制:
XMLHttpRequest只能向与当前页面具有相同协议、主机和端口的服务器发送请求,这意味着它无法直接访问其他域名下的数据,从而限制了Ajax技术的应用范围。 - 无法跨域发送Cookie:由于同源策略的限制,
XMLHttpRequest无法跨域发送Cookie,这可能会影响到某些应用程序的功能。 - 只能发送异步请求:
XMLHttpRequest只能发送异步请求,这意味着它无法在请求发送完成之前阻塞页面,从而可能会导致一些问题,例如页面加载顺序不正确、用户体验不佳等。 - 对浏览器兼容性要求高:虽然现代浏览器都支持
XMLHttpRequest,但是在一些旧版浏览器中可能存在兼容性问题,需要进行额外的处理。 - 可维护性差:由于
XMLHttpRequest通常需要编写大量的JavaScript代码,因此可能会导致代码可维护性差,特别是在复杂的应用程序中。
总之,XMLHttpRequest虽然是Ajax技术的核心,但也存在一些缺点,需要在实际应用中进行权衡和取舍。
fetch
fetch是XMLHttpRequest的替代品,它提供了一种更加现代化、简单和强大的方式来进行网络请求。相比于XMLHttpRequest,fetch有以下优点:
- 更加简单易用:
fetch的API设计更加简单易用,可以通过链式调用来设置请求参数和处理响应结果,从而减少了代码量和学习成本。 - 更加灵活:
fetch支持Promise和async/await等现代JavaScript特性,可以更加灵活地处理异步请求和响应结果。 - 更加强大:
fetch支持流式请求和响应,可以处理大文件和流媒体等复杂场景,同时还支持跨域请求和发送Cookie等功能。 - 更加标准化:
fetch是W3C标准的一部分,得到了浏览器厂商和Web开发者的广泛支持,可以提高代码的可移植性和可维护性。 - 更加安全:
fetch支持CORS(跨域资源共享)和SRI(子资源完整性)等安全特性,可以提高Web应用程序的安全性。 - 更加高效:
fetch的底层实现使用了HTTP/2协议和流式传输等技术,可以提高网络传输效率和性能。
fetch处理异步请求和响应结果
链式调用:
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error))
async/await:
async function getData() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
}
fetch设置请求头
在使用fetch发送请求时,可以通过设置headers选项来设置请求头。请求头可以包含一些额外的信息,例如Content-Type、Authorization等。
以下是一个使用fetch设置请求头的示例:
fetch('https://example.com/api/data', {
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer xxxxxxxx'
}
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error))
这个示例使用了fetch来获取一个JSON数据。在请求中,我们设置了headers选项来设置请求头。我们设置了Content-Type为application/json,表示请求体中的数据为JSON格式。我们还设置了Authorization为Bearer xxxxxxxx,表示使用Bearer Token进行身份验证。在获取到响应后,我们使用response.json()方法将响应转换为JSON格式的数据。最后,我们使用.then()方法打印数据,或使用.catch()方法处理错误。
fetch跨域请求
在使用fetch进行跨域请求时,需要注意以下几点:
- 服务器需要设置CORS(跨域资源共享)策略,允许指定的域名访问资源。
- 在请求中需要设置
mode为cors,以便浏览器知道这是一个跨域请求。 - 如果需要携带cookie,需要设置
credentials为include,以便浏览器允许携带cookie。
以下是一个使用fetch进行跨域请求的示例:
fetch('https://example.com/api/data', {
mode: 'cors',
credentials: 'include'
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error))
这个示例使用了fetch来获取一个跨域的JSON数据。在请求中,我们设置了mode为cors,以便浏览器知道这是一个跨域请求。如果需要携带cookie,我们还需要设置credentials为include。在获取到响应后,我们使用response.json()方法将响应转换为JSON格式的数据。最后,我们使用.then()方法打印数据,或使用.catch()方法处理错误。
监听fetch的请求进度
在使用fetch发送请求时,可以通过Response对象的body属性来获取响应体。body属性是一个ReadableStream对象,可以通过Response对象的headers属性来获取响应头。通过这些属性,我们可以监听请求和响应的进度。
以下是一个使用fetch监听请求进度的示例:
fetch('https://example.com/api/data')
.then(response => {
const total = response.headers.get('content-length');
let loaded = 0;
const reader = response.body.getReader();
return new ReadableStream({
start(controller) {
function push() {
reader.read().then(({ done, value }) => {
if (done) {
controller.close();
return;
}
loaded += value.byteLength;
console.log(`Progress: ${Math.floor(loaded / total * 100)}%`);
controller.enqueue(value);
push();
}).catch(error => {
console.error(error);
controller.error(error);
});
}
push();
}
});
})
.then(stream => new Response(stream))
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
这个示例使用了fetch来获取一个JSON数据。在获取到响应后,我们通过Response对象的headers属性获取了响应头中的content-length字段,表示响应体的总大小。我们使用response.body.getReader()方法获取了一个ReadableStreamDefaultReader对象,可以通过这个对象读取响应体的数据。在ReadableStream的start方法中,我们使用reader.read()方法读取响应体的数据,每读取一次就会触发一次push函数。在push函数中,我们统计已经加载的数据大小,计算出请求进度,并输出到控制台。最后,我们通过controller.enqueue(value)方法将读取到的数据放入ReadableStream中,以便后续处理。在获取到完整的ReadableStream后,我们将其转换为Response对象,并使用.then()方法将响应转换为JSON格式的数据。最后,我们使用.then()方法打印数据,或使用.catch()方法处理错误。
如何中断fetch请求
在使用fetch发送请求时,可以使用AbortController来中断请求。AbortController是一个用于中断异步操作的API,可以通过调用AbortController对象的abort()方法来中断请求。
以下是一个使用AbortController中断fetch请求的示例:
const controller = new AbortController();
const signal = controller.signal;
fetch('https://example.com/api/data', { signal })
.then(response => response.json())
.then(data => console.log(data))
.catch(error => {
if (error.name === 'AbortError') {
console.log('Request aborted');
} else {
console.error(error);
}
});
setTimeout(() => {
controller.abort();
}, 5000);
这个示例使用了fetch来获取一个JSON数据。我们创建了一个AbortController对象,并通过controller.signal属性获取了一个AbortSignal对象,用于传递给fetch请求。在获取到响应后,我们使用.then()方法将响应转换为JSON格式的数据。在捕获错误时,我们判断错误的类型是否为AbortError,如果是则表示请求被中断。在5秒后,我们调用controller.abort()方法来中断请求。
fetch的缺点
fetch不支持跨域请求时的cookie自动发送,需要手动设置credentials选项。fetch不支持超时设置,需要使用AbortController来手动中断请求。fetch在处理网络错误时,只会捕获网络错误,而不会捕获HTTP错误状态码(如404、500等)。fetch不支持进度事件的监听,需要使用Response对象的body属性和ReadableStream来手动实现。
因此,在使用fetch时需要注意这些缺点,并根据实际情况选择是否使用fetch。如果需要支持跨域请求时的cookie自动发送、超时设置、HTTP错误状态码的捕获和进度事件的监听,可以考虑使用其他网络请求库,例如axios、superagent等。