封裝ajax請求的兩種方式

ajax請求能夠說是在前端開發工做中必不可少的一個東西html

一、ajax請求原理前端

  • ajax技術的核心是XMLHttpRequest對象,其經過建立一個XMLHttpRequest對象,利用對象的open方法發送請求,判斷對象中readyState屬性(請求、響應過程的當前活動階段)的值和status(Http的響應狀態)的值,獲得responseText
  • open方法的三個參數
    • 第一個參數:請求方式
    • 第二個參數:請求的URL
    • 第三個參數:是否異步(大多數使用異步請求)
  • readyState屬性的值
    • 0:未初始化。還沒有調用open()方法
    • 1:啓動。已經調用open()方法,但還沒有調用send()方法。
    • 2:發送。已經調用send()方法,但還沒有接收到響應。
    • 3:接收。已經接收到部分響應數據。
    • 4:完成。已經接收到所有響應數據,並且已經能夠在客戶端使用了。
  • status屬性的值
    • 成功:(>= 200 && < 300) || === 304
    • 失敗:除去成功的狀況

二、jQuery、小程序風格的ajax請求封裝 jQuery風格的代碼樣式node

$.ajax({
    url: xxx,
    success: () =>  {},
    fail: err => {},
})
複製代碼

由上,咱們知道在封裝函數的時候,首先,它接收的是一個對象,因此,第一步:ajax

const ajax = ({}) => {}
複製代碼

接下來,咱們定義對象內部須要接收的參數,有:url(請求的地址)、data(發送的數據)、method(請求方式)、header(請求頭部信息)、success(請求成功回調函數)、fail(請求失敗回調函數)、async(請求是否異步)、timeout(設置請求超時時間)、onTimeOut(超時處理回調函數)、...小程序

const ajax = ({
        url,
        data = {},
        method = 'get', // 默認爲'get'請求
        header,
        async = true, // 默認爲異步請求
        timeout,
        success,
        fail,
    }) => {}
複製代碼

咱們一般會使用get請求向服務器查詢一些信息,也一般會在請求的url後面拼接上數據,像這樣:api

http://www.baidu.com?a=b&c=d...
複製代碼

那咱們如何實現呢?咱們定義一個拼接url函數,須要兩個參數,一個是自己的url,另一個是向後臺發送的數據param,因此:promise

// 數據拼接url
    addURL = (url, param) => {
        if(param && Object.keys(param).length) { // 數據不爲空
            // 判斷url後添加的字符是'?'仍是'&'
            url += (url.indexOf('?') === -1 ? '?' : '&');
            // 拼接數據
            Object.keys(param).map(key => {
                url += `${key}=${param[key]}`
            })
        }
    	return url; 
    }
複製代碼

一般呢,咱們使用get方法會遇到查詢字符串格式錯誤的問題,因此,這時須要咱們用encodeURIComponent()進行編碼,上面代碼改變以下:瀏覽器

addURL = (url, param) => {
        if(param && Object.keys(param).length) {
            url += (url.indexOf('?') === -1 ? '?' : '&');
            Object.keys(param).map(key => {
                url += encodeURIComponent(key) + '=' + encodeURIComponent(param[key])
            })
        }
    }
複製代碼

若是使用的是post方法,咱們只須要將數據給服務端傳遞過去,最終,咱們寫出了封裝後的代碼:bash

