ajax与fetch

264 阅读14分钟

ajax与XMLHttpRequest

Ajax(Asynchronous JavaScript and XML)是一种用于创建动态Web应用程序的技术。它使用JavaScript和XMLHttpRequest对象来实现异步数据交互,从而避免了页面的刷新和重载,提高了用户体验。

Ajax本身不是一种技术,而是一种将一些现有技术结合起来使用的方法,包括:HTMLXHTMLCSSJavaScriptDOMXMLXSLT、以及最重要的 XMLHttpRequest 对象。

尽管 Ajax 中的 X 代表 XML,但是 JSON 才是首选,因为它更加轻量,而且是用 JavaScript 编写的。在 Ajax 模型中,JSON 和 XML 都被用来包装信息。

Ajax技术可以实现以下功能:

  1. 异步加载数据:通过Ajax技术,可以在不刷新页面的情况下,向服务器发送请求并获取数据,然后将数据更新到页面中。
  2. 动态更新页面:使用Ajax技术,可以动态更新页面的部分内容,而不需要刷新整个页面。
  3. 表单验证:通过Ajax技术,可以在用户提交表单之前,使用JavaScript验证表单数据的有效性,并向用户提供及时的反馈。
  4. 自动完成:使用Ajax技术,可以实现自动完成功能,即在用户输入关键字时,自动向服务器发送请求并获取匹配的数据。
  5. 实时聊天:通过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对象发送跨域请求需要注意以下几点:

  1. 跨域请求需要服务器端支持CORS(跨域资源共享)。

    CORS是一种机制,允许Web应用程序从不同的域访问其资源。服务器端需要设置响应头,允许指定的域名访问资源。例如,设置Access-Control-Allow-Origin响应头,允许指定的域名访问资源。

  2. 发送跨域请求时,需要设置withCredentials属性为true

    withCredentials属性是XMLHttpRequest对象的一个属性,用于指示是否发送跨域请求时携带cookie等认证信息。如果需要发送cookie等认证信息,则需要将withCredentials属性设置为true

  3. 发送跨域请求时,需要使用XMLHttpRequest对象的open()方法,指定请求方法、URL和是否异步。

    xhr.open('GET', 'https://example.com/api/data', true);
    
  4. 发送跨域请求时,需要使用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请求的使用场景通常是在用户需要取消或中止正在进行的请求时。例如:

  1. 用户在上传大文件时,如果发现上传速度过慢或者上传的文件不正确,可以通过中断请求来取消上传过程,避免浪费时间和带宽资源。
  2. 在使用Ajax加载数据时,如果用户在数据加载过程中关闭了页面或者进行了其他操作,可以通过中断请求来停止数据加载过程,避免浪费服务器资源和网络带宽。
  3. 在使用轮询技术实现实时数据更新时,如果用户不再需要实时更新或者需要更新的数据已经被获取到,可以通过中断请求来停止轮询过程,避免浪费服务器资源和网络带宽。

总之,中断XMLHttpRequest请求可以帮助我们更好地管理网络请求,避免浪费资源和提高用户体验。

XMLHttpRequest的缺点

XMLHttpRequestAjax技术的核心,它也存在一些缺点,包括:

  1. 受同源策略限制:XMLHttpRequest只能向与当前页面具有相同协议、主机和端口的服务器发送请求,这意味着它无法直接访问其他域名下的数据,从而限制了Ajax技术的应用范围。
  2. 无法跨域发送Cookie:由于同源策略的限制,XMLHttpRequest无法跨域发送Cookie,这可能会影响到某些应用程序的功能。
  3. 只能发送异步请求:XMLHttpRequest只能发送异步请求,这意味着它无法在请求发送完成之前阻塞页面,从而可能会导致一些问题,例如页面加载顺序不正确、用户体验不佳等。
  4. 对浏览器兼容性要求高:虽然现代浏览器都支持XMLHttpRequest,但是在一些旧版浏览器中可能存在兼容性问题,需要进行额外的处理。
  5. 可维护性差:由于XMLHttpRequest通常需要编写大量的JavaScript代码,因此可能会导致代码可维护性差,特别是在复杂的应用程序中。

总之,XMLHttpRequest虽然是Ajax技术的核心,但也存在一些缺点,需要在实际应用中进行权衡和取舍。

fetch

fetchXMLHttpRequest的替代品,它提供了一种更加现代化、简单和强大的方式来进行网络请求。相比于XMLHttpRequestfetch有以下优点:

  1. 更加简单易用:fetch的API设计更加简单易用,可以通过链式调用来设置请求参数和处理响应结果,从而减少了代码量和学习成本。
  2. 更加灵活:fetch支持Promise和async/await等现代JavaScript特性,可以更加灵活地处理异步请求和响应结果。
  3. 更加强大:fetch支持流式请求和响应,可以处理大文件和流媒体等复杂场景,同时还支持跨域请求和发送Cookie等功能。
  4. 更加标准化:fetch是W3C标准的一部分,得到了浏览器厂商和Web开发者的广泛支持,可以提高代码的可移植性和可维护性。
  5. 更加安全:fetch支持CORS(跨域资源共享)和SRI(子资源完整性)等安全特性,可以提高Web应用程序的安全性。
  6. 更加高效: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-TypeAuthorization等。

以下是一个使用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-Typeapplication/json,表示请求体中的数据为JSON格式。我们还设置了AuthorizationBearer xxxxxxxx,表示使用Bearer Token进行身份验证。在获取到响应后,我们使用response.json()方法将响应转换为JSON格式的数据。最后,我们使用.then()方法打印数据,或使用.catch()方法处理错误。

fetch跨域请求

在使用fetch进行跨域请求时,需要注意以下几点:

  1. 服务器需要设置CORS(跨域资源共享)策略,允许指定的域名访问资源。
  2. 在请求中需要设置modecors,以便浏览器知道这是一个跨域请求。
  3. 如果需要携带cookie,需要设置credentialsinclude,以便浏览器允许携带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数据。在请求中,我们设置了modecors,以便浏览器知道这是一个跨域请求。如果需要携带cookie,我们还需要设置credentialsinclude。在获取到响应后,我们使用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对象,可以通过这个对象读取响应体的数据。在ReadableStreamstart方法中,我们使用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的缺点
  1. fetch不支持跨域请求时的cookie自动发送,需要手动设置credentials选项。
  2. fetch不支持超时设置,需要使用AbortController来手动中断请求。
  3. fetch在处理网络错误时,只会捕获网络错误,而不会捕获HTTP错误状态码(如404、500等)。
  4. fetch不支持进度事件的监听,需要使用Response对象的body属性和ReadableStream来手动实现。

因此,在使用fetch时需要注意这些缺点,并根据实际情况选择是否使用fetch。如果需要支持跨域请求时的cookie自动发送、超时设置、HTTP错误状态码的捕获和进度事件的监听,可以考虑使用其他网络请求库,例如axiossuperagent等。

其他