24_用js写一个ajax的原生实现方法

2020-04-17  本文已影响0人  沐向

一、JS原生Ajax

1、基本概念

2、函数封装

前端代码:

  ajax({
        url: "http://localhost:3000/api/userinfo",              //请求地址
        type: "POST",                       //请求方式
        data: { name: "super", age: 20 },        //请求参数
        dataType: "json",
        success: function (response, xml) {
            console.log(response)
            response = JSON.parse(response) || {}
            var temp = [];
            if (response.status === 'success') {
                var data = response.data;
                Object.keys(data).forEach(function(key){
                    temp.push('<span>'+key+':'+data[key]+'</span><br/>');
                })
                document.write(temp.join(''));
            }
        },
        fail: function (status) {
            console.error(status);
        }
});

function ajax(options) {
    options = options || {};
    options.type = (options.type || "GET").toUpperCase();
    options.dataType = options.dataType || "json";
    var params = formatParams(options.data);

    //创建 - 非IE6 - 第一步
    if (window.XMLHttpRequest) {
        var xhr = new XMLHttpRequest();
    } else { //IE6及其以下版本浏览器
        var xhr = new ActiveXObject('Microsoft.XMLHTTP');
    }

    //接收 - 第三步
    xhr.onreadystatechange = function () {
        if (xhr.readyState == 4) {
            var status = xhr.status;
            if (status >= 200 && status < 300) {
                options.success && options.success(xhr.responseText, xhr.responseXML);
            } else {
                options.fail && options.fail(status);
            }
        }
    }

    //连接 和 发送 - 第二步
    if (options.type == "GET") {
        xhr.open("GET", options.url + "?" + params, true);
        xhr.send(null);
    } else if (options.type == "POST") {
        xhr.open("POST", options.url, true);
        //设置表单提交时的内容类型
        xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        xhr.send(params);
    }
}
//格式化参数
function formatParams(data) {
    var arr = [];
    for (var name in data) {
        arr.push(encodeURIComponent(name) + "=" + encodeURIComponent(data[name]));
    }
    arr.push(("v=" + Math.random()).replace(".",""));
    return arr.join("&");
}

后端逻辑:

//server.js
var http = require('http');
var fs = require('fs');
var querystring = require('querystring');

var server = http.createServer(function(req, res) {

    // 前端页面请求路由
    if (req.url == "/index") {
        fs.readFile('index.html', function(err, data) {
            res.writeHead(200, { "Content-type": "text/html;charset=utf-8" });
            res.end(data);
        });
    } else {
        fs.readFile('404.html', function(err, data) {
            res.writeHead(200, { "Content-type": "text/html;charset=utf-8" });
            res.end(data);
        });
    }

    
    // 请求方法
    if (req.method.toLowerCase() === 'post') {
        // 接口路由
        if(req.url == "/api/userinfo") {
            var reqData = '';
            req.on('data', function (chunk) {
                reqData += chunk;
            });

            req.on('end', function () {
                //将字符串转换位一个对象
                console.log(reqData); //name=super&age=20&v=0865843628884519

                var dataString = reqData.toString();
                //将接收到的字符串转换位为json对象
                var dataObj = querystring.parse(dataString);
                dataObj.sex = '男'
                //输出数据
                var data={
                status:'success',
                message:'获取数据成功',
                    data:dataObj
                };
                res.writeHead(200, { "Content-Type": "application/json;charset=UTF-8" });
                res.end(JSON.stringify(data));
            });
        } 
    };
});

server.listen(3000, '127.0.0.1');

console.log('启动服务,监听 127.0.0.1:3000');

3、函数解析

(1)创建

(2)连接和发送

要点:

(3)接收

要点

二、Fetch API

Fetch API提供了一个fetch()方法,它被定义在BOM的window对象中,你可以用它来发起对远程资源的请求。 该方法返回的是一个Promise对象,让你能够对请求的返回结果进行检索。

1、Fetch 基本用法

fetch()方法,包含了需要fetch 的网址和对应的属性设定( 例如method、headers、mode、body...等,最基本的写法属性不一定要填),执行之后会送出Request,如果得到回应就会回传带有Response 的Promise 内容,使用then 将回传值传递下去。

fetch('网址')
    .then(function(response) {
// 处理 response
    }).catch(function(err) {
// 错误处理
});

2、Fetch 的 Request 属性

以下列出Fetch常用的的Request属性。

属性 设定值
url 第一个参数,必填项,代表需要fetch对象的网址
method GET、POST、PUT、DELETE、HEAD ( 默认GET )
headers 设置相关的Headers 内容( 预设{} )
mode cors、no-cors、same-origin、navigate ( 默认cors )
referrer no-referrer、client 或某个网址( 默认client )
credentials omit、same-origin、include ( 默认omit )
redirect follow、error、manual ( 默认manual )
cache default、no-store、reload、no-cache、force-cache ( 默认default )

3、Fetch 的Response 属性

以下列出Fetch常用的Response属性。

