大起底 ajax 、axios 和fetch

1. ajax

传统 Ajax 指的是 XMLHttpRequest(XHR), 最早出现的发送后端请求技术,隶属于原始js中,核心使用XMLHttpRequest对象,多个请求之间如果有先后关系的话,就会出现回调地狱
使用:

var xhr = new XMLHttpRequest();
xhr.open('GET','请求的服务器地址',true);
xhr.setRequestHeader(header,value);
xhr.onreadystatechange = function(){
        if(xhr.readyState === 4 && xhr.stastus === 200){
            console.log(xhr.responseText);
        }
}
xhr.send(data)

JQuery ajax 是对原生XHR的封装,除此以外还增添了对JSONP的支持。经过多年的更新维护,真的已经是非常的方便了,优点无需多言;如果是硬要举出几个缺点,那可能只有:
1.本身是针对MVC的编程,不符合现在前端MVVM的浪潮
2.基于原生的XHR开发,XHR本身的架构不清晰。
3.JQuery整个项目太大,单纯使用ajax却要引入整个JQuery非常的不合理(采取个性化打包的方案又不能享受CDN服务)
4.不符合关注分离(Separation of Concerns)的原则
5.配置和调用方式非常混乱,而且基于事件的异步模型不友好。

使用:

$.ajax({
   type: 'POST',
   url: url,
   data: data,
   dataType: dataType,
   success: function () {},
   error: function () {}
});

 封装ajax

/* 封装ajax函数
 * @param {string}opt.type http连接的方式,包括POST和GET两种方式
 * @param {string}opt.url 发送请求的url
 * @param {boolean}opt.async 是否为异步请求,true为异步的,false为同步的
 * @param {object}opt.data 发送的参数,格式为对象类型
 * @param {function}opt.success ajax发送并接收成功调用的回调函数
 */
function nativeAjax (opt) {
    opt = opt || {}
    opt.method = opt.method ?  opt.method.toUpperCase() : 'GET'
    opt.url = opt.url || ''
    opt.async = opt.async
    opt.data = opt.data || null
    opt.success = opt.success || function () {}
    opt.error = opt.error || function () {}
    var xmlHttp = window.XMLHttpRequest ? new XMLHttpRequest() : new window.ActiveXObject('Microsoft.XMLHTTP')
    var params = []
    for (var key in opt.data){
        params.push(key + '=' + opt.data[key])
    }
    xmlHttp.onreadystatechange = function () {
        if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
            opt.success(xmlHttp.responseText)
        } else {

        }
    }
    var postData = params.join('&');
    if (opt.method.toUpperCase() === 'POST') {
        xmlHttp.open(opt.method, opt.url, opt.async)
        xmlHttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=utf-8')
        xmlHttp.send(postData)
    }
    else if (opt.method.toUpperCase() === 'GET') {
        xmlHttp.open(opt.method, opt.url + '?' + postData, opt.async)
        xmlHttp.send(null)
    } 
}

/*获取地址栏参数*/	
function GetQueryString(name) {
    var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
    var r = window.location.search.substr(1).match(reg);
    if (r != null) return unescape(r[2]);
    return null;
}

function VerifyUser(f) {
	//alert(f,"29js");
    
    nativeAjax({
        method: 'POST',
        url: "http://xxx/LoginHandler.ashx?UserName=" + GetQueryString('userId') ,
        
        dataType: 'json',
        async: false,
        success: function (text) {
			//alert(text)
            f(text)
        },
        error: function (xhr, type) {
            alert('加载错误')
        }
    });
}

2.axios

axios({
    method: 'post',
    url: '/user/12345',
    data: {
        firstName: 'Fred',
        lastName: 'Flintstone'
    }
})
.then(function (response) {
    console.log(response);
})
.catch(function (error) {
    console.log(error);
});


axios 是一个基于Promise 用于浏览器和 node.js 的 HTTP 客户端,本质上也是对原生XHR的封装,只不过它是Promise的实现版本,符合最新的ES规范,它本身具有以下特征:
1.从浏览器中创建 XMLHttpRequest
2.支持 Promise API
3.客户端支持防止CSRF
4.提供了一些并发请求的接口(重要,方便了很多的操作)
5.从 node.js 创建 http 请求
6.拦截请求和响应
7.转换请求和响应数据
8.取消请求
9.自动转换JSON数据
PS:防止CSRF:就是让你的每个请求都带一个从cookie中拿到的key, 根据浏览器同源策略,假冒的网站是拿不到你cookie中得key的,这样,后台就可以轻松辨别出这个请求是否是用户在假冒网站上的误导输入,从而采取正确的策略。

配置拦截器:请求(request)拦截器和 响应(response)拦截器。拦截器一旦配置。所有请求都会走拦截器。

