欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【JavaScript】从入门到深入了解AJAX

程序员文章站 2022-06-09 23:15:34
...

写在前面

AJAX作为前端中的核心部分,我们可能在工作中,只是搬瓦工的角色,所以只会在写业务的时候给后端发送一个axiosfetch等请求,拿到数据渲染到页面后就不管不顾了,但是作为一个不想只是搬砖的工程师,深入的了解它还是非常有必要的,这篇文章将带你深入了解AJAX

关于ajax的基础知识

ajax: 即async javascript and xml,就是异步的js和xml

关于XML

最早的时候,基于ajax从服务器获取的数据一般都是xml格式数据,只不过现在基本都是应用更小巧更方便操作的json格式处理

  • html超文本标记语言

  • xhtml严谨的html

  • xml可拓展的标记语言(基于标签结构存储数据)

异步js

基于ajax实现局部刷新

  • 服务器渲染  一般都是同步 全局刷新

  • 客户端渲染   一般是异步  局部刷新

基础操作

对于ajax的操作有核心的四步操作:

创建xhr对象

let xhr = new XMLHttpRequest;
// 不兼容XMLHttpRequest的浏览器用ActiveXObject
let xhr = new ActiveXObject

打开请求连接(配置请求信息)

xhr.open(method, url, async, user-name, user-pass),其中methodhttp请求的方法 不管是哪一种请求方式,客户端都可以把信息传递给服务器,服务器也可以把信息返回给客户端,只不过get偏向于拿(给的少拿得多)

  • get:从服务器获取

get请求url有长度限制:具体表现为ie: 2083字符,大约为2kb,谷歌: 8182字符大约为4kb

  • get/head(只获取响应头信息,不获取响应主题内容)

  • delete删除,一般代指删除服务器上指定的文件

  • options 试探性请求

    > 在cross跨域请求中,所以正常请求发送前,先发送一个试探请求,验证是否可以和服务器正常的建立连接
    
  • post:向服务器发送

post偏向于给(给的多拿的少),post请求理论上没有大小限制

  • post/put新增,一般代指向服务器中新增文件

  • get请求和post的区别

  • get请求相对于post来说,不安全,get请求传参是基于URL问号传参,会被别人基于URL劫持的方式把信息获取到有一句话说得好:“互联网面前人人都在裸奔”,所以没有绝对的安全,我们需要更多的去处理安全性。

  • get请求容易产生缓存,原因还是因为get是基于问号传参传递信息的。浏览器在每一次获取数据后,一般会缓存一下数据,下一次如果请求的地址和参数和上一次一样,浏览器会直接获取缓存中的数据,所以我们基于get发送请求,需要清除缓存的时候,一般都会在地址栏中添加一个随机数,类似于这样:_ = Math.random

  • 基于get向服务器发送请求,传递给服务器的方式:

    • 基于请求头传递给服务器(本地cookie信息传递给服务器)

    • 请求url地址后面的问号传参 === 主要方式

    • 监听请求状态,在不同状态中做不同的事情

    • 发送AJAX请求 ajax任务开始 直到响应主体信息返回

  • 基于post向服务器发送请求,传递给服务器的方式

传递给服务器的数据格式为application/x-www--form-urlencoded xx=xxx&xx=xxxxmultipart/form-data(表单提交、文件上传)、raw(可以上传textjsonxml等格式的文本)、binary(上传二进制数据或者编码格式的数据)

let xhr = new XMLHttpRequest
  • ajax状态码

   0   1        2
  3   4
UNSENT OPENED HEADERS_RECEIVED LOADING DONE
创建完成 完成open操作 响应头信息回来 响应主体信息正在返回中 响应主体已经返回
  • HTTP状态码

这里只列举常见状态码

200: OK,请求成功。一般用于GET与POST请求

304: Not Modified,未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源

400: Bad Request,客户端请求的语法错误,服务器无法理解

401: Unauthorized,请求要求用户的身份认证

403: Forbidden,服务器理解请求客户端的请求,但是拒绝执行此请求

