前后端交互
XMLHttpRequest
原生的 JavaScript 提供了 XMLHttpRequest 构造函数。
XML 是具有结构性的标记语言,用途于 JSON 字符串相同,其与 JSON 格式的优劣这里不讨论。
它会实例化一个对象, 用这个对象可以进行 ajax(Asynchronous JavaScript and XML)请求,是用异步javascript 请求 JSON 数据并动态渲染到页面的一种行为。
在 IE 老版本的浏览器中是 ActiveXObject 构造函数。
let xhr = new XMLHttpRequest() //创建xhr对象,通过对对象得控制
xhr.open("GET", "localhost:8000", true) //设置请求方法和请求得地址
xhr.send(null) //设置请求发送的数据以及发送数据 get请求不需要发送
xhr.onreadstatechange = function() {
// 监听后台请求的状态变化
if (xhr.readState == 4 && xhr.status == 200) {
// 整个过程没问题
console.log(xhr.response) // 打印数据获得data, 在xhr.response中
}
}
xhr.open 方法 ,接受三个参数:
- method: 请求方式, 常见的有 GET 请求或者 POST 请求
- url: 发送请求的后台地址
- async: 是否异步,true 为异步 , false 为非异步
xhr.send:发送请求所携带得数据,在 get 请求中不填或者填 null
在 post 请求中填写发送的数据内容,后台会接收到数据。从而做相应得处理
xhr.onreadstatechange:监听 xhr 对象状态改变的函数
这里用 ajax 状态码和 http 状态码来判断。
HTTP 状态码
常见的状态码可以帮助我们对当前请求的状态进行判断。常见的 http 状态码有:
状态码 | 含义 |
---|---|
200 | 请求已成功,请求所希望的响应头或数据体将随此响应返回。 |
304 | 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源 |
404 | 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面 |
408 | (请求超时) 服务器等候请求时发生超时。 |
500 | 服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。一般来说,这个问题都会在服务器的程序码出错时出现。 |
HTTP 动作
参照 RESTful 规范:
- GET - 用于获取数据。
- PUT - 用于覆盖式的更新数据。
- DELETE - 用于删除数据。
- POST - 用于添加数据。
在不借助 form 表单时,一般需要设置请求头
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
除了 GET 请求(其实是 GET|HEAD|OPTIONS|TRACE),其他动作一般还会有请求体 Body。
HTTP content-type
请求体 body 格式需要在请求头的 Content-Type 中声明,常见的:
//text/plain
xhr.send("纯文本")
//application/x-www-form-urlencoded
var data = new URLSearchParams()
data.append("name", "nanari")
xhr.send(data) //注意这里是用于body而不是?query
//multipart/form-data
var data = new FormData()
data.append("name", "nanari")
xhr.send(data)
//application/json
var data = JSON.stringify({
name: "nanari",
})
xhr.send(data)
响应头中也存在 Content-Type,它决定了浏览器行为,如新窗口打开查看原图/播放等。
特别的,application/octet-stream
是用于浏览器的下载行为
CORS 跨域问题
浏览器为了保护用户, 保证用户安全,使用同源策略 来针对请求做出响应。
同源:
- 协议相同:protocol(ftp file http https 等协议)不同得协议被服务器认为不同源
- 域名相同:domain 网站得域名必须一致。
- 端口相同:port 默认 80(443)端口,但是不同端口也被认为跨域
以上任何不相同都被认为是跨域。
JavaScript 只能访问和操作自己域下的资源,不能访问和操作其他域下的资源。跨域问题是针对 JS 和 ajax 的,html 本身没有跨域问题,比如 a 标签、script 标签、甚至 form 标签(可以直接跨域发送数据并接收数据)等。这是为了保证用户得安全。
跨域的解决方案:
JSONP 方式
- script 标签 src 允许跨域,我们利用 script 标签得 src 来访问资源,然后使用 callback 来承接返回得数据,处理。
javascript//这里以哔哩哔哩的内容搜索框为例 let input = document.queryselector("input") input.oninput = function() { let script = document.createElement("script") let url = `https://s.search.bilibili.com/main/suggest?jsoncallback=mycallback&term=${input.value}` script.src = url document.body.appendChild(script) script.onload = () => document.body.removeChild(script) } function mycallback(data) { console.log(data) }
其中 url 是我们需要请求得 src,在 src 中拼接了一个完整得 get 请求,包括请求得参数请求时发送的数据,以及回调接口。
将 script 添加到页面中的时候,script 的代码会立即解析执行,执行完成之后删除。其中
mycallback 是本地的函数,这个函数接受参数用于使用参数。
CORS 后台允许跨域
- 在后台添加允许跨域:"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源(协议 + 域名 + 端口)服务器,发出 XMLHttpRequest 请求,从而克服了 AJAX 只能同源使用的限制。
- 一般在前后端分离的开发过程中会让使用 CORS,上线后会关闭 CORS。
第三方(也就是代理服务器,在前端模块化开发时,脚手架中会使用到)
jQuery.ajax
contentType 默认为 application/x-www-form-urlencoded
特别的,data 为 FormData 对象时需要额外处理
$.ajax({
type: "POST",
data: formData,
...
processData: false,
contentType: false, //设置false才会是multipart/form-data
...
data 为表单 dom.serialize()时不需要 contentType
$.ajax({
type: "POST",
url: location.pathname,
data: $("form").serialize(),
async: false,
error: function(err) {
alert("error", err)
},
success: function(data) {
alert("success", data)
},
})
fetch
window.fetch 是原生es6 基于 Promise 的 ajax 请求的二度封装。
默认使用 get 请求。这里以 await 的方式使用。也可以使用 Promise.then
let search = new URLSearchParams()
search.append("limit", limit)
search.append("offset", offset)
let res = await fetch(`http://127.0.0.1:8080/dicts?${search}`)
let data = await res.json()
post 等方法请求,body 可以传字符串或 formdata 对象。传字符串时最好指定content-type
。
let res = await fetch(`http://127.0.0.1:8080/dict`, {
body: JSON.stringify({
name: input.value,
}),
cache: "no-cache",
headers: new Headers({
"Content-Type": "application/json",
}),
method: "POST",
})
let data = await res.json()
Response
上文里的 res 是标准的 Response 对象。另外还有 Request 对象。
res.ok
可以判断请求是否正常,res.state
用来获取 http 状态码。
无论请求正常与否,都可以使用await res.json()
解析结果。
此外还有await res.blob()
可以解析文件流
取消 fetch
特别的 ,由于某些原因 ,Promise.cancel()无法取消 fetch
AbortController
允许开发人员使用信号中止一个或多个 fetch
调用。
const controller = new AbortController()
const {
signal
} = controller
fetch("http://localhost:8000", {
signal,
})
.then((response) => {
console.log(`Request 1 is complete!`)
})
.catch((e) => {
console.warn(`Fetch 1 error: ${e.message}`)
})
// Abort request
controller.abort()
Axios
Axios是基于 Promise 的对 ajax 的第三方封装 需要下载引入。适用于浏览器和 node 环境
简单用法:await axios.http 动作方法()
try {
console.log(
await axios.post(url, data, {
headers,
params,
})
)
} catch (err) {
console.log(`请求失败:${err}`)
}
get 请求时只有 2 个参数,不接受 data 参数。
params 是地址栏的 query 参数,不限于 get 请求。
data 参数可以直接接受 URLSearchParams、FormData 等对象
websocket
不同于 http 协议,websocket 可以让服务端主动给客户端推送请求
var ws = new WebSocket("wss://echo.websocket.org")
ws.onopen = function(evt) {
console.log("Connection open ...")
ws.send("Hello WebSockets!")
}
ws.onmessage = function(evt) {
console.log("Received Message: " + evt.data)
ws.close()
}
ws.onclose = function(evt) {
console.log("Connection closed.")
}
ws.send("your message")