一、Ajax学习与理解
1、如何发请求?
用 form 可以发请求,但是会刷新页面或新开页面。
用 a 可以发 get 请求,但是也会刷新页面或新开页面。
用 img 可以发 get 请求,但是只能以图片的形式展示。
用 link 可以发 get 请求,但是只能以 CSS、favicon 的形式展示。
用 script 可以发 get 请求,但是只能以脚本的形式运行。
<form action="xxx" method="POST">
<input type="text" name="password">
<input type="submit">
</form>
使用form发送请求,查看请求的内容:
password=123456就是POST请求的第四部分
思考:有没有什么方式可以实现?
- get、post、put、delete 请求都行
- 想以什么形式展示就以什么形式展示
2、微软的突破
IE 5 率先在 JS 中引入 ActiveX 对象(API),使得 JS 可以直接发起 HTTP 请求(想用getpost都可以,想以什么形式展示就以什么形式展示)。
随后 Mozilla、 Safari、 Opera 也跟进(抄袭)了,取名 XMLHttpRequest(全局对象),并被纳入 W3C 规范。
XMLHttpRequest使得浏览器有了和软件一样的体验,不局限于看文章和刷新。
3、AJAX
Jesse James Garrett 讲如下技术取名叫做 AJAX:异步的 JavaScript 和 XML。
- 使用 XMLHttpRequest 发请求
- 服务器返回 XML 格式的字符串
(因为当时XML在当时是流行的数据传输格式,后来用json)
- JS 解析 XML,并更新局部页面
2005年2月,AJAX 这个词第一次正式提出,它是 Asynchronous JavaScript and XML 的缩写,指的是通过 JavaScript 的异步通信,从服务器获取 XML 文档从中提取数据,再更新当前网页的对应部分,而不用刷新整个网页。后来,AJAX 这个词就成为 JavaScript 脚本发起 HTTP 通信的代名词,也就是说,只要用脚本发起通信,就可以叫做 AJAX 通信。W3C 也在2006年发布了它的国际标准。
具体来说,AJAX 包括以下几个步骤:
- 创建 XMLHttpRequest 实例
- 发出 HTTP 请求
- 接收服务器传回的数据
- 更新网页数据
概括起来,就是一句话,AJAX 通过原生的XMLHttpRequest对象发出 HTTP请求,得到服务器返回的数据后,再进行处理。现在,服务器返回的都是JSON格式的数据,XML 格式已经过时了,但是 AJAX这个名字已经成了一个通用名词,字面含义已经消失了。
4、如何使用XMLHttpRequest对象
(1)服务器端代码:
if(path === '/'){
response.statusCode = 200
let string = fs.readFileSync('./index.html')
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write(string)
response.end()
}
对于响应来说,第四部分始终都是字符串,因为response.write(string)返回的是字符串,我们给浏览器返回了符合html格式的字符串。
然后再设置响应头中的Content-Type,response.setHeader('Content-Type', 'text/html;charset=utf-8'),即要求浏览器以HTML的语法解析这段字符串,所以我们可以设置浏览器使用的解析方法为json,也可设置为xml.所以JSON 是一门语言。
http请求的路径都是绝对路径.所以都是以/开头。
(2)开始使用:
我们请求一个以xml格式解析的字符串,然后看响应是什么
服务器端代码:
else if (path === '/xxx') {
response.statusCode = 200
response.setHeader('Content-Type', 'text/xml;charset=utf-8')
response.write(`
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>
`)
response.end()
}
main.js
let myButton = document.getElementById('myButton');
myButton.addEventListener("click",(e)=>{
let request = new XMLHttpRequest();
request.open('GET','/xxx')
//配置request.参数分别为方法和路径
request.send();//发送请求
})
index.html
<body>
<button id="myButton">点我</button>
<script src="main.js"></script>
</body>
当点击点我按钮时,查看发送的请求和收到的响应:
将request打印出来,观察结构:
(3)理解代码中的时间概念:
a. XMLHttpRequest.readyState返回一个整数,表示实例对象的当前状态。该属性只读。
b. 能够返回0,1,2,3,4,具体数字代表看上面的文档.
c. 4,表示服务器返回的数据已经完全接收,或者本次接收已经失败。
d. 通信过程中,每当实例对象发生状态变化,它的readyState属性的值就会改变。这个值每一次变化,都会触发readyStateChange事件。
var xhr = new XMLHttpRequest();
if (xhr.readyState === 4) {
// 请求结束,处理服务器返回的数据
} else {
// 显示提示“加载中……”
}
上面代码中,xhr.readyState等于4时,表明脚本发出的 HTTP 请求已经成功。其他情况,都表示 HTTP 请求还在进行中。
从时间角度看这个过程:
当我们发送一个/xxx请求,使用的时间为9ms,9毫秒实际上很长,看一看在代码中9毫秒可以干什么。
在控制台执行:
console.time();
var a=1 ;
console.timeEnd();
返回结果为default: 0.008056640625ms,声明一个变量只用了0.008ms,打印一句话只用了1ms。
所以9ms对于浏览器来说,对于代码来说是很长的,可以做很多事情。接下来看看readyState属性在一次请求中的变化过程:
let request = new XMLHttpRequest();
request.open('GET','/xxx')
request.send();
setInterval(()=>{//在发送请求的过程中,每一毫秒问一下
console.log(request.readyState);
},1)
结果为:
readyState在这个过程中从1变为4
readyState各个值的含义:
刚刚只显示了1和4的原因是因为2,3太快,比一毫秒还快。01234这四个状态是逐个经过的。我们只需要记住4,4代表请求已经把响应下载完毕了。
(4)XMLHttpRequest.onreadystatechange
XMLHttpRequest.onreadystatechange = callback;
当 readyState 的值改变的时候,callback 函数会被调用。
var xhr= new XMLHttpRequest(),
method = "GET",
url = "https://developer.mozilla.org/";
xhr.open(method, url, true);
xhr.onreadystatechange = function () {
if(xhr.readyState === XMLHttpRequest.DONE && xhr.status ===200) {
console.log(xhr.responseText)
}}
xhr.send();
XMLHttpRequest.DONE就是4
onreadystatechange测试
把监听函数往上写,这样就不会错过每一个readyState的变化:
myButton.addEventListener("click",(e)=>{
let request = new XMLHttpRequest();
request.onreadystatechange = ()=>{//把监听函数往上写,这样就不会错过每一个readyState的变化
console.log(request.readyState);
}
request.open('GET','/xxx')//配置request.参数分别为方法和路径
request.send();//发送请求
// console.log(request)
})
打印结果为1,2,3,4
4代表请求已经把响应下载完毕了,但是请求成功和失败还要看status状态码是大于200小于300还是大于400
request.onreadystatechange = ()=>{
if(request.readyState ===4){
console.log("请求和响应都完毕了");
if ( request.status>=200&&request.status<=400){
console.log('说明请求成功');
console.log(request.responseText);//打印响应的第四部分,字符串
}else if(request.status>=400){
console.log("响应失败");
}
}
}
但是xml结构不方便,需要使用DOMapi去获取数据.现在使用json
(5)使用json解析响应的第四部分
什么是json:json是一门数据格式化语言,用来表示数据
参考网站:https://www.json.org/
以下都是合法的json语法:
"hi",null,["a","b"],{"name":"马涛涛","isBoy":true}
下面不符合:{'x':"y"}必须是双引号
接下来我们返回一个符合json语法的字符串
else if (path === '/xxx') {
response.statusCode = 200
response.setHeader('Content-Type', 'text/xml;charset=utf-8')
response.write(`
{
"note":{
"from":"mataotao",
"to":"ni",
"bool":true,
"arr":["a",1,2,3],
"num":3
}
}
`)
response.end()
使用window.JSON这个API,把符合json语法的字符串转化为js对应的值。这个API就像window.document.getElementById一样,是浏览器提供的api。修改一下main.js将json转化为:
if ( request.status>=200&&request.status<=400){
console.log('说明请求成功');
console.log(request.responseText);
console.log( typeof request.responseText);//string
let string = request.responseText;
//把符合json语法的字符串转化为js对应的值
let object2 = window.JSON.parse(string);
console.log( typeof object2)
console.log(object2)
}
这样我们就可以用object.note.from取到"mataotao"这个字符串
http响应第四部分永远是字符串,只是写的这个字符串刚好符合json对象的语法
5、使用原生JS发送Ajax请求
myButton.addEventListener("click",(e)=>{
//这四句一定要记住
let request = new XMLHttpRequest();
request.onreadystatechange = ()=>{
if(request.readyState === XMLHttpRequest.DONE && request.status === 200) {
console.log(request.responseText)
}
}
request.open('GET','/xxx')//配置request.参数分别为方法和路径
request.send();//发送请求
//这四句一定要记住
})
- 简化版自己实现JQuery
1、自己写jQuery与用jQuery
<!DOCTYPE html><html lang="en"><head>
<meta charset="UTF-8">
<title>jQuery-Study</title>
<script src="01_myjQuery1.js"></script>
<script src="jQuery3.3.1min.js"></script>
<style>
.red{
color: red;
}
.blue{
color: blue;
}
.big{
font-size: larger;
}
.green{
color: green;
}
.pink{
color: pink;
}
</style></head><body>
01_myjQuery1.js
// 得到兄妹结点//传入的参数是一个节点,返回兄妹结点的数组function getSiblings(node) {
var allSilings = node.parentNode.children;
var siblingsArray = {length:0};
for (let i = 0; i < allSilings.length; i++) {
if (allSilings[i]!==node){
siblingsArray[siblingsArray.length] = allSilings[i];
siblingsArray.length++;
}
}
return siblingsArray;
}//添加或删除class//传入的第一个参数是结点,第二个参数是对象, 这个对象里是key:value,key就是要操作的class,value判断操作类型,true添加,false删除function addClass(node,object){
for (let key in object){
//对象调用方法的两种方式:
// obj.f()
// obj['f']()
var methodName;
methodName = object[key] ? 'add':'remove';
node.classList[methodName](key);
/*上面两句代码相当于
if (object[key]) {
node.classList.add(key);
}else {
node.classList.remove(key);
}*/
}
}window.mydom = {};
mydom.getSiblings = getSiblings;
mydom.addClass = addClass;//命名空间,常用的设计或者组合就叫做设计模式.哈希,数组,都是一种设计模式//所以jQuery就是这样来的,jQuery就是一个命名空间,里面有很多函数.
// 命名空间作用:1.方便识别库,2.如果都放在window里,可能会覆盖别的库,所以命名空间是为了防止覆盖别的函数
window.onload = function () {// 测试
// 如何直接使用item3.addclass()?,item3.getSiblings()?
// 方法一:给原型链加公有属性公有方法,但是有缺点,容易覆盖
Node.prototype.getSiblings = function () {
var allSilings = this.parentNode.children;//谁调用这个函数,this就是谁
var siblingsArray = {length:0};
for (let i = 0; i < allSilings.length; i++) {
if (allSilings[i]!==this){
siblingsArray[siblingsArray.length] = allSilings[i];
siblingsArray.length++;
}
}
return siblingsArray;
}
let item3 = document.getElementById('item3');
// console.log(item3.getSiblings.call(item3));如果用cal.第一个参数就是函数里的this,如果不用call(),那么this就自动变成了item3
console.log(' 方法一:');
console.log(item3.getSiblings());//测试成功
Node.prototype.addClass = function (object) {
for (let key in object){
var methodName;
methodName = object[key] ? 'add':'remove';
this.classList[methodName](key);
}
}
// item3.addClass.call(item3,{a:true,xxx:false,c:true});;如果用call.第一个参数就是函数里的this,如果不用call(),那么this就自动变成了item3
console.log(' 方法一:');
item3.addClass.call(item3,{a:true,xxx:false,c:true});
// item3.addClass({a:true,xxx:false,c:true});
console.log(item3);//测试成功
// 如何直接使用item3.addclass()?,item3.getSiblings()?
// 方法二:因为方法一在原型中添加函数容易覆盖,所以自己做一个类似的Node出来
var Node2 = function (node) {// 将要操作的结点传进去,然后返回一个对象,这个对象里给添加了有操作这个节点方法,所以对象.方法就可以实现操作了,而不需要mydom.addclass(item3,...)这样了
return{
getSiblings: function () {
var allSilings = node.parentNode.children;
var siblingsArray = {length:0};
for (let i = 0; i < allSilings.length; i++) {
if (allSilings[i]!==node){
siblingsArray[siblingsArray.length] = allSilings[i];
siblingsArray.length++;
}
}
return siblingsArray;
},
addClass:function (object) {
for (let key in object){
var methodName;
methodName = object[key] ? 'add':'remove';
node.classList[methodName](key);
}
}
}
}
let item4 = document.getElementById('item4');
var item4obj = Node2(item4);
console.log(' 方法二:');
console.log(item4obj.getSiblings());//测试成功
console.log(' 方法二:');
item4obj.addClass({a:true,xxx:false,c:true});
console.log(item4);//测试成功
// 改为jQuery
var jQuery = function (nodeOrSelector) {//将Node2改为jQuery,jQuery可以根据选择器去找到对应的元素
var node;
if(typeof nodeOrSelector==="string"){
node = document.querySelector(nodeOrSelector);
}else {
node = nodeOrSelector;
}
return{
getSiblings: function () {
var allSilings = node.parentNode.children;
var siblingsArray = {length:0};
for (let i = 0; i < allSilings.length; i++) {
if (allSilings[i]!==node){
siblingsArray[siblingsArray.length] = allSilings[i];
siblingsArray.length++;
}
}
return siblingsArray;
},
addClass:function (object) {
for (let key in object){
var methodName;
methodName = object[key] ? 'add':'remove';
node.classList[methodName](key);
}
}
}
}
let item5 = document.getElementById('item5');
var $item5 = jQuery(item5);
console.log(' 改为jQuery方法:');
console.log($item5.getSiblings());
console.log(' 改为jQuery方法:');
$item5.addClass({red:true,xxx:false,c:true});
console.log(item5);//测试成功
var child3 = jQuery('ul>li:nth-child(3)');
child3.addClass({blue:true});
// jQuery操作多个节点
var jQueryS = function (nodeOrSelector) {
var node = {};
if (typeof nodeOrSelector ==='string'){
var temp = document.querySelectorAll(nodeOrSelector);//先用querySelectorAll获取这个伪数组
for (let i = 0; i < temp.length; i++) {
node[i] = temp[i];
}
node.length = temp.length;//将伪数组净化,净化成只有0123值和length的伪数组
} else if(nodeOrSelector instanceof Node){// 如果是node,也将其转化成伪数组
node[0] =nodeOrSelector;
node.length = 1;
}
node.getSiblings = function () {
};
node.addClass = function (classesArray) {//传入class数组,给选择的多个节点都加上数组中class
classesArray.forEach(value=>{
for (let i = 0; i < node.length; i++) {
node[i].classList.add(value);
}
})
}
node.getTexts = function () {
var texts=[];
for (let i = 0; i < node.length; i++) {
texts.push(node[i].textContent);
}
return texts;
}
node.setTexts = function (text) {
for (let i = 0; i < node.length; i++) {
node[i].textContent = text;
}
}
//set和get合并
node.text = function (text) {
if (text===undefined){
var texts=[];
for (let i = 0; i < node.length; i++) {
texts.push(node[i].textContent);
}
return texts;
}else{
for (let i = 0; i < node.length; i++) {
node[i].textContent = text;
}
}
}
return node;
}
var allNodes = jQueryS('ul>li:nth-child(even)');//偶数孩子
allNodes.addClass(['big','green']);
console.log(allNodes.getTexts());
console.log(allNodes.text());
// console.log(allNodes.text(1));//测试成功
//总结:jQuery的作用就是将选择其选择的元素放到一个对象里,这个对象里有01234标序,代表每一个选择的元素,有length代表所有元素加起来总共的长度,有各种方法,addclass,gettext等等.就是反悔了这样一个hash
};
UseJquery.js
var $nodes = $('ul>li:nth-child(even)');//注意$nodesjQuery声明的变量前面要加一个$,防止混淆,因为jQuery声明的变量只能用jQuery的api,不能用dom的api. console.log($nodes);
x.onclick = function () {
$nodes.toggleClass('pink');//toggle,开关,切换
// console.log(1);
}
var colorArray = ['blue','yellow','red','pink','big']
var $nodes2 = $('#ol2>li');
$nodes2.addClass(function (index,currentClass) {
return colorArray[index];
//ol里面的每一个li加了'blue','yellow','red','pink','big'这几个属性
}) //https://www.jquery123.com/addClass/
2、jQuery的原型
测试代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.0.js"></script>
<script>
console.log($.prototype)
console.log($.prototype.__proto__)
console.log($.prototype.__proto__ === Object.prototype)//继承自Object
</script>
</head>
<body>
</body>
</html>
结果如下:
就像Boolea,Array一样,
他的原型就是jQuery.prototyp
e
- 自己实现jQuery的例子
var myjQueryS = function(node){
var Node = {}
Node.length = 0
if((typeof node)==='string'){
var nodearr = document.querySelectorAll(node)
for (let index = 0; index < nodearr.length; index++) {
let element = nodearr[index];
Node[index] = element
Node.length++
}
}else{
Node['0'] = node
Node.length++
}
Node.addClass = function(addclass){
for (let index = 0; index < Node.length; index++) {
let element = Node[index]
element.classList.add(addclass)
}
}
Node.text = function(text){
if(text===undefined){
let textArr = []
for (let index = 0; index < Node.length; index++) {
let element = Node[index]
textArr.push(element.textContent)
}
return textArr
}else{
for (let index = 0; index < Node.length; index++) {
let element = Node[index]
element.textContent = text
}
}
}
return Node
}
var $div = myjQueryS('div')
console.log($div)
$div.addClass('xxx')
console.log($div.text())
// $div.text('xxx')
六、参考资料
1、如何实现一个简化版的 jQuery
https://www.jianshu.com/p/139154f2972f
2、自己封装的简易版jQuery
https://blog.csdn.net/weixin_42755677/article/details/92637467
3、AJAX 教程-w3school
https://www.w3school.com.cn/ajax/index.asp
4、jQuery ajax - ajax() 方法
https://www.w3school.com.cn/jquery/ajax_ajax.asp