Ajax基础和手写封装Ajax函数

5,897 阅读11分钟

ajax概述

背景

  • 在了解 AJAX 之前我们可以简单的认为「JavaScript 能力有限」,因为在此之前 Web 平台提供所有的 API 都只停留在「单机」的阶段。

这样就会造成一些无法实现的功能,例如:

  • 无法在实现用户登录功能时,当用户输入邮箱地址显示用户对应的头像。
  • 无法在实现用户注册功能时,当用户输入邮箱或用户名就提示是否存在
  • 无法在实现留言板功能时,实时看到最新的用户留言。

这些功能的开发都卡在一个相同的问题

  • 数据存放在服务端,无法通过已知的API获取

需求

  • 对服务端发出请求并且接受服务端的响应
  • 如果可以通过JS直接发送网络请求,那么web的可能就会更多,随之能够实现的功能也会更多,至少不再是只能开发[单机游戏]

Google Suggest

  • AJAX(Asynchronous JavaScript and XML),最早出现在 2005 年的 Google Suggest
  • 它不是像 HTML、JavaScript 或 CSS 这样的一种“正式的”技术
  • 它是在浏览器端进行网络编程(发送请求、接收响应)的技术方案
  • 它使我们可以通过 JavaScript 直接获取服务端最新的内容而不必重新加载页面
  • 让 Web 更能接近桌面应用的用户体验

AJAX

  • AJAX 就是浏览器提供的一套 API,可以通过 JavaScript 调用,从而实现通过代码控制请求与响应。实现通过 JavaScript 进行网络编程。
  • XML:最早在客户端与服务端之间传递数据时所采用的数据格式。

应用场景

  • 按需获取数据
  • 对用户数据校验
  • 自动更新页面内容
  • 提升用户体验,无刷新的体验

体验ajax

原生AJAX

发送ajax请求步骤

  • 1.创建XMLHttpRequest类型的对象
  • 2.准备发送,打开与一个网址之间的连接
  • 3.执行发送动作
  • 4.指定xhr状态变化事件处理函数

XMLHttpRequest类型对象

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

兼容问题

  • var xhr = new XMLHttpRequest();
  • IE6兼容:xhr = new ActiveXObject("Microsoft.XMLHTTP");
  • 代码演示
//1.获取XMLHttpRequest类型的对象
// 兼容问题
var xhr;
if(window.XMLHttpRequest){
    xhr = new XMLHttpRequest();
}else {
    // IE6兼容
    xhr = ActiveXObject("Microsoft.XMLHTTP");
}

open方法开启请求

  • 本质上XMLHttpRequest就是JavaScript在web平台中发送HTTP请求的手段,因此发送出去的请求仍然是HTTP请求,同样符合HTTP约定的格式。

语法:xhr.open(method,url);

  • method: 要使用的HTTP方法。
    • 比如[GET](获取数据)、[POST](提交新的数据)、[PUT](数据中进行更改)、[DELETE](删除数据)等。
  • url: 要向其发送请求的url地址,字符串格式
  • 代码演示
// 2.打开连接
// get方式:可在地址后面追加信息,多条用&连接
// post方式,必须通过send方法进行传递数据
xhr.open("get","https://jsonplaceholder.typicode.com/users?id=1");
xhr.open("post","https://jsonplaceholder.typicode.com/users");

setRequestHeader()方法设置请求头

  • 此方法必须在 open() 方法和 send() 之间调用

语法:xhr.setRequestHeader(header,value)

  • header: 一般设置"Content-Type",传输数据类型,即:服务器需要我们传送的数据类型。
  • value: 具体的数据类型,常用"application/x-www-form-urlencoded"和"application/json"。
  • 代码演示
// 设置响应头
// get方式不用设置,而post必须设置
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");

send方法和请求头

  • 用于发送HTTP请求

语法:xhr.send(body)

  • body: 在XHR请求中要发送的数据体,根据请求头中的类型进行传参。
  • 如果是GET方法,无需设置数据体,可以传null或者不传参。
  • 代码演示
// 3.执行发送动作
// get方式
// xhr.send(null);
// post方式
xhr.send("name=mh&age=18");

readyState属性

  • readyState 属性返回一个 XMLHttpRequest 代理当前所处的状态,由于 readystatechange 事件是在 xhr 对象状态变化时触发(不单是在得到响应时),也就意味着这个事件会被触发多次。
  • 如图所示

readyState.png

readyState1.png

  • 代码演示
//1.获取XMLHttpRequest类型的对象
// 兼容问题
var xhr;
if(window.XMLHttpRequest){
    xhr = new XMLHttpRequest();
}else {
    // IE6兼容
    xhr = ActiveXObject("Microsoft.XMLHTTP");
}
// 初始化
console.log("UNSEND",xhr.readyState);

// 2.打开连接
xhr.open("post","https://jsonplaceholder.typicode.com/users");
// 创建连接
console.log("OPENED",xhr.readyState);

// 设置响应头
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");

// 3.执行发送动作
// post方式
xhr.send("name=mh&age=18");

// 4.指定xhr状态变化事件处理函数
xhr.onreadystatechange = function (){
    if(this.readyState === 2){
       // 接收到响应头
       console.log("HEADERS_RECEIVED",xhr.readyState);
    }else if(this.readyState === 3){
        // 响应体加载中
       console.log("LOADING",xhr.readyState);
    }else if(this.readyState === 4){
        // 加载完成
        console.log("DONE",xhr.readyState);
    }
}

事件处理函数

  • 一般都是在readyState值为4时,执行响应的后续逻辑。
  • 代码演示
// 4.指定xhr状态变化事件处理函数
xhr.onreadystatechange = function (){
    if(this.readyState === 4){
        console.log(this.responseText);
    }
}

同步与异步

显示场景理解

  • 同步:一个人在同一个时刻只能做一件事情,在执行一些耗时的操作(不需要看管)不去做别的事,只是等待。
  • 异步:在执行一些耗时的操作(不需要看管)去做别的事,而不是等待。

ajax中的实现

  • xhr.open()方法第三个参数要求传入的是一个boolean值,其作用就是设置此次请求是否采用异步方式执行。
  • 默认:true 异步,如果需要同步执行可以通过传递false实现。
  • 同步执行,代码会卡在xhr.send()这一步,等到所有的数据都传输完成,才会往下执行。

建议

  • 问题:同步执行的时候,为什么onreadystatechange事件不会被触发
    • 因为:onreadystatechange事件,只有在readystate变化的时候才会被触发。当同步执行的时候,全部的数据传输完成后,那么readystate将不会再变化,那么此时注册了事件,也不会再被触发。
    • 解决方法:为了让事件更加可靠(一定触发),再发送请求send()之前,一定是先注册onreadystatechange事件。
  • 代码演示(不论是同步还是异步都能触发成功)
 //1.获取XMLHttpRequest类型的对象
// 兼容问题
var xhr;
if(window.XMLHttpRequest){
    xhr = new XMLHttpRequest();
}else {
    // IE6兼容
    xhr = ActiveXObject("Microsoft.XMLHTTP");
}

// 2.打开连接
// 异步执行
// xhr.open("post","https://jsonplaceholder.typicode.com/users",true);
// 同步执行
xhr.open("post","https://jsonplaceholder.typicode.com/users",false);

// 设置响应头
// get方式不用设置,而post必须设置
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");

// 4.指定xhr状态变化事件处理函数
xhr.onreadystatechange = function (){
    if(this.readyState === 4){
        // 加载完成
        console.log("DONE",xhr.readyState);
    }
}
// 3.执行发送动作
// post方式
xhr.send("name=mh&age=18");

// ajax后面的代码
console.log("after ajax");
  • 了解同步模式即可,切记不要使用同步模式。

响应数据格式

提问

  • 如果希望服务端返回一个复杂数据,该如何处理?
  • 关心的问题:服务端发出何种格式的数据,这种格式如何在客户端用JavaScript解析。

XML

  • 一种数据描述手段
  • 淘汰的原因:
    • 数据冗余太多。元数据(用来描述数据的数据)占用的数据量比较大,不利于大量数据的网络传输。
    • 在其他语言中,比如js解析内部数据时,方法比较复杂,不方便使用。
  • 代码演示
<?xml verson="1.0" encoding="utf-8" ?>
<booklist>
    <book>
       <name>三国演义</name>
       <author>罗贯中</author>
       <cate>古典名著</cate>
    </book>
    <book>
       <name>西游记</name>
       <author>吴承恩</author>
       <cate>古典名著</cate>
    </book>
    <book>
       <name>红楼梦</name>
       <author>曹雪芹</author>
       <cate>古典名著</cate>
    </book>
</booklist>

JSON

  • JavaScript Object Notation 对象表示法
  • 也是一种数据描述手段,类似于JavaScript字面量方式
  • 服务端采用JSON格式返回值,客户端按照JSON格式解析数据

JSON格式的数据与对象字面量的区别:

  • 1.JSON 数据并不需要存储在变量里面
  • 2.结束时不需要写分号
  • 3.JSON 数据中的属性名必须加 引号
  • 演示代码
// 对象字面量
var obj = {
    name:"mw",
    age:45,
    brother:{
        name:"lh",
        age:36
    }
};
// json格式的数据
{
    "name":"zs",
    "age":18,
    "hobby":["paino","paint"]
}

ES5新增JSON对象

  • JSON对象的parse方法
    • 参数:字符串(符合JSON格式)
    • 返回值:转换成一个对象
  • JSON对象的stringify方法
    • 参数:对象
    • 返回值:字符串(符合JSON格式)
  • 演示代码
// 对象字面量
var obj = {
    name:"mw",
    age:45,
    brother:{
        name:"lh",
        age:36
    }
};

// 创建一个符合JSON数据格式的字符串
var str = '{"name":"ff","age":99}';
// es5中新增了JSON对象
// JSON对象的parse方法和stringify方法
// 1.stringify方法
// 将对象字面量转换成 字符串形式
console.log(JSON.stringify(obj));
// 将符合JSON数据格式的字符串,转换成对象
var s = JSON.parse(str);
console.log(s);
// 可以通过打点调用属性
console.log(s.name); // ff
console.log(s.age);  // 99

注意

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

JSON Server

  • 平时会写一些数据,通过ajax获取。所以,需要在本地搭建一个临时服务器。
  • json-server是一个Node模块,运行Express服务器,可以指定一个json文件作为api的数据源。
  • 可以使用json-server快速搭建一个web服务器。
  • 网址:github.com/typicode/js…

原生AJAX具体用法

JSON文件书写方法

  • JSON文件中的属性名,后续会成为路由名。

在命令行中

  • 进入某一个具体的文件夹:输入cd,先不回车,然后在后边输入想要进入的文件夹路径,即可进入该文件夹。(将JSON文件托管到json-server服务端时,要注意路径是否正确)
  • 否则会出现:文件找不到的情况,那么服务端会自动创建一个同名的json文件。
  • 如下图

json_server路径配置.png

GET请求

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

案例:对自己在服务端部署的JSON文件进行get请求

  • 返回:符合要求的数据
// 创建xhr对象,兼容写法
var xhr = window.XMLHttpRequest 
? new XMLHttpRequest()
: new ActiveXObject("Microsoft.XMLHTTP");

// 打开链接
// xhr.open(method,url);
// get请求
// 传递参数,筛选获取
xhr.open("get","http://localhost:3000/users?age=15");
// 执行发送
xhr.send(null);

// 指定xhr状态变化事件处理函数
xhr.onreadystatechange = function (){
    if(this.readyState === 4){
        console.log(this.responseText);
    }
}

POST请求

  • POST请求过程中,都是采用请求体承载需要提交的数据。
  • 需要设置请求头中的Content-Type,以便于服务端接收数据。
  • 需要提交到服务端的数据可以通过send方法的参数传递。

案例:对自己在服务端部署的JSON文件进行post请求

  • 返回:新增加的数据,并且查看json文件中也已经成功添加了数据,id自动增加。
// 创建xhr对象,兼容写法
var xhr = window.XMLHttpRequest 
? new XMLHttpRequest()
: new ActiveXObject("Microsoft.XMLHTTP");
        
// 打开链接
// xhr.open(method,url);
// post请求
xhr.open("post","http://localhost:3000/users");

// post请求,必须要设置请求头
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
// 执行发送
xhr.send('name=bb&age=23&class=2');

// 指定xhr状态变化事件处理函数
xhr.onreadystatechange = function (){
    if(this.readyState === 4){
        console.log(this.responseText);
    }
}
  • send()方法传递值时的三种写法
  • 代码演示
// 第一种方法
// post请求,必须要设置请求头
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
// 执行发送
xhr.send('name=bb&age=23&class=2');

// 第二种方法
xhr.setRequestHeader("Content-Type","application/json");
// json数据格式
xhr.send('{"name":"bn","age":23,"class":1}');

// 第三种方法
xhr.setRequestHeader("Content-Type","application/json");
// 对象转字符串
xhr.send(JSON.stringify({
    name:"nn",
    age:12,
    class:2
}));

处理响应数据渲染

  • 客户端中拿到请求的数据后,需要把这些数据呈现到界面上。
  • 数据结构简单:可以直接通过**字符串操作(拼接)**的方式处理。当数据过于复杂时,不推荐使用,字符串拼接维护成本太大。
  • 数据结构复杂:推荐使用模板引擎或者ES6提供的模板字符串
  • 代码演示
* {
    margin: 0;
    padding: 0;
}
table {
    width: 300px;
    /* 上下30 左右居中 */
    margin: 30px auto;
    font: 14px/28px "微软雅黑";
    color: #333;
    /* 取消边框之间的缝隙 */
    border-collapse: collapse;
    /* border: 1px solid #666; */
}
td,th {
    width: 100px;
}
<!-- table表格中,tr表示一行,th表示表头,td表示每个单元格
         其中 th和td 必须在tr中 -->
    <table border = "1" class ="box"> 
        <tr>
            <th>学号</th>
            <th>姓名</th>
            <th>班级</th>
            <th>年龄</th>
        </tr>
    </table>
    <script src="js/jquery.min.js"></script>
    <script>
        // 获取元素
        // var $box = $(".box");
        var box = document.getElementsByClassName("box")[0];

        // 创建xhr对象,兼容写法
        var xhr = window.XMLHttpRequest 
        ? new XMLHttpRequest()
        : new ActiveXObject("Microsoft.XMLHTTP");
        
        // 打开链接
        xhr.open("get","http://localhost:3000/users");
        // 执行发送
        xhr.send(null);

        // 指定xhr状态变化事件处理函数
        xhr.onreadystatechange = function (){
            if(this.readyState === 4){
                // 返回数组 数组也是对象
                // console.log(this.responseText);
                // 将返回的结构 渲染到表格中
                var data = JSON.parse(xhr.responseText);
                console.log(data);
                var str = "";
                for(var i = 0; i < data.length; i++){
                    // 1. 拼接字符串(适用于:简单数据结构)
                    str += '<tr><td>'+ data[i].id +'</td><td>'+ data[i].name +'</td><td>'+ data[i].class +'</td><td>'+ data[i].age +'</td></tr>';
                    // 2.数据结构复杂:推荐使用**模板引擎**或者**ES6提供的模板字符串**
                    // ES6推荐的模板字符串 可以包含空格
                    str += `<tr>
                        <td>${data[i].id}</td>
                        <td>${data[i].name}</td>
                        <td>${data[i].class}</td>
                        <td>${data[i].age}</td>
                        </tr>`;
                }
                // console.log("hi");
                box.innerHTML += str;
            }
        }
    </script>

封装AJAX库

封装一个ajax函数

  • 主要是为了了解封装的过程,一般情况在开发中都是使用第三方提供的 AJAX 库,因为它们可能更加严谨。
  • 为了在后续的开发过程中可以更方便的使用这套 API,一般的做法都是将其封装到一个函数中以便调用。
  • 代码演示(将封装好的函数单独提取到一个文件中)
<script>
        // 封装自己的ajax函数
        /* 参数1:{string} method 请求方法
           参数2:{string} url 请求地址
           参数2:{Object} params 请求参数
           参数3:{function} done 请求完成后执行的回调函数
        */
       function ajax(method,url,params,done){
            // 创建xhr对象,兼容写法
            var xhr = window.XMLHttpRequest 
            ? new XMLHttpRequest()
            : new ActiveXObject("Microsoft.XMLHTTP");

            // 将method转换成大写
            method = method.toUpperCase();
            // 参数拼接
            var pair = [];
            for(var k in params){
                pair.push(k + "=" + params[k]);
            }
            var str = pair.join("&");
            // 判断请求方法
            if(method === "GET"){
                // 字符串拼接 或者 模板字符串
                url += "?" + str;
            }
            xhr.open(method,url);

            var data = null;
            if(method === "POST"){
                // 需要请求头
                xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
                data = str;
            }
            xhr.send(data);

            // 指定xhr状态变化事件处理函数
            // 执行回调函数
            xhr.onreadystatechange = function (){
                if(this.readyState === 4){
                    // 返回的应该是一个对象,这样客户端更好渲染
                    done(JSON.parse(xhr.responseText));
                }
            }
       }

    //    调用自己写的ajax函数
    ajax("get","http://localhost:3000/users",{
        name:"zs",
        age:45
    },function (a){
        console.log(a);
    });
</script>
  • 如图所示,以后只需要引用即可

ajax.png