AJAX 基础

209 阅读2分钟

AJAX概述

AJAX (Asynchronous JavaScript and XML),最早出现在2005年的Google Suggest。它不是像HTML、JavaScript或CSS这样的一种“正式的”技术,它是在浏览器端进行网络编程(发送请求、接收响应)的技术方案。它使我们可以通过JavaScript直接获取服务端最新的内容而不必重新加载页面,让Web更能接近桌面应用的用户体验。
AJAX就是浏览器提供的一套API,可以通过JavaScript调用,从而实现通过代码控制请求与响应。实现通过JavaScript进行网络编程。
XML:最早在客户端与服务端之间传递数据时所采用的数据格式。

应用场景

  • 按需获取数据
  • 对用户数据校验
  • 自动更新页面内容
  • 提升用户体验,无刷新的体验。只需要页面局部刷新就可实现数据的更新。

AJAX 体验

  • 使用JQuery中封装的Ajax,快速体验Ajax带来的效果。 先在html代码中进行JQuery引用,没下载的可参考jQuery基础进行下载引用:
<script src="js/jquery-1.12.4.min.js"></script>

再使用JQuery进行Ajax调用:

<script>
  $.ajax({
    url: 'https://jsonplaceholder.typicode.com/todos/1',// 请求的url
    type: 'GET',// 请求数据方法
    dataType: 'json',// 返回数据类型
    success: function (data) {// 获取返回数据后的处理
      console.log(data);
    }
  })
</script>

即可在浏览器中,得到返回的数据:

  • 免费接口:jsonplaceholder.typicode.com/ 在该网站首页,往下滑动就可以找到一个官方示例: 我们刚刚使用JQuery中封装的Ajax的示例就是使用了这上面的接口,当然这个网站的接口还有很多,在首页继续往下滑动就可看到: 请求的url加上这些后缀,使用对应的请求方法即可获得数据。比如我们将JQuery示例中的url值改掉:
<script>
  $.ajax({
    url: 'https://jsonplaceholder.typicode.com/posts',// 请求的url
    type: 'GET',// 请求数据方法
    dataType: 'json',// 返回数据类型
    success: function (data) {// 获取返回数据后的处理
      console.log(data);
    }
  })
</script>

得到100条数据: 得到的数据太多,我们需要进行筛选。可以在请求的时候增加请求参数来实现:

$.ajax({
  url: 'https://jsonplaceholder.typicode.com/posts',// 请求的url
  data: {'id': 1},// 添加请求参数来实现筛选
  type: 'GET',// 请求数据方法
  dataType: 'json',// 返回数据类型
  success: function (data) {// 获取返回数据后的处理
    console.log(data);
  }
})

这样我们就得到id=1的这一条数据了

原生AJAX详解

工作中我们使用的是各种封装好的AJAX库,下面我们说下原生AJAX。

原生AJAX的请求步骤

发送AJAX请求的步骤分为四步:
1、创建XMLHttpRequest类型的对象
2、准备发送,打开与一个网址之间的连接
3、执行发送动作
4、指定xhr状态变化事件处理函数

<script>
  // 1.创建一个 XMLHttpRequest 类型的对象  --- 相当于打开了一个浏览器
  var xhr = new XMLHttpRequest();
  // 2.打开一个与网址之间的连接  --- 相当于在地址栏输入网址
  xhr.open("GET","https://jsonplaceholder.typicode.com/users");
  // 3.通过连接发送一次请求 --- 相当于点击回车或者超链接
  xhr.send(null);
  // 4.指定 xhr 状态变化事件处理函数   --- 相当于处理网页呈现后的操作
  xhr.onreadystatechange = function () {
    // 通过判断 xhr 的 readyState ,确定此次请求是否完成
    if (this.readyState === 4) {
      console.log(this.responseText)
    }
  }
</script>

xhr对象

AJAX API中核心提供的是一个XMLHttpRequest类型,所有的AJAX操作都需要使用到这个类型。

var xhr = new XMLHttpRequest();

IE6兼容写法

xhr = newActiveXObject("Microsoft.XMLHTTP");

open方法

本质上XMLHttpRequest就是JavaScript在Web平台中发送HTTP请求的手段,所以我 们发送出去的请求仍然是HTTP请求,同样符合HTTP约定的格式。
语法:xhr.open(method,url)
method:要使用的HTTP方法,比如「GET」(查)、「POST」(增)、「PUT」(改)、「DELETE」(删)等。
url:要向其发送请求的URL地址,字符串格式。

