所有代码都在我的GitHub项目ajax-1
AJAX是什么
- AJAX是一种技术
- AJAX内容:用JS发请求和收响应
AJAX的背景
AJAX是浏览器上的功能
-
浏览器可以发请求,收响应
-
浏览器在window上加了一个XMLHttpRequest函数
用这个构造函数(类)可以构造出一个对象
其实和window.Object差不多,Object用来创建普通对象,XMLHttpRequest用来创建XMLRquest对象的。
JS通过它实现发请求,收响应
- 准备一个服务器
- 使用server.js作为我们的服务器
- 下载或复制代码即可用node server.js 8888启动
可用
node-dev server.js 8888来启动服务器
,因为这个可以自动重启更新服务器(需要用yarn global add node-dev
先安装node-dev
) - 添加index.html / main.js两个路由
发送请求,并且这个请求发送成功了的话,我们就对服务器返回给我们的响应干些啥
(一)步骤
第一步:生成请求对象从而生成请求
const request = new XMLHttpRequest();
第二步:用请求对象的open方法设置请求
open(请求动词 请求的url) 就用这俩参数!
request.open(method, url);
第三步:用请求对象的onload、onerror方法监听请求的成功或者失败。你得知道有没有成功啊。如果成功了,我们肯定得到了响应,我们就可以对响应体干些啥!
request.onload = () => { }//如果onload(请求成功),就执行这个函数(对得到的响应内容干些啥)
request.onerror = () => {};//如果onerror(请求失败),就执行这个函数
事实上,当请求失败并不会触发这个onerror。因为onerror不支持Ajax。 所以专业的前端用onreadystatechange
事件来监听请求的成功或失败
第四步:用请求对象的send方法发送请求
request.send();
(二)例子
1、请求CSS
(1)浏览器请求CSS
index.html里有一句<link rel="stylesheet" href="/style.css" />
,意思是index.html的CSS样式是这样来的:浏览器先读到我有个link标签里面指定我的CSS样式在/style.css这个请求的响应体里,浏览器需要请求它然后才能渲染到我身上。所以浏览器知道后就会去请求/style.css,然后得到响应体(也就是style.css文件里的所有CSS内容),然后把这些CSS内容渲染到index.html上
- 文件的名字不重要,重要的是文件里的代码。我们请求这个文件(路径,url)其实只是要它里面的代码,也就是响应体
(2)用Ajax请求CSS
const request = new XMLHttpRequest();
request.open("get", "/style.css");
//如果请求这个url:/style.css成功了,请求对象会获得响应(request.response:style.css里的所有内容)
//思考一下怎么把一串css渲染到html上?
//在head里新建一个style标签,把这串css放到style标签里,浏览器就会自动渲染啦(用DOM)
request.onload = () => {
const style = document.createElement("style"); //生成一个style标签
style.innerHTML = request.response; // 把这串CSS插入到style标签
document.head.appendChild(style); //最后把style标签插入head里完事//CSS就可以在index.html生效了
};
request.onerror = () => {
console.log("失败了");
};
request.send();
我们在index.html里加一个<button id="getCSS">请求CSS</button>
,在main.js里监听这个按钮,当点击按钮时,才请求CSS,方便我们学习研究。
getCSS.onclick = () => {
const request = new XMLHttpRequest();
request.open("get", "/style.css");
request.onload = () => {
const style = document.createElement("style");
style.innerHTML = request.response;
document.head.appendChild(style);
};
request.onerror = () => {};
request.send();
};
2、请求JS
(1)浏览器请求JS
- 在index.html里有
<script src="/2.js"></script>
- script标签里面要是有内容,就是js代码,浏览器直接执行。
- 如果没内容,但是有src,src属性指定外部文件路径
/2.js
,浏览器根据src属性值请求外部文件然后将外部文件的内容也就是js代码插入script标签之间,结果会覆盖原本script标签之间的内容。然后执行这些js代码。
(2)Ajax请求JS
getJS.onclick = () => {
const request = new XMLHttpRequest();
request.open("get", "/2.js");
//当请求这个路径/2.js成功,就会获得响应体,也就是2.js的所有js代码
//思考一下怎么把这些js代码运用到index.html中去
//在body里新建script标签,把这些js代码放到script标签里面去,浏览器就会自动运行这些js代码啦(用DOM)
request.onload = () => {
const script = document.createElement("script");
script.innerHTML = request.response;
document.body.appendChild(script);
};
request.onerror = () => {};
request.send();
};
3、请求HTML
只有Ajax可以请求html
getHTML.onclick = () => {
const request = new XMLHttpRequest();
request.open("get", "/2.html");
//当请求这个路径/2.html成功,就会获得响应体,也就是2.html的所有html代码
//思考一下怎么把这些html代码运用到index.html中去
//在body里新建div标签,把这些html代码放到div标签里面去,浏览器就会自动渲染这些html代码啦(用DOM)
request.onload = () => {
const div = document.createElement("div");
div.innerHTML = request.response;
document.body.appendChild(div);
};
request.onerror = () => {};
request.send();
};
(三)修改第三步:重新用onreadystatechange事件来监听请求的成功或失败
- 每个XMLHttpRequest请求对象都有readyState属性,用于储存该请求对象的状态。他的属性值如下
- 从 0 到 4 发生变化。
- 0: 请求未初始化
- 1: 服务器连接已建立--即第二步请求对象.open()已经执行
- 2: 请求已接收--即第四步请求对象.send()已经执行
- 3: 请求处理中--即已经收到了部分的响应内容
- 4: 请求已完成,且响应已就绪--即所有的响应都收到了
- 当
readState
属性值发生改变时会触发readystatechange事件 - 每个请求对象都有
onreadystatechang
e属性,他会被我们用个函数赋值:监听readystatechange
事件,当这个事件发生,也就是每当readyState
属性改变时,就会调用该函数。 - 我们把这个变为新的第二步,那么之后,每当
readyState
属性改变(2,3,4),就会执行一次onreadystatechange
函数 - 注意请求肯定都会成功的,因为就是你的请求url写错了,在服务器中还是对应着最后一项404那个,所以
readyState
肯定会从0-4
getCSS.onclick = () => {
const request = new XMLHttpRequest();
request.open("get", "/style.css"); //readyState=1
//当readyState变化一次(2/3/4),就会调用这个函数一次,所以之后会调用这个函数三次
//因为前面的readyState=1已经出现了,在我们定义这个函数前就有了
request.onreadystatechange = () => {
if (request.readyState === 4) { //当readyState变为2/3时,调用这个函数但是啥也没干;但是当readyState变为4时,调用这个函数干的事情就多了。
console.log("下载完成"); //首先,肯定表示响应体已经全部下载完了
if (request.status >= 200 && request.status < 300) { //其次,如果响应的状态码为2开头,表示是找到了对应的路径,返回的也是我们想要的响应体CSS代码,那我们就可以开始对响应体搞事情了
const style = document.createElement("style");
style.innerHTML = request.response;
document.head.appendChild(style);
} else { //否则,状态码是 404,表示在服务器里没找到这个路径,响应体也只是空,那么我们就好心的提醒一下你的CSS加载失败了吧
alert("加载CSS失败");
}
}
};
request.send();
};
总结
- HTTP是个框,什么都能往里装
- 可以装HTML、CSS、 JS、XML .....
- 记得在服务器里设置正确的
Content-Type
这是好习惯 - 只要你知道怎么解析这些内容,就可以使用这些内容。不同类型的数据有不同类型的解析办法
- 得到CSS之后生成style标签
- 得到JS之后生成script标签
- 得到HTML之后使用innerHTML和DOM API
- 得到XML之后使用responseXML和DOM API
//请求CSS
getCSS.onclick = () => {
const request = new XMLHttpRequest();
request.open("get", "/style.css"); //readyState=1
request.onreadystatechange = () => {
console.log(request.readyState);
//当statechange发生变化,就会调用这个函数
if (request.readyState === 4) {
console.log("下载完成");
if (request.status >= 200 && request.status < 300) {
const style = document.createElement("style");
style.innerHTML = request.response;
document.head.appendChild(style);
} else {
alert("加载CSS失败");
}
}
};
request.send();
};
//请求JS
getJS.onclick = () => {
const request = new XMLHttpRequest();
request.open("get", "/2.js");
request.onreadystatechange = () => {
console.log(request.readyState);
//当statechange发生变化,就会调用这个函数
if (request.readyState === 4) {
console.log("下载完成");
if (request.status >= 200 && request.status < 300) {
const script = document.createElement("script");
script.innerHTML = request.response;
document.body.appendChild(script);
} else {
alert("加载CSS失败");
}
}
};
request.send();
};
//请求HTML
getHTML.onclick = () => {
const request = new XMLHttpRequest();
request.open("get", "/2.html");
request.onreadystatechange = () => {
console.log(request.readyState);
if (request.readyState === 4) {
console.log("下载完成");
if (request.status >= 200 && request.status < 300) {
const div = document.createElement("div");
div.innerHTML = request.response;
document.body.appendChild(div);
} else {
alert("加载CSS失败");
}
}
};
request.send();
};
//请求XML
getXML.onclick = () => {
const request = new XMLHttpRequest();
request.open("get", "/4.xml");
request.onreadystatechange = () => {
console.log(request.readyState);
if (request.readyState === 4) {
console.log("下载完成");
if (request.status >= 200 && request.status < 300) {
console.log(request.response);
console.log(request.responseXML);
//如果请求xml,那么就不要用response来获得响应体,用responseXML
//这样获得的响应体会自动变成一个dom对象,我们就可以用DOM操作这个XML DOM对象了
//(DOM让HTML成为一个对象,也让XML成为一个对象)
const dom = request.responseXML;
const text = dom.getElementsByTagName("body")[0].textContent;
console.log(text);
} else {
alert("加载CSS失败");
}
}
};
request.send();
};
JSON
什么是JSON
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。是标记语言。和HTML、XML、markdown一样是用来展示数据的
JSON语法
- 必看中文官网
- 只支持6种数据类型
- string-只支持
双引号
,不支持单引号和无引号 - number-支持科学记数法
- bool- true和false
- null -没有undefined
- object
- array
- 不支持函数,不支持变量(所以也不支持引用)
常用API
JSON.parse()
- 将符合JSON语法的字符串转换成JS对应类型的数据
- JSON字符串=> JS数据
- 由于JSON只有六种类型,所以转成的数据也只有6种
- 如果不符合JSON语法,则直接抛出一个Error对象
- 一般用try catch捕获错误
let object
try{object = JASON.parse('{'name':'frank'}')}
catch(error){
console.log('出错了,错误详情是')
console.log(error)
object = {'name':'no name'}
}
console.log(object)
JSON.stringify()
- 是JSON.parse的逆运算
- JS数据=> JSON字符串
- 由于JS的数据类型比JSON多,所以不一定能成功.如果失败,就抛出一个Error对象。或者直接把函数忽略
请求JSON
getJSON.onclick = () => {
const request = new XMLHttpRequest();
request.open("get", "/5.json");
request.onreadystatechange = () => {
if (
request.readyState === 4 &&
request.status >= 200 &&
request.status < 300
) {
console.log(request.response);
const object = JSON.parse(request.response); //用JSON的parse函数可以把符合json语法的东西变成对象或者xx
console.log(object);
myName.textContent = object.name;
}
};
request.send();
};
在index.html里加个 <span id="myName"></span>
,这样当点击按钮,就会把json变成的的dom对象object的name属性的值给span标签,这样页面就会出现我的名字。
加载列表
需求
- 用户打开页面,看到第一页数据
- 用户点击下一页, 看到第二页数据
- 用户点击下一页, 看到第三页数据
- 用户点击下一页, 提示没用更多了
优化点
能不能在点击第三页的时候就禁用下一页按钮
步骤
新建文件夹db,文件page1.JSON、page2.JSON、page3.JSON
(1)第一页
不需要动态加载。静态加载。打开页面就可以看见第一页数据。所以应该在index.html里面就写好:那就在服务器的路由的响应体里从数据库调出数据再加上。前后端不分离的渲染技术
- 在index.html里添加一个占位符
<div>{{page1}}</div>
- 在服务器index.html的路由的响应体里把这个占位符替换成文件page1.JSON的内容
if (path === "/index.html") {
//如果请求是这个,该请求的响应就是这些
response.statusCode = 200;
response.setHeader("Content-Type", "text/html;charset=utf-8");
//把index.html变成字符串string
let string = fs.readFileSync("public/index.html").toString();
//把page1.json变成字符串page1
const page1 = fs.readFileSync("db/page1.json").toString();
//因为字符串page1符合JSON语法,所以变成js数据中的数组
const array = JSON.parse(page1);
//把数组中每一项元素的id都包上li,成为一新数组,之后把这个新数组变成字符串result
const result = array.map(item => `<li>${item.id}</li>`).join("");
//把字符串string里的 占位符 变成 result外部再包上ul的字符串,成为新的字符串string
string = string.replace("{{page1}}", `<ul id='xxx'>${result}</ul>`);
//响应体则为字符串string
response.write(string);
response.end();
}
(2)第二页
需要点击下一页才能看到,用AJAX
- 在index.html里添加按钮
<button id="getPAGE"请求下一页</button>
- 在server.js里加上page2.json的路由
- 在main.js里面监听该按钮
getPAGE.onclick = () => {
const request = new XMLHttpRequest();
request.open("get", "/page2.json");
request.onreadystatechange = () => {
if (
request.readyState === 4 &&
request.status >= 200 &&
request.status < 300
) {
console.log(request.response);
//把符合json语法的响应体变成对应的js数组
const array = JSON.parse(request.response);
//数组中的每一个元素都
array.forEach(element => {
const li = document.createElement("li"); //先新建元素li
li.textContent = element.id; //让li里的内容变成元素的id
xxx.appendChild(li); //让li成为ul的孩子
});
}
};
request.send();
};
(3)第三页
重新修改监听请求下一页的按钮
- 在server.js里加上page2.json的路由
let n = 1; //声明页数为n,初始值是1。 第一页
getPAGE.onclick = () => {
const request = new XMLHttpRequest();
request.open("get", `/page${n + 1}.json`); //每次点击,请求的url是page n+1(当前页数的下一页)
request.onreadystatechange = () => {
if (
request.readyState === 4 &&
request.status >= 200 &&
request.status < 300
) {
console.log(request.response);
//把符合json语法的响应体变成对应的js数组
const array = JSON.parse(request.response);
//数组中的每一个元素都
array.forEach(element => {
const li = document.createElement("li"); //先新建元素li
li.textContent = element.id; //让li里的内容变成元素的id
xxx.appendChild(li); //让li成为ul的孩子
});
n+=1 //每次请求成功了,函数都执行完了,就把n变成n+1.上面的n+1只是输出n+1,并没有改变n的值
}
};
request.send();
};
关于路径
在server.js里 if里面的是用户将要输入的路径,当时是根据用户来,是请求路径,而不是根据你文件夹里的路径来啊
在响应体里的路径,当然是根据你的文件夹里那个文件的路径来,因为你确实是要调用这个文件里的内容。所以server.js和public是同级,当前路径就是server.js和public的爸爸(根路径),所以public里的文件可以写成"(当前路径:空)public/main.js"
else if (path === "/main.js") {
response.statusCode = 200;
response.setHeader("Content-Type", "text/javascript;charset=utf-8");
response.write(fs.readFileSync("public/main.js"));
response.end();
} else if (path === "/style.css") {
在main.js里ajax时,请求的路径当然是if里面那个路径,因为是要对她请求的请求路径啊
getCSS.onclick = () => {
const request = new XMLHttpRequest();
request.open("get", "/style.css"); //readyState=1
request.onreadystatechange = () => {
console.log(request.readyState);
//当statechange发生变化,就会调用这个函数
if (request.readyState === 4) {
console.log("下载完成");
if (request.status >= 200 && request.status < 300) {
const style = document.createElement("style");
style.innerHTML = request.response;
document.head.appendChild(style);
} else {
alert("加载CSS失败");
}
}
};
request.send();
};
在index.html里你要引用main.js,那也是浏览器去向它发请求,必须是请求路径!
<script src="/main.js"></script>
但是以前我们写网页,没自己写服务器,用的是http server
静态服务器 提供的网页预览,(他会自动把应该是请求的路径都改成请求路径,就算我们没有写对)这句话先写着,可能不对。