• 先定一个小目标,自己封装个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()方法终止正在发送的请求!

        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方法哦,其他项目中高频出现的方法,以及常用的小组件后续慢慢补充....
     
  • 相关阅读:
    BZOJ 2073: [POI2004]PRZ [DP 状压]
    POJ 2404 Jogging Trails [DP 状压 一般图最小权完美匹配]
    BZOJ 2595: [Wc2008]游览计划 [DP 状压 斯坦纳树 spfa]【学习笔记】
    BZOJ 1226: [SDOI2009]学校食堂Dining [DP 状压]
    BZOJ 2734: [HNOI2012]集合选数 [DP 状压 转化]
    BZOJ 1097: [POI2007]旅游景点atr [DP 状压 最短路]
    BZOJ 1072: [SCOI2007]排列perm [DP 状压 排列组合]
    USACO 状压DP练习[3]
    CF781D Axel and Marston in Bitland [倍增 矩阵乘法 bitset]
    Codeforces Round #403 (Div. 2, based on Technocup 2017 Finals)
  • 原文地址:https://www.cnblogs.com/liliangel/p/6163994.html
Copyright © 2020-2023  润新知