【ajax请求】Xhr → ajax→ axios

Asynchronous Javascript And XML, a(async异步)j(JavaScript)a(and)x(XML:与json同级语言格式)
ajax 作为前端必修课,页面无刷新获取数据。是一种创建动态网页(即前后端交互)的技术。

原理:浏览器底部抛出 XMLHttpRequest(简写XHR) 接口,XHR是用于对象和服务器的交互。

ajax请求函数本质上是对XHR封装,axios则是用promiseajax的封装库。axios仍是学习重点。

打开Chrome浏览器里Network选项卡里的Fetch/Xhr一栏即为ajax请求。





XMLHttpRequest XHR

XMLHttpRequest 属性

XMLHttpRequest.onreadystatechange: 监控事件,当 xhr.readyState 属性发生改变触发
XMLHttpRequest.readyState: xhr的状态0-4共五个状态码,请求响应过程的当前活动阶段
XMLHttpRequest.onload=callback: 请成功并完成时触发,进入callback内代表readyState已经为4
XMLHttpRequest.status: 响应的http状态
XMLHttpRequest.response: 返回响应体
XMLHttpRequest.responseType : 指定响应体格式
XMLHttpRequest.responseType : 设置超时时间

XMLHttpRequest 对象的 readyState 属性,表示请求响应过程的当前活动阶段:
0: 未初始化,创建了XMLHttpRequest对象,但未调用open()方法
1: 启动,已经调用open()方法,但未调用send()方法
2: 发送,已经调用send()方法,但未接收到响应
3: 接收,已经接收到部分响应数据
4: 完成,已经接收到全部响应数据,并且可以在客户端使用


XMLHttpRequest 方法

XMLHttpRequest.open(method, url, async): 打开要发送请求的地址,参数:请求方式、请求的url地址、请求是否异步的布尔值(默认true)
XMLHttpRequest.send(requsetBody): 发送请求(体)
XMLHttpRequest.setRequestHeader(key, value): 设置请求头
XMLHttpRequest.getResponseHeader(key): 获取响应头
XMLHttpRequest.abort(): 请求发出,立刻终止





ajax

手写一个ajax,没用promise

    // 1.实例化xhr对象
    const xhr = new XMLHttpRequest();
    // 监控 xhr.readyState
    xhr.onreadystatechange = () => {    // xhr实例出来的那一刻为0
        if (xhr.readyState === 4) {     // xhr有五种状态0,1,2,3,4,其中4为完全接收状态
            if (xhr.status > 200 && xhr.status < 300) {
                // 2.指定发送请求的url地址:method、url带query参数、true开启异步
                xhr.open('GET', 'http://localhost:8080/testrequest?name=paul&age=18',false)
                // 3.发送请求
                xhr.send()
            }
        }
    }




★axios

axios 是一个基于promise 的 http 库,可以用于浏览器和 nodejs 中

用promise封装ajax,无状态

// 用promise封装一个axios请函数,传入参数config(对象包含1.请求路径2.请求方式3.)
function axios(config) {
    // 调用axios方法,将返回一个promise实例
    return new Promise((resolve, reject) => {
        // 解构出config参数里的url、method、data
        const { url = '', method = 'GET', data = {} } = config
        // Xhr实例化
        const xhr = new XMLHttpRequest()
        // 指定要发送的请求(注意参数)
        xhr.open(method, url, true)
        // 请求完成时要执行的函数
        xhr.onload = function () {
            // 判断状态码
            if (xhr.status >= 200 && xhr.status < 300) {
                resolve(xhr.responseText)       // 返回成功的promise,
            } else {
                reject(new Error('faile'))      // 返回失败的promise
            }
        }
        // 发送请求
        xhr.send()
    })
} 

ps:只要进入xhr.onload回调里,即代表xhr.onreadystatechange ===4,已经完成响应。


一、axios配置

axios有三种形式的配置,全局配置,实例配置,请求配置。

优先级:请求配置 > 实例配置 > 全局配置

全局配置

