前段时间学习了《锋利的 jQuery 第六章》jQuery 与 Ajax 应用,现做如下笔记:

什么是 Ajax 呢?

我们先来了解一下,什么是 Ajax 呢?Ajax 全称为 "Asynchronous JavaScript and XML"(异步 JavaScript 和 XML),它并不是指一种单一的技术,而是有机地利用了一系列交互式网页应用相关的技术所形成的结合体。简短地说,在不重载整个页面的情况下,Ajax 通过后台加载数据,并在网页上进行显示。

我们来简要回顾一下 Ajax。早在 1998 年,微软就引入了一个 ActiveX 控件,从而能够在脚本控制下执行异步请求。在 2005 年,Adaptive Path 的 Jesse James Garrett 创造了术语 Ajax(Asynchronous JavaScript and XML,异步的 JavaScript 与 XML)。

Ajax 请求的生命周期,是从客户端向服务器端发送请求,然后再从服务器端返回。在向服务器发出请求之前,需要执行如下的设置步骤:

  1. 指定 HTTP 方法,比如(POST 或者 GET);
  2. 提供要接触的服务器端资源的 URL;
  3. 告诉 XHR 实例如何通报进度;
  4. 为 POST 请求提供任何主体内容。

6.1 Ajax 的优势和不足

它的优势有:

  1. 不需要插件支持:Ajax 不需要任何浏览器插件,就可以被绝大多数主流浏览器所支持,用户只需要允许 JS 在浏览器上执行即可。
  2. 优秀的用户体验:这也是 Ajax 技术的最大优点,能在不刷新整个页面的前提下更新数据,使得 Web 应用程序能更为迅速地回应用户的操作。
  3. 提高 Web 程序的性能:Ajax 模式和传统模式相比在性能上的最大区别就在于传输数据的方式,在传统模式中,数据提交是通过表单(Form)来实现的,而数据获取是靠全页面刷新来重新获取整页的内容。即 Ajax 是以异步方式提交数据给一个服务器,而不再采用旧式的”提交——处理——重新加载页面”(post-process-reload)技术,Ajax 模式只是通过 XMLHttpRequest 对象来向服务器端提交希望提交的数据,即按需发送。
  4. 减轻服务器和带宽的负担:Ajax 的工作原理相当于在用户和服务器之间加了一个中间层,使用户操作与服务器响应异步化。它在客户端创建 Ajax 引擎,把传统方式下的一些服务器负担的工作转移到客户端,便于客户端资源来处理,减轻服务器和带宽的负担。

Ajax 主要有以下几点不足之处

  1. 浏览器对 XMLHttpRequest 对象的支持度不足。Ajax 的不足之一首先来自于浏览器。
  2. 破坏浏览器”前进”、”后退”按钮的正常功能。在 Ajax 中”前进”和”后退”按钮的功能都会失效,虽然可以通过一定的方法(添加锚点)来使得用户可以使用”前进”和”后退”按钮,但相对于传统的方式却麻烦了很多。
  3. 对搜索引擎的支持不足。
  4. 开发和调试工具的缺乏。
  5. 另外,像其他方面的一些问题,比如说违背了 URL 和资源定位的初衷。例如,我给你一个 URL 地址,如果采用了 Ajax 技术,也许你在该 URL 地址下面看到的和我在这个 URL 地址下看到的内容是不同的。这个和资源定位的初衷是相背离的。

6.2 Ajax 的 XMLHttpRequest 对象

Ajax 的核心是 XMLHttpRequest 对象,它是 Ajax 实现的关键——发送异步请求,接收响应及执行回调都是通过它来完成的。

6.3 安装 Web 环境——AppServ

文中安装的是 AppServ,我自己电脑上用的是 nginx。

6.4 编写第一个 Ajax 例子

主要讲解了一个用传统 JavaScript 实现的 Ajax 例子。

6.5 jQuery 中的 Ajax

jQuery 中,jQuery 对 Ajax 操作进行了封装,提供了多个与 Ajax 有关的方法。通过这些方法,能够使用 HTTP Get 和 HTTP Post 从远程服务器上请求文本、HTML、XML 或 JSON,同时我们能把这些外部数据直接载入网页的被选元素中。

