ajax详解

184 阅读5分钟

什么是ajax?

ajax全称为Asynchronous Javascript And XML,它是一种技术,实现了前后端交互的方式,有利于前后端更好的分离。可以使页面局部刷新,对用户更加友好。

原生ajax

   let xhr = new XMLHttpRequest;
    //open(参数1,参数2) 参数1指的是 请求的方式(GET,POST) 参数2指的是请求的地址(接口)
    /* 
    请求方式:
    get系列:get   delete...
    post系列:post put option...
    */
    xhr.open('GET', './data.json', true); //true异步  false同步
        /* 
        readyState:请求状态
                    
        done:4  完成请求
        headers_recelved:2 已经接收了 响应头部信息
        loading:3 正在接收响应体
        opened:1 执行过open之后变成
        unsent:0

        */
    xhr.onreadystatecahnge = function(){
    let {readyState,status,response} = xhr;
        if (readyState === 4 && status === 200) {
            console.log(response);
        }
    };
    xhr.send();

对ajax进行封装

    (function() {
        class ajax {
            constructor(obj) {
                this.obj = obj;
                this.obj = {
                    method: 'get', //请求方式
                    url: '', //接口地址
                    data: {}, //要传递的参数
                    catchs: true, //默认走缓存
                    success() {}, //成功回调函数
                    error() {}, //失败回调函数
                    ...this.obj //赋予默认值
                }; //赋默认值
                let {url,method,data,success,error,catchs} = this.obj;
                this.str = ``;
                if (!catchs && /get/i.test(this.obj.method)) {
                    //catchs 为 false 并且请求方式为get 在要传的参数中添加一个当前时间戳 防止走缓存
                    this.obj.data.____ = Date.now();
                }
                if (/get/i.test(this.obj.method)) {
                    //如果是get请求就将参数拼接到url后面
                    Object.keys(this.obj.data).forEach(item => {
                        this.str += `${item} = ${this.obj.data[item]}&`;
                        this.str = this.str.replace(/\&$/, '')
                    })
                    //在前面加上?
                    this.str = '?' + this.str;
                };
                //将url和参数拼接到一起
                this.url = url + this.str;
                this.xhr = new XMLHttpRequest();
                this.xhr.open(method, this.url);
                this.xhr.onreadystatechange = () => {
                    let {readyState,status,response} = this.xhr;
                    if (readyState === 4 && status === 200) {
                        this.data = response;
                        this.obj.success(this.data);
                    } else if (readyState === 4 && /[45]\d{2}/.test(status)) {
                        this.obj.error(new Error('请求出错'));
                    }
                }
                this.xhr.setRequestHeader('content-Type', 'application/x-www-form-urlencoded')
                this.xhr.send(this.str != '' ? this.str : JSON.stringify(this.obj.data));
                //send传递的参数只对post请求起作用
            }
        }
        //暴露API
        window.ajax = ajax;
        const get = function get(obj) {
            obj = {
                data: {},
                ...obj
            }
            let {url, success, error,data} = obj;
            new ajax({
                url: url,
                data: data,
                caches: true,
                success: success,
                error: error,
                method: 'get'
            });
        };
        const post = function post(obj) {
            obj = {
                data: {},
                ...obj
            }
            let {url,success,error, data} = obj;
            new ajax({
                url: url,
                data: data,
                success: success,
                method: 'post',
                error: error,
                caches: false,
            });
        }
        window.get = get;
        window.post = post;
    })();

前端的存储

localStorage

//存储
localStorage.setItem('name',要存储的值);
//获取
localStorage.getItem('name');
//移除
localStorage.removeItem('name');

sessionStorage

//存储
sessionStorage.setItem('name',要存储的值);
//获取
sessionStorage.getItem('name');
//移除
sessionStorage.removeItem('name');

cookie

//获取
document.cookie('name');
//设置
document.cookie('name',要存储的值);
//移除 目前没有提供删除方法,但是可以把它的Max-age设置为0,也就是立马失效,也就是删除了