const ajax = ({
        url,
        data = {},
        method = 'get', // 默認爲'get'請求
        header,
        async = true, // 默認爲異步請求
        timeout = 60 * 1000, //默認60s
        success,
        fail,
    }) => {
       const requestURL = method === 'get' ? this.addURL(url, data) : url;
       const sendData = method === 'get' ? null : data;
       const xhr = new XMLHttpRequest();
       if(header && Object.keys(header).length) {
           Object.keys(header).map(key => {
               xhr.setRequestHeader(key, header[key]);
           })
       }
       xhr.onreadystatechange = () => {
           if(xhr.readyState === 4) {
            try {
                if((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
                   const response = xhr.responseText;
                   success(response);
               } else {
                   const error = xhr.status + xhr.statusText;
                   fail(error);
               }
            } catch (ex) {
                
            }
           }
       }
       xhr.open(method, requestURL, async);
       xhr.timeout = timeout;
       xhr.ontimeout = () => {
           console.log('timeout');
       }
       xhr.send(sendData);
    }
    // 拼接url
    addURL = (url, param) => {
        if(param && Object.keys(param).length) {
            url += (url.indexOf('?') === -1 ? '?' : '&');
            Object.keys(param).map(key => {
                url += encodeURIComponent(key) + '=' + encodeURIComponent(param[key])
            })
        }
        return url;
    }
複製代碼

上述代碼中,利用了try-catch語句。這是由於當請求在指定時間內沒有返回,就會自動終止,請求終止後,會調用ontimeout事件處理程序,若是readyState已經變爲4,就會調用onreadystatechange事件處理程序,這種狀況下,會在請求終止後再次訪問status屬性,就會致使瀏覽器報告錯誤,因此,爲了不錯誤,咱們將status屬性語句封裝在try-catch語句中。服務器

下面,咱們驗證一下,咱們封裝好的函數是否可用

// html
    <button id="ajax_btn">ajax請求</button>
    // js
    const ajax_btn = document.getElementById('ajax_btn');
    ajax_btn.onclik = () => {
        ajax({
            url: 'http://localhost:3001/123',
            data: {},
            header: {},
            timeout: 20 * 1000,
            success: res => {
                console.log(res);
            },
            fail: err => {
                throw err;
            }
        });
    };
複製代碼

頁面樣式

點擊按鈕,就會發送請求,接下來,是見證奇蹟的時刻:

咱們成功得到了服務端返回的數據(服務端是本身用node寫的一個極其簡易的接口)

三、promise風格的ajax請求封裝

promise風格的代碼

ajax.get('/api').then(res => {}).catch(err => {}).finally();
複製代碼

定義封裝ajax函數須要的參數

const ajax = ({
        url,
        data,
        method = 'get',
        header,
        async = true,
        timeout = 60 * 1000,
    }) => {};
複製代碼

接下來,按照jQuery風格的思路,進行封裝

const ajax = ({
        url,
        data = {},
        method = 'get', // 默認爲'get'請求
        header,
        async = true, // 默認爲異步請求
        timeout = 60 * 1000, //默認60s
    }) => {
        return new Promise((resolve, reject) => {
            const requestURL = method === 'get' ? this.addURL(url, data) : url;
            const sendData = method === 'get' ? null : data;
            const xhr = new XMLHttpRequest();
            if(header && Object.keys(header).length) {
                Object.keys(header).map(key => {
                   xhr.setRequestHeader(key, header[key]);
                })
            }
            xhr.onreadystatechange = () => {
                if(xhr.readyState === 4) {
                    try {
                        if((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
                           const response = xhr.responseText;
                           resolve(response);
                        } else {
                           const error = xhr.status + xhr.statusText;
                           reject(error);
                        }
                    } catch (ex) {
                        // 
                    }
                }
            }
            xhr.open(method, requestURL, async);
            xhr.timeout = timeout;
            xhr.ontimeout = () => {
                console.log('timeout');
            }
            xhr.send(sendData);
        })
    }
    // 拼接url
    addURL = (url, param) => {
        if(param && Object.keys(param).length) {
            url += (url.indexOf('?') === -1 ? '?' : '&');
            Object.keys(param).map(key => {
                url += encodeURIComponent(key) + '=' + encodeURIComponent(param[key])
            })
        }
        return url;
    }
    // get請求
    ajax.get = (url, data) => {
        return ajax({
            url,
            data,
        })
    }
    // post請求
    ajax.post = (url, data) => {
        return ajax({
            url,
            data,
            method = 'post',
        })
    }
複製代碼

下面,咱們驗證一下封裝好的promise風格的ajax函數的可用性

// html
    <button id="ajax_btn">ajax請求</button>
    // js
    const ajax_btn = document.getElementById('ajax_btn');
    ajax_btn.onclick = () => {
        ajax.get('http://localhost:3001/test')
            .then(res => {
                console.log(res);
            })
            .catch(err => {
                throw err;
            })
            .finally(console.log('finally'))
    }
複製代碼

點擊按鈕

當咱們將 get請求換成 post請求

至此,咱們成功地封裝了 promise風格的 ajax請求函數

四、優化

IE中,XHR對象是經過MSXML庫中的ActiveX對象實現的。因此,在IE中,可能會有三種不一樣版本的XHR對象(MSXML2.XMLHttpMSXML2.XMLHttp.3.0MSXML2.XMLHttp.6.0),若是咱們要使用庫中的XHR對象,就須要編寫一個函數(只適用於IE7之前的版本)

function createXHR() {
        if (typeof arguments.callee.activeXString !== 'string') {
            const versions = ['MSXML2.XMLHttp.6.0', 'MSXML2.XMLHttp.3.0', 'MSXML2.XMLHttp'];
            for(let i = 0; i< versions.length; i++) {
                try {
                    new ActiveXObject(versions[i]);
                    arguments.callee.activeXString = versions[i];
                    break;
                } catch (ex) {
                    // 跳過
                }
            }
        }
        return new ActiveXObject(arguments.callee.activeXString);
    }
複製代碼

若是想要支持IE7以上的版本,只須要在上述函數中加入對原生XHR對象的支持,即:

function createXHR() {
        if (typeof XMLHttpRequest !== 'undefined') {
            return new XMLHttpRequest();
        } else if(typeof ActiveXObject !== 'undefined') {
            if(typeof arguments.callee.activeXString !== 'string') {
                const versions = ['MSXML2.XMLHttp.6.0', 'MSXML2.XMLHttp.3.0', 'MSXML2.XMLHttp'];
                for(let i = 0; i< versions.length; i++) {
                    try {
                        new ActiveXObject(versions[i]);
                        arguments.callee.activeXString = versions[i];
                        break;
                    } catch (ex) {
                        // 跳過
                    }
                }
            }
            return new ActiveXObject(arguments.callee.activeXString);
        } else {
            throw new Error('no XHR object available');
        }
    }
複製代碼

經過這樣的方式,咱們能夠直接建立XHR對象:

const xhr = new createXHR();
複製代碼

效果:

相關文章
相關標籤/搜索