AJAX知识总结

Ajax简介

Ajax(Asynchronous JavaScript And XML),可以通过其在浏览器中向服务器发送异步请求,无需刷整个页面就可以获取数据
Ajax不是新的编程语言,而是一种将现在的标准组合在一起使用的新方式

使用场景:实现懒加载,当需要展示某一数据时再去请求数据
优点:

  • 无需刷新整个页面就可以与服务器端进行通信
  • 允许根据用户时间来更新部分页面内容

缺点:

  • 没有浏览历史,不能回退
  • 存在跨域问题
  • SEO不友好:使用ajax请求的数据,是异步请求的方式。SEO主要是获取源文档内容,也就是响应体内的内容,但是响应体内的内容需要在解析的过程中动态去获取的,所以SEO无法获取通过ajax请求的数据,也即通过ajax展示的数据对SEO不友好。

XML

XML(可扩展标记语言):被设计用来传输和存储数据。XML和HTML类似,不同的是HTML中都是预定义标签,而XML都是自定义标签,用来表示一些数据。但是目前大部分使用JSON来替代XML,原因:JSON数据更容易书写,并且转换也更方便。

HTTP

HTTP(hypertext transport protocol)协议,超文本传输协议,协议详细规定了浏览器和万维网服务器之间互相通信的规则。主要规定了请求和响应规则。

格式:

  • 请求报文
行 POST  /getNames HTTP/1.1 (分别是:请求方式/请求资源路径/HTTP版本号)

头 Host: xxx.xxx.xxx
    Connection: keep-alive 
    Pragma: no-cache Cache-Control: no-cache
    Accept-Encoding: gzip, deflate, br(以上都是浏览器告诉服务器想要请求什么样的数据,以及想要跟服务器进行怎样的约定)
 
 空行
 
 体  username=admin&password=admin(如果是post请求则请求体有数据)
 
  • 响应报文
行 HTTP/1.1  200 OK (分别是:HTTP版本号/响应状态码/响应状态码的描述)

头 
    cache-control: no-cache
    content-encoding:br
    content-type: application/json; charset=utf-8(以上都是服务器告诉浏览器响应的数据相关的信息)
 
 空行
 
 体  响应数据

Ajax使用

ajax基本使用

// 获取xmlhttprequest实例
        const xhr = new XMLHttpRequest();
        // 初始化
        xhr.open('GET','http://127.0.0.1:8000/server');
        // 发送请求 如果部需要发送请求体,则必须传null,因为该参数在一些浏览器中是必须的。
        xhr.send(null);
        // 事件绑定,处理响应结果,每次readyState数据发生变化都会触发onreadystatechange事件。
        xhr.onreadystatechange = function () {
        // readyState表示当前处在请求/响应的哪个阶段
            if (xhr.readyState === 4) {
                if (xhr.status >= 200 && xhr.status < 300) {
                    // 响应行
                    console.log(xhr.status);// 状态码
                    console.log(xhr.statusText);//状态码字符串
                    // 所有的响应头
                    console.log(xhr.getAllResponseHeaders());
                    // 响应体
                    console.log(xhr.response);
                    // or 响应体的字符串表示,该方式不受reponseType设置影响
                    console.log(xhr.responseText);
                }
            }
        }

readyState的属性值有:

  • 0:未初始化。尚未调用open方法。
  • 1:已打开(open)。已调用open方法,但是还没有调用send()方法
  • 2:已发送(send)。已调用send方法,尚未收到响应。
  • 3:接受中。已收到部分响应。
  • 4:完成。已收到全部的响应。

所以可以看出,一般通过判断readyState是否为4从而判断是否获取到了响应数据。

以上基本使用中用到了open方法作为请求的初始化操作。那么open方法如何去使用呢?

xhr.open('GET', 'http://127.0.0.1',true);

第一个参数代表请求的方式,一般有:

  • GET
  • POST

第二个参数代表请求的URL,如果是GET方式请求,当要传输查询字符串的时候,则需要在URL后面进行拼接
例如:

/test?uname=admin&password=admin

第三个参数表示是否使用异步请求,默认为true。如果为false,则是同步请求,会导致阻塞。

初始化完成之后,可以使用send方法发送数据。

  • GET方法,由于该方法传输查询参数是通过拼接字符串的形式,所以当使用send方法的时候,不需要传入任何数据,但是有些浏览器需要该参数,因此最好传一个null。
  • POST方法,可以使用该方法传输任何数据,该数据作为请求体进行传输。

send方法可以不写么?不可以,原因:open仅仅初始化了请求,但是并没有发送请求,而发送请求的真正时间点,在send方法调用之后。

设置请求头

可以使用setRequestHeader设置预定义请求头和自定义请求头

xhr.setRequestHeader('uname','admin');// 自定义请求头

注意:自定义请求头可以会触发预检请求。

获取json数据

当服务器端返回的数据是json字符串数据的时候,那客户端如何获取json数据本身呢?

  1. 手动转换