在 jQuery 中 $.ajax() 方法属于最底层的方法,第 2 层是 load()$.get()$.post() 方法,第三层是 $.getScript()$.getJSON() 方法。

6.5.1 $.load() 方法

结构:load(url[, data][, callback])

下面是一个 load() 方法的例子的 jQuery 代码:

$(function(){
    $("#btn1").click(function(){
        $("#test").load("here.html");
    })
})

注意load() 方法中,无论 Ajax 请求是否成功,只要当请求完成(complete)后,回调函数(callback)就被触发。

6.5.2 $.get() 方法

$.get() 方法通过 HTTP GET 方法来进行异步请求。

结构:$.get(url[, data][, callback][, type])

下面的例子使用 $.get() 方法从服务器上的一个文件中取回数据:

$(function(){
    $.get('test.txt', function(data, status) {
        alert("数据:" + data + "\n状态:" + status);
    });
    // $.get() 的第一个参数是我们希望请求的 URL。
    // 第二个参数是回调函数。第一个回调参数存有被请求页面的内容,第二个回调参数存有请求的状态。
    // 而且回调函数只有在数据成功返回(success)后才被调用,这点与 load() 方法不一样。
})

6.5.3 $.post() 方法

它与 $.get() 方法的结构和使用方式都相同,不过它们之间仍然有以下区别:

$.post() 方法通过 HTTP POST 请求从服务器上请求数据。

语法:

$.post(URL, data, callback);

由于 POST 和 GET 方式提交的所有数据都可以通过 $_REQUEST[] 来获取,因此只需要改变 jQuery 函数,就可以将程序在 GET 请求和 POST 请求之间切换。

另外,当 load() 方法带有参数传递时,会使用 POST 方式发送请求。因此也可以使用 load() 方法来完成同样的功能。

但在此期间报错了,后来找到原因:Apache、IIS、Nginx 等绝大多数 Web 服务器,都不允许静态文件响应 POST 请求。网上找到的资料:服务器不允许静态文件响应 POST 请求的解决方法,貌似需要修改 nginx 的相关配置。

6.5.4 $.getScript() 与 $.getJSON() 方法

1. $.getScript() 方法

有时候,在页面初次加载时就取得所需的全部 JavaScript 文件是完全没有必要的。虽然可以在需要哪个 JavaScript 文件时,动态地创建 <script> 标签,如:

$(document.createElement("script")).attr("src", "test.js").appendTo("head");

或者:

$("<script type='text/javascript' src='test.js'/>").appendTo("head");

但这种方式并不理想。为此,jQuery 提供了 $.getScript() 方法来直接加载 .js 文件,与加载一个 HTML 片段一样简单方便,并且不需要对 JavaScript 文件进行处理,JavaScript 文件会自动执行。jQuery 代码如下:

$(function(){
    $("#send").click(function(){
        $.getScript('test.js');
    })
})

这个方法也有回调函数。

2. $.getJSON() 方法

$.getJSON() 方法用于加载 JSON 文件,与 $.getScript() 方法的用法相同:

$(function(){
    $('#send').click(function(event) {
        $.getJSON('test.json', function(data){
            // data:返回的数据
        })
    });
})

6.5.5 $.ajax() 方法

$.ajax() 方法是最底层的 Ajax 实现。

结构为:$.ajax(options)

该方法只有一个参数,但在这个对象里包含了 $.ajax() 方法所需要的请求设置以及回调函数等信息,参数以 key/value 的形式存在,所有参数都是可选的。

前面用到的 $.load()$.get()$.post()$.getScript()$.getJSON() 这些方法,都是基于 $.ajax() 方法构建的。$.ajax() 方法是 jQuery 最底层的 Ajax 实现,因此可以用它来代替前面的所有方法。

下面是一个简单的 Ajax 请求的例子:

$(function(){
    $.ajax({
        url: "here.html",
        dataType: "html",
        success: function(r){
            console.log("make it");
        },
        error: function(r){
            console.log("something didn't work");
        }
    })
})

如果想使某个 Ajax 请求不受全局方法的影响,那么可以在使用 $.ajax(options) 方法时,将参数中的 global 设置为 false

设置请求默认值

