说说AJAX

之前讲了讲JSONP,主要就是发请求的问题。本文会慢慢说明啥是AJAX,然后自己实现一个AJAX,同时介绍下Promise优化。

  1. 我们回顾下,如何发请求?

用 form 可以发请求,但是会刷新页面或新开页面
用 a 可以发 get 请求,但是也会刷新页面或新开页面
用 img 可以发 get 请求,但是只能以图片的形式展示
用 link 可以发 get 请求,但是只能以 CSS、favicon 的形式展示
用 script 可以发 get 请求,但是只能以脚本的形式运行

那么有没有什么方式:

  1. 无论get, post, put, delete请求方式都可以。
  2. 想以什么方式展示就以什么方式展示呢

就是我们主角啦:AJAX

  1. 使用XMLHttpRequest发送请求。
  2. 服务器返回XML(JSON)格式的字符串。(注意是字符串哟,可不是对象)
  3. JS解析XML(JSON),并更新局部页面。

还是先上server.js代码:

var http = require('http')
var fs = require('fs')
var url = require('url')
var port = process.argv[2]

if(!port){
    console.log('请指定端口号好不啦?\nnode server.js 8888 这样不会吗?')
    process.exit(1)
}

var server = http.createServer(function(request, response){
    var parsedUrl = url.parse(request.url, true)
    var pathWithQuery = request.url
    var queryString = ''
    if(pathWithQuery.indexOf('?') >= 0){ queryString = pathWithQuery.substring(pathWithQuery.indexOf('?')) }
    var path = parsedUrl.pathname
    var query = parsedUrl.query
    var method = request.method

    /******** 从这里开始看,上面不要看 ************/

    console.log('方方说:含查询字符串的路径\n' + pathWithQuery)

    if(path === '/'){
        var string = fs.readFileSync("./index.html");
        response.statusCode = 200
        response.setHeader('Content-Type', 'text/html;charset=utf-8')
        response.write(string);
        response.end()
    } else if(path === "/main.js") {
        var script = fs.readFileSync("./main.js");
        response.statusCode = 200;
      response.setHeader("Content-type", "text/javascript");
      response.write(script);
      response.end();
    } else if(path === "/xxx") {
        response.statusCode = 200;
        response.setHeader("Content-type", "text/json; charset=utf-8");
        response.write(`{    // 此处返回一个JSON对象形式的字符串
            "node": {
                "name": "Jason",
                "age": "23"
            }
        }`);
        response.end();
    } else{
        response.statusCode = 404
        response.setHeader('Content-Type', 'text/html;charset=utf-8')
        response.write('呜呜呜')
        response.end()
    }

    /******** 代码结束,下面不要看 ************/
})

server.listen(port)
console.log('监听 ' + port + ' 成功\n请用在空中转体720度然后用电饭煲打开 http://localhost:' + port)

html代码:

<button id="myButton">Click me!</button>

main.js代码:

myButton.addEventListener("click", function() {
    var request = new XMLHttpRequest();
    request.open("GET", "/xxx");  // 配置request
    request.onreadystatechange = function() {
        if(request.readystate === 4) {
            if(request.status >= 200 && request.status < 300) {
                console.log("success")
            } else {
                if(request.status > 400) {
                    console.log("fail")
                }
            }
        }
    };
    request.send();
});

我们点击按钮后,会有请求到"/xxx",并返回JSON形式的字符串。


image.png

image.png

我们看到在接口中返回的一些数据,但是我们前端怎么获取这些数据呢?

myButton.addEventListener("click", function() {
    var request = new XMLHttpRequest();
    request.open("GET", "/xxx");  // 配置request
    request.onreadystatechange = function() {
        if(request.readyState === 4) {
            console.log(request)
            if(request.status >= 200 && request.status < 300) {
                console.log("请求成功!");
                console.log(request.responseText);
            } else if(request.status >= 400) {
                console.log("请求失败!")
            }
        }
    };
    request.send();
});

看上述代码,在request.open()和request.send()之间多了一个request.onreadystatechange的属性,request.readyState会从0变到4(请求完成)。

  1. JS 可以设置任意请求 header 吗?
    第一部分:request.open("get", "/xxx");
    第二部分:request.setHeader("Content-type", "x-www-form-urlencoded");
    第四部分:request.send("name=Jason&age=23")

2.JS 可以获取任意响应 header 吗?
第一部分: response.status(状态码) / response.statusText
第二部分: response.getAllResponseHeaders() (获取响应的所有第二部分内容) / response.getResponseHeader(key) ( 获取第二部分中部分内容,比如response.getResponseHeader("Content-type") )
第四部分: request.responseText

接下来我们来封装下AJAX。


// 这是我们以前自己封装jQuery的例子
window.jQuery = function(nodeOrSelector) {
    let nodes = {};
    nodes.addClass = function() {};
    nodes.html = function() {};
    return nodes;
}

window.$ = window.jQuery;


//  接下来我们自己封装下AJAX
window.jQuery.ajax = function(url, method, body, successFn, failFn) {
    let request = new XMLHttpRequest();
    request.open(method, url);    //  配置request
    request.setRequestHeader('content-type','x-www-form-urlencoded');
    request.onreadystatechange = function() {
        if(request.readyState === 4) {  //  加载完成
            if(request.status >= 200 && request.status < 300) {
                successFn.call(undefined, request.responseText);
            } else if(request.status > 400) {
                console.log("请求失败!");
                failFn.call(undefined, request);
            }
        }
    };
    request.send(body);
};

