Js之AJAX

前言


在日常工作中,我們經常使用Jquery的Ajax來獲取接口數據。但是有的時候項目里面用不到Jquery,為了減少加載Jquery庫的時間。也不用負擔Jquery復雜的邏輯處理帶來的性能消耗,所以我打算封裝一個源生的Ajax函數

需求整理


一般來說前端與服務器的通信是使用XHR的,但是我希望封裝的函數能有跨域的功能。所以我把JSONP整合進來了.
下面看主體功能圖

功能圖.png

輸入參數


首先必須要定義個AJAX函數,并設置一些輸入參數

function ajax(options){
 var url = options.url || "", //請求的鏈接
        type = (options.type || "get").toLowerCase(), //請求的方法,默認為get
        data = options.data || null, //請求的數據
        contentType = options.contentType || "", //請求頭
        dataType = options.dataType || "jsonp", 
       //請求的類型要是jsonp他利用得get 問號后面是?callback = 執行成功的方法 callback可以依照規矩修改
        async = options.async === undefined && true, //是否異步,默認為true.
        timeOut = options.timeOut, //超時時間。 
        before = options.before || function(){}, //發送之前執行的函數
        error = options.error || function(){}, //錯誤執行的函數
        success = options.success || function() {}; //請求成功的回調函數
}
參數表

參數表.jpg

調用


     //var json = "name=garfield&age=18";  //字符串
     var json ={"haha":[{"name":"張三"},{"age":"23"}]};  //大JSON包
  ajax({
    type:"post",  //發送數據的類型
    url:"index.php", //添加自己的接口鏈接
    timeOut:5000,  //過期時間
    data:json,     //發送的數據
    before:function(){
        console.log("發送成功")     //發送之前要做的事情 
    },
    success:function(str){        //接收到數據成功后
       console.log(str);
    },
    error:function(status,statusText){   //如果出現錯誤后,第一個表示錯誤的狀態 第二個表示錯誤的原因
      console.log(status+"||"+statusText);
    }
  }) 

PHP接收


<?php
  header("content-type:text/html; charset=utf-8");
  $name = $_POST["haha"];  //對應的就是JSON的name
  echo($name[0]["name"]);
?>

編碼


一般來說發送到后端的數據,若是包括中文或者某些標點符號,就要對數據進行編碼

  • 如果data為字符串,通過&分割,對鍵名與鍵值分別編碼
  • 如果data為對象,把鍵值轉化為字符串,在進行編碼
  • 由于encodeURIComponent不對+編碼,所以只有使用replace方法手動編碼
  • 若是使用get方法或者JSONP,則數據是通過URL參數傳到后臺,所以手動添加數據到URL
  //編碼數據
        function setData() {
            //設置對象的遍碼
            function setObjData(data, parentName) {
                function encodeData(name, value, parentName) {
                    var items = [];
                    name = parentName === undefined ? name : parentName + "[" + name + "]";
                    if (typeof value === "object" && value !== null) {
                        items = items.concat(setObjData(value, name));
                    } else {
                        name = encodeURIComponent(name);
                        value = encodeURIComponent(value);
                        items.push(name + "=" + value);
                    }
                    return items;
                }
                var arr = [],value;
                if (Object.prototype.toString.call(data) == '[object Array]') {
                    for (var i = 0, len = data.length; i < len; i++) {
                        value = data[i];
                        arr = arr.concat(encodeData( typeof value == "object"?i:"", value, parentName));
                    }
                } else if (Object.prototype.toString.call(data) == '[object Object]') {
                    for (var key in data) {
                        value = data[key];
                        arr = arr.concat(encodeData(key, value, parentName));
                    }
                }
                return arr;
            };
            //設置字符串的遍碼,字符串的格式為:a=1&b=2;
            function setStrData(data) {
                var arr = data.split("&");
                for (var i = 0, len = arr.length; i < len; i++) {
                    name = encodeURIComponent(arr[i].split("=")[0]);
                    value = encodeURIComponent(arr[i].split("=")[1]);
                    arr[i] = name + "=" + value;
                }
                return arr;
            }

            if (data) {
                if (typeof data === "string") {
                    data = setStrData(data);
                } else if (typeof data === "object") {
                    data = setObjData(data);
                }
                data = data.join("&").replace("/%20/g", "+");
                //若是使用get方法或JSONP,則手動添加到URL中
                if (type === "get" || dataType === "jsonp") {
                    url += url.indexOf("?") > -1 ? (url.indexOf("=") > -1 ? "&" + data : data) : "?" + data;
                }
            }
        }

