什么是Ajax
- Ajax(asynchronous JavaScript and xml) 异步的 JavaScript(脚本语言) 和 xml(扩展标记语言:用于传输和存储数据 ,是树状结构),是非同源的接口数据。
- Ajax 是用于发送 http 请求,也可以发送异步请求,还可以完成页面的局部刷新功能(在整个页面不刷新的前提下,发送对应的请求改变对应的部分dom)
- 核心对象为 XMLHttpRequest(xhr)。
Ajax基础代码
//创建请求对象
var xhr = new XMLHttpRequest()
// 以对应的请求方式来打开请求地址
xhr.open('get', 'https://jsonplaceholder.typicode.com/todos')
// 发送请求
xhr.send()
// 监听
xhr.onreadystatechange = () => {
// 接收对应的响应数据
if (xhr.readyState == 4 && /^2\d{2}$/.test(xhr.status)) {
// readyState:0-4,status:状态码,100-599
console.log(xhr.responseText); //接收文本数据(字符串)
// console.log(xhr.responseXML); //接收xml数据 null
}
}
xhr (xmlHttpRequest )的方法及属性
属性
- readyState: xhr 独有的状态值
- response: 获取响应
- responseText: 获取响应文本数据(字符串)
- responseType: 获取响应类型
- responseURL: 获取响应地址
- responseXML: 获取响应的xml数据
- status: 获取http的状态码 (后台返回)
- statusText: 获取http的状态文本 (后台返回)
- timeout: 超时时间 (自己设置)
方法
- upload :返回一个上传对象 (XMLHttpRequestUpload)
- open: 打开一个连接,传入对应的请求方式和地址
- send: 发一个请求,内部传入的是请求体的内容
- getAllResponseHeaders: 获取所有的响应头,返回的是字符串
- getResponseHeader: 获取指定响应头,传入对应的响应头的名字
- setRequestHeader: 设置请求头,里面传入对应的请求头名字和对应的值
- XMLHttpRequest 的 overrideMimeType 方法是指定一个 MIME 类型用于替代服务器指定的类型,使服务端响应信息中传输的数据按照该指定 MIME 类型处理。例如强制使流方式处理为 "text/xml" 类型处理时会被使用到,即使服务器在响应头中并没有这样指定
事件
- onreadystateChange :readyState的值发生变化的时候调用
- ontimeout :超时之后调用的
后台接口返回(响应)具备的三个内容
- 状态码 (status)
- 状态文本(相关信息 statusText)
- 数据 (data)
请求和响应的组成
请求相关
- 请求头: requestHeader (对应的请求头先被服务器接收,验证后再接收请求体)
- 请求体: requestBody
//常用请求头
- Content-type :请求的内容的类型
- Cookie :cookie存在于请求头
- Connection :是http1.1新增的 (keep-alive :缓存当前连接,长连接)
- User-Agent :浏览器相关版本信息
- Referer :对应的请求的详细地址
//请求体
- post 请求是将数据添加到请求体中(不可见的)
- 请求体数据量大,发送的时候是分段发送的(后台获取的时候需要拼接获取)
响应相关
- 响应头:responseHeader
- Cache-Control :缓存控制器 (强制缓存,http1.1)
- expires :缓存控制 (http1.0)
- Content-Type :响应内容的类型
- Accpet-Control-Allow-Origin :设置跨域的响应头
- last-modified :文件最后修改的时间 (协商缓存)
- etag :文件标识名 (协商缓存)
- 响应体 :responseBody(数据 状态码 状态文本)
http状态码 (status)
主要取值为100-599内 分别用对应的开头来表示对应的内容
开头状态码 | 含义 | 常用状态码 |
1开头 | 表示成功但是需要下一步操作 | 100 |
2开头 | 表示成功 | 200 |
3开头 | 表示重定向 | 304、303 |
4开头 | 表示客户端错误 | 404、401、402、403 |
5开头 | 表示服务端错误 | 500、501 |
xhr状态值(readyState)
readyState取值 | 含义 |
0 | 请求未发送 |
1 | 请求准备发送 |
2 | 请求发送成功 |
3 | 请求成功响应中 |
4 | 请求成功 响应完成 |
强制缓存和协商缓存
http的缓存
为了节省对应的资源,减少对应的请求。产生了缓存。缓存相当于下载也就是说如果缓存了那么就不需要再请求对应的服务器了。
强制缓存 :对应的内容强制被缓存 ,没有请求的发送
cache-control 来控制
- cache-control :no-cache —— 开启强制缓存、no-storage ——不开启强制缓存
- cache-control :MaxAge(9000) —— 规定时间内使用强制缓存
- cache-control :public —— 一定缓存 (第三方代理缓存)
- cache-control :private —— 看客户端的处理
协商缓存 :不使用强制缓存,才会采用协商缓存,一定要发请求
- last-modified :最后修改时间
- etag :文件标识名
【注意】
- 如果这个文件在访问的时候是a文件, 第二次访问的时候, 我将a文件进行了修改那么对应的 last-modified 就会发生更改, 就会进行数据的重新请求,如果没有进行修改,那么这个时候对应的a文件还是a文件,那就需要使用etag比对a文件是否是a文件 ,如果是就不缓存,如果不是则重新请求。
- 协商缓存一定要发请求,如果被缓存那么对应的状态码为304 ,如果没有缓存那么就是一个新的请求(如果成功就是200)
总结
- 优先使用强制缓存 ,如果没有强制缓存, 使用协商缓存
- 如果强制缓存了, 那么协商缓存就不会被使用且不会有请求发送
- 如果没有强制缓存那么对应的协商缓存就会触发 ,主要比对的是最后修改时间和对应的文件标识,如果都比对成功就是使用协商缓存,如果比对失败就不会缓存。
- 协商缓存成功则对应的状态码为304 ,反之对应的状态码就是一个新请求的状态码(成功就是200)
- 强制缓存使用对应的响应头的cache-control来控制, 协商缓存使用响应头的 etag 和 last-modified来控制
- 强制缓存没有请求,协商缓存必须请求
get和post请求
get请求 (获取数据)
- get请求一般用于获取数据(安全性低 效率高 速度快)
- 默认的请求方式为 get
- 传输的参数利用?进行拼接,拼接到对应的url
- get请求会默认缓存对应的参数
- 传输的参数会显示在请求头中url
- get请求传输的数据大小有限制 (2kb)
post请求 (文件上传、登录、注册...)
- post请求的数据(封装为一个表单对象)在请求体中
- post请求相对get请求要安全
- post请求的数据量大于get请求,其数据没有大小限制
- post请求的数据不会被缓存
- post请求必须手动设置为post请求
get请求的封装
回调函数
//封装get请求的方法
function get(params = {}, callback) {
//判断是否传入url
if (!params.url) {
throw new Error('必须传入url地址')
}
//创建xhr对象
var xhr = new XMLHttpRequest()
//参数和baseUrl进行拼接
let baseURL = params.url
//遍历参数对象
for (var key in params) {
if (key != 'url') {
//判断是否存在?
// 如果没有前面带?
// 如果有前面带&
let s = baseURL.includes('?') ? '&' : '?'
baseURL += `${s+key}=${params[key]}`
}
}
//打开连接
xhr.open('get', baseURL)
//发送请求
xhr.send()
//监听事件
xhr.onreadystatechange = () => {
if (xhr.readyState == 4 && /^2\d{2}$/.test(xhr.status)) {
//调用回调函数 进行相关操作 传入对应的返回的数据
callback(JSON.parse(xhr.responseText))
}
}
}
promise版本
//封装get请求的方法 利用promise来解决
function get(params = {}) {
//判断是否传入url
if (!params.url) {
throw new Error('必须传入url地址')
}
//创建xhr对象
var xhr = new XMLHttpRequest()
//参数和baseUrl进行拼接
let baseURL = params.url
//遍历参数对象
for (var key in params) {
if (key != 'url') {
//判断是否存在?
// 如果没有前面带?
// 如果有前面带&
let s = baseURL.includes('?') ? '&' : '?'
baseURL += `${s+key}=${params[key]}`
}
}
//打开连接
xhr.open('get', baseURL)
//发送请求
xhr.send()
//返回一个新的promise
return new Promise((resolve, reject) => {
//监听事件
xhr.onreadystatechange = () => {
if (xhr.readyState == 4 && /^2\d{2}$/.test(xhr.status)) {
//调用resolve传递数据 给then
resolve(JSON.parse(xhr.responseText))
}
}
})
}
post请求
基础post请求
- post请求数据发送使用send函数携带 (post请求携带到对应的请求体中)
- 在发送之前要指定对应的请求头 content-type :application/x-www-form-urlencoded (指定数据类型为表单)
//创建xhr
var xhr = new XMLHttpRequest()
//打开连接
xhr.open('post', 'https://jsonplaceholder.typicode.com/posts')
//设置请求头 告诉服务器发送的数据是表单 x-www-form-urlencoded 表示发送的表单数据
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded')
//发送请求 请求体 name=张三&age=18
xhr.send("name=张三&age=18")
//监听
xhr.onreadystatechange = () => {
//接收数据处理
console.log(xhr.responseText)
}
回调函数的封装
//封装post请求的方法
function post(params = {}, callback) {
//判断是否传入url
if (!params.url) {
throw new Error('必须传入url地址')
}
//创建xhr对象
var xhr = new XMLHttpRequest()
//参数和baseUrl进行拼接
let baseURL = params.url
let paramString = ""
//遍历参数对象
for (var key in params) {
if (key != 'url') {
let s = paramString?'&':""
paramString += `${s+key}=${params[key]}`
}
}
//打开连接
xhr.open('post', baseURL)
//发送请求
//设置请求头
xhr.setRequestHeader('content-type','application/x-www-form-urlencoded')
xhr.send(paramString)
//监听事件
xhr.onreadystatechange = () => {
if (xhr.readyState == 4 && /^2\d{2}$/.test(xhr.status)) {
//调用回调函数 进行相关操作 传入对应的返回的数据
callback(JSON.parse(xhr.responseText))
}
}
}
promise的封装
//封装post请求的方法
function post(params = {}) {
//判断是否传入url
if (!params.url) {
throw new Error('必须传入url地址')
}
//创建xhr对象
var xhr = new XMLHttpRequest()
//参数和baseUrl进行拼接
let baseURL = params.url
let paramString = ""
//遍历参数对象
for (var key in params) {
if (key != 'url') {
let s = paramString?'&':""
paramString += `${s+key}=${params[key]}`
}
}
//打开连接
xhr.open('post', baseURL)
//发送请求
//设置请求头
xhr.setRequestHeader('content-type','application/x-www-form-urlencoded')
xhr.send(paramString)
//监听事件
return new Promise((resolve,reject)=>{
xhr.onreadystatechange = () => {
if (xhr.readyState == 4 && /^2\d{2}$/.test(xhr.status)) {
//调用回调函数 进行相关操作 传入对应的返回的数据
resolve(JSON.parse(xhr.responseText))
}
}
})
}
get请求和post请求的联合封装
class Ajax {
//传入根路径和超时
constructor(url, time) {
this.baseURL = url
this.timeout = time
}
// 创建一个请求对象的方法
static createRequest(option) {
let defaultOption = {
baseURL: '',
timeout: 3000
}
//防止原option的内容被覆盖,遍历option,判断option内的键值是否存在于defaultOption中,有则替换,没有也不会对原来的进行覆盖
for (var key in option) {
defaultOption[key] = option[key]
}
// 读取对应的配置,返回一个Ajax对象
return new Ajax(defaultOption.baseURL, defaultOption.timeout)
}
//准备对应的request方法{url,contentType,method,data}
request(requestConfig) {
//判断url,method,data的内容是否传入,没传报错,所以必须要传
if (requestConfig.url == undefined || (requestConfig.method.toLowerCase() != 'get' && requestConfig.method.toLowerCase() != 'post') || !requestConfig.data) {
throw new Error()
}
//默认以表单提交
requestConfig.contentType ? requestConfig.contentType : 'application/x-www-form-urlencoded'
let xhr = new XMLHttpRequest()
//设置对应的延时
xhr.timeout = this.timeout
let url = this.baseURL + requestConfig.url
//get数据的拼接
if (requestConfig.method.toLowerCase() == 'get') {
for (var key in requestConfig.data) {
if (url.includes('?')) {
url += `&${key}=${requestConfig.data[key]}`
} else {
url += `?${key}=${requestConfig.data[key]}`
}
}
}
xhr.open(requestConfig.method, url)
//设置请求头
if (requestConfig.method.toLowerCase() == 'post') {
xhr.setRequestHeader('content-type', requestConfig.contentType)
}
//post数据的拼接
if (requestConfig.method.toLowerCase() == 'post') {
var paramsString = ''
for (var key in requestConfig.data) {
if (!paramsString) {
paramsString += `${key}=${requestConfig.data[key]}`
} else {
paramsString += `&${key}=${requestConfig.data[key]}`
}
}
}
xhr.send(paramsString)
return new Promise((resolve, reject) => {
xhr.onreadystatechange = () => {
if (xhr.readyState == 4 && /^2\d{2}$/.test(xhr.status)) {
resolve(JSON.parse(xhr.responseText))
}
}
})
}
}