Ajax 学习
-
Ajax 可以再在不刷新网页的情况下更新页面内容
-
检测用户名能否使用就是使用了 Ajax
-
XML 和 HTML 的区别:HTML中都是预定义标签,例如 a 就是超链接标签,span 就是行内元素标签,在 XML 中没有预定义标签
-
Ajax 可以通过用户事件来更新页面部分的内容 (例如鼠标移动)
-
Ajax 的缺点,没有浏览历史,不能回退,存在跨域问题,SEO 不友好
// 原生 Ajax 请求模板
// 发送请求
const xhr = new XMLHttpRequest();
// 初始化
xhr.open('GET', 'http://127.0.0.1:8000/json-server');
xhr.send();
xhr.addEventListener('readystatechange', () => {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
}
}
})
HTTP 协议
-
HTTP 是一种超文本传输协议,他规定了请求和响应的部分
-
请求报文:
- 请求行:GET 或 POST + URL + HTTP 协议版本
- 请求头:Host: baidu.com,Cookie: name = noobming
- 空行:真的空了一行
- 请求体 GET 请求体为空,POST 请求体可以不为空
-
响应报文:
- 响应行:协议版本 + 相应状态码 (200) + 相应状态字符串 (‘OK’)
- 响应头: 对响应体内容进行描述
- 空行:真的空了一行
- 响应体:返回的结果
-
在 google 浏览器中查看报文:打开浏览器里的 F12 开发者工具,然后到 network 选项里并且刷新页面,就可以看到一堆请求 (看不到请求的话请 F5 刷新一下)
- Query String Parameters 是对请求行的 URL 里面的参数提取出来方便查看
安装 Nodejs 和 Express
- 首先在工作区终端执行
npm init --yes
完成初始化 - 再执行
npm i express
安装 express - 编写 express:
// 引入 express
const express = require('express');
// 创建应用对象
const app = express();
// 创建路由规则
app.get('/', (request, response) => {
// 设置简单的响应
response.send('hello express');
})
// 监听端口,启动服务
app.listen(8000, () => {
console.log("服务启动,监听 8000 端口");
})
- 在脚本目录下执行
node expressTest.js
- 在浏览器中打开 localhost:8000 观察结果
Ajax 案例的准备
- 不能同时启动两个服务,要先关闭掉一个服务 (关闭服务的方法:Ctrl+c)
- 其他的和上面的是一样的,路由规则这里有一点改动
// 创建路由规则
app.get('/server', (request, response) => {
// 设置响应头 设置允许跨域
response.setHeader('Access-Control-Allow-Origin', '*');
// 设置响应体
response.send('hello ajax');
})
-
打开 localhost:8000/server 观察响应头和页面文字
-
readystate 属性,分别有状态 0,1,2,3,4
readystate属性 | 解释 |
---|---|
0 | 未初始化 |
1 | 载入 (调用 open 方法) |
2 | 载入完成 |
3 | 交互 (开始接受服务器响应的数据) |
4 | 完成响应 |
- readystatechange 属性是只要状态码改变就触发这个事件,但是我们需要判断 readystate == 4 的时候也就是数据已经到客户端的时候才能执行代码
- 完整代码:
const btn = document.querySelector('button');
const result = document.querySelector('#result');
btn.addEventListener('click', () => {
// 创建对象
const xhr = new XMLHttpRequest();
// 初始化
xhr.open('GET', 'http://127.0.0.1:8000/server');
// 发送
xhr.send();
// 事件绑定,处理服务端返回的结果
xhr.addEventListener('readystatechange', () => {
// 服务端返回了所有的结果时才执行这个事件
if (xhr.readyState === 4) {
// 判断响应的状态码 == 200
if (xhr.status >= 200 && xhr.status < 300) {
// 处理客户端响应结果
// 相应行:
console.log(xhr.status);// 状态码
console.log(xhr.statusText);// 状态字符串
console.log(xhr.getAllResponseHeaders());// 获取所有响应头
console.log(xhr.response); // 响应体
// 设置返回到页面的文本
result.innerHTML = xhr.response;
}
}
})
})
-
这样我们就没有刷新页面就得到了服务端返回的值
-
设置 Ajax 请求的参数
- 在 URL 后面添加参数,例如:
?a=10&b=20
- 在 URL 后面添加参数,例如:
Ajax Post 请求
- post 请求需要修改
open
函数
xhr.open('POST', 'http:/127.0.0.1:8000/server');
- 并且需要在服务端添加 post 方法 (和 get 方法基本一样)
app.post('/server', (request, response) => {
// 设置响应头 设置允许跨域
response.setHeader('Access-Control-Allow-Origin', '*');
// 设置响应体
response.send('hello ajax by POST');
})
- post 请求传递参数是在 send 函数里添加的
xhr.send('a=100&b=200');
Ajax 设置请求头的信息
- 在 open 方法后面添加方法
xhr.setRequestHeader('请求头名','请求头值');
- Content-Type:设置请求体类型
- 有些自定义请求头浏览器不允许我们发送 (例如 name)
- 需要在服务端的 post 方法中添加这样一个自定义属性
// 表示所有类型的头信息我都可以接受
response.setHeader('Access-Control-All-Headers','*');
- 注意:setHeader 需要放在 send 方法的前面,否则会报错
- 这时浏览器会发一个 option 的请求来判断是否有这个权限,这时可以把 post 请求改成 all 表示接受所有类型的请求
Ajax 服务端响应 json
response.send()
方法只能传递字符串类型的数据,所以要对 json 格式的数据进行转换
// 数据构建
const data = {
name: 'noobming'
};
// 转换成字符串类型
let str = JSON.stringify(data);
// 设置响应体
response.send(str);
- html 页面获取的字符串可以手动转换成 json 格式
let data = JSON.parse(xhr.response);
-
自动转换:设置响应体数据类型
-
在上面设置
xhr.responseType = 'json'
nodemon 工具
- nodemon 自动重新启动应用
Ajax 中的 IE 缓存问题
- 当 IE 接收 Ajax 请求的时候他会将请求的数据缓存到本地,当第二次请求的时候 IE 不从服务器获取数据,而是从本地缓存获取数据 (数据未更新)
- 这个问题在后续的 ie 版本中被解决了
- 解决方法 在请求地址后面增加时间戳参数,这样浏览器就会认为这是两次不同的请求,这样他就会发一个新的请求而不是走本地缓存
- 例如:
xhr.open("GET","http://127.0.0.1:8000/ie?t="+Date.now());
Ajax 网络请求超时或者网络异常的处理
- 网络请求超过 2 秒没有回应,就取消这个请求
xhr.timeout = 2000;
- 要在网络良好的时候设置延时多少秒可以使用 setTimeout 函数包裹延时函数
app.get('/delay', (request, response) => {
// 设置响应头 设置允许跨域
response.setHeader('Access-Control-Allow-Origin', '*');
setTimeout(()=>{
response.send('hello delay');
},3000)
})
- 或者也可以使用回调函数监听这个事件
// 需要提前设置
xhr.ontimeout = function() {
alert('网络异常');
}
- 还有网络请求失败的事件
xhr.onerror = function() {
alert('网络错误');
}
- 网络错误可以在 chrome 里面的开发者工具里面的 network 里面设置,可以设置正常网速,3g 网,或者掉线
Ajax 取消请求
- 取消请求使用
abort()
函数,使用时这个请求就会被取消 - 注意变量作用域的问题,他们两个要处在同一个对象调用的函数才能终止请求
Ajax 重复发送请求
- 当用户点击一下就会给服务器发送一个请求,点击次数过多导致请求过多导致服务器压力过大
这个我想到了轮播图里的节流阀的原理
- 解决方法:给本地添加一个节流阀,当第二个相同请求发送的时候就先把第一个未完成的请求给取消掉
- 设置一个 flag 变量,当开始发送请求的时候将 flag 改为 true 当 readyState 为 4 的时候,也就是完成响应的时候把 flag 改为 false
- 判断 flag,如果为 true 的话 (也就是正在发送请求) 时取消上一个请求 (abord),发送新请求
// 是否正在发送请求 (flag)
let isSending = false;
btns[0].onclick = function() {
// 判断标识变量是否有正在发送请求,如果有,就取消这个请求
if(isSending) x.abort();
x = new XMLHttpRequest();
isSending = true;
x.open('GET', 'http://127.0.0.1:8000/delay');
x.send();
x.onreadystatechange = function() {
if(x.readyState===4) {
// 请求完成,修改标识变量
isSending = false;
}
}
}
在 jQuery 中发送 Ajax 请求
- 在 jQuery 中发送 Ajax 的模板
$("button").eq(0).on("click", function () {
$.get('http://127.0.0.1:8080/jquery-server',{a:100,b:200},function(data) {
// data 参数是响应体参数
},'json');
// 最后一个参数是响应体类型
})
-
服务端和正常的是一样的
-
get 方法和 post 方法的区别,get 方法是将请求的参数贴到 url 地址的后面,而 post 请求是将参数存到请求体中
-
第四个参数是可能返回的数据格式,假如你返回的是一个字符串格式的json (
JSON.stringify()
) 如果你不加最后一个参数 json 的话,他返回的就是一个普通的字符串,如果你加了最后一个参数,他返回的就是一个 json 格式的对象 -
通用型方法 Ajax
$.ajax({
// url
url: "http://127.0.0.1:8000/jquery-server",
// 参数
data: {
a: 10,
b: 20
},
// 请求类型
type: "GET",
// 响应体结果
dataType: 'json',
// 成功的回调函数
success: function(data) {
console.log(data);
},
// 超时时间
timeout: 2000,
// 失败的回调函数
error: function() {},
// 头信息的设置
headers: {
a: 100,
b: 200
// 在使用这个的时候需要设置响应体的跨域
}
})
- 在相应的返回值上可以不用将 json 转换为字符串格式他也能成功返回
- 在设置跨域请求的时候需要在服务端添加上这两个响应头:
response.setHeader('Access-Control-Allow-Origin', '*');
response.setHeader('Access-Control-All-Headers', '*');
axios 发送 Ajax 请求
- 使用 cdn 链接引入 axios 推荐 bootCDN
- 设置 baseURL 之后,下面的所有 url 都不用加前面相同的那一堆,直接写最后的部分就可以
// 设置 baseURL
axios.defaults.baseURL = 'http://127.0.0.1:8000';
// GET 请求
axios.get("/axios-server", {
// URL 参数
params: {
id: 100,
name: "noobMisng"
},
// 请求头信息
headers: {
age: 20
}
}).then(value => {
// 返回结果
console.log(value);
})
- post 请求的时候多了一个请求体参数 data (对象格式)
- post 请求中第二个参数作为请求体的参数传递过去,第三个数据是其他参数
axios.post("/axios-server",{
// 请求体传递参数 (data)
name: "noobMing",
age: 18
},{
// 其他参数
params: {
id: 100,
name: "noobMisng"
},
})
-
使用 axios 方法发送 Ajax 请求
-
代码如下:
axios({
// 请求方法
method: "POST",
// url (前提设置 baseURL)
url:"/axios-server",
// url 参数
params: {
vip: 10,
level:20
},
// 头信息
headers: {
a:100,
b:200,
},
// 请求体参数
data: {
username:"noobMing",
age:18
}
}).then(response=>{
console.log(response);
})
使用 fetch 函数发送 Ajax 请求
- fetch 是全局对象,可以直接调用,返回一个 promise 对象
- 要是想传递 url 参数的话就直接在 url 后面连接上
fetch('http://127.0.0.1:8000/axios-server', {
// 请求方法
method: "POST",
// 请求头
headers: {
name: "noobMing"
},
// 请求体
body: 'username=admin'
}).then(response => {
console.log(response);
});
跨域
同源策略
- 同源:url 和 Ajax 请求的 url 必须协议,域名,端口号必须完全相同
- 违背同源策略就是跨域
jsonp 解决跨域
-
jsonp 是一种非官方解决跨域的方法,只支持 get 请求
-
有一些标签天生具备跨域的能力,例如 img link iframe 和 script
-
用 script 实现 jsonp 服务的时候必须使用 JavaScript 代码作为返回值,才能被网页接收
<!-- jsonp 服务 -->
<script src="要跨域的部分"></script>
- 下面是原生跨域操作的实例:
// 服务端
app.all('/result', (request, response) => {
const data = {
name: "noobMing"
};
let str = JSON.stringify(data);
response.send(`handle(${data})`);
})
<!-- 客户端 -->
<script>
// 处理数据
function handle(data) {
const result = document.querySelector('#result');
result.innerHTML = data.name;
}
// script 标签甚至可以自己创建而不用预先写
const script = document.createElement('script');
// 设置 src 属性
script.src = 'http://127.0.0.1:8000/result';
// 把 script 标签放入文档中
document.body.appendChild(script);
</script>
- 这个方法就相当于 script 向后端发送一个 get 请求,返回的是一个前端 (服务端) 预先定义好的一个函数,通过参数来把数据传递过来,前端使用这个函数来处理传过来的数据
jQuery 发送 jsonp 请求
- 使用 jQuery 发送 jsonp 请求的时候需要添加一个 url 参数
?callback=?
$.getJSON('url',function(data){});
- 在 jQuery 中感觉他不用前端需要提前注册好函数,他会自动注册一个函数,存在 callback 参数里
// 获取函数
let cb = request.query.callback;
// 返回的时候可以直接这样写
response.send(`${cb}(${str})`);
// 这样返回给前端的就是一个函数,在回调函数的 data 存放的就是后端的数据
跨域请求的另一种解决方案 cors
-
cors:跨域资源共享,他是官方给出的解决方案
-
支持 get post,不需要在客户端做改变,完全在服务端处理
-
这个响应头表示哪些网页可以给我们发请求
-
cors 是设置一个响应头,里面会标出允许跨域的网站的域名,被设置允许的才可以跨域
response.setHeader('Access-Control-Allow-Origin', '*');
- 更多的可以去 MDN上的CORS上查看