三者的区别

localStaorage的生命周期很长,即使页面关闭也不会消失,只要不删除,就会一直存在。容量大小为5MB,作用域:只要是在同一浏览器即使在不同标签下,只要是同源的情况下,都可以使用。

sessionStaorage 的生命周期:在浏览器或者页面关闭就会消失,作用域 只在当前页面可用 容量同样为5MB

cookie是保存在客户端的,一般由后端设置值,可以设置过期时间 容量只有4KB 一般是用来存储用户信息的 在http下cookie是明文传输的(发送请求默认携带cookie) 不安全

cookie属性有

http-only:不能被客户端更改访问,防止XSS攻击(保证cookie安全性的操作)

Secure:只允许在https下传输

Max-age:cookie生成后失效的秒数

expire: cookie的最长有效时间,若不设置则cookie生命期与会话期相同

localStorage 方法封装

设置一套具备有效周期的LocalStorage存储方案:存储信息的时候多设置一个时间;获取信息的时候;首先获取时间,在和当前时间做对比较手动计算是否超过了限定的周期;如果没有超过,说明存储的信息有效,我们拿过来使用即可! 如果超过了 说明存储的信息已经过期 我们把其清除掉,并且从服务器重新获取数据

    const Storage = {
        //存储信息:记录存储时间
        set(name, data) {
            localStorage.setItem(name, {
                time: +new Date,
                data
            })

        },
        //获取信息:做有效性的检测 limit我们设定的存储周期(单位:毫秒)默认一小时
        get(name, limit = 3600000) {
            let temp = localStorage.getItem(name);
            if (temp) {
                let {time,data} = temp;
                if (+new Date() - time <= limit) {
                    //信息在有效期内
                    return data;
                };
                //无效:移除存储的信息
            };
            return null;
        },
        //移除信息
        remove(name) {
            localStorage.removeItem(name);
        }
    }

HTTP结构-请求报文

通用头:General: Request URL:请求的接口地址 Requst Method:请求方式 Status Code:本次请求的状态码

        200:成功
        301:重定向 永久
        302:重定向  临时
        304:缓存  向后端请求数据 后端没有返回新数据 即可使用之前缓存
        403:访问被拒绝
        404:访问路径出错
        405:访问的方式出错
        
        5xx 一般都是服务端错误

image.png

请求头:Request Headers,是请求发送的时候前端带给后端的一些信息,前端可以设置,后端设置不了

access-control-allow-origin 是否支持跨域 * 表示支持跨域
cache-control:当前是否使用缓存
Connection:keep-alive 与服务器的连接状态
Host 主机域

响应头:Response Headers,是请求回来的时候后端带给前端的一些信息,响应头是后端设置的,前端只能看 不能动

cache-control
etag 唯一标识,缓存用的
last-modified最后修改时间

设置请求头

setRequestHeader设置请求头中的属性,getRequestHeader获取请求头中的属性,getAllRequestHeader获取全部请求头属性。

 let xhr = new XMLHttpRequest;
    xhr.open('GET', './data.json', true); 
    xhr.onreadystatecahnge = function(){
    let {readyState,status,response} = xhr;
        if (readyState === 4 && status === 200) {
            console.log(response);
        }
    };
    xhr.setRequestHeader('content-Type', 'application/x-www-form-urlencoded')//必须写在send前面
    xhr.send();

获取响应头

getResponseHeader获取响应头,getAllResponseHeader获取全部响应头

GET和POST的区别

GET请求 主要用来从服务器获取数据,传参方式为在url后面进行拼接,所以传参的大小会有限制,GET请请求会有缓存, POST请求 主要用来向服务端传递数据,传参方式为放在请求头中(放在send()中),所以传递的参数的长度不会被限制,POST请求没有缓存,POST请求比GET请求更加安全

传递参数

如果后端需要JSON格式的请求体 那么我们需要把参数转为JSON格式字符串 传递给send 同时一般需要设置 请求头中的 content-type 为application/json

