UDN-企业互联网技术人气社区

板块导航

浏览  : 886
回复  : 2

[jQuery] 分别使用 XHR、jQuery 和 Fetch 实现 AJAX

[复制链接]
芭芭拉的头像 楼主
发表于 2016-12-31 15:59:57 | 显示全部楼层 |阅读模式
  AJAX 即 Asynchronous JavaScript and XML,异步的 JavaScript 和 XML。使用 AJAX 可以无刷新地向服务端发送请求接收服务端响应,并更新页面。

  一、原生 JS 实现 AJAX

  JS 实现 AJAX 主要基于浏览器提供的 XMLHttpRequest(XHR)类,所有现代浏览器(IE7+、Firefox、Chrome、Safari 以及 Opera)均内建 XMLHttpRequest 对象。

  1. 获取XMLHttpRequest对象
  1. // 获取XMLHttpRequest对象
  2. var xhr = new XMLHttpRequest();
复制代码

  如果需要兼容老版本的 IE (IE5, IE6) 浏览器,则可以使用 ActiveX 对象:
  1. var xhr;
  2. if (window.XMLHttpRequest) { // Mozilla, Safari...
  3.   xhr = new XMLHttpRequest();
  4. } else if (window.ActiveXObject) { // IE
  5.   try {
  6.     xhr = new ActiveXObject('Msxml2.XMLHTTP');
  7.   } catch (e) {
  8.     try {
  9.       xhr = new ActiveXObject('Microsoft.XMLHTTP');
  10.     } catch (e) {}
  11.   }
  12. } 
复制代码

  2. 发送一个 HTTP 请求

  接下来,我们需要打开一个URL,然后发送这个请求。分别要用到 XMLHttpRequest 的 open() 方法和 send() 方法。
  1. // GET
  2. var xhr;
  3. if (window.XMLHttpRequest) { // Mozilla, Safari...
  4.   xhr = new XMLHttpRequest();
  5. } else if (window.ActiveXObject) { // IE
  6.   try {
  7.     xhr = new ActiveXObject('Msxml2.XMLHTTP');
  8.   } catch (e) {
  9.     try {
  10.       xhr = new ActiveXObject('Microsoft.XMLHTTP');
  11.     } catch (e) {}
  12.   }
  13. }
  14. if (xhr) {
  15.   xhr.open('GET', '/API?username=admin&password=root', true);
  16.   xhr.send(null);
  17. }
  18. // POST
  19. var xhr;
  20. if (window.XMLHttpRequest) { // Mozilla, Safari...
  21.   xhr = new XMLHttpRequest();
  22. } else if (window.ActiveXObject) { // IE
  23.   try {
  24.     xhr = new ActiveXObject('Msxml2.XMLHTTP');
  25.   } catch (e) {
  26.     try {
  27.       xhr = new ActiveXObject('Microsoft.XMLHTTP');
  28.     } catch (e) {}
  29.   }
  30. }
  31. if (xhr) {
  32.   xhr.open('POST', '/api', true);
  33.   // 设置 Content-Type 为 application/x-www-form-urlencoded
  34.   // 以表单的形式传递数据
  35.   xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  36.   xhr.send('username=admin&password=root');
  37. }
复制代码

  open() 方法有三个参数:

  open() 的第一个参数是 HTTP 请求方式 – GET,POST,HEAD 或任何服务器所支持的您想调用的方式。按照HTTP规范,该参数要大写;否则,某些浏览器(如Firefox)可能无法处理请求。有关HTTP请求方法的详细信息可参考 https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html

  第二个参数是请求页面的 URL。由于同源策略(Same origin policy)该页面不能为第三方域名的页面。同时一定要保证在所有的页面中都使用准确的域名,否则调用 open() 会得到 permission denied 的错误提示。

  第三个参数设置请求是否为异步模式。如果是 TRUE ,JavaScript 函数将继续执行,而不等待服务器响应。这就是 AJAX 中的 A。

  如果第一个参数是 GET ,则可以直接将参数放在 url 后面,如: http://nodejh.com/api?name=admint&password=root

  如果第一个参数是 POST ,则需要将参数写在 send() 方法里面。send() 方法的参数可以是任何想送给服务器的数据。这时数据要以字符串的形式送给服务器,如: name=admint&password=root 。或者也可以传递 JSON 格式的数据:
  1. // 设置 Content-Type 为 application/json
  2. xhr.setRequestHeader('Content-Type', 'application/json');
  3. // 传递 JSON 字符串
  4. xhr.send(JSON.stringify({ username:'admin', password:'root' }));
