手写Ajax(JSONP)

参考:原生 JavaScript 实现 AJAX、JSONP

XMLHttpRequest的使用

参考:XMLHttpRequest
XMLHttpRequest发送请求和获取响应

例子:

var request = new XMLHttpRequest();
request.open("GET", "get.php", true);
request.send();
//该属性每次变化时会触发
request.onreadystatechange = function(){
    //若响应完成且请求成功
    if(request.readyState === 4 && request.status === 200){
        //do something, e.g. request.responseText
    }
}

发送请求:

  1. 建立XMLHttpRequest对象:var request = new XMLHttpRequest();
  2. .open初始化请求:request.open(方法名, URL, 是否异步);
  3. .send(data)发送数据:request.send(data);

监听回复:

​ 1.服务器回复之后readystate变化,调用onreadystatechange

​ 2.根据readystate请求状态码,status服务器返回的响应状态。处理请求成功之后服务器返回的数据

不考虑json的ajax

ajax的使用方式:

ajax({ 
 url: 'test.php',  // 请求地址
 type: 'POST',  // 请求类型,默认"GET",还可以是"POST"
 data: {'b': '异步请求'},  // 传输数据
 success: function(res){  // 请求成功的回调函数
  console.log(JSON.parse(res)); 
 },
 error: function(error) {}  // 请求失败的回调函数
});

需求分析:

​ 根据type的不同发送不同请求,根据服务器返回的状态调用susccess请求成功,error请求失败函数

function  ajax(params) {
    let {method='GET',data={},url,success=function () {},error=function () {}}=params
    let request = new XMLHttpRequest()
    request.open(method,url,true)
    request.send(data)
    request.onreadystatechange=function () {
        if(request.readyState === 4){
            let status = request.status
            if(request.status>=200&&request.status<300){
                success(request.responseText,request.responseXML)
            }else {
                error(status)
            }
        }
    }
}

功能完善:

​ 1.为保证服务器接收数据正确,如果数据类型为key1=val1&key2=val2时客户端所发送的数据都要经encodeURIComponent进行编码。

​ 也就是:1.1发送GET请求

​ 1.2发送POST请求的Content-Type='application/x-www-form-urlencoded'

​ 2.GET方法send(null),POST方法根据Content-Type的不同发送不同的数据数据类型

​ 本例中的Content-Type可为:application/jsonapplication/x-www-form-urlencoded

let TYPE_URLENCODED='application/x-www-form-urlencoded'
let TYPE_JSON = 'application/json'
function  ajax(params) {
    let method = params.method.toUpperCase()||'GET'
    let data = params.data||{}
    let url = params.url
    let success = params.success||function () {}
    let error =params.error||function () {}
    let contentType = params.contentType||'application/x-www-form-urlencoded'
    if(!url){
        console.log('url can\'t be undefined')
        return
    }

    const request = new XMLHttpRequest()

    function urlEncodeFormat(data){
        let encoded=[]
        for (let key in data){
           let unit=encodeURIComponent(key)+'='+encodeURIComponent(data[key])
               encoded.push(unit)
        }
        return encoded.join('&')
    }

    if(method==='GET'){
        data=urlEncodeFormat(data)
        debugger
        let url=params.url+'?'+data
        request.open(method,url,true)
        request.send(null)

    }else if(method==='POST'){
        request.open('POST',url,true)
        if(contentType === TYPE_URLENCODED){
            data=urlEncodeFormat(data)
        }else if( contentType === TYPE_JSON){
            data=JSON.stringify(data)
        }
        request.setRequestHeader('Content-Type',contentType)
        request.send(data)
    }
    request.onreadystatechange = function () {
        if (request.readyState === 4){
            if(request.status>=200&& request.status<300){
                success(request.responseText,request.responseXML)
            }else{
                error(request.status)
            }
        }
    }
}

//使用示例
let baseURL='http://localhost:3333'
ajax({
    url:baseURL+'/getTest',
    method:'GET',
    data:{
        name:'get data',
        data:'gggg'
    },
    success:function (data) {
        console.log(data)
    },
    error:function (err) {
        console.log(err)
    }
})

