前端开发 / jQuery Ajax

jQuery Ajax

前言

相信很多有一定前端经验的同学肯定听说过 jQuery,并且稍有研究的人肯定也知道 jQuery 提供了 Ajax 系列的工具方法。

事实上,市面上有各种各样的 Ajax 类库可以供我们使用,它们更加健壮、更加成熟、并且设计也更为合理。在我们在实际开发的过程中,我们并不直接使用 XMLHttpRequest,而是会使用这些封装好的经得起考验的类库,来为我们的程序提供服务,让我们的开发者更加专注在本身的业务上。

那么,本章节挑选了曾经盛极一时的 jQuery 的工具方法 Ajax 来做展开介绍。

首先,我先引用 jQuery 的设计宗旨:

Write less, do more.

稍有研究的同学都知道 jQuery 非常好用,这些都得益于 jQuery 优秀的设计思想。它不但能够提供便捷的元素选择及操作方式,还提供了一系列的工具方法、链式操作等等,使得我们在开发过程中变得足够简易。Ajax 同样继承了 jQuery 的优秀设计思想,提供了更为人性化的接口和配置。接下来我们来聊聊 jQuery Ajax :

1.引入 Jquery

因为我们使用的 Ajax 方法是 jQuery 提供的,因此我们需要在页面中引入 jQuery 脚本。

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js"></script>

Tips: 注意 jQuery 脚本要放在使用到 jQuery 的脚本之前,这样才可以在我们的页面中愉快的玩耍~

2.简单的 load

load(url, [data], [callback])

load 是 jQuery 一个非常便捷的 Ajax 交互方法,从接口文档上看,load 方法接受三个参数,分别是 url,发送的参数以及响应结果的回调函数。实际上,后两个参数我们也常常可以忽略。

我们先来看一个简单的例子:

假设我们要在页面中插入一个从后端请求回来的数据,那么我们会怎么做?

有同学非常聪明,可能会说:我先启用 ajax ,发送 GET 请求,在成功的时候回调函数里面再操作页面的元素对象,为 innerHTML 赋值结果。

是的,基本流程就是查询之后再操作元素添加数据即可。但是,jQuery 的 load 可能更加简单哦。不信请看:

2.1 后端返回数据

router.get("/jquery_ajax/load", function(req, res) {
    res.send('<p>返回的内容</p>');
});

2.2 前端关键代码

<div id="container"></div>
$('#container').load('/jquery_ajax/load')

2.3 看看效果

图片描述

怎么样,只需要一行,我们就可以往目标元素中添加数据了。这确实是非常的方便!

3.$.ajax

事实上, load 属于基于 $.ajax 再次封装的一个简易的 API 。有兴趣的同学可以去扣一下源码来看看。大概实现就是这样:

jQuery.fn.load = function( url, params, callback ) {
    var selector, type, response,
        self = this,
        off = url.indexOf( " " );

   // xxx

    // If we have elements to modify, make the request
    if ( self.length > 0 ) {
        jQuery.ajax( {
            // xxx
        } ).done( function( responseText ) {
            // xxx
        } ).always( callback && function( jqXHR, status ) {
            // xxx
        } );
    }
    return this;
};

这里我隐去了一些具体实现,目的只是为了说明 load 内部的基本实现是怎么样的即可。对具体实现有兴趣的同学可以自行读一下源代码。

事实上,load 的定制化相对较高,必定无法满足更为灵活的需求。因此,我们多数时候会使用 jQuery 封装好的更为底层的 API —— $.ajax

3.1 API 介绍

jQuery.ajax(url,[settings])

如上所示,$.ajax 接受两个参数,分别为 url 和 配置对象。其中:

  • url : 一个用来包含发送请求的URL字符串。
  • [settings] : 一个以 { key : value } 组成的 Ajax 请求设置对象。所有选项都是可选的。

在配置对象中,我们通常会传入我们希望指定的配置项,来指定我们的 Ajax 的行为和属性。当然有些参数我们并不一定要去指定,因为有的配置项本身会有默认配置,只要该默认配置符合你的使用需求即可。有兴趣的同学也可以到 jQuery Ajax 详细查看每个配置项的默认值以及含义。

3.2 常用配置项简介

常用的配置项我们简单带过,比如:

  • data:代表着发送到服务器的数据。 在不同的情况下会转化字符串的格式,在 GET 方法的时候,会变为 “&” 拼接的参数附带在 url 后面。
  • dataType:预期服务器返回的数据类型。 如果没有指定的话,Ajax 会根据 HTTP 的 MIME 信息来进行判断。
  • cache:缓存控制相关。 默认是 true,如果设置为 false,那么浏览器不缓存此页面。
  • headers:请求头。 我们通常会给一个 { key : value} 这样的键值对对象来设置我们的请求头内容。
  • type:请求方法。默认是 GET 。

3.3 阶段处理

$.ajax 提供了非常人性化的函数配置项,分别有:beforeSend、error、dataFilter、success、complete。我把它们分别归为请求前和请求后两个阶段。

3.3.1 请求前

对于一个 Ajax 的请求来说,我们在正式发送请求之前可能会对我们的 Ajax 做一些提前的配置或者预先要进行的操作。那么,我们可以非常愉快的使用 beforeSend 。

3.3.1.1 beforeSend

beforeSend(xhr)