复制代码

  如果不设置请求头,原生 AJAX 会默认使用 Content-Type 是 text/plain;charset=UTF-8 的方式发送数据。

  3. 处理服务器的响应

  当发送请求时,我们需要指定如何处理服务器的响应,我们需要用到 onreadystatechange 属性来检测服务器的响应状态。使用 onreadystatechange 有两种方式,一是直接 onreadystatechange 属性指定一个可调用的函数名,二是使用一个匿名函数:
  1. // 方法一 指定可调用的函数
  2. xhr.onreadystatechange = onReadyStateChange;
  3. function onReadyStateChange() {
  4.   // do something
  5. }

  6. // 方法二 使用匿名函数
  7. xhr.onreadystatechange = function(){
  8.     // do the thing
  9. };
复制代码

  接下来我们需要在内部利用 readyState 属性来获取当前的状态,当 readyState 的值为 4,就意味着一个完整的服务器响应已经收到了,接下来就可以处理该响应:
  1. // readyState的取值如下
  2. // 0 (未初始化)
  3. // 1 (正在装载)
  4. // 2 (装载完毕)
  5. // 3 (交互中)
  6. // 4 (完成)
  7. if (xhr.readyState === 4) {
  8.     // everything is good, the response is received
  9. } else {
  10.     // still not ready
  11. }
复制代码

  完整代码如下:
  1. // POST
  2. var xhr;
  3. if (window.XMLHttpRequest) { // Mozilla, Safari...
  4.   xhr = new XMLHttpRequest();
  5. } else if (window.ActiveXObject) { // IE
  6.   try {
  7.     xhr = new ActiveXObject('Msxml2.XMLHTTP');
  8.   } catch (e) {
  9.     try {
  10.       xhr = new ActiveXObject('Microsoft.XMLHTTP');
  11.     } catch (e) {}
  12.   }
  13. }
  14. if (xhr) {
  15.   xhr.onreadystatechange = onReadyStateChange;
  16.   xhr.open('POST', '/api', true);
  17.   // 设置 Content-Type 为 application/x-www-form-urlencoded
  18.   // 以表单的形式传递数据
  19.   xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  20.   xhr.send('username=admin&password=root');
  21. }


  22. // onreadystatechange 方法
  23. function onReadyStateChange() {
  24.   // 该函数会被调用四次
  25.   console.log(xhr.readyState);
  26.   if (xhr.readyState === 4) {
  27.     // everything is good, the response is received
  28.     if (xhr.status === 200) {
  29.       console.log(xhr.responseText);
  30.     } else {
  31.       console.log('There was a problem with the request.');
  32.     }
  33.   } else {
  34.     // still not ready
  35.     console.log('still not ready...');
  36.   }
  37. }
复制代码

  当然我们可以用onload来代替onreadystatechange等于4的情况,因为onload只在状态为4的时候才被调用,代码如下:
  1. xhr.onload = function () {    // 调用onload
  2.     if (xhr.status === 200) {    // status为200表示请求成功
  3.         console.log('执行成功');
  4.     } else {
  5.         console.log('执行出错');
  6.     }
  7. }
复制代码

  然而需要注意的是,IE对 onload 属性的支持并不友好。除了 onload 还有以下几个属性也可以用来监测响应状态:

  • onloadstart
  • onprogress
  • onabort
  • ontimeout
  • onerror
  • onloadend

  二、 jQuery 实现 AJAX

  jQuery 作为一个使用人数最多的库,其 AJAX 很好的封装了原生 AJAX 的代码,在兼容性和易用性方面都做了很大的提高,让 AJAX 的调用变得非常简单。下面便是一段简单的 jQuery 的 AJAX 代码:
  1. $.ajax({
  2.   method: 'POST',
  3.   url: '/api',
  4.   data: { username: 'admin', password: 'root' }
  5. })
  6.   .done(function(msg) {
  7.     alert( 'Data Saved: ' + msg );
  8.   });