属性 设定值
headers 包含与response 相关的Headers 内容
ok 成功返回true,不成功返回false
status 状态代码,成功为200
statusText 状态信息,成功为ok
type response 的类型,例如basic、cors...等
url response 的url

4、Fetch 的Response 方法

以下列出Fetch常用的Response方法。

属性 设定值
json() 返回Promise,resolves 是JSON 对象
text() 返回Promise,resolves 是text string
blob() 返回Promise,resolves 是blob ( 非结构化对象,例如文字或二进制信息)
arrayBuffer() 返回Promise,resolves 是ArrayBuffer ( 有多少bytes )
formData() 返回Promise,resolves 是formData ( 表单资料对应的的Key 或Value )
clone() 创建一个Response对象的克隆。
error() 返回Response 的错误内容

5、Fetch 的Get 用法

Get 是Fetch 最简单的方法,使用Get 必须要将fetch 第二个参数里的method 设定为get,如果遇到跨域问题,就搭配其他属性例如mode、credentials 来进行细部设定(但针对非跨域的就没用了),下方的示例做了一个简单的后端请求,通过fetch 传递姓名和年纪的参数,就会看到后端回应一串文字。

前端代码:

const name = 'ooxx';
const age = 18;
const uri = `http://127.0.0.1:3000/api/fetch/userinfo?name=${name}&age=${age}`;
fetch(uri, {method:'GET'})
    .then(res => {
        return res.text();// 使用 text() 可以得到纯文字 String
    }).then(result => {
      console.log(result); // 得到「你的名字是:ooxx,年纪:18 岁」
});

后端逻辑:

var pathname = url.parse(req.url).pathname;
if (pathname == "/api/fetch/userinfo") {
    var reqData = url.parse(req.url).query;
    var dataStr = querystring.parse(reqData);
    res.writeHead(200, { "Content-Type": "application/json;charset=UTF-8" });
    res.end('你的名字是:'+dataStr.name +',年纪:'+dataStr.age+' 岁');
}

6、Fetch 的Post 用法

使用POST方法可以搭配body属性设定传递参数,比如我的接口地址,可以接收name和age所组成的JSON请求,当网址接收到要求后,就会回应一个json对象,需要注意的是,如果是传递「中文」可能会出现乱码,这时可以使用encodeURI来做转码,且要通过JSON.stringify来转换成string方式传递。

前端代码:

fetch(uri, {
    method:'POST',
    body:encodeURI(JSON.stringify({
        name:'ooxx',
        age:18
    })),
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'
    }
})
    .then(res => {
        return res.json(); // 使用 json() 可以得到 json 对象
    }).then(result => {
    console.log(result);
    // 得到 {name: "ooxx", age: 18, text: "你的名字是 ooxx,年纪18岁~"}
});

后端逻辑:

if(req.url == "/api/fetch/userinfo") {
    var reqData = '';
    req.on('data', function (chunk) {
        reqData += chunk;
    });

    req.on('end', function () {
      //将字符串转换位一个对象
      var dataObj = JSON.parse(decodeURI(reqData.toString()));
      var data = {
        name: dataObj.name,
        age: dataObj.age,
        text: '你的名字是 '+dataObj.name+',年纪'+dataObj.age+'岁~'
      }

      res.writeHead(200, { "Content-Type": "application/json;charset=UTF-8" });
      res.end(JSON.stringify(data));
    });
}

7、Fetch 搭配async、await、promise.all

过去在XMLHttpRequest 或jQuery AJAX 的全盛时期,如果要确保每个GET 或POST 的要求,都要按照指定的顺序进行,往往会用上一连串的callback 辅助,但是当callback 越来越多,代码也就越来越难管理,然而fetch 返回的是一个Promise,我们也就能直接利用await 或promise.all 的作法,轻松掌握同步与非同步之间的转换。

下方的例子是一个非同步的示例,因为没有进行任何的同步处理,所以执行之后,会先出现hello的文字,接着才是通过fetch 得到的结果。

const postURL = (name,age) => {
    return fetch(uri, {
        method:'POST',
        body:encodeURI(JSON.stringify({
            name:name,
            age:age
        })),
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'
        }
    })
        .then(res => {
            return res.json();
        }).then(result =>{
            console.log(result);
        });
};
postURL('Lily',18);
console.log('hello!!!');
postURL('Tom',18);

因为fetch 的特性,可以改成async 和await 的写法,执行后也就能按照我们要的顺序进行。

async function fetchAll () {
  await Promise.all([postURL('Lily',18), postURL('Tom',18)]);
  console.log('hello!!!');
}
fetchAll()

8、兼容性

关于Fetch API的兼容性,现代浏览器大部分还是支持的,可以放心使用,如下图所示:

Fetch API 的神奇,简化了许多原本较为复杂的用法,也让项目代码写起来更加干净易读好维护。更重要的是 JavaScript ES6 原生支持,你不需要安装任何依赖包,直接可以在项目中使用。

上一篇 下一篇

猜你喜欢

热点阅读