先定一個小目標,本身封裝個ajax

你是否發現項目中有不少頁面只用到了框架不到十分之一的內容,還引了壓縮後還有70多kb的jquery庫
你是否發現項目中就用了兩三個underscore提供的方法,其餘大部分的你方法你甚至歷來沒有看過
你是否發現fetch好像比ajax好用那麼一點
你是否想過本身封裝個ajax....
 
純前端寫得久了,便想折騰點事情。好比先定一個小目標,年前本身寫個類jquery輕量級庫....
 
那麼就從本身封裝一個ajax切入吧,首先我整理的一個流程圖,一目瞭然

 

解析參數數據

一般咱們的請求後面會有一些參數,若是是get請求固然能夠直接經過'&'拼在url後面。那麼post就須要作一下處理了,若是參數是字符串,則將字符串用‘&’符號切割轉化成鍵值對形式,同時用encodeURIComponent轉碼,最後相似於jquery的處理,將/%20/g(空格)替換成'+'。
      getData: function(){
                var name, value;
                if (opts.data) {
                    if (typeof opts.data === "string") {
                        opts.data = opts.data.split("&");
                        for (var i = 0, len = opts.data.length; i < len; i++) {
                            name = opts.data[i].split("=")[0];
                            value = opts.data[i].split("=")[1];
                            opts.data[i] = encodeURIComponent(name) + "=" + encodeURIComponent(value);
                        }
                        opts.data = opts.data.replace("/%20/g", "+");
                    } else if (typeof opts.data === "object") {
                        var arr = [];
                        for (var name in opts.data) {
                            var value = opts.data[name].toString();
                            name = encodeURIComponent(name);
                            value = encodeURIComponent(value);
                            arr.push(name + "=" + value);
                        }
                        opts.data = arr.join("&").replace("/%20/g", "+");
                    }

                    //使用GET方法或JSONP,則手動添加到URL中
                    if (opts.type === "GET" || opts.dataType === "jsonp") {
                        opts.url += opts.url.indexOf("?") > -1 ? opts.data : "?" + opts.data;
                    }
                }
            },

 

建立jsonp

若是dataType爲jsonp的話,其實咱們就能夠理解爲不是ajax請求了,而是僞造了一個script標籤,經過script的src屬性至關於發起了一個請求,其中帶了一個callback(這裏是名稱叫jsonp_timeName)的參數,最終約定好後臺的數據包經過jsonp_name(data)這種形式包裹起來,這樣就至關於前端再回調了一個jsonp_name的方法,將數據經過參數的形式帶過來了,因此前端js須要實現一個名叫jsonp_name的方法。其實jsonp跨域的方法有不少種,好比還有僞造iframe等,跨域詳情解決方案能夠參考個人博文  http://www.cnblogs.com/liliangel/p/5760426.html
 
      createJsonp: function(){
                var script = document.createElement("script"),
                    timeName = new Date().getTime() + Math.round(Math.random() * 1000),
                    callback = "jsonp_" + name;

                window[callback] = function(data) {
                    clearTimeout(ajax.options.timeoutFlag);
                    document.body.removeChild(script);
                    try {
                        data && (data = JSON.parse(data));
                    } catch (e) {
                        console.error('ajax error for json parse responseText');
                    }  
                    ajax.success(data);
                }
                script.src = url +  (url.indexOf("?") > -1 ? "" : "?") + "callback=" + callback;
                script.type = "text/javascript";
                document.body.appendChild(script);
                ajax.timeout(callback, script);
           },

 

建立XHR