myButton.addEventListener("click", function() {
    $.ajax(
        "/xxx",
        "GET",
        "name=Jason&age=23",
        () => {
            console.log("success!");
        },
        () => {
            console.log("fail!");
        }
    );
});

在上述代码中,我们可以看到对myButton监听函数里的ajax函数的参数很杂,很容易忘记参数的对应属性是啥,也就是语义化不明显,我们可以优化下, 同时增加请求的第二部分header属性。

myButton.addEventListener("click", function() {
    $.ajax({
        url: "/xxx",
        method: "GET",
        header: {
          "Content-type": "x-www-form-urlencoded",
          "Jason": 23
        },
        body: "name=Jason&age=23",
        successFn: () => {
            console.log("success!");
        },
        failFn: () => {
            console.log("fail!");
        }}
    );
});

我们将属性都放入一个对象,这样显得有结构与层次些,那么在ajax函数定义里面也要有相应的修改。

window.jQuery.ajax = function(obj) {
    let { url, method, header, body, successFn, failFn } = obj;
    let request = new XMLHttpRequest();
    request.open(method, url);    //  配置request
    // 设置header部分
    for(let key in header) {
        request.setRequestHeader(key, headers[key]);
    }
    request.setRequestHeader('content-type','x-www-form-urlencoded');
    request.onreadystatechange = function() {
        if(request.readyState === 4) {  //  加载完成
            if(request.status >= 200 && request.status < 300) {
                successFn.call(undefined, request.responseText);
            } else if(request.status > 400) {
                console.log("请求失败!");
                failFn.call(undefined, request);
            }
        }
    };
    request.send(body);
};

然后我们又有如下问题,如果有多个库:

jason.ajax({
  成功: function() {},
  失败: function() {}
});

jack.ajax(null, null, null, successFn, failFn);

这会有一个问题,如果我们不看文档,我们怎么知道传的参数是什么呢?没有一个规范,那么就有了我们这个Promise规范。我们来看看同上的效果,jQuery库怎么实现的。

function success(responseText) {
    console.log(responseText)
}

function fail(request) {
    console.log("fail");
    console.log(request);
}


myButton.addEventListener("click", function() {
    $.ajax({
            url: "/xxx",
            method: "GET",
            body: "name=Jason&age=23",
        }).then(success, fail);
});

或者直接把success函数和fail函数写进then函数里面,作为参数:

myButton.addEventListener("click", function() {
    $.ajax({
            url: "/xxx",
            method: "GET",
            body: "name=Jason&age=23",
        }).then(
            (responseText) => {
                console.log(responseText)
            },
            (request) => {
                console.log("fail");
                console.log(request);
            });
});

如果我们想在success函数里进行多个操作呢?

myButton.addEventListener("click", function() {
    $.ajax({
            url: "/xxx",
            method: "GET",
            body: "name=Jason&age=23",
        }).then(
            (responseText) => {
                console.log(responseText);
                return "成功";
            },
            (request) => {
                console.log("fail");
                console.log(request);
                return "error";
            }).then(
        (上一次操作的结果) => {
            console.log(上一次操作的结果)
        },
        (上一次操作的结果) => {
            console.log(上一次操作的结果)
        },
    );
});
image.png

image.png

那我们自己怎么封装这种jQuery操作呢?

// 这是我们以前自己封装jQuery的例子
window.jQuery = function(nodeOrSelector) {
    let nodes = {};
    nodes.addClass = function() {};
    nodes.html = function() {};
    return nodes;
}

window.$ = window.jQuery;

//  接下来我们自己封装下AJAX
window.jQuery.ajax = function(obj) {
    return new Promise(function(resolve, reject) {
        let { url, method, header, body} = obj;
        let request = new XMLHttpRequest();
        request.open(method, url);    //  配置request
        // 设置header部分
        for(let key in header) {
            request.setRequestHeader(key, headers[key]);
        }
        request.setRequestHeader('content-type','x-www-form-urlencoded');
        request.onreadystatechange = function() {
            if(request.readyState === 4) {  //  加载完成
                if(request.status >= 200 && request.status < 300) {
                    resolve.call(undefined, request.responseText);
                } else if(request.status > 400) {
                    console.log("请求失败!");
                    reject.call(undefined, request);
                }
            }
        };
        request.send(body);
    });
};




myButton.addEventListener("click", function() {
    $.ajax({
            url: "/xxx",
            method: "GET",
            body: "name=Jason&age=23",
        }).then(
        (text) => console.log(text),
        (request) => console.log(request)
    );
});
image.png
image.png

就是window.jQuery.ajax里面返回一个Promise对象,这个对象的参数是一个函数,分别传resoleve(success函数)和reject(fail函数), 然后把之前的操作全部放入这个函数中。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML标准。 注意:讲述HT...
    kismetajun阅读 28,331评论 1 45
  • AJAX全称为Asynchronous JavaScript and XML(异步的JavaScript和XML)...
    betterwlf阅读 3,321评论 0 0
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 136,084评论 19 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 176,123评论 25 709
  • 不到24小时,66万。 23号,Sir带出云舅项目《无名狂》,首次面向社会定下众筹小目标:60天、66万。 结果,...
    Sir电影阅读 3,733评论 0 5

友情链接更多精彩内容