// axios全局默认值
axios.defaults.baseURL = 'http://localhost:8080';
axios.defaults.timeout = 1000 ;
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

实例配置

// axios实例配置(new axios出来的实例)
let request = axios.create({
    baseURL:'http://localhost/api/',      // URL基础路径
    url: '/user',      // 在baseURL路径后拼接完整url地址
    method: 'GET',      // 请求方式(默认get),get,post,put,patch,delete
    params: { delay: 3000 },      // 参数用于GET请求,把数据拼接在URL的查询字符串中。配置url参数,虽然叫params配置但是携带的是query参数。
    data: { c: 2, d: 3 },      //  参数放在请求体中(json参数),只使用于PUT,POST,PATCH
    data: `e=4&f=6`,      // 置请求主体(urlencode编码),只使用于PUT,POST,PATCH
    timeout:2000,      // 请求超时毫秒
    headers:{'Content-Type': 'application/json;charset=UTF-8'},      // 自定义情头头
    responseType:'json'      // 指定响应格式(默认json),老项目可能用xml
})

请求配置

// axios请求配置
instance.get('/longRequest', {    //// 为已知需要花费很长时间的请求覆写超时设置
  timeout: 5000
});


二、axios拦截器

axios拦截器分为请求拦截器和响应拦截器。

拦截器的作用,在发起请求前做些什么,在响应回来前做些什么。

在发起请求或响应被 then 或 catch 处理前拦截它们
axios.get().then().catch(err=>{})

axios请求拦截器

// 请求拦截器:在发请求之前,请求拦截器可以检测到,可以在请求发出去之前做一些事情
// 此处为通过携带token字段控制登录状态
axios.interceptors.request.use((config) => {
    // config:配置对象,对象里面有一个属性很重要,headers请求头
    // 在发送请求之前做些什么,给拦截器设置过滤
    if (condition) {
        config.headers.token = ''
    }
    // 将axios的config配置返回出去,不然起不到过滤作用
    return config
}, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error)
})

axios响应拦截器

// 响应拦截器:在请求返回之前,响应拦截器可以检测到
axios.interceptors.response.use((response) => {
    // 拦截成功做些什么,返回成功的数据数据
    if (condition) {
        // axios发送请求的数据返回都是双层data,这里利用响应拦截器自动脱掉一层data
        return response.data
    }
}, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error)
})