XMLHttpRequerst


  • 創建XHR對象 ,并對IE進行兼容性處理
  • 調用XHR的open方法,設置請求的方法,請求的鏈接,是否異步
  • 設置請求頭
  • 添加監聽,如果成功則返回success函數,報錯則執行error函數
  • 調用XHR的send方法,發送數據。如果get方法,我們通過setData方法把數據添加到URL中了,這里data設置為null
  // XHR
        function createXHR() {
            //由于IE6的XMLHttpRequest對象是通過MSXML庫中的一個ActiveX對象實現的。
            //所以創建XHR對象,需要在這里做兼容處理。
            function getXHR() {
                if (window.XMLHttpRequest) {
                    return new XMLHttpRequest();
                } else {
                    //遍歷IE中不同版本的ActiveX對象
                    var versions = ["Microsoft", "msxm3", "msxml2", "msxml1"];
                    for (var i = 0; i < versions.length; i++) {
                        try {
                            var version = versions[i] + ".XMLHTTP";
                            return new ActiveXObject(version);
                        } catch (e) {}
                    }
                }
            }
            //創建對象。
            xhr = getXHR();
            xhr.open(type, url, async);
            //設置請求頭
            if (type === "post" && !contentType) {
                //若是post提交,則設置content-Type 為application/x-www-four-urlencoded
                xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
            } else if (contentType) {
                xhr.setRequestHeader("Content-Type", contentType);
            }
            //添加監聽
            xhr.onreadystatechange = function() {
                if (xhr.readyState === 4) {
                    if (timeOut !== undefined) {
                        //由于執行abort()方法后,有可能觸發onreadystatechange事件,
                        //所以設置一個timeout_bool標識,來忽略中止觸發的事件。
                        if (timeout_bool) {
                            return;
                        }
                        clearTimeout(timeout_flag);
                    }
                    if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {

                        success(xhr.responseText);
                    } else {
                        error(xhr.status, xhr.statusText);
                    }
                }
            };
            //發送請求
            xhr.send(type === "get" ? null : data);
            setTime(); //請求超時
        }

JSONP


  • 創建script標簽
  • 設置回調函數名稱
  • 監聽回調函數
  • 設置URL,并添加到文檔中
        function createJsonp() {
            var script = document.createElement("script"),
                timeName = new Date().getTime() + Math.round(Math.random() * 1000),
                callback = "JSONP_" + timeName;

            window[callback] = function(data) {
                clearTimeout(timeout_flag);
                document.body.removeChild(script);
                success(data);
            }
            script.src = url + (url.indexOf("?") > -1 ? "&" : "?") + "callback=" + callback;
            script.type = "text/javascript";
            document.body.appendChild(script);
            setTime(callback, script);
        }

超時請求

  • 設置一個全局的定時器標識,用來在回調函數中清除定時器
  • JSONP
    • 傳入兩個參數,一個是回調函數名,一個是script標簽
    • 超時以后,移除監聽函數,移除script標簽
  • XHR
    • 超時之后,調用XHR的about方法,停止請求
    • 由于執行about()方法后,有可能觸發onreadystatechange事件,所以設置一個timeout_bool標識,來忽略終止觸發的事件.
        //設置請求超時
        function setTime(callback, script) {
            if (timeOut !== undefined) {
                timeout_flag = setTimeout(function() {
                    if (dataType === "jsonp") {
                        delete window[callback];
                        document.body.removeChild(script);

                    } else {
                        timeout_bool = true;
                        xhr && xhr.abort();
                    }
                    console.log("timeout");

                }, timeOut);
            }
        }

全部代碼


/**
 * Created by Administrator on 2017/4/12.
 * 自己封裝了一個AJAX小類庫
 */