beforeSend 指定的是发送请求之前要先执行的方法。 并且,jQuery 会在 beforeSend 中传入当前的 XMLHttpRequest 对象,方便我们在该函数中做预先的操作。打个比方,我们可以通过这个函数,给当前的 Ajax 请求添加自定义的 headers 项。

3.3.2 请求后

当我们成功的发送了请求,那么我们接下来就是要解决返回结果的问题了。

3.3.2.1 dataFilter 和 success

在服务器成功响应之后,我们需要对结果进行获取和处理,这个时候 dataFilter 和 success 就派上用场了。其中:

dataFilter :

dataFilter (Object data, String type)

dataFilter 接受一个服务端返回的原始数据和一个 dataType 值作为参数,会在请求成功之后调用,目的是为了我们可以在请求成功之后对数据进行进一步的处理、过滤。 对比另外一个函数 —— success。

success

success(( Object data, String textStatus, jqXHR jqXHR ))

success 是成功响应后执行的函数,优先级在 dataFilter 之后。 它接受三个参数,分别是:数据对象、状态描述字符串以及一个 XHR 对象。我们通常会在这个函数里面进行对结果的操作。

3.3.2.2 错误处理

当然也会有请求失败的情况,那么我们该如何来处理呢?

$.ajax 同样提供了人性化的一个回调函数 error 。

error(jqXHR jqXHR, String textStatus, String errorThrown)

error 接受三个参数,分别为 一个 XHR 对象、一个状态描述字符串以及一个捕获异常的字符串。假如我在程序中定义了这样一个 $.ajax :

$.ajax({
    url: '/jquery_ajax/get/2',
    method: 'GET',
    error (xhr, status, err) {
        console.log(xhr, status, err)
    }
})

其中,我们的 url 指定的是一个不存在的接口,那么我们可以在控制台看到这样的打印信息:

图片描述

显而易见,我们可以很轻易在这个函数中获取到请求的错误信息。事实上,我们常常会在 error 中对异常做进一步的操作。举个栗子,在我们开发的应用中,我们不可能把请求失败的这样一个结果自己在程序中抹干净不上报给用户知道,如果我们内部自己消化了,用户不知道自己到底成不成功,这样的体验就非常糟糕了!这个时候,我们或许会选择弹一个框,告知我们失败的结果以及下一步的操作。

3.3.2.3 最后处理

好了,来继续思考这样一个场景:假如我们发起一个 $.ajax(),为了让用户感知,我们一般会弹起一个 loading 的界面,让用户稍微等待一下。那问题来了,我们的 loading 界面到底什么时候应该关闭呢?是在请求成功之后关闭吗?那如果请求发生错误怎么办?总不能错误了就不关闭 loading 了吧?那或者在成功和失败的回调中都执行关闭 loading 的方法,这貌似是一个不错的做法,但是这样的做法我们要在两个地方都进行改动,未免太过麻烦,并且后期代码也不好维护呢。

别忘了,我们还有一个 complete

complete(jqXHR jqXHR, String textStatus)

complete 会在请求完成之后执行,无论请求成功或者失败。 我们可以很好地将一个请求的善后工作交给它来进行。

4.$.ajax 和 deferred

在 jQuery 中,通常都会有一些耗时比较长的操作,我们是无法马上得到结果的。对于 $.ajax 而言,我们的请求往往都要依靠网络传输和服务端的处理。这时我们的一贯处理方式就是给定某个回调函数,当耗时操作结束返回数据以后,及时调用这个回调函数来进行进一步的处理。

在 jQuery 1.5 版本之后就改变了这一点,deffered 在 jQuery 中诞生了!

字面来看,defer 代表的是“延迟”的意思,这也对应着我们的延时操作。使用 deffered ,我们将改变使用回调函数的做法,转而使用的是链式调用。这其实也正是拥抱了 jQuery 的设计思想,使用过 jQuery 的同学都知道,jQuery 对节点的操作往往都会封装为一个 jQuery 的对象进行统一管理,而 deffered 也正是如此,会对延时操作做一个统一的管理,并且提供统一的编程接口,比如 done 和 fail

举个栗子,我们使用 $.ajax 来进行数据的请求,可以变体为:

$.ajax({
    url: '/jquery_ajax/get',
    method: 'GET',
    data: {
        a: '123',
        b: '234'
    }
}).done(data => {
    console.log(data)
}).fail(err => {
    console.log(err)
})

事实上在 deffered 出来之后,$.ajax 也开始返回一个 deffered 对象。细心的同学可以看到,这不是一个 promise 么?是的,可以这样说,但是也不尽然,jQuery 根据自己的需要实现了这样一个东西,它支持 promise 的一些规范,同时也支持自己的特权方法。我们在使用 deffered 的时候,很直观的可以看到,我们从回调函数的传统方式中解脱了出来。除此之外,由于 Promise 的反控制反转,使得我们的程序变得更加健壮,我们对结果操作更有掌控权。

5.总结

  • 市场上有很多 Ajax 类库,它们更加成熟、设计更加合理,在实际工作中可以首选使用。
  • jQuery 提供了非常简单易用的 Ajax 工具方法,包括简单的 $.load 方法,还有更为灵活也更为底层的 $.ajax 方法。
  • deferred 能够很好的应用于一些耗时操作,并且提供统一的编程接口。
  • deffered 改变了以往 $.ajax 的处理方式,使得我们可以通过链式调用的方式来进行结果的操作,既改变了回调函数的老式做法,也让我们对结果操作更有掌控权。