利用JSON.parse(jsonstr)来进行转换

  1. 配置,自动转换

XMLHttpRequest实例对象上有一个属性:responseType,可以通过这个属性对返回的数据进行自动转换,这里需要赋值为json,即:xhr.responseType = 'json'这样,获取到的响应体数据会自动转换为json格式,但是需要注意的是,只有response这个属性数据才会受responseType数据设置的影响。responseText本身就是对响应体的字符串表示,所以不受影响。

超时和网络异常处理

  • 对于请求超时,客户端可以这样设置
xhr.timeout = 2000;

表示,如果2s中还未收到响应,则作为超时处理,此时会取消请求。
例如:


Image.png

一般对于请求超时,客户端都会通知用户,类似的,这里可以使用超时回调,来进行超时的一个处理:

xhr.ontimeout = function() {
   alert('网络超时');
}
  • 对于网络异常/断网的情况下,也有对应的回调函数供开发人员去操作
xhr.onerror = function() {
  alert('哦豁,网络异常');
}

取消请求

可以利用XMLHttpRequest实例对象上的方法abort()取消还没有获得响应的请求。

xhr.abort();

重复请求问题

有一种场景:用户点击按钮,发送请求。但是如果用户不断点击按钮就可能发生不断发请求的情况,服务器就需要处理相同的请求,从而造成服务器的压力比较大。
例如:


在这里插入图片描述

以上便是用户点击多次的一个情况,可以看到请求也发了多次。

为了避免这个情况,需要在用户多次点击的时候进行处理,比如:当用户点击多次的时候,如果上一次请求还在发送过程中,则取消上一次的请求,重新开启一个新的请求。这种方式也就是我们经常所说的防抖解决方案。

let xhr = null;
    let isSending = false; // 标识是否正在发送请求
    btn.addEventListener('click', function () {
        // 获取xmlhttprequest实例
        if(isSending) xhr.abort();
        xhr = new XMLHttpRequest();
        isSending = true;
        xhr.responseType = 'json';

        xhr.onerror = function() {
            alert('哦豁,网络出错')
        }
        // 初始化
        xhr.open('POST','http://127.0.0.1:8000/server');
        xhr.setRequestHeader('uname', 'admin');
        // 发送请求
        xhr.send();
        // 事件绑定,处理响应结果
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                isSending = false;
                if (xhr.status >= 200 && xhr.status < 300) {
                    // input.innerHTML = JSON.parse(xhr.responseText).uname;
                    input.innerHTML = xhr.response.uname;
                }
            }
        }
    })

其实除了防抖利用标识符解决外,最有效的方式则是禁用当前的按钮,不允许用户进行点击,一方面告诉用户请求已经发送了,另一方面也防止用户多次点击的情况。

Jquery中使用Ajax

  1. 引入jquery

这里采用cdn引入


  1. 编写get/post请求
$.get('http://127.0.0.1:8000/server',{a: 12, b: 34}, function (data) {
    console.log(data)
},'json')

$.post('http://127.0.0.1:8000/server', {a:12, b:34},function (data) {
    console.log(data)
}, 'json')

这两个方法第一个参数表示请求的url,第二个参数表示传递的数据,第三个参数表示对返回的响应体的格式要求,也即responseType的值。

通过的请求方法:

$.ajax({
    // url
    url: 'http://127.0.0.1:8000/server',
        // method
        type: 'POST',
        // data
        data: {a: 1, b: 2},
    // 响应体类型
    dataType: 'json',
        // headers
        headers: {
            a: 1
    },
    // 超时时间设置
    timeout: 2000,
        // 成功回调
        success: function (data) {
            console.log(data);
    },
    // 失败回调,包括网络异常/超时/等情况
    error: function (err) {
        console.log(err);
    }
})

fetch发送请求

fetch属于js全局的方法,所以可以直接调用,他是一个基于promise的http请求方法。且是一个异步请求。

// 当调用fetch的时候,基本上请求就开始发送了。
let f = fetch('http://127.0.0.1:8000/server', {
// 初始化配置选项
    method: 'POST',
        headers: {
            uname: 'chen'
    },
    // 请求体
    body: {
        uname: 'chen'
    }
});
f.then(response=> {
    // return response.text();// 返回一个已解决的响应体字符串表示的期约
    return response.json();// 返回一个已解决的响应体json表示的期约
}).then(data=> {
    console.log(data);
})

ajax工具库-axios

axios基于promise的网络请求库,可以适用于浏览器和node环境

  1. 引入axios(采用CDN的方式)

  • 发送get请求
// 配置基础的url
axios.defaults.baseURL = 'http://127.0.0.1:8000';

axios.get('/server', {
    params: {
        a: 1,
            b: 2
    },
    headers: {
        uname: 'chen'
    }
}).then(data=>{
    console.log(data);
})

第一个参数表示请求的路径,如果预先设置了基础的url,此处设置的url会跟基础的url进行拼接。第二个参数是对请求的基础配置

  • 发送post请求