(function(window,undefined) {
    function ajax(options) {
        //編碼數據
        function setData() {
            //設置對象的遍碼
            function setObjData(data, parentName) {
                function encodeData(name, value, parentName) {
                    var items = [];
                    name = parentName === undefined ? name : parentName + "[" + name + "]";
                    if (typeof value === "object" && value !== null) {
                        items = items.concat(setObjData(value, name));
                    } else {
                        name = encodeURIComponent(name);
                        value = encodeURIComponent(value);
                        items.push(name + "=" + value);
                    }
                    return items;
                }
                var arr = [],value;
                if (Object.prototype.toString.call(data) == '[object Array]') {
                    for (var i = 0, len = data.length; i < len; i++) {
                        value = data[i];
                        arr = arr.concat(encodeData( typeof value == "object"?i:"", value, parentName));
                    }
                } else if (Object.prototype.toString.call(data) == '[object Object]') {
                    for (var key in data) {
                        value = data[key];
                        arr = arr.concat(encodeData(key, value, parentName));
                    }
                }
                return arr;
            };
            //設置字符串的遍碼,字符串的格式為:a=1&b=2;
            function setStrData(data) {
                var arr = data.split("&");
                for (var i = 0, len = arr.length; i < len; i++) {
                    name = encodeURIComponent(arr[i].split("=")[0]);
                    value = encodeURIComponent(arr[i].split("=")[1]);
                    arr[i] = name + "=" + value;
                }
                return arr;
            }

            if (data) {
                if (typeof data === "string") {
                    data = setStrData(data);
                } else if (typeof data === "object") {
                    data = setObjData(data);
                }
                data = data.join("&").replace("/%20/g", "+");
                //若是使用get方法或JSONP,則手動添加到URL中
                if (type === "get" || dataType === "jsonp") {
                    url += url.indexOf("?") > -1 ? (url.indexOf("=") > -1 ? "&" + data : data) : "?" + data;
                }
            }
        }
        // JSONP
        function createJsonp() {
            var script = document.createElement("script"),
                timeName = new Date().getTime() + Math.round(Math.random() * 1000),
                callback = "JSONP_" + timeName;

            window[callback] = function(data) {
                clearTimeout(timeout_flag);
                document.body.removeChild(script);
                success(data);
            }
            script.src = url + (url.indexOf("?") > -1 ? "&" : "?") + "callback=" + callback;
            script.type = "text/javascript";
            document.body.appendChild(script);
            setTime(callback, script);
        }
        //設置請求超時
        function setTime(callback, script) {
            if (timeOut !== undefined) {
                timeout_flag = setTimeout(function() {
                    if (dataType === "jsonp") {
                        delete window[callback];
                        document.body.removeChild(script);

                    } else {
                        timeout_bool = true;
                        xhr && xhr.abort();
                    }
                    console.log("timeout");

                }, timeOut);
            }
        }

        // XHR
        function createXHR() {
            //由于IE6的XMLHttpRequest對象是通過MSXML庫中的一個ActiveX對象實現的。
            //所以創建XHR對象,需要在這里做兼容處理。
            function getXHR() {
                if (window.XMLHttpRequest) {
                    return new XMLHttpRequest();
                } else {
                    //遍歷IE中不同版本的ActiveX對象
                    var versions = ["Microsoft", "msxm3", "msxml2", "msxml1"];
                    for (var i = 0; i < versions.length; i++) {
                        try {
                            var version = versions[i] + ".XMLHTTP";
                            return new ActiveXObject(version);
                        } catch (e) {}
                    }
                }
            }
            //創建對象。
            xhr = getXHR();
            xhr.open(type, url, async);
            //設置請求頭
            if (type === "post" && !contentType) {
                //若是post提交,則設置content-Type 為application/x-www-four-urlencoded
                xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
            } else if (contentType) {
                xhr.setRequestHeader("Content-Type", contentType);
            }
            //添加監聽
            xhr.onreadystatechange = function() {
                if (xhr.readyState === 4) {
                    if (timeOut !== undefined) {
                        //由于執行abort()方法后,有可能觸發onreadystatechange事件,
                        //所以設置一個timeout_bool標識,來忽略中止觸發的事件。
                        if (timeout_bool) {
                            return;
                        }
                        clearTimeout(timeout_flag);
                    }
                    if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {

                        success(xhr.responseText);
                    } else {
                        error(xhr.status, xhr.statusText);
                    }
                }
            };
            //發送請求
            xhr.send(type === "get" ? null : data);
            setTime(); //請求超時
        }
        var url = options.url || "", //請求的鏈接
            type = (options.type || "get").toLowerCase(), //請求的方法,默認為get
            data = options.data || null, //請求的數據
            contentType = options.contentType || "", //請求頭
            dataType = options.dataType || "", //請求的類型
            async = options.async === undefined ? true : options.async, //是否異步,默認為true.
            timeOut = options.timeOut, //超時時間。
            before = options.before || function() {}, //發送之前執行的函數
            error = options.error || function() {}, //錯誤執行的函數
            success = options.success || function() {}; //請求成功的回調函數
        var timeout_bool = false, //是否請求超時
            timeout_flag = null, //超時標識
            xhr = null; //xhr對角
        setData();
        before();
        if (dataType === "jsonp") {
            createJsonp();
        } else {
            createXHR();
        }
    }
    window.ajax = ajax;
})(window);
上面就是封裝好的AJAX類庫??梢阅脕碇苯邮褂?,支持IE和跨域
源碼見這里
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,431評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,637評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,555評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,900評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,629評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,976評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,976評論 3 448
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,139評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,686評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,411評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,641評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,129評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,820評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,233評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,567評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,362評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,604評論 2 380

推薦閱讀更多精彩內容