三、axios 二次封装写法 (转载自https://www.jianshu.com/p/d6796986e2ab)

// request.js
import axios from 'axios';
import NProgress from 'nprogress';
import { notification, message } from 'antd';
import { routerRedux } from 'dva/router';
import store from '../index';

/**
 * 一、功能:
 * 1. 统一拦截http错误请求码;
 * 2. 统一拦截业务错误代码;
 * 3. 统一设置请求前缀
 * |-- 每个 http 加前缀 baseURL = /api/v1,从配置文件中获取 apiPrefix
 * 4. 配置异步请求过渡状态:显示蓝色加载条表示正在请求中,避免给用户页面假死的不好体验。
 * |-- 使用 NProgress 工具库。
 * 
 * 二、引包:
 * |-- axios:http 请求工具库
 * |-- NProgress:异步请求过度条,在浏览器主体部分顶部显示蓝色小条
 * |-- notification:Antd组件 > 处理错误响应码提示信息
 * |-- routerRedux:dva/router对象,用于路由跳转,错误响应码跳转相应页面
 * |-- store:dva中对象,使用里面的 dispatch 对象,用于触发路由跳转
 */

// 设置全局参数,如响应超市时间,请求前缀等。
axios.defaults.timeout = 5000
axios.defaults.baseURL = '/api/v1';
axios.defaults.withCredentials = true;

// 状态码错误信息
const codeMessage = {
    200: '服务器成功返回请求的数据。',
    201: '新建或修改数据成功。',
    202: '一个请求已经进入后台排队(异步任务)。',
    204: '删除数据成功。',
    400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
    401: '用户没有权限(令牌、用户名、密码错误)。',
    403: '用户得到授权,但是访问是被禁止的。',
    404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
    406: '请求的格式不可得。',
    410: '请求的资源被永久删除,且不会再得到的。',
    422: '当创建一个对象时,发生一个验证错误。',
    500: '服务器发生错误,请检查服务器。',
    502: '网关错误。',
    503: '服务不可用,服务器暂时过载或维护。',
    504: '网关超时。',
};

// 添加一个请求拦截器,用于设置请求过渡状态
axios.interceptors.request.use((config) => {
    // 请求开始,蓝色过渡滚动条开始出现
    NProgress.start();
    return config;
}, (error) => {
    return Promise.reject(error);
});

// 添加一个返回拦截器
axios.interceptors.response.use((response) => {
    // 请求结束,蓝色过渡滚动条消失
    NProgress.done();
    return response;
}, (error) => {
    // 请求结束,蓝色过渡滚动条消失
    // 即使出现异常,也要调用关闭方法,否则一直处于加载状态很奇怪
    NProgress.done();
    return Promise.reject(error);
});

export default function request(opt) {
    // 调用 axios api,统一拦截
    return axios(opt)
        .then((response) => {
            // >>>>>>>>>>>>>> 请求成功 <<<<<<<<<<<<<<
            console.log(`【${opt.method} ${opt.url}】请求成功,响应数据:%o`, response)

            // 打印业务错误提示
            if (response.data && response.data.code != '0000') {
                message.error(response.data.message);
            }

            return { ...response.data };
        })
        .catch((error) => {
            // >>>>>>>>>>>>>> 请求失败 <<<<<<<<<<<<<<
            // 请求配置发生的错误
            if (!error.response) {
                return console.log('Error', error.message);
            }

            // 响应时状态码处理 
            const status = error.response.status;
            const errortext = codeMessage[status] || error.response.statusText;

            notification.error({
                message: `请求错误 ${status}`,
                description: errortext,
            });

            // 存在请求,但是服务器的返回一个状态码,它们都在2xx之外
            const { dispatch } = store;

            if (status === 401) {
                dispatch(routerRedux.push('/user/login'));
            } else if (status === 403) {
                dispatch(routerRedux.push('/exception/403'));
            } else if (status <= 504 && status >= 500) {
                dispatch(routerRedux.push('/exception/500'));
            } else if (status >= 404 && status < 422) {
                dispatch(routerRedux.push('/exception/404'));
            }

            // 开发时使用,上线时删除
            console.log(`【${opt.method} ${opt.url}】请求失败,响应数据:%o`, error.response);

            return { code: status, message: errortext };
        });
}

接口api函数统一管理

// 发请求:axios发请求返回结果是Promise对象
// 对外暴露一个函数,只要外部调用这个函数,就向服务器发起ajax请求,当前者个函数只需要把服务器返回的结果返回即可

// 无参数
export const reqA = () => requests({
    url: '/a',
    method: 'GET',
})

// 若无接口,可以使用mock模拟数据。无参数
export const reqB = () => mockRequest({
    url: '/b',
    method: 'GET'
})

// 请求参数:params参数:,模板字符串手动拼入路径
export const reqC = (id) => requests({
    url: `/d/${id}`,
    method: 'GET'
})

// 请求参数:query参数,会写入路径
// 这里的params是指形参的意思(你可以写作任意),aioxs规定params配置项必须为对象,**所以这里params实际为是query参数**
// 若params:{name:paul},则最终请求的路径为 http://localhost/c?name=paul
export const reqD = (params) => requests({
    url: '/c',
    method: 'POST',
    params
})

// 请求主体发送数据:data
// 给服务器传递参数params(这里的params是指形参的意思,aioxs规定data为对象)
export const reqE = (params) => requests({
    url: '/e',
    method: 'POST',
    data: params
})

// 混合带参,很奇怪。 1.请求参数:params参数  2.请求体参数data
export const reqF = (params, data) => requests({
    url: `/f/${params}`,
    method: 'POST',
    data
})
posted @ 2022-04-17 10:54  wanglei1900  阅读(228)  评论(0编辑  收藏  举报