前端面试必问:ajax、$ajax、axios、fetch的区别?

454 阅读7分钟

ajax:依赖XMLHttpRequest,再基于ajax的四步,实现从服务器获取数据

image.png

  • 请求方式:GET(GET/DELETE/HEAD/OPTIONS) POST(POST/PUT/PATCH)
  • - GET和POST的区别:
    • 约定俗成的区别:GET请求传递给服务器的信息基于URL问号传参;POST是基于请求主体;
    • GET传输内容有大小限制(ie2kb,google4kb)[URL长度有限制];
    • POST相对于GET来讲更安全【相对安全】;
    • GET会产生不可控的缓存,两次请求的地址和参数相同时,会把上一次请求的值传回来【保证每次请求的地址和参数不一样(URL末尾加随机数/时间戳)】。
  • - xhr.readyState
    • 0 请求未初始化 创造出来xhr实例就是0
    • 1 服务器连接已建立 执行open方法后就会由0变成1
    • 2 请求已接收 响应头信息已经返回
    • 3 请求处理中 响应主体信息正在处理
    • 4 请求已完成,且响应已就绪 响应主体信息已经返回
  • - xhr.status HTTP状态码
    • 1xx:信息响应类,表示接收到请求并且继续处理
    • 2xx:处理成功响应类,表示动作被成功接收、理解和接受
    • 3xx:重定向响应类,为了完成指定的动作,必须接受进一步处理
    • 4xx:客户端错误,客户请求包含语法错误或者是不能正确执行
    • 5xx:服务端错误,服务器不能正确执行一个正确的请求
    • 200:OK
    • 301 302(307) 304
    • 400(服务器拒绝响应并告诉原因) 401 403(拒绝执行) 405(请求方式出错) 408(请求超时)
    • 500(服务器错误) 501 502(网关错误) 503(服务器超负荷)
  • - xhr.send(): 基于请求主体把信息主体传给服务器 传输的数据有格式要求:一般格式对象不支持,你写的对象,浏览器都会变成字符串传递给服务器;我们需要设置请求头中的 Content-Type 让其和传递的格式相符
  • 字符串格式
    • JSON格式字符串: 'Content-Type', application/json
    • URLENCODED格式字符串 'Content-Type', 'application/x-www-form-urlencoded'
    • 普通文本:'Content-Type',text/plain
  • FormDate格式对象:【文件上传】
    • 'Content-Type', 'multipart/form-data';boundary=...
  • buffer格式【进制编码格式】
  • - xhr常用的属性
    • onabort
    • ontimeout
    • readyState
    • response/responseText
    • status
    • timeout
    • withCredentials:false 跨域请求是否允许携带资源凭证(例如:cookie)
    • upload.onprogress 监听文件上传的进度
  • - xhr常用的方法
    • abort
    • getAllResponseHeaders
    • getResponseHeader
    • open
    • overrideMimeType
    • send
    • setRequestHeader

$ajax:是JQ类库对原生ajax的封装,基于回调函数方式封装

核心还是XMLHttpRequest

当请求成功,触发success回调函数执行,在这个函数中可以拿到从服务器获取数据

弊端:串行,容易产生回调地狱

axios:基于promise封装的ajax库

核心还是XMLHttpRequest

优势:基于promise,更好的管理异步编程

每一次基于axios发送请求,都会返回promise实例,请求成功,实例fulfilled,值是从服务器获取的结果;请求失败,实例是rejected,值是失败的原因。

axios.request({
	url:'',
	method:'',
	params:{}
})
axios.get({
	params:{}
})

基于axios发送请求

  • axios([config])
  • axios.request([config])
  • axios.get/delete/head([url],[config])
  • axios.post/put([url],[data],[config])
  • [config]配置项:www.axios-js.com/zh-cn/docs/
  • url:'';
  • baseURL:请求地址的公共前缀。最后向服务器发送请求,请求地址baseURL+url。如果url中存在http(s)://则不拼接baseURL。
  • method:'get';
  • params:对象 URL问号传参信息,我们传递的是一个对象,axios内部会把对象变为urlencoded格式字符串,拼接到url末尾。
  • data:字符串/FormData对象/对象 POST 系列请求,基于请求主体传递给服务器的信息,格式要求,对于原生ajax来讲,不能传递普通对象,但是axios内部做了封装,会把普通对象默认变为JSON格式字符串。【而且axios内部会根据我们编写的格式自动设置的请求头Content-Type的类型值。】
  • headers:对象 自定义请求头信息
  • timeout:设置超时时间
  • withCredentials:跨域是否携带cookie
  • responseType:'json/arraybuffer/blob/text/document',预设服务器响应主体返回的信息格式(预设:服务器返回啥格式是服务器决定的,axios只是会把服务器返回的结果变为我们指定的格式)
  • onDownloadProgress onDownloadProgress:函数
  • validateStatus:status=>status >= 200 && status < 300 设置服务器返回什么样的HTTP状态码算作请求成功,默认以2开始的才算
  • transformRequest:函数 只对post系列请求有作用,基于请求主体传递给服务器的data,在发送前做格式化处理。

image.png

axios二次封装:把每一次发送请求公共的配置项统一封装好,以后再发请求,公共的部分就不用再写了,简化axios的使用。

