之前讲了讲JSONP,主要就是发请求的问题。本文会慢慢说明啥是AJAX,然后自己实现一个AJAX,同时介绍下Promise优化。
- 我们回顾下,如何发请求?
用 form 可以发请求,但是会刷新页面或新开页面
用 a 可以发 get 请求,但是也会刷新页面或新开页面
用 img 可以发 get 请求,但是只能以图片的形式展示
用 link 可以发 get 请求,但是只能以 CSS、favicon 的形式展示
用 script 可以发 get 请求,但是只能以脚本的形式运行
那么有没有什么方式:
- 无论get, post, put, delete请求方式都可以。
- 想以什么方式展示就以什么方式展示呢
就是我们主角啦:AJAX
- 使用XMLHttpRequest发送请求。
- 服务器返回XML(JSON)格式的字符串。(注意是字符串哟,可不是对象)
- 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(请求完成)。
- 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函数), 然后把之前的操作全部放入这个函数中。
网友评论