如果后端需要formData格式的请求体 那么我们需要把参数转为formData格式的字符串 传递给send 同时一般需要设置 请求头中的 content-type 为 application/x-www-form-urlencoded

HTTP缓存

HTTP的缓存分为强缓存和协商缓存

强缓存:客户端请求数据之前会先经过缓存 如果有缓存就不会再去服务器请求数据 直接使用缓存

在浏览器加载资源时,先看看cache-control里的max-age,判断数据有没有过期,如果没有直接使用该缓存 ,有些用
户可能会在没有过期的时候就点了刷新按钮,这个时候浏览器就回去请求服务端,要想避免这样做,可以在cache-
control里面加一个immutable.
public 允许客户端和虚拟服务器缓存该资源,cache-control中的一个属性
private 只允许客户端缓存该资源
no-cache 不允许强缓存,可以协商缓存
no-store  不允许缓存

协商缓存就是强制缓存失效后或者强缓存未命中

 响应头
 last-modified:web,27 Apr 2022 08:37:30 GMT
 etag:W/"11a6f3c6fa0c19a7b4270dea6138b20c"

 请求头
 if-modified-since:web 27 Apr 2022 08:37:30 GMT
 服务器在给资源的时候 会同时给上个资源的修改时间  或者 一个标识
 以后我们前端再去服务端请求资源的时候 会带上这个修改时间或者标识
 
 浏览器加载资源时,没有命中强缓存,这时候就去请求服务器,去请求服务器的时候,会带着两个参数,一个是If-
 None-Match,也就是响应头中的etag属性,每个文件对应一个etag;另一个参数是If-Modified-Since,也就是响应
 头中的Last-Modified属性,带着这两个参数去检验缓存是否真的过期,如果没有过期,则服务器会给浏览器返回一
 个304状态码,表示缓存没有过期,可以使用旧缓存。
 
etag的作用
有时候编辑了文件,但是没有修改,但是last-modified属性的时间就会改变,导致服务器会重新发送资源
,但是etag的出现就完美的避免了这个问题,他是文件的唯一标识

缓存位置:

  • 内存缓存Memory-Cache
  • 离线缓存Service-Worker
  • 磁盘缓存Disk-Cache
  • 推送缓存Push-Cache

清除缓存

为什么要清除缓存?

有时候我们需要每次从服务端获取到的都是最新的数据,这时候我们就不能让浏览器从缓存中获取数据

实现方法 在url上补充一个每次不一样的一个参数即可 因为参数若不同 浏览器会理解成两个不同地址 就不会再去缓存去获取内容了

axios

axios是一个基于Promise和Ajax封装的插件,可以帮助我们更加方便的管理ajax请求,是一款比较常用的插件,执行结果为一个promise实例,我们可以直接使用then和catch接收。

使用axios进行get请求,参数需要传递在第二个参数中的pamas中(第一个参数为接口地址,第二个参数为一个对象)

axios.get('https://baidu.com/list',{
pamas:{
aa:1,
bb:2
}
}).then(response=>{
console.log(response);
}).catch(err=>{
console.log(err);
)

使用axios进行post请求,参数直接写在第二个参数中即可

axios.post('https://baidu.com/list',{
       aa:1,
       bb:2
}).then(response=>{
console.log(response);
}).catch(err=>{
console.log(err);
)

我们可以使用axios统一配置请求地址和请求头

axios.defaults.baseURL='https://baidu.com';//设置地址 后续请求只需要写此域名后的接口地址即可
axios.defaults.headers.common['aaa'] = 123;//设置请求头  在请求头中添加属性

设置请求拦截器和响应拦截器

我们可以在请求拦截器中设置统一的请求标头,在响应拦截器中我们可以进行数据的处理。

axios.interceptors.request.use(config=>{
config.headers.aaa = 666888; //设置请求标头
});
axios.interceptors.response.use(response=>{
return response.data
})