首先經過兼容性處理的getXHR()方法獲得xhr對象,接着設置請求頭,區分post、get。每當 readyState 改變時,就會觸發 onreadystatechange 事件,readyState 屬性存有 XMLHttpRequest 的狀態信息。這裏我至關於重寫readystate事件監聽,來作我本身的相應邏輯處理,因爲執行abort()方法後,有可能觸發onreadystatechange事件,因此設置一個ajax.options.timeoutBool標識,來忽略停止觸發的事件。最後調用xhr的send()方法發送出請求,同時渲染timeout()方法。
    createXHR: function(){
                //建立對象
                xhr = ajax.getXHR();
                xhr.open(opts.type, opts.url, opts.async);
                //設置請求頭
                if (opts.type === "POST" && !opts.contentType) {
                    //如果post提交,則設置content-Type 爲application/x-www-four-urlencoded
                    xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
                } else if (opts.contentType) {
                    xhr.setRequestHeader("Content-Type", opts.contentType);
                }
                //添加監聽
                xhr.onreadystatechange = function() {
                    if (xhr.readyState === 4) {
                        if (opts.timeout !== undefined) {
                            //因爲執行abort()方法後,有可能觸發onreadystatechange事件,因此設置一個ajax.options.timeoutBool標識,來忽略停止觸發的事件。
                            if (ajax.options.timeoutBool) {
                                return;
                            }
                            clearTimeout(ajax.options.timeoutFlag);
                        }
                        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
                            var responseText = xhr.responseText;
                            try {
                                xhr.responseText && (responseText = JSON.parse(responseText));
                                opts.success(responseText);
                            } catch (e) {
                                console.error('ajax error for json parse responseText');
                                //opts.error(xhr);
                            }           
                        } else {
                            opts.error(xhr);
                        }
                    }
                };
                //發送請求
                xhr.send(opts.type === "GET" ? null : opts.data);
                ajax.timeout(); //請求超時    
            }

 

兼容IE6

獲取xhr對象可能會存在低版本ie兼容性問題,爲此這樣判斷處理一下
      getXHR: function(){
                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) {
                            console.log('error ajax',e)
                        }
                    }
                }
            }

 

設置請求超時

前面我定義了一個全局的屬性timeoutFlag,這裏經過settimeout延時函數給它賦值。若是是jsonp,則移除原來追加的script標籤,不然經過全局的xhr條用abort()方法終止正在發送的請求!javascript

    timeout: function(callback, script){
                if (opts.timeout !== undefined) {
                    ajax.options.timeoutFlag = setTimeout(function() {
                        if (opts.dataType === "jsonp") {
                            delete window[callback];
                            document.body.removeChild(script);
                        } else {
                            ajax.options.timeoutBool = true;
                            xhr && xhr.abort();
                        }
                    }, opts.timeout);
                }
            },

全局變量

var defaultOpts = {
            url: '', //ajax 請求地址
            type : 'GET', //請求的方法,默認爲GET
            data : null, //請求的數據
            contentType : '',//請求頭
            dataType : 'json', //請求的類型,默認爲json
            async : true, //是否異步,默認爲true
            timeout: 5000, //超時時間,默認5秒鐘
            before : function() {
                console.log('before')
            }, //發送以前執行的函數
            error: function() {
                console.log('error')
            }, //錯誤執行的函數
            success: function() {
                console.log('success')
            } //請求成功的回調函數
        }

        for (i in defaultOpts) {
            if (opts[i] === undefined) {
                opts[i] = defaultOpts[i];
            }
        }
    var xhr = null;

    options: {
                timeoutFlag: null, //超時標識
                timeoutBool: false //是否請求超時
            },

 

初始化調用

我這裏是用面向對象函數方式寫的,核心初始化代碼以下:
init: function(){
                opts.before();
                ajax.getData();
                opts.dataType === "jsonp" ? ajax.createJsonp() : ajax.createXHR();
            },

    ajax.init();

設置AMD等規範

若是是用requireJS這種方法引用,那麼還須要設置一下amd、cmd或者commonJs規範
// AMD && CMD
    if(typeof define === 'function'){
        define(function(){
            return li;
        });
    // CommonJS
    }else if(typeof module !== "undefined" && module !== null){
        module.exports = li;
    // window
    }else{
        window.li = li;
    }

 

調用示例

調用示例,我這裏只貼出異步的方式,固然還有好幾種狀況,好比出錯等,這裏不一一展現
 
默認爲gety異步請求,超時時間5秒鐘。before爲請求前執行的函數,一般咱們能夠寫統一的loading動畫。當發生405/500等這種錯誤時,會調用error方法,固然咱們一般在success回調函數裏作邏輯處理。這裏儘可能跟jquery庫的ajax封裝保持一致,是爲了更符合原有的開發編碼習慣
 

 

結果

控制檯輸出以下:

 

我這裏用$這個符合做爲全局對象引入,也是爲了更符合原來用jquery的編碼習慣,可是這裏只實現了其中的ajax方法哦,其餘項目中高頻出現的方法,以及經常使用的小組件後續慢慢補充....
 
相關文章
相關標籤/搜索