axios.get('/user/list')
    .then(value => {
        console.log(value);
});
axios.get('/user/list').then(response => {
    // 请求成功:response
    //   + status/statusText
    //   + request 原生的XHR对象 
    //   + config 用户设置的配置项
    //   + headers 响应头信息
    //   + data 响应主体信息
    console.log(response.data);
}).catch(reason => {
    // 请求失败:做提示(根据不同的情况做不同的提示处理)
    // @1 HTTP状态码不对  @2 请求超时/中断  @3 断网
    alert('小主,当前网络繁忙,请稍后再试~');
}); 

响应拦截器

请求拦截器

image.png

image.png

http.js 公共文件

// 检测是否为纯粹的对象
const isPlainObject = function isPlainObject(obj) {
    let proto, Ctor;
    if (!obj || Object.prototype.toString.call(obj) !== "[object Object]") return false;
    proto = Object.getPrototypeOf(obj);
    if (!proto) return true;
    Ctor = proto.hasOwnProperty('constructor') && proto.constructor;
    return typeof Ctor === "function" && Ctor === Object;
};

/* 对AXIOS的二次封账「发送请求公共部分的提取」 */
axios.defaults.baseURL = 'http://127.0.0.1:9999';
axios.defaults.withCredentials = true;
axios.defaults.timeout = 60000;
axios.defaults.transformRequest = data => {
    if (isPlainObject(data)) data = Qs.stringify(data);
    return data;
};
axios.defaults.validateStatus = status => {
    return status >= 200 && status < 400;
};

// 请求拦截器
axios.interceptors.request.use(config => {
    return config;
});

// 响应拦截器
axios.interceptors.response.use(response => {
    return response.data;
}, reason => {
    alert('小主,当前网络繁忙,请稍后再试~');
    return Promise.reject(reason);
});

axios的请求、响应拦截器怎么使用?

  • 基于拦截器进行公共部分提取
  • 请求拦截器:发生在 “配置项准备完毕” 和 “发送请求” 之间
  • 响应拦截器:发生在 “服务器返回结果” 和 “业务代码自己处理 .then” 之间
//请求拦截
axios.interceptors.request.use(config => {
					// config对象包含的就是准备好的配置项,最后返回啥配置,就按照这些配置发请求
  return config;
});
//响应拦截:如果成功就将结果返回给用户,如果失败,进行处理再返回
axios.interceptors.response.use(response => {
    // 请求成功:把响应主体信息返回给业务层去使用
    return response.data;
}, reason => {
    // 请求失败:根据不同的失败原因做不同的提示
    if (reason && reason.response) {
        // @1 有返回结果,只不过状态码不对
        let {status} = reason.response;
        switch (+status) {//switch是做===比较
            case 403:
                alert('服务器不爱搭理你~~');
                break;
            case 404:
                alert('你傻啊,地址都错了~~');
                break;
            case 500:
                alert('服务器开小差了~~');
                break;
        }
    } else {
        // @2 请求超时或者中断 
        if (reason && reason.code === "ECONNABORTED") {
            alert('请求超时或者被中断了~~');
        }
        // @3 断网
        if (!navigator.onLine) {
            alert('当前网络出问题了~~');
        }
    }
    // 统一失败提示处理完,到业务代码处,我们还是要失败的状态,这样才能继续做一些自己单独想做的失败处理
    return Promise.reject(reason);
});

fetch:ES6新增的方法

核心告别了XMLHttpRequest,基于新的通信机制实现可客户端和服务器的数据传输。

默认就是基于promise管理的。

不兼容IE浏览器(而且@babel/polyfill也没有实现他)、相关机制没有XMLHttpRequest完善。

用法:

    fetch('http://localhost:9999/job/list',{
        method:'GET',
        headers:{},//请求头信息
        //必须是字符串
        // body:JSON.stringify({id:0})//请求体中的参数,GET/HEAD/OPtions/DELETE方法没有请求体,不能有body
    }).then(function(response){
        console.log(response);//获取的是一个响应信息的对象,看不到真正的响应体
        return response.json(); //想要获取响应体的信息,需要调用对应格式的response.xxx()方法

    }).then(function(value){
        console.log(value);
    })

注意点:

  • GET/DELETE/OPTIONS/HEAD方法中不能存在body。
  • fetch得到的结果是一个Promise对象,可以调用then方法PromiseResult是响应的信息,但找不到响应体信息。
  • 想要获取响应体信息需要调用result对应数据格式的方法,比如想获取json格式的响应数据:result.json()/result.text()/result.formdata()/…
  • result.json等格式获取方法只能使用一次,且只能有一种

axios和Fetch的区别?

  • 相同点:他俩都是发送请求的
  • 实现原理不同:axios是基于promise封装的ajax类库,fetch是ES6中浏览器原生的API
  • 传参字段不同:
  • axios的get请求参数放到params中,post请求体信息放到data里
  • fetch的get请求参数直接跟在url后面,post请求体信息放在body里
  • 环境支持程度:node环境中不支持fetch,需要引入node-fetch文件才能支持
  • 功能差异:axios中的配置和拦截器等功能,fetch中都没有。