一、AJAX
1.1 特点
1.1.1 优点
-
可以无需刷新页面与服务器端进行通信
-
允许你根据用户时间来更新部分页面内容
1.1.2 缺点
-
没有浏览历史,不能回退
-
存在跨域问题(同源)
-
SEO 不友好
1.2 HTTP 超文本传输协议
1.2.1 请求报文
-
行
请求方式(POST) / URL / 版本(HTTP/1.1)
-
头
Host: atguigu.com
Cookie: name=zehui
Content-type: application/x-www-form-urlencoded
User-Agent: chrome 83
-
空行
-
请求体
1.2.2 响应报文
-
行
HTTP/1.1 200 OK
-
头
Content-type: text/html;charset=utf-8
Content-length: 2048
Content-encoding: gzip
-
空行
-
响应体
1.3 express的使用
npm init --yes
npm i express
// 引入express
const { response } = require('express')
const express = require('express')
// 创建应用对象
const app = express()
// 创建路由规划
app.get('/', (request, response) => {
response.send('hello express')
})
// 监听端口启动服务
app.listen(8000, () => {
console.log('服务已启动,8000端口监听中...');
})
127.0.0.1:8000查看请求
1.4 ajax 的基本操作
1.4.1 GET
const btn = document.querySelector('button')
const result_box = document.querySelector('.result__box')
btn.onclick = () => {
// 创建对象
const xhr = new XMLHttpRequest()
// 初始化 设置请求方法和url
xhr.open('GET', 'http://127.0.0.1:8000/server')
// 发送
xhr.send()
// 事件绑定 处理服务端返回的结果
// readyState 是xhr对象的属性 状态值: 0 1 2 3 4
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) { // 服务端返回了所有结果
// 判断状态码
if (xhr.status >= 200 && xhr.status < 300) {
result_box.innerHTML = xhr.response
} else {
console.log('error');
}
}
}
}
app.get('/server', (request, response) => {
// 允许跨域
response.setHeader('Access-Control-Allow-Origin', '*')
response.send('hello ajax')
})
1.4.2 POST
const result_box = document.querySelector('.result__box')
result_box.addEventListener('mouseover', function() {
const xhr = new XMLHttpRequest()
xhr.open('POST', 'http://127.0.0.1:8000/server')
// 设置请求头信息
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
xhr.setRequestHeader('name', 'szh')
// 设置post方式的请求体
xhr.send('a=1&b=2')
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
result_box.innerHTML = xhr.response
}
}
}
})
const express = require('express')
const app = express()
app.post('/server', (req, res) => {
// 设置响应头
res.setHeader('Access-Control-Allow-Origin', '*')
res.setHeader('Access-Control-Allow-Header', '*')
res.send('it is post')
})
app.listen(8000, () => {
console.log('123');
})
1.4.3 服务端响应JSON数据
const box = document.querySelector('.box')
window.addEventListener('keypress', () => {
const xhr = new XMLHttpRequest()
xhr.responseType = 'json' // 设置响应体类型
xhr.open('POST', 'http://127.0.0.1:8000/json-server')
xhr.send()
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
box.innerHTML += xhr.response.name
}
}
}
})
app.all('/json-server', (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*')
res.setHeader('Access-Control-Allow-Header', '*')
const obj = {
name: 'szh',
age: '22'
}
res.send(obj)
})
1.5 nodemon 自动重启工具安装
1.6 IE浏览器缓存问题
解决方法:在请求后面加上一个时间戳
xhr.open('http://127.0.0.1:8000/ie?t=' + Date.now())
1.7 超时请求与异常
客户端发送请求
window.addEventListener('keypress', () => {
const xhr = new XMLHttpRequest()
/***********************************************/
xhr.timeout = 2000 // 超时设置
xhr.ontimeout = () => {
// 超时回调
console.log('请求超时或失败,重新发送请求...')
}
xhr.onerror = () => {
// 网络异常回调
console.log('网络异常,请检查网络后重新发送请求!')
}
xhr.open('POST', 'http://127.0.0.1:8000/timeout')
xhr.send()
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
result_box.innerHTML = xhr.response
}
}
}
})
服务端设置延时模拟超时、浏览器设置offline模拟断网状态
// 请求超时与异常
// 延时响应
app.post('/timeout', (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*')
setTimeout(() => {
res.send('请求时间为3s')
}, 3000)
})
1.8 取消请求(手动)
xhr.abort() //取消发送
1.9 请求发送重复问题
思路:添加一个全局变量进行判断发送的状态,false时才进行发送。
const submit = document.querySelector('.submit')
let xhr = null
let isSending = null // 判断是否在发送请求
submit.addEventListener('click', () => {
if (!isSending) {
xhr = new XMLHttpRequest()
isSending = true
xhr.open('GET', 'http://127.0.0.1:8000/repeat')
xhr.send()
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
isSending = false
}
}
} else {
console.log('请求正在发送中...');
}
})
二、JQuery中的Ajax
.get() .post() .ajax()
btn[0].onclick = function() {
/*
** 三个参数 url 携带参数 回调函数(响应体)
*/
let jqxhr = $.get('http://127.0.0.1:7000/getAjax', {
a: 1,
b: 2
}, function(res) {
console.log(res);
}).done(() => {
console.log('success')
}).always(() => {
console.log('complete')
})
}
btn[1].onclick = function() {
let jqxhr = $.post('http://127.0.0.1:7000/postAjax', {
a: 1,
b: 2
}, function(res) {
console.log(res);
}).done(() => {
console.log('success')
}).always(() => {
console.log('complete')
})
}
btn[2].onclick = function() {
$.ajax({
type: 'POST',
url: 'http://127.0.0.1:7000/postAjax',
data: {
k: 1,
},
dataType: 'json',
success: data => {
console.log(data)
},
headers: {
t: 123
},
})
}
/*
** JQuery-Ajax
*/
app.get('/getAjax', (req, res) => {
res.setHeaders('Access-Control-Allow-Origin', '*')
res.send('jquery-get')
})
app.all('/postAjax', (req, res) => {
res.setHeaders('Access-Control-Allow-Origin', '*')
res.setHeaders('Access-Control-Allow-Headerss', '*')
const obj = {
name: 'szh'
}
res.send(JSON.stringify(obj))
})
fetch()
fetch
规范与 jQuery.ajax()
主要有以下的不同:
-
当接收到一个代表错误的 HTTP 状态码时,从
fetch()
返回的 Promise 不会被标记为 reject,即使响应的 HTTP 状态码是 404 或 500。相反,它会将 Promise 状态标记为 resolve(如果响应的 HTTP 状态码不在 200 - 299 的范围内,则设置 resolve 返回值的 ok 属性为 false),仅当网络故障时或请求被阻止时,才会标记为 reject。 -
fetch
不会发送跨域 cookie,除非你使用了 credentials 的初始化选项。
基本语法
// fetch请求
async function fetchData(url = '', data = {}) {
const response = await fetch(url, {
method: 'POST',
mode: "cors",
cache: "no-cache",
credentials: "same-origin",
headers: {
'Content-Type': 'application/json',
szh: 123
},
redirect: "follow",
referrerPolicy: "no-referrer",
body: JSON.stringify(data)
})
return response.json()
}
window.addEventListener('keypress', () => {
fetchData('http://127.0.0.1:7000/fetch', {
name: 'szh'
}).then(data => {
console.log(data.ok)
console.log(data)
}).catch(e => {
console.log(e)
})
})
// fetch
app.all('/fetch', (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*')
res.setHeader('Access-Control-Allow-Headers', '*')
const obj = {
name: 'szh'
}
res.send(JSON.stringify(obj))
})
三、跨域问题
3.1 同源策略
同源策略是浏览器的一种安全策略,所谓的同源指:协议、域名、端口号 必须完全相同
违背同源策略即为跨域
3.2 解决跨域问题
3.2.1 JSONP
-
是什么?
JSONP (JSON with Padding),是一个非官方的跨域解决方案, 只支持get请求
-
怎么工作的?
网页有一些标签具有跨域能力,比如:img、link、iframe、script
JSONP 就是利用script标签的跨域能力来发送请求的。
-
如何使用?
function handle(obj) { console.log(obj); if (obj.exit === 1) { $('input')[0].style.color = 'red' } } $('input').blur(() => { let uname = $(this).val // 1-动态创建script标签 const script = document.createElement('script') // 2-设置src script.src = 'http://127.0.0.1:7000/jsonp' // 3-添加节点 document.body.appendChild(script) })
// 跨域问题 app.all('/jsonp', (req, res) => { const data = { exit: 1, msg: '账号已存在' } let obj = JSON.stringify(data) res.end(`handle(${obj})`) })
3.2.2 CORS
-
是什么?
CORS (Cross-Origin Resource Sharing),跨域资源共享。CORS是官方的跨域解决方案,它的特点是不需要在客户端做任何特殊的操作,完全在服务器中进行处理,支持get和 post 请求。跨域资源共享标准新增了一组HTTP首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源
-
怎么工作的?
CORS是通过设置一个响应头来告诉浏览器,该请求允许跨域,浏览器收到该响应以后就会对响应放行。
-
如何使用?
res.setHeader('Access-Control-Allow-Headers', '*')
// 第二个参数是允许跨域的请求