ajax({
    url:baseURL+'/postTest',
    method:'POST',
    data:{
        name:'post data',
        data:'ppppppp'
    },
    success:function (data) {
        console.log(data)
    },
    error:function (err) {
        console.log(err)
    }
})

ajax({
    url:baseURL+'/postTest',
    method:'POST',
    contentType:'application/json',
    data:{
        name:'post data',
        data:'jjjjj'
    },
    success:function (data) {
        console.log(data)
    },
    error:function (err) {
        console.log(err)
    }
})

JSONP

JSONP原理:1.<script>标签不受同源政策影响,可以跨域根据scr的地址请求资源

​ 2.1用<sctipt>标签请求到的是javascript代码,相当于浏览器直接请求到了这段代码并且执行。服务器向客户端jsonp传参就是在返回的javasctipt代码的函数中传参,类似jsonpCallBack({data:'ddddd'})

​ 2.2 succuss: jsonpCallBack在发送请求前被声明,会拿到服务器返回的参数,调用success(data),将参数传给success

​ 2.3 error:由于没有使用XMLHttpRequest,所以无法根据服务器返回状态调用error——>设置超时定时器,如果一段时间内没有调用jsonpCallBack成功调用success说明超时、请求错误

仅仅实现了客户端,服务端PHP请参考:原生 JavaScript 实现 AJAX、JSONP

let TYPE_URLENCODED='application/x-www-form-urlencoded'
let TYPE_JSON = 'application/json'
function  ajax(params) {
    let data = params.data||{}
    let url = params.url
    let success = params.success||function () {}
    let error =params.error||function () {}
    let jsonpCbCount=0
    if(!url){
        console.log('url can\'t be undefined')
        return
    }

    function urlEncodeFormat(data){
        let encoded=[]
        for (let key in data){
            let unit=encodeURIComponent(key)+'='+encodeURIComponent(data[key])
            encoded.push(unit)
        }
        return encoded.join('&')
    }

    function normalRequest() {
        let method = params.method.toUpperCase()||'GET'
        let contentType = params.contentType||'application/x-www-form-urlencoded'
        const request = new XMLHttpRequest()

        if(method==='GET'){
            data=urlEncodeFormat(data)
            let url=params.url+'?'+data
            request.open(method,url,true)
            request.send(null)

        }else if(method==='POST'){
            request.open('POST',url,true)
            if(contentType === TYPE_URLENCODED){
                data=urlEncodeFormat(data)
            }else if( contentType === TYPE_JSON){
                data=JSON.stringify(data)
            }
            request.setRequestHeader('Content-Type',contentType)
            request.send(data)
        }
        request.onreadystatechange = function () {
            if (request.readyState === 4){
                if(request.status>=200&& request.status<300){
                    success(request.responseText,request.responseXML)
                }else{
                    error(request.status)
                }
            }
        }
    }

    function jsonpRequest() {
        let method = 'GET'
        let timeout=params.timeout||1000
        let data = params.data||{}
        let jsonpCbFn='jsonpCb'+jsonpCbCount//每次jsonp请求返回的script调用的函数都是唯一的
        jsonpCbCount++

        //添加script发送请求
        data.jsonpCbFn=jsonpCbFn
        data= urlEncodeFormat(data)
        let script = document.createElement('script')
        script.src=params.url+'?'+data
        let head=document.getElementsByTagName('head')[0]
        head.appendChild(script)

        //定义服务器返回数据时调用的函数
        window[jsonpCbFn] = function (res) {
            clearTimeout(timer)
            head.removeChild(script)
            success(res)
        }

        let timer = setTimeout(function () {
            head.removeChild(script)
            params.error('jsonp error,timeout:'+timeout)
        },timeout)
    }

    params.jsonp?jsonpRequest():normalRequest()

}

//使用示例
let baseURL='http://localhost:3333'
ajax({
    url:baseURL+'/jsonpTest',
    jsonp:true,
    data:{
        name:'jsonp',
        data:'jsonp/jsonp/jsonp'
    },
    success:function (data) {
        console.log(data)
    },
    error:function (err) {
        console.log(err)
    }
})

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,284评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,115评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,614评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,671评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,699评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,562评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,309评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,223评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,668评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,859评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,981评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,705评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,310评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,904评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,023评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,146评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,933评论 2 355

推荐阅读更多精彩内容