复制代码

  对比原生 AJAX 的实现,使用 jQuery 就异常简单了。当然我们平时用的最多的,是下面两种更简单的方式:
  1. // GET
  2. $.get('/api', function(res) {
  3.   // do something
  4. });

  5. // POST
  6. var data = {
  7.   username: 'admin',
  8.   password: 'root'
  9. };
  10. $.post('/api', data, function(res) {
  11.   // do something
  12. });
复制代码

  三、Fetch API

  使用 jQuery 虽然可以大大简化 XMLHttpRequest 的使用,但 XMLHttpRequest 本质上但并不是一个设计优良的 API:

  不符合关注分离(Separation of Concerns)的原则

  配置和调用方式非常混乱

  使用事件机制来跟踪状态变化

  基于事件的异步模型没有现代的 Promise,generator/yield,async/await 友好

  Fetch API 旨在修正上述缺陷,它提供了与 HTTP 语义相同的 JS 语法,简单来说,它引入了 fetch() 这个实用的方法来获取网络资源。

  Fetch 的浏览器兼容图如下:

  原生支持率并不高,幸运的是,引入下面这些 polyfill 后可以完美支持 IE8+:

  1. 一个使用 Fetch 的例子

  先看一个简单的 Fetch API 的例子 :chestnut: :
  1. fetch('/api').then(function(response) {
  2.   return response.json();
  3. }).then(function(data) {
  4.   console.log(data);
  5. }).catch(function(error) {
  6.   console.log('Oops, error: ', error);
  7. });
复制代码

  使用 ES6 的箭头函数后:
  1. fetch('/api').then(response => response.json())
  2.   .then(data => console.log(data))
  3.   .catch(error => console.log('Oops, error: ', error))
复制代码

  可以看出使用Fetch后我们的代码更加简洁和语义化,链式调用的方式也使其更加流畅和清晰。但这种基于 Promise 的写法还是有 Callback 的影子,我们还可以用 async/await 来做最终优化:
  1. async function() {
  2.   try {
  3.     let response = await fetch(url);
  4.     let data = response.json();
  5.     console.log(data);
  6.   } catch (error) {
  7.     console.log('Oops, error: ', error);
  8.   }
  9. }
复制代码

  使用 await 后,写代码就更跟同步代码一样。 await 后面可以跟 Promise 对象,表示等待 Promise resolve() 才会继续向下执行,如果 Promise 被 reject() 或抛出异常则会被外面的 try...catch 捕获。

  Promise,generator/yield,await/async 都是现在和未来 JS 解决异步的标准做法,可以完美搭配使用。这也是使用标准 Promise 一大好处。

  2. 使用 Fetch 的注意事项

  Fetch 请求默认是不带 cookie,需要设置 fetch(url, {credentials: 'include'}) `

  服务器返回 400,500 错误码时并不会 reject,只有网络错误这些导致请求不能完成时,fetch 才会被 reject

  接下来将上面基于 XMLHttpRequest 的 AJAX 用 Fetch 改写:
  1. var options = {
  2.     method: 'POST',
  3.     headers: {
  4.       'Accept': 'application/json',
  5.       'Content-Type': 'application/json'
  6.     },
  7.     body: JSON.stringify({ username: 'admin', password: 'root' }),
  8.     credentials: 'include'
  9.   };

  10. fetch('/api', options).then(response => response.json())
  11.   .then(data => console.log(data))
  12.   .catch(error => console.log('Oops, error: ', error))
复制代码

  Github Issue: https://github.com/nodejh/nodejh.github.io/issues/15

相关帖子

发表于 2016-12-31 16:00:32 | 显示全部楼层
总觉得哪里有点问题啊
使用道具 举报

回复

发表于 2017-1-1 21:30:56 | 显示全部楼层
感觉JavaScript很有前途
使用道具 举报

回复

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关于我们
联系我们
  • 电话:010-86393388
  • 邮件:udn@yonyou.com
  • 地址:北京市海淀区北清路68号
移动客户端下载
关注我们
  • 微信公众号:yonyouudn
  • 扫描右侧二维码关注我们
  • 专注企业互联网的技术社区
版权所有:用友网络科技股份有限公司82041 京ICP备05007539号-11 京公网网备安1101080209224 Powered by Discuz!
快速回复 返回列表 返回顶部