当我们计划发起大量请求时,如果能为页面上的这些选项设置默认值会很方便。jQuery 就为我们提供了一种方式来定义一组默认的 Ajax 属性值,如果没有覆盖这些属性的值,则会使用这些默认值。如果要发起很多相似的 Ajax 请求,这可以简化页面。

$.ajaxSetup(options)

把传去的一组选项属性创建为随后调用 $.ajax() 的默认值。参数 options:(对象)对象实例,其属性定义了一组默认的 Ajax 选项。

注意:通过这个函数设置的默认值不会应用到 load() 方法。对于诸如 $.get()$.post() 的实用函数,这些默认值也不会覆盖 HTTP 方法。例如,设置默认 type 为 GET 不会导致 $.post() 使用 HTTP 的 GET 方法。

假设要创建一个涵盖大多数 Ajax 请求的页面(使用实用函数来创建,而不是 load() 方法),我们希望设置一些默认值,以免每次调用时都指定它们。我们可以在页面头部的 <script> 元素的第一行编写这样的代码:

$.ajaxSetup({
    type: 'POST',
    timeout: 5000,
    dataType: 'html'
});

这将会确保随后的每个 Ajax 调用(除了前面提到的 load() 方法)都会使用这些默认值,除非使用传入 Ajax 使用函数的属性来显式地覆盖它们。

6.6 jQuery 中的 Ajax 全局事件

下面来看一下 jQuery 中的 Ajax 全局事件。jQuery 简化 Ajax 操作不仅体现在调用 Ajax 方法和处理响应方面,而且还体现在对调用 Ajax 方法的过程中的 HTTP 请求的控制。通过 jQuery 提供的一些自定义全局函数,能够为各种与 Ajax 相关的事件注册回调函数。例如当 Ajax 请求开始时,会触发 ajaxStart() 方法的回调函数;当 Ajax 请求结束时,会触发 ajaxStop() 方法的回调函数。这些方法都是全局的方法,因此无论创建它们的代码位于何处,只要有 Ajax 请求发生,就会触发它们。

$(function(){
    $("#loading").ajaxStart(function(){
        $(this).show();
    });
    $("#loading").ajaxStop(function(){
        $(this).hide();
    });
    $.get('test.txt', function(data, status) {
        alert("数据:" + data + "\n状态:" + status);
        // console.log(123);
    });
})

除了 ajaxStart()ajaxStop(),还有另外几个方法。

期间关于 ajaxSend() 遇到些问题,记录下来:

$(document).ready(function(){
    $("#div1").ajaxSend(function(){
        alert(123);
    });
    $("button").click(function(){
        $("#div1").load("here.html");
    });
});
// 上面定义 $("#div1").ajaxSend(function(){}); 事件不在任何一个 onClick 事件中。
// 如果发生了全局事件的重复定义,在本例中也就是每一个 onClick 事件中都定义一次全局事件
// $("#div1").ajaxSend(function(){}); 的话,那么该全局事件会增加执行次数,而不是覆盖前面的定义。

是在这里找到的相关资料:jQuery ajaxSend 全局事件相关问题

jQuery 中各个事件执行顺序如下:

  1. ajaxStart(全局事件)
  2. beforeSend
  3. ajaxSend(全局事件)
  4. success
  5. ajaxSuccess(全局事件)
  6. error
  7. ajaxError(全局事件)
  8. complete
  9. ajaxComplete(全局事件)
  10. ajaxStop(全局事件)

序列化问题

项目过程中,经常会有传输数据的时候,在传输数据的过程中,发送方需要把这个对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为对象。而序列化(Serialization)就是将对象的状态信息转换为可以存储或传输的形式的过程。

jQuery 中提供了 serialize() 方法,作用于一个 jQuery 对象,将 DOM 元素内容序列化为字符串,用于 Ajax 请求。

格式:var data = $("form").serialize();

功能:将表单内容序列化成一个字符串。

这样在 Ajax 提交表单数据时,就不用一一列举出每一个参数。只需将 data 参数设置为 $("form").serialize() 即可。

$(document).ready(function(){
    $("button").click(function(){
        $("div").text($("form").serialize());
    });
});

因为 serialize() 方法作用于 jQuery 对象,所以不光只有表单能使用它,其他选择器选取的元素也都能使用它,如:

$(":checkbox,:radio").serialize();

serializeArray() 方法

格式:var jsonData = $("form").serializeArray();

功能:将页面表单序列化成一个 JSON 结构的对象。注意该方法不是返回字符串,而是将 DOM 元素序列化后,返回 JSON 格式的数据。也因为是一个对象,所以可以使用 $.each() 函数对数据进行迭代输出。

比如,[{"name":"lihui", "age":"20"},{...}] 获取数据为 jsonData[0].name

$(function(){
    $("#send").click(function(){
        var $data = $(":checkbox,:radio").serializeArray();
        alert($data);
    })
})

$.param() 方法

它是 serialize() 方法的核心,用来对一个数组或对象按照 key/value 进行序列化。

$(function(){
    var obj = {a:1, b:2, c:3};
    var k = $.param(obj);
    alert(k);
}) // 输出 a=1&b=2&c=3

避免缓冲

对于 AJAX 请求,GET 请求中的数据缓冲存在根本性的问题。遗憾的是,即使有必要,服务器也很少在 AJAX 请求中重新请求数据。浏览器简单地从本地缓存中读取数据(要知道,浏览器在历史上就从不是为异步数据请求设计的)。这个问题首先影响到 Internet Explorer。在手工编程中,在查询字符串后附加由 JavaScript 生成的随机数或者时间戳(不管在服务器还是在客户端,这个数值基本上都没有进一步的用途)是一个好的变通方法。这种方法只是为了告诉浏览器必须重新加载数据。事实上,这种技巧是目前避免缓冲行为(在传统的 Web 数据请求中确实很有用的一种行为)的唯一简单而可靠的方法。明确提供这类机制的框架(例如 YUI)在后台所做的也就是这些。在 jQuery 中,目前必须为大部分方法手工创建一个额外的参数,并将这个参数附加到 URL 中。只有 ajax() 方法通过特殊参数提供了对应的避免缓冲选项。

$.ajax() 的扩展技术

在 jQuery 1.5 中,引入了 $.ajax() 的 3 个扩展功能:

前置过滤器

前置过滤器(prefilter)指的是任何 AJAX 请求被发送之前和 $.ajax() 处理任何选项之前执行的回调函数。

$.ajaxPrefilter(
    function(requestOptions, originalOptions, jqXHR){
        // Modify options, control the original options, store jqXHR object etc
    });

转换器

转换器指的也是新型的回调函数。转换器在服务器以预期之外的数据类型发送响应数据时调用。可以在回调函数中采取相应的措施,转换数据类型或者引入自己的数据类型。转换器保存在 ajaxSetting 中,可以全局添加。

({
    converters: {
        "text mydatatype": function(textValue){
            if (valid(textValue)) {
                if (valid(textValue)) {
                    //some logic
                    return mydatatypeValue;
                }
                else{
                    //indicate parse-error
                    throw exceptionObject;
                }
            };
        }
    }
})

可以用转换器创建自定义数据类型(数据类型必须采用小写字母)。如果观察前一个程序清单中的典型转换器,就可以请求类型为 mydatatype 的数据,如:

$.ajax(url, {
    dataType: "mydatatype"
});

分发器

jQuery 中的分发器(transport)是提供以下两个方法的一个对象:send()abort()。上述两个方法都由 $.ajax() 在内部使用,可以用于扩展 $.ajax()。但是,jQuery 文档还指出,分发器应该只作为最后手段,在前置过滤器和转换器都不足以影响 AJAX 请求时使用。因为每个请求需要自己的分发器对象实例,这些对象不能被直接注册。作为替代,应该提供一个函数来返回这样的对象。这种生成对象的函数被称为工厂(Factory)。分发器工厂的注册如下:

$.ajaxTransport(function(requestOptions, originalOptions, jqXHR){
    if (/*conditions for transport*/) {
        return {
            send: function(header fields as map, callback){
                /*code for sending*/
            },
            abort: function(){
                /*code in case of abort*/
            }
        }
    };
})

本次笔记以《锋利的 jQuery》(第二版)为主,参考资料有《jQuery 实战》(第二版)、《jQuery 应用开发实践指南》。有任何建议,还望大家不吝赐教[抱拳]。