// 配置基础的url
axios.defaults.baseURL = 'http://127.0.0.1:8000
axios.post('/server', {
    // 请求体
    name: 'admin',
        password: '123'
}, {
    // 请求配置
    headers: {
        uname: 'chen'
    },
    // post请求也可以在Url后面添加额外的信息
    params: {
        a: 1,
            b: 2
    }
}).then(data=>{
    console.log(data);
})

第一个参数跟get请求一致,第二个参数表示请求体数据,第三个参数表示请求配置。需要注意的是,由于axios的post请求默认的Content-Type为application/json;charset=UTF-8,因此会触发预检请求,需要对数据进行配置/服务器进行配置

跨域问题

ajax请求是不支持跨域的,也就是ajax请求满足同源策略。所以对于想要获取不同源的数据,就需要解决跨域问题。

跨域问题的解决方案有:

  • JSONP

JSONP是一个非官方的跨域解决方案,只支持get请求。在网页中一些标签:img link iframe script。不受同源策略的影响。而JSONP就是利用script标签的跨域能力来发送请求的。

script标签如何发送请求呢?

我们知道如果要引入一个外部脚本,可以使用script标签的src属性进行指定。当浏览器解析到当前的这个script标签的时候,会将其src的js脚本进行引入,类似于将js脚本中的代码放在script标签中相应的位置。

比如:当前有一个外部脚本test.js

console.log(124);

在html文件中进行引入




    
    JSONP


    

此时浏览器解析完成时(这里先不考虑脚本中代码的执行),HTML呈现为:




    
    JSONP


    

可以看到对应的位置被外部Js脚本替换。(这里的外部js脚本必须是可执行的js脚本,如果js脚本里面是123,则认为是不合法的)

以上引入的js脚本是同一个域下的,同样可以引入不同域下面的js脚本,我们用的比较多的就是cdn引入的jquery脚本


所以按照以上的思路,我们可以利用script标签的src引入不同域的外部资源。但是如何获取数据呢?
之前有提到过,script标签引入的外部资源必须是可执行的js脚本。而如果简单的想要获取某个非可执行的js脚本数据,这是不合法的。
所以JSONP就说,既然需要执行的js脚本,那么服务器就给你提供一个客户端已有的函数字符串。同时将你需要的数据拼接到函数字符串中。

描述比较生硬,可能不容易理解,可以看以下的例子。

具体实现:

客户端:

发送请求

服务器端(node):

app.get('/server',(req,res) => {
    let data = {
        uname: 'op'
    }

    let resData = JSON.stringify(data);

    // 将数据拼接到函数字符串中
    res.send(`handle(${resData})`);
})

可以看到服务器将含有数据的函数调用字符串返回给了客户端,
具体返回的内容如下:
handle({"uname":"op"})

客户端接收到了这个字符串内容,就会尝试解析,类似于执行js脚本,由于客户端之前有这个handle函数,所以就会直接调用handle函数,利用handle函数就可以获取到服务器端传过来的数据了。

利:

  • 实现简单

弊:

  • 需要服务器知道客户端已有的函数名称
  • 容易遭受xss攻击

  • jQuery发送jsonp

jquery有一个方法getJSON可以很方便得发送jsonp,同时不需要服务器端知道函数名称得情况下传递函数字符串。

服务器(node):

app.get('/server',(req,res) => {
    let data = {
        uname: 'op'
    }

    let resData = JSON.stringify(data);
    // 获取客户端传递得函数名称
    let ca = req.query.callback;
    res.send(`${ca}(${resData})`);
})

客户端:

发送请求

第一个参数为目标Url,这里需要注意得是后面必须拼接callback参数,否则服务端无法获得函数名称,第二个参数是函数回调,利用该函数可以获取到服务器返回得数据。

  • CORS

CORS:跨域资源共享。是官方得跨域解决方案,特点:不需要客户端做任何特殊得操作,完全在服务器中进行处理,支持get/post请求。
工作形式:当浏览器发现当前请求违反了CORS,则会发起一个预检请求,询问服务器是否允许这个请求,服务器通过设置响应头告诉浏览器,允许该请求,则浏览器会将用户发送得请求发送到指定得服务器。

一般设置以下响应头信息:

  • Access-Control-Allow-Origin: 表示允许的访问来源。或者可以使用"*" 表示允许所有的访问来源。这个字段一般用于对跨域请求的支持。
  • Access-Control-Allow-Headers:表示允许的自定义请求头。
  • Access-Control-Allow-Methods:表示允许的请求方式。

例子node():

app.use('/server', (req,res,next) => {
    // 设置所有域都可以访问该域的资源
    res.setHeader('Access-Control-Allow-Origin','*');
    // 设置允许的自定义请求头
    res.setHeader('Access-Control-Allow-Headers', '*');
    next();
})

你可能感兴趣的:(AJAX知识总结)