send方法和请求头

  • send方法:
    用于发送HTTP请求
    语法:xhr.send(body)
    body:在×HR请求中要发送的数据体,根据请求头中的类型进行传参。
    如果是GET方法,无需设置数据体,可以传null或者不传参。
  • setRequestHeader()请求头:
    此方法必须在open()方法和send()之间调用。
    语法:xhr.setRequestHeader(header,value);
    header:一般设置"Content-Type",传输数据类型,即服务器需要我们传送的数据类型
    value:具体的数据类型,常用"application/x-www-form-urlencoded"和"application/json"

响应状态分析

readyState属性
readyState属性返回一个XMLHttpRequest代理当前所处的状态,由于readystatechange事件是 在xhr对象状态变化时触发(不单是在得到响应时),也就意味着这个事件会被触发多次,所以我们 有必要了解每一个状态值代表的含义:

readyState状态描述说明
0UNSENT代理XHR被创建,但尚未调用open()方法
1OPENEDopen()方法已经被调用,建立了连接
2HEADERS_RECEIVEDsend()方法已经被调用,并且已经可以获取状态行和响应头
3LOADING响应体下载中,responseText属性可能已经包含部分数据
4DONE响应体下载完成,可以直接使用responseText

// 1.创建一个 XMLHttpRequest 类型的对象 
var xhr = null;
// 兼容写法
if (window.XMLHttpRequest) {
  // 标准浏览器
  xhr = new XMLHttpRequest();
} else {
  // IE 6 浏览器
  xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
// 2.open() 方法开启请求
// xhr.open("GET","https://jsonplaceholder.typicode.com/users?id=1");// 可以通过这种方法直接传参
xhr.open("POST","https://jsonplaceholder.typicode.com/users");
// 设置请求头
// 一般 get 方法不需要设置,而 post 方法必须设置
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
// 3.send() 方法发送一次请求
// 如果是 get 方法请求,不需要再 send 中传参数,它如果想传参数,直接写在网
// xhr.send(null);
xhr.send("name=harry&age=19");
// 4.指定回调函数,处理返回的数据    
xhr.onreadystatechange = function () {
  // 通过判断 xhr 的 readyState ,确定此次请求是否完成
  if (this.readyState === 4) {
    console.log(this.responseText)
  }
}

同步和异步

同步:一个人在同一个时刻只能做一件事情,在执行一些耗时的操作(不需要看管)不去 做别的事,只是等待
异步:在执行一些耗时的操作(不需要看管)去做别的事,而不是等待
xhr.open()方法第三个参数要求传入的是一个boolean值,其作用就是设置此次请求是否采用异步方式执行
默认为true异步,如果需要同步执行可以通过传递false实现。采用同步方式执行时,代码会卡死在xhr.send()这一步
建议
为了让这个事件可以更加可靠(一定触发),在发送请求send()之前,一定是先注册 readystatechange,不论是同步或异步都能触发成功。
了解同步模式即可,切记不要使用同步模式

数据格式

  • XML:一种数据描述手段,因为数据冗余太多,现已被淘汰。
  • JSON JavaScript Object Notation, JavaScript对象表示法,也是一种数据描述手段,类似于JavaScript字面量方式。
// js 对象字面量
var obj = {
  name: "tom",
  age: 19,
  cp: {
    name: "harry",
    age: 18
  }
};
// JSON 格式的数据,与 js 对象的区别
// 1.JSON 数据不需要存到变量中
// 2.结束时不需要写分号
// 3.JSON 数据中的属性名必须加引号
var str = '{"name": "tom","age": 80}';
// JSON 对象
// console.log(JSON)
console.log(obj);
console.log(JSON.stringify(obj));
// 使用 JSON 对象的 parse 方法可以将 json 格式的字符串转换成 对象格式,
// 具有了属性和方法,方便我们在js 中进行使用
console.log(JSON.parse(str));
var strObj = JSON.parse(str);
console.log(strObj.name)
console.log(strObj.age)

服务端采用JSON格式返回数据,客户端按照JSON格式解析数据。
注意
不管是JSON也好,还是XML,只是在AJAX请求过程中用到,并不代表它们与AJAX之间有必然的联系,它们只是数据协议罢了。
不管服务端是采用XML还是采用JSON本质上都是将数据返回给客户端。
服务端应该根据响应内容的格式设置一个合理的Content-Type。

json-server的使用

json-server的使用

原生Ajax应用-GET请求

通常在一次GET请求过程中,参数传递都是通过URL地址中的?参数传递。
一般在GET请求中,无需设置请求头
无需设置响应体,可以传null或者干脆不传

var xhr = new XMLHttpRequest();
// 发送 GET 请求
xhr.open("GET", "http://localhost:3000/users?age=19");
xhr.send(null);
xhr.onreadystatechange = function () {
  if (this.readyState === 4) {
    console.log(this.responseText);
  }
}

原生Ajax应用-POST请求

POST请求过程中,都是采用请求体承载需要提交的数据。

需要设置请求头中的Content-Type,以便于服务端接收数据
需要提交到服务端的数据可以通过send方法的参数传递

var xhr = new XMLHttpRequest();
// post 请求
xhr.open("POST","http://localhost:3000/users");
// 设置请求头
// xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
xhr.setRequestHeader("Content-Type","application/json");
// 发送请求,方法一
// xhr.send("name=lily&age=19&class=2");
// 发送请求,方法二
// xhr.send(`{
//   "name": "lulu",
//   "age": 18,
//   "class": 2
// }`);
// 发送请求,方法三
xhr.send(JSON.stringify({
  "name": "harry",
  "age": 18,
  "class": 1
}));
xhr.onreadystatechange = function () {
  if (this.readyState === 4) {
    console.log(this.responseText);
  }
}

处理响应数据渲染

客户端中拿到请求的数据过后最常见的就是把这些数据呈现到界面上。
如果数据结构简单,可以直接通过字符串操作(拼接)的方式处理。
但是如果数据过于复杂,字符串拼接维护成本太大,就不推荐了。

// 获取元素
var box = document.getElementById("box");
var xhr = new XMLHttpRequest();
// 发送 GET 请求
xhr.open("GET", "http://localhost:3000/users");
xhr.send(null);
xhr.onreadystatechange = function () {
  if (this.readyState === 4) {
    // 将获取的响应体的字符串转为对象
    var data = JSON.parse(this.responseText)
    console.log(data);
    var str = "";
    // 循环遍历数组
    for (var i = 0 ; i < data.length ;i++) {
      // 进行字符串拼接 
      // str += "<tr><td>" + data[i].id + "</td><td>" + data[i].name + "</td><td>" + data[i].age + "</td><td>" + data[i].class + "</td></tr>";
      // 使用 模板字符串 进行拼接
      str += `<tr>
        <td>${data[i].id}</td>
        <td>${data[i].name}</td>
        <td>${data[i].age}</td>
        <td>${data[i].class}</td>
      </tr>`
    }
    box.innerHTML += str;
  }
};

封装AJAX库

这只是一个例子,实际工作会使用各种Ajax库。

// 封装自己的 Ajax 函数
/**
 * 参数1:{string}    method  请求方法
 * 参数2:{string}    url     请求地址
 * 参数3:{Object}    params  请求参数
 * 参数4:{function}  done    请求完成后执行的回调函数
*/ 
function ajax(method, url, params, done) {
  // 统一将方法中的字母转大写,便于后面判断
  method = method.toUpperCase();
  // 书写 IE 6 的兼容
  var xhr = window.XMLHttpRequest 
  ? new XMLHttpRequest() 
  : new ActiveXObject("Microsoft.XMLHTTP");
  // 将对象格式的参数转为 urlencoded的格式
  var pairs = [];
  for (var k in params) {
    pairs.push(k + "=" + params[k]);
  }
  var str = pairs.join("&");
  // 判断是否是 get 方法,需要更改 url 的值
  if (method === "GET") {
    url += "?" + str;
  }
  // 创建打开一个连接
  xhr.open(method, url);
  var data = null;
  // 如果是 post 方法,需要设置请求头,还有请求体
  if (method === "POST") {
    xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
    data = str;
  }
  xhr.send(data);
  // 执行回调函数
  xhr.onreadystatechange = function () {
    if (this.readyState !== 4) return;
    // 执行外部传进来的回调函数即可
    // 需要用到响应体
    done(JSON.parse(this.responseText));
  }
}
// 调用函数
ajax("GET", "http://localhost:3000/users",{"id": 1},function (data) {
  console.log(data);
});
ajax("POST", "http://localhost:3000/users",{"name": "john","age": 19,"class": 2},function (data) {
  console.log(data);
});