zoukankan      html  css  js  c++  java
  • 原生Ajax函数

    前言

    在日常工作中,我经常使用Jquery的Ajax来获取接口数据。这几天有一个的官网要制作,由于网站比较小,有一些与服务器通信的接口处理,并没有涉及到复杂的交互功能。为了可以减少加载Jquery库的时间,也不用负担Jquery复杂的逻辑处理带来的性能消耗。我决定不使用jquery,自己写了一个原生的Ajax函数。

    需求整理

    一般来说,前端与服务器的通信是使用XHR对象的。我做的官网是有几个异域的接口,然而XHR对象是没有跨域功能的。所以我把JSONP整合进来。
    接下来,我们来看看整体功能图:
    流程图流程图

    输入参数

    首先,我们定义一个Ajax函数,并设置了一些输入参数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function ajax(options){

    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, //是否异步,默认为true.
    timeOut = options.timeOut, //超时时间。
    before = options.before || function(){}, //发送之前执行的函数
    error = options.error || function(){}, //错误执行的函数
    success = options.success || function() {}; //请求成功的回调函数
    }

    参数表:

    参数默认值描述可选值
    url “” 请求的链接 string
    type get 请求的方法 get,post
    data null 请求的数据 object,string
    contentType “” 请求头 string
    dataType “” 请求的类型 jsonp
    async true 是否异步 blooean
    timeOut undefined 超时时间 number
    before function(){} 发送之前执行的函数 function
    error function(){} 请求报错执行的函数 function
    success function(){} 请求成功的回调函数 function

    编码

    一般来说,发送到后端的数据,若是包括中文或某些标点符号时,就要对发送的数据进行编码了。

    • 如果data为字符串,通过&分割,对键名与键值分别编码
    • 如果data为对象,把键值转化为字符串,再进行编码
    • 由于encodeURIComponent不对+编码,所以我们用replace方法手动编码
    • 若是使用get方法或JSONP,则数据是通过URL参数的方法传到后台,所以我们手动添加数据到URL
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    //编码数据
    function setData() {
    var name, value;
    if (data) {
    if (typeof data === "string") {
    data = data.split("&");
    for (var i = 0, len = data.length; i < len; i++) {
    name = data[i].split("=")[0];
    value = data[i].split("=")[1];
    data[i] = encodeURIComponent(name) + "=" + encodeURIComponent(value);
    }
    data = data.replace("/%20/g", "+");
    } else if (typeof data === "object") {
    var arr = [];
    for (var name in data) {
    var value = data[name].toString();
    name = encodeURIComponent(name);
    value = encodeURIComponent(value);
    arr.push(name + "=" + value);
    }
    data = arr.join("&").replace("/%20/g", "+");
    }
    //若是使用get方法或JSONP,则手动添加到URL中
    if (type === "get" || dataType === "jsonp") {
    url += url.indexOf("?") > -1 ? data : "?" + data;
    }
    }
    }

    XMLHttpRequerst

    • 创建XHR对象,并针对IE进行兼容性处理
    • 调用XHR的open方法,设置请求的方法,请求的链接,是否异步
    • 设置请求头
    • 添加监听,如果成功则执行success函数,报错则执行error函数
    • 调用XHR的send方法,发送数据。如果是get方法,我们已经通过setData方法把数据添加到URL中了,所以这里data设置为null
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    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 ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {

    success(xhr.responseText);
    } else {
    error(xhr.status, xhr.statusText);
    }
    }
    };
    //发送请求
    xhr.send(type === "get" ? null : data);

    }

    JSONP

    • 创建script标签
    • 设置回调函数名称
    • 监听回调函数
    • 设置URL,并添加到文档中
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 创建JSONP
    function createJsonp() {
    var script = document.createElement("script"),
    timeName = new Date().getTime() + Math.round(Math.random() * 1000),
    callback = "JSONP_" + timeName;

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

    超时设置

    • 设置一个全局的定时器标识,用来在回调函数中清除定时器
    • JSONP
      • 传入两个参数,一个是回调函数名,一个是script标签
      • 超时之后,移除监听函数,移除script标签
    • XHR
      • 超时之后,调用XHR的abort方法,停止请求
      • 由于执行abort()方法后,有可能触发onreadystatechange事件,所以设置一个timeout_bool标识,来忽略中止触发的事件。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    var timeout_flag = null, //定时器标识
    timeout_bool = false;//是否请求超时
    //设置请求超时
    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);
    }
    }

    添加超时函数,并设置在回调成功后移除定时器

    JSONP

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 创建JSONP
    function createJsonp() {
    ……
    window[callback] = function(data) {
    clearTimeout(timeout_flag);
    document.body.removeChild(script);
    success(data);
    }
    ……
    document.body.appendChild(script);
    setTime(callback, script);
    }

    XHR

    function createXHR() {
        ……    
        //添加监听
        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4) {
                if (timeOut !== undefined) {
                    //由于执行abort()方法后,有可能触发onreadystatechange事件,
                    //所以设置一个timeout_bool标识,来忽略中止触发的事件。
                    if (timeout_bool) {
                        return;
                    }
                    clearTimeout(timeout_flag);
                }
               ……
            }
        };
        //发送请求
        xhr.send(type === "get" ? null : data);
        setTime(); //请求超时
    }
    

    全部代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138

    function ajax(options) {
    //编码数据
    function setData() {
    var name, value;
    if (data) {
    if (typeof data === "string") {
    data = data.split("&");
    for (var i = 0, len = data.length; i < len; i++) {
    name = data[i].split("=")[0];
    value = data[i].split("=")[1];
    data[i] = encodeURIComponent(name) + "=" + encodeURIComponent(value);
    }
    data = data.replace("/%20/g", "+");
    } else if (typeof data === "object") {
    var arr = [];
    for (var name in data) {
    var value = data[name].toString();
    name = encodeURIComponent(name);
    value = encodeURIComponent(value);
    arr.push(name + "=" + value);
    }
    data = arr.join("&").replace("/%20/g", "+");
    }
    //若是使用get方法或JSONP,则手动添加到URL中
    if (type === "get" || dataType === "jsonp") {
    url += url.indexOf("?") > -1 ? 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, //是否异步,默认为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();
    }
    }

    源码下载

    源码放在github中了。欢迎下载,地址

    本文摘自:littleBlack

  • 相关阅读:
    <转>MSDN上关于XPath的语法文章
    <转>正则表达式语法
    <转>反射技术的简单介绍
    <转>css中用expression实现js的onmouseover/onmouseout事件
    <转>在xslt中实现split方法对查询字符串进行分隔
    <转>SQL Server中的XML数据进行insert、update、delete
    <转>VS2010项目转换到VS2008下方法
    <转>在xslt 1.0 中取得当前时间
    C# 发送邮件的Helper类
    DataTable 类的学习笔记
  • 原文地址:https://www.cnblogs.com/rik28/p/6780475.html
Copyright © 2011-2022 走看看