// http request 请求拦截器
axios.interceptors.request.use(
    config => {
        if (store.state.token) { 
         // 判断是否存在 token, 如果存在的话, 则每个 http header 都加上 token
            config.headers.Authorization = `token ${store.state.token}`;
        }

        //if (sessionStore.get('token')) {
        //    config.headers['Blade-Auth'] = sessionStore.get('token')
        //    config.headers['Tenant-Id'] = sessionStore.get('TENANT_ID')
        //}

        return config;
    },
    err => {
        return Promise.reject(err);
    }
);
// http response 响应拦截器
axios.interceptors.response.use(
    response => {
        console.log('响应拦截器- response', response)
        return response;
    },
    error => {
        if (error.response) {
            switch (error.response.status) {
                case 401:
                    // 返回 401 清除 token 信息并跳转到登录页面
                    store.commit(types.LOGOUT);
                    router.replace({
                        path: 'login',
                        query: {redirect: router.currentRoute.fullPath}
                    })
                    break;
                case 403:
                    message.error("您的访问被拒绝")
                    break;
                case 404:
                    message.error("未找到请求路径")
                    break;
                case 500:
                    message.error("服务器出错了")
                    break;
                default:
                    message.error(error.message)
                    break;
            }
        }
        return Promise.reject(error.response.data)  
         // 返回接口返回的错误信息
    }
);

react中对axios的封装

import qs from 'qs'
import axios from 'axios'
import { message } from 'antd'
import url from './url'
import sessionStore from './SessionStore';
// import {get} from '.'
axios.defaults.baseURL = url.serverUrl
axios.defaults.timeout = 60000
//axios.defaults.headers['X-Requested-With'] = 'XMLHttpRequest'
//axios.defaults.withCredentials = true
axios.defaults.crossDomain = true
axios.defaults.headers.get['Content-Type'] = 'application/x-www-form-urlencoded'
axios.defaults.headers['Authorization'] = 'Basic Y2xpZW50OnNlY3JldA=='

/** 请求拦截器,添加token */
axios.interceptors.request.use(
    config => {
        console.log('请求拦截器- config', config)
        if (sessionStore.get('token')) {
            config.headers['Blade-Auth'] = sessionStore.get('token')
            config.headers['Tenant-Id'] = sessionStore.get('TENANT_ID')
        }
        return config
    },
    error => {
        message.error('请求出错' + error);
        return Promise.reject(error);
    }
)

/** 响应拦截器 */
axios.interceptors.response.use(
    response => {
        console.log('响应拦截器- response', response)
        return response.data
    },
    error => {
        console.log('响应拦截器--error', error)
        if (error && error.response) {
            switch (error.response.status) {
                case 401:
                    // 返回 401 清除 token 信息并跳转到登录页面
                    message.error("未授权,请重新登录", 1, () => {
                        window.location.replace('http://xxxx/LoginUrl')
                    })
                    break;
                case 403:
                    message.error("您的访问被拒绝")
                    break;
                case 404:
                    message.error("未找到请求路径")
                    break;
                case 500:
                    message.error("服务器出错了")
                    break;
                default:
                    message.error(error.message)
                    break;
            }
        } else {
            message.error('请求出错')
        }

        return Promise.reject(error)
    }
)


export const get = (url, params = {}) => {
    return axios.get(url, { params: params })
}


export const post = (url, { }, data = {}) => {
    return axios({
        url,
        method: 'post',
        headers: { 'Content-Type': 'application/json;charset=UTF-8' },
        data: JSON.stringify(data)
    })
}

export const paramsPost = (url, params = {}) => {
    return axios({
        url,
        method: 'post',
        headers: { 'Content-Type': 'application/json;charset=UTF-8' },
        params: params
    })
}

export const postJson = (url, query = {}, data = {}, options = {}) => {
    return axios({
        url: url,
        type: 'post',
        data: JSON.stringify(data),
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        ...options
    })
}

export const postFormData = (url, data = {}) => {
    return axios({
        url,
        method: 'post',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        data: qs.stringify(data)
    })
}


export const all = (requests = []) => {
    return axios.all(requests).then(axios.spread((...result) => {
        return [...result]
    })).catch(error => {
        console.log(error)
    })
}

export const handleResult = (res, hint = true, data = false) => {
    if (res.code === 200) {
        if (hint) message.success(res.msg)
        return res.data
    } else {
        message.error(res.msg)
        return data
    }
}

3.fetch

try {
  let response = await fetch(url);
  let data = response.json();
  console.log(data);
} catch(e) {
  console.log("Oops, error", e);
}

fetch是在ES6出现的,号称是AJAX的替代品,使用了ES6中的promise对象。

Fetch是基于promise设计的。Fetch的代码结构比ajax简单,参数有点像jQuery ajax。但fetch是原生js,不是ajax的进一步封装,没有使用XMLHttpRequest对象

fetch的优点:
1.  语法简洁,更加语义化,更好更方便的写法
2.  基于标准 Promise 实现,支持 async/await
3.  同构方便,使用 [isomorphic-fetch](https://github.com/matthew-andrews/isomorphic-fetch)
4. 更加底层,提供的API丰富(request, response)
5. 脱离了XHR,是ES规范里新的实现方式
6. 符合关注分离,没有将输入、输出和用事件来跟踪的状态混杂在一个对象里

fetch存在的问题:
1)fetch只对网络请求报错,对400,500都当做成功的请求,服务器返回 400,500 错误码时并不会 reject,只有网络错误这些导致请求不能完成时,fetch 才会被 reject。
2)fetch默认不会带cookie,需要添加配置项: fetch(url, {credentials: 'include'})
3)fetch不支持abort,不支持超时控制,使用setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行,造成了流量的浪费
4)fetch没有办法原生监测请求的进度,而XHR可以

你可能感兴趣的:(前端架构,ajax,javascript,前端)