404: Not Found,服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面

500: Internal Server Error,服务器内部错误,无法完成请求

502: Bad Gateway,作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应

503: Service Unavailable,由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中

504: Gateway Time-out,充当网关或代理的服务器,未及时从远端服务器获取请求

注意:在真实项目中,后台开发者可能不是按照这个规则来处理的,不管传参或者权限是否正确等,只要服务器接收到请求最后都给返回200,在返回的json数据,基于某个字段表示错误信息。所以我们要和后端沟通好~

  • xhr的一些方法

status = xhr.status;  // http状态码

state = xhr.readyState;

if(/^(2|3)\d{2}$/.test(status))  // 成功

xhr.getAllResponseHeaders()  响应头信息

xhr.getAllResponse("date")  服务器日期是格林尼治时间GMT  比北京时间晚了八小时  new Date处理

xhr.response         // 响应主体信息

xhr.setheaders

xhr.timeout

xhr.withCredentials   // 允许携带跨域

xhr.abort()  打断请求

// 请求头信息不能是中文和特殊字符,要编码

ajax几种状态的同异步问题

let xhr = new XMLHttpRequest;
 /**
  *  xhr.readyState
  *  UNSENT 0 创建完默认为0
  *  OPENED 1 已经完成open操作
  *  HEADERS_RECEIVED 2 服务器已经把响应头信息返回了
  *  LOADING 3 响应主体正在返回中
  *  DONE 4 响应主体已经返回、
  *  xhr.open第三个参数控制的同步异步指的是从当前send发送请求开始,算任务开始,一直到状态为4才算任务结束,同步:在此期间 所有的事件都不去处理,
  *  而异步是:在此期间,该干啥干啥。
  *  异步 在SEND后会把这个请求的任务放在EVENT QUEUE中,属于宏任务
  */
// let xhr = new XMLHttpRequest;
// xhr.open("get", "./2.js", true);  // true为异步 false为同步
// // console.log(xhr.readyState);   // 1
// xhr.onreadystatechange = function() {
//     // 坚挺到状态改变后才会触发的事件
//     console.log(xhr.readyState);    // 2,3,4

// } 
// xhr.send();   // 任务开始

// let xhr = new XMLHttpRequest;
// xhr.open("get", "./2.js", true);  // true为异步 false为同步
// xhr.send();   // 任务开始
 // 此时状态是1
// xhr.onreadystatechange = function() {
//     // 坚挺到状态改变后才会触发的事件
//     console.log(xhr.readyState);    // 2,3,4

// } 

// let xhr = new XMLHttpRequest;
// xhr.open("get", "./2.js", false);  // true为异步 false为同步   同步特点 任务状态 不为4啥也干不了
// xhr.send();   // 任务开始

// // 状态为4,当下面变为5时才会改变, 所以没输出

// xhr.onreadystatechange = function() {   
//     console.log(xhr.readyState);      // 没有输出
// } 

// let xhr = new XMLHttpRequest;
// xhr.open("get", "./2.js", false);  // true为异步 false为同步   同步特点 任务状态 不为4啥也干不了

// // 这里状态为1, 
// xhr.onreadystatechange = function() {     // 这也是异步 的
//     console.log(xhr.readyState);      // 4
// } 

// xhr.send();   // 同步任务开始 状态不为4 啥也干不了  任务为4的时候,主栈才结束

let xhr = new XMLHttpRequest;
xhr.open("get", "./2.js", false);  // true为异步 false为同步   同步特点 任务状态 不为4啥也干不了

// 这里状态为1, 
xhr.onreadystatechange = function() {     // 这也是异步 的
    console.log(xhr.readyState);      // 4
} 

xhr.send();   // 同步任务开始 状态不为4 啥也干不了  任务为4的时候,主栈才结束

实现倒计时功能

其实倒计时功能对于前端开发而言,是很简单的事情,但是在这简单的事情中,我们还要注意些什么呢?

假如我们现在想要实现一个倒计时抢购功能,我们可能第一下想到的就是获取本地时间,计算与目标时间的时间差,试想一下,这样做会存在哪些问题?

大家都听说过这样一句话:“不要相信前端传送的任何数据”。是这样的,前端所有的数据都是可修改的,那么倒计时也是同理,如果我修改了系统时间岂不是就可以提前抢购了????

所以为了避免这种情况的发生,我们需要在真实项目中获取服务器的时间~

对于这件事情,我问过相关同事的处理方案,他们团队是通过接口获取的开始时间与结束时间,当开始时才显示,不开始时不显示任何倒计时,当然这个是和需求有关系的,如果想在开始前显示倒计时,那么获取服务器当前时间是必不可少的~

那么获取服务器的时间,又会存在什么样的问题呢?

我们要基于ajax请求获取响应头中的信息,那么在客户端和服务端通信的过程中,势必存在时间的消耗,那么到达本地的时间就会存在一定的误差,这个误差是根据网络环境等因素影响的,差值也并不是固定的,所以它是一定存在的,我们在开发过程中,也只能是尽可能的去减少这个误差~

假如我们抢购的目标时间是2020.6.10 20:00【这个模块是在6月10号这一天写的,哈哈哈】,下面我们完成这个倒计时抢购功能

  • 首先在页面中要有一个显示倒计时时间的区域

 <div>
    距离抢购时间还剩:
    <span id="spanBox"></span>
</div>
  • 封装获取服务器时间函数

function queryServerTime() {
    return new Promise(resolve => {
        let xhr = new XMLHttpRequest;
        xhr.open("head", './data.json');
        xhr.onreadystatechange = function() {
            if(!/^(2|3)\d{2}$/.test(xhr.status)) return;
            if(xhr.readyState === 2) {  // 响应头信息返回时,即获取
                // 响应头信息已经返回
                let time = xhr.getResponseHeader("date");
                time = new Date(time);
                resolve(time)
            }
        }
        xhr.send();
    })
}
  • 业务函数

 async function init() {
    let serverTime = await queryServerTime(),
        targetTime = new Date('2020/06/10 20:00:00'),
        autoTimer = null;

    // 计算时间差
    function computed() {
        let spanTime = targetTime - serverTime;
        if(spanTime <= 0) {
            spanBox.innerHTML = `00:00:00`;
            clearInterval(autoTimer);
        }
        let hours = Math.floor(spanTime/(60*60*1000));
        spanTime = spanTime - hours*60*60*1000;
        let minutes = Math.floor(spanTime/(60*1000))
        spanTime = spanTime -minutes*60*1000;
        let seconds = Math.floor(spanTime / 1000)

        hours = hours < 10 ? "0" + hours : hours;
        minutes = minutes < 10 ? "0" + minutes : minutes;
        seconds = seconds < 10 ? "0" + seconds : seconds;
        spanBox.innerHTML = `${hours}:${minutes}:${seconds}`;
    }
    computed();

    // 1s计算一次
    autoTimer = setInterval(_ => {
        // 重新获取服务器时间  但是这样有很大延迟 、服务器压力也大
        // 我们可以基于第一次获取的时间,在原来的基础上,让其自动累加
        serverTime = new Date(serverTime.getTime() + 1000)
        computed()
    }, 1000)
}

init();

这样,倒计时的功能就基于ajax实现了,完整的代码在这里【https://github.com/Alberqing/sourceCode/tree/master/ajax】

封装一个自己的ajax

说到封装,我的第一篇掘金博客就是封装一个自己的ajax,不过那个有些过于“单薄”,链接在这里:封装ajax请求的两种方式

对于封装的过程本篇文章不详细列举~封装好的、更完善一些的ajax在这里【https://github.com/Alberqing/sourceCode/tree/master/ajax】,各位小伙伴移步这里获取哦~

最后

本篇文章从说ajax的基础一路到封装,如果文章有不对之处还请大家指出,我们共同学习~共同进步~

最后,推荐一下我的公众号「web前端日记」,欢迎小伙伴前来关注哦~

【JavaScript】从入门到深入了解AJAX