Ajax 是什么
Ajax 是 Asynchronous JavaScript and XML(异步 JavaScript 和 XML)的简写 。
- Ajax中的异步:可以异步地向服务器发送请求,在等待响应的过程中,不会阻塞当前页面,浏览器可以做自己的事情。直到成功获取响应后,浏览器才开始处理响应数据。
- XML(可扩展标记语言)是前后端数据通信时传输数据的一种格式。 XML 现在已经不怎么用了,比较常用的是 JSON。
Ajax 其实就是浏览器与服务器之间的一种异步通信方式。
使用 Ajax 可以在不重新加载整个页面的情况下,对页面的某部分进行更新。
Ajax 的基本用法
XMLHttpRequest
Ajax 想要实现浏览器与服务器之间的异步通信,需要依靠 XMLHttpRequest,它是一个构造函数。
注意:不论是 XMLHttpRequest,还是 Ajax,都没有和具体的某种数据格式绑定。
Ajax 的使用步骤
-
创建 xhr 对象
-
监听事件,处理响应
当获取到响应后,会触发 xhr 对象的
readystatechange事件,可以在该事件中对响应进行处理-
readystatechange事件监听readyState这个状态的变化 -
它的值从 0 ~ 4,一共 5 个状态
-
0:未初始化。尚未调用 open()
-
1:启动。已经调用 open(),但尚未调用 send()
-
2:发送。已经调用 send(),但尚未接收到响应
-
3:接收。已经接收到部分响应数据
-
4:完成。已经接收到全部响应数据,而且已经可以在浏览器中使用了
readystatechange事件也可以配合addEventListener使用,不过要注意,IE6~8 不支持addEventListener为了兼容性,
readystatechange中不使用 this,而是直接使用 xhr由于兼容性的原因,最好放在
open()之前 -
-
准备发送请求
-
调用
open()并不会真正发送请求,而只是做好发送请求前的准备工作- 第一个参数 HTTP 方法 GET、POST、PUT、DELETE
- 第二个参数 地址 URL
https://www.baidu.com/search/suggest?words=js./index.html./index.xml./index.txt - 第三个参数 是否异步 true
-
-
发送请求
-
调用 send() 正式发送请求,send() 的参数是通过请求体携带的数据
xhr.send(null)GETxhr.send('username=alex&age=18')POSTapplication/x-www-form-urlencodedxhr.send(JSON.stringify({username:'xiaoliu'age:18}))POSTapplication/JSON
-
const url = 'https://www.imooc.com/api/http/search/suggest?words=js'
// 创建 xhr 对象
const xhr = new XMLHttpRequest()
// 监听事件,处理响应
xhr.onreadystatechange = () => {
// 数据还没准备好,先返回,没必要进行下去
if (xhr.readyState !== 4) return
// HTTP CODE
// 获取到响应后,响应的内容会自动填充 xhr 对象的属性
// xhr.status: HTTP 200 404
// xhr.statusText:HTTP 状态说明 OK Not Found
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.log(xhr.responseText) // {"code":200,"data":[{"word":"jsp"},{"word":"js"},{"word":"json"},{"word":"js \u5165\u95e8"},{"word":"jstl"}]}
console.log(typeof xhr.responseText) // string
}
}
// //使用addEventListener监听事件
// xhr.addEventListener('readystatechange', () => {}, false)
// 准备发送请求
xhr.open('GET', url, true)
// 准备发送请求
// 可以不加null,不过为了保证兼容性,建议加上
xhr.send(null) // XHR finished loading: GET "https://www.imooc.com/api/http/search/suggest?words=js".
GET请求
携带数据
GET 请求不能通过请求体携带数据,但可以通过请求头携带
const url = 'https://www.imooc.com/api/http/search/suggest?words=js&username=alex&age=18'
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState != 4) return
if ((xhr.status >= 200 && xhr < 300) || xhr.status === 304) {
console.log(xhr.responseText)
}
}
xhr.open('GET', url, true)
xhr.send(null)
// 不会报错,但不会发送数据
// xhr.send('sex=male')
当发起一个GET请求时,参数会以url string的形式进行传递,即?后的字符串则为其请求参数,并以&作为分隔符
数据编码
如果携带的数据是非英文字母的话,比如说汉字,就需要编码之后再发送给后端,不然会造成乱码问题
可以使用 encodeURIComponent() 编码
const url = `https://www.imooc.com/api/http/search/suggest?words=${encodeURIComponent('前端')}`
POST请求
携带数据
POST 请求主要通过请求体携带数据,同时也可以通过请求头携带
const url = 'https://www.imooc.com/api/http/search/suggest?words=js'
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState != 4) return
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.log(xhr.responseText)
}
}
xhr.open('POST', url, true)
// 如果想发送数据,直接写在 send() 的参数位置,一般是字符串
xhr.send('username=alex&age=18')
数据编码
xhr.send(`username = ${encodeURIComponent('张三')}&age=18`)
JSON
JSON 是什么
JSON 是 JavaScript Object Notation(JavaScript 对象表示法) 的简写
Ajax 发送和接收数据的一种格式
为什么需要 JSON
JSON 有 3 种形式,每种形式的写法都和 JS 中的数据类型很像,可以很轻松的和 JS 中的数据类型互相转换
JSON的三种形式
1.简单值形式
JSON 的简单值形式就对应着 JS 中的基础数据类型
数字 字符串 布尔 null
5
"string"
false
null
注意事项
① JSON 中没有 undefined 值
② JSON 中的字符串必须使用双引号
③ JSON 中是不能注释的
2.对象形式
JSON 的对象形式就对应着 JS 中的对象
{
"name":"小刘",
"age":18,
"hobby":["看书","绘画"],
"family":{
"father":"老刘",
"mother":"老岳"
}
}
注意事项
①JSON 中对象的属性名必须用双引号,属性值如果是字符串也必须用双引号
②JSON 中只要涉及到字符串,就必须使用双引号
③不支持 undefined
3.数组形式
JSON 的数组形式就对应着 JS 中的数组
[1, "hi", null]
[ { "id":1, "username":"小刘", "comment":"666" }, { "id":2, "username":"小孙", "comment":"999" } ]
注意事项
①数组中的字符串必须用双引号
②JSON 中只要涉及到字符串,就必须使用双引号
③不支持 undefined
JSON的常用方法
1.JSON.parse()
JSON.parse() 可以将 JSON 格式的字符串解析成 JS 中的对应值
一定要是合法的 JSON 字符串,否则会报错
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState != 4) return
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.log(JSON.parse(xhr.responseText)) // {code: 200, data: {word: "jsp"},{word: "js"},{word: "json"},{word: "js 入门"},{word: "jstl"}}
console.log(JSON.parse(xhr.responseText).data) // [{word: "jsp"},{word: "js"},{word: "json"},{word: "js 入门"},{word: "jstl"}]
}
}
xhr.open('GET', 'https://www.imooc.com/api/http/search/suggest?words=js', true)
xhr.send(null)
2.JSON.stringify()
JSON.stringify() 可以将 JS 的基本数据类型、对象或者数组转换成 JSON 格式的字符串
const xhr = new XMLHttpRequest()
xhr.open('POST', 'https://www.imooc.com/api/http/search/suggest?words=js', true)
xhr.send(
JSON.stringify({
username: '小刘',
age: 18
})
)
3.使用 JSON.parse() 和 JSON.stringify() 封装 localStorage
//封装storage.js
const storage = window.localStorage
// 设置
const set = (key, value) => {
storage.setItem(key, JSON.stringify(value))
}
// 获取
const get = key => {
return JSON.parse(storage.getItem(key))
}
// 删除
const remove = key => {
storage.removeItem(key)
}
// 清空
const clear = () => {
storage.clear()
}
export { set, get, remove, clear }
//导入html使用
import { get, set, remove, clear } from './storage.js'
set('username', '小刘')
console.log(get('username')) // 小刘
set('zs', {
name: '张三',
age: 18
})
console.log(get('zs')) // {name: "张三", age: 18}
remove('username')
console.log(get('username')) // null
clear()
console.log(get('zs')) // null
初识跨域
1.跨域是什么
向一个域发送请求,如果要请求的域和当前域是不同域,就叫跨域
不同域之间的请求,就是跨域请求
//不同域,跨域,被浏览器阻止
// Access to XMLHttpRequest at 'https://www.baidu.com/' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
const url = 'https://www.baidu.com'
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState != 4) return
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.log(xhr.responseText)
}
}
xhr.open('GET', url, true)
xhr.send(null) // GET https://www.baidu.com/ net::ERR_FAILED
2.什么是不同域,什么是同域
https(协议):// www.baidu.com(域名):443(端口号)/search(路径)
协议、域名、端口号,任何一个不一样,就是不同域
与路径无关,路径一不一样无所谓
// 不同域名
https://www.baidu.com/search
http://www.baidu.com/search
http://www.baidu.com/search
http://m.baidu.com/search
http://baidu.com/search
https://www.baidu.com:443/search
https://www.baidu.com:80/search
// 同域名
https://www.baidu.com/
https://www.baidu.com/search
3.跨域请求为什么会被阻止
阻止跨域请求,其实是浏览器本身的一种安全策略--同源策略
同源策略/SOP(Same origin policy)是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个IP地址,也非同源。
其他客户端或者服务器都不存在跨域被阻止的问题
4.跨域解决方案
①CORS 跨域资源共享
② JSONP
优先使用 CORS 跨域资源共享,如果浏览器不支持 CORS 的话,再使用 JSONP
CORS 跨域资源共享
1.CORS 是什么
设置通过后端完成,前端只负责沟通
Access-Control-Allow-Origin: *
表明允许所有的域名来跨域请求它,* 是通配符,没有任何限制
只允许指定域名的跨域请求
Access-Control-Allow-Origin: http://127.0.0.1:5500
2.使用 CORS 跨域的过程
① 浏览器发送请求
② 后端在响应头中添加 Access-Control-Allow-Origin 头信息
③ 浏览器接收到响应
④ 如果是同域下的请求,浏览器不会额外做什么,这次前后端通信就圆满完成了
⑤ 如果是跨域请求,浏览器会从响应头中查找是否允许跨域访问
⑥ 如果允许跨域,通信圆满完成
⑦ 如果没找到或不包含想要跨域的域名,就丢弃响应结果
3.CORS 的兼容性
IE10 及以上版本的浏览器可以正常使用 CORS
JSONP
1.JSONP 的原理
script 标签跨域不会被浏览器阻止
JSONP 主要就是利用 script 标签,加载跨域文件
2.使用 JSONP 实现跨域
服务器端准备好 JSONP 接口
https://www.imooc.com/api/http/jsonp?callback=handleResponse
动态加载 JSONP 接口
const script = document.createElement('script')
script.src = 'https://www.imooc.com/api/http/jsonp?callback=handleResponse'
document.body.appendChild(script)
const handleResponse = data => {
console.log(data)
}
手动加载 JSONP 接口
<script src="https://www.imooc.com/api/http/jsonp?callback=handleResponse"></script>
\
XHR的属性
1.responseType 和 response 属性
// 1.responseType 和 response 属性
const url = 'https://www.imooc.com/api/http/search/suggest?words=js'
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState != 4) return
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
// 文本形式的响应内容
// responseText 只能在没有设置 responseType 或者 responseType = '' 或 'text' 的时候才能使用
console.log('responseText:', xhr.responseText) //ajax.js:11 Uncaught DOMException: Failed to read the 'responseText' property from 'XMLHttpRequest': The value is only accessible if the object's 'responseType' is '' or 'text' (was 'json').
// 可以用来替代 responseText
console.log('response:', xhr.response) // {code: 200, data: Array(5)},json格式字符串,自动调用json.parse转换数据类型
// console.log(JSON.parse(xhr.responseText))
}
}
xhr.open('GET', url, true)
// xhr.responseType = ''
// xhr.responseType = 'text'
xhr.responseType = 'json' //设置为json格式,response会报错
xhr.send(null)
// IE6~9 不支持,IE10 开始支持
设置response='json'的时候使用xhr.responseText
2.timeout 属性
设置请求的超时时间(单位 ms)
const url = 'https://www.imooc.com/api/http/search/suggest?words=js'
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState != 4) return
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.log(xhr.response)
}
}
xhr.open('GET', url, true)
xhr.timeout = 5 // 使用极短的时间访问失败
xhr.send(null) // XHR failed loading: GET "https://www.imooc.com/api/http/search/suggest?words=js".
// IE6~7 不支持,IE8 开始支持
3.withCredentials 属性
指定使用 Ajax 发送请求时是否携带 Cookie
使用 Ajax 发送请求,默认情况下,同域时,会携带 Cookie;跨域时,不会
xhr.withCredentials = true
最终能否成功跨域携带 Cookie,还要看服务器同不同意
const url = 'https://www.imooc.com/api/http/search/suggest?words=js'
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState != 4) return
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.log(xhr.response) //Access to XMLHttpRequest at 'https://www.imooc.com/api/http/search/suggest?words=js' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
}
}
xhr.open('GET', url, true)
xhr.withCredentials = true
xhr.send(null)
// IE6~9 不支持,IE10 开始支持
服务器不同意,携带失败
XHR的方法
1.abort()
终止当前请求
一般配合 abort 事件一起使用
const url = 'https://www.imooc.com/api/http/search/suggest?words=js'
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState != 4) return
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.log(xhr.response)
}
}
xhr.open('GET', url, true)
xhr.send(null) // XHR failed loading: GET "https://www.imooc.com/api/http/search/suggest?words=js".
xhr.abort()
2.setRequestHeader()
可以设置请求头信息
xhr.setRequestHeader(头部字段的名称, 头部字段的值)
// const url = 'https://www.imooc.com/api/http/search/suggest?words=js'
const url = 'https://www.imooc.com/api/http/json/search/suggest?words=js'
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState != 4) return
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.log(xhr.response) // {"code":200,"data":[{"word":"jsp"},{"word":"js"},{"word":"json"},{"word":"js \u5165\u95e8"},{"word":"jstl"}]}
}
}
xhr.open('POST', url, true)
// 请求头中的 Content-Type 字段用来告诉服务器,浏览器发送的数据是什么格式的
// xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
xhr.setRequestHeader('Content-Type', 'application/json')
// xhr.send(null)
xhr.send(`username=${decodeURIComponent('小刘')}&age=18`)
xhr.send(
JSON.stringify({
username: '小张' // ajax.js:21 XHR finished loading: POST "https://www.imooc.com/api/http/json/search/suggest?words=js".
})
// 后面的send会被忽略,这里分别测试了两种类型
)
将Content-Type设置为application/x-www-form-urlencoded时:
Request Headers
将Content-Type设置为application/json时,不改变服务器接口:
Request Headers
将Content-Type设置为application/json时,改为JSON接口:
\
XHR的事件
1.load 事件
响应数据可用时触发
const url = 'https://www.imooc.com/api/http/search/suggest?words=js'
const xhr = new XMLHttpRequest()
xhr.onload = () => {
//响应可用的时候触发,不需要判断readyState 为 4 了
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.log(xhr.response) //{"code":200,"data":[{"word":"jsp"},{"word":"js"},{"word":"json"},{"word":"js \u5165\u95e8"},{"word":"jstl"}]}
}
}
// xhr.addEventListener(
// 'load',
// () => {
// if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
// console.log(xhr.response)
// }
// },
// false
// )
xhr.open('GET', url, true)
xhr.send(null) // ajax.js:22 XHR finished loading: GET "https://www.imooc.com/api/http/search/suggest?words=js".
// IE6~8 不支持 load 事件
2.error 事件
请求发生错误时触发
// const url = 'https://www.imooc.com/api/http/search/suggest?words=js'
const url = 'https://www.iimooc.com/api/http/search/suggest?words=js'
const xhr = new XMLHttpRequest()
xhr.addEventListener(
'load',
() => {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.log(xhr.response)
}
},
false
)
xhr.addEventListener(
'error',
() => {
console.log('error') //error
},
false
)
xhr.open('GET', url, true)
xhr.send(null) //GET https://www.iimooc.com/api/http/search/suggest?words=js net::ERR_TUNNEL_CONNECTION_FAILED
// IE10 开始支持
3.abort 事件
调用 abort() 终止请求时触发
const url = 'https://www.imooc.com/api/http/search/suggest?words=js'
const xhr = new XMLHttpRequest()
xhr.addEventListener(
'load',
() => {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.log(xhr.response)
}
},
false
)
xhr.addEventListener(
'abort',
() => {
console.log('abort') // abort
},
false
)
xhr.open('GET', url, true)
xhr.send(null) // ajax.js:26 XHR failed loading: GET "https://www.imooc.com/api/http/search/suggest?words=js".
xhr.abort()
// IE10 开始支持
4.timeout 事件
请求超时后触发
const url = 'https://www.imooc.com/api/http/search/suggest?words=js'
const xhr = new XMLHttpRequest()
xhr.addEventListener(
'load',
() => {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.log(xhr.response) //
}
},
false
)
xhr.addEventListener(
'timeout',
() => {
console.log('timeout') // timeout
},
false
)
xhr.open('GET', url, true)
xhr.timeout = 5
xhr.send(null) // XHR finished loading: GET "https://www.imooc.com/api/http/search/suggest?words=js".
// IE8 开始支持
如果timeout时间设置过小,会超时,XHR加载失败
FormData
可以用于发送表单数据;亦可独立于表单,用于发送键值对数据。
IE10 及以上可以支持
1.使用 Ajax 提交表单
const login = document.getElementById('login')
const { username, password } = login
const btn = document.getElementById('submit')
const url = 'https://www.imooc.com/api/http/search/suggest?words=js'
btn.addEventListener(
'click',
e => {
// 阻止表单自动提交
e.preventDefault()
// 表单数据验证
// 略...
// 发送 Ajax 请求
const xhr = new XMLHttpRequest()
xhr.addEventListener(
'load',
() => {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.log(xhr.response)
}
},
false
)
xhr.open('POST', url, true)
// 组装数据
const data = `username=${username.value}&password=${password.value}`
// FormData 可用于发送表单数据
// const data = new FormData(login)
// data.append('age', 18)
// data.append('sex', 'male')
// console.log(data)
// for (const item of data) {
// console.log(item)
// }
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
xhr.send(data)
// xhr.send('username=alex&password=12345')
},
false
)
2.FormData 的基本用法
// 通过 HTML 表单元素创建 FormData 对象
const fd = new FormData(表单元素)
// 通过 append() 方法添加数据
const fd = new FormData(表单元素)
fd.append('age', 18)
fd.append('sex', 'male')
// 可以通过xhr.send(FormData 数据) 发送
xhr.send(fd)
封装AJAX(学习向)
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>封装Ajax</title>
</head>
<body></body>
<script type="module">
import { ajax, get, getJSON, post } from './ajax/index.js'
const url = 'https://www.imooc.com/api/http/search/suggest?words=js'
const xhr = ajax(url, {
method: 'GET',
params: { username: '小孙' },
data: {
age: 18
},
responseType: 'json',
success(response) {
console.log(response)
},
httpCodeError(err) {
console.log('http code error', err)
},
error(xhr) {
console.log('error', xhr)
},
abort(xhr) {
console.log('abort', xhr)
},
timeout(xhr) {
console.log('timeout', xhr)
}
})
xhr.abort()
</script>
</html>
ajax.js
// 常量
import { HTTP_GET, CONTENT_TYPE_FORM_URLENCODED, CONTENT_TYPE_JSON } from './constants.js'
// 工具函数
import { serialize, addURLData, serializeJSON } from './utils.js'
// 默认参数
import DEFAULTS from './defaults.js'
// Ajax类
class Ajax {
constructor(url, options) {
this.url = url
this.options = Object.assign({}, DEFAULTS, options)
// 初始化
this.init()
}
// 初始化
init() {
const xhr = new XMLHttpRequest()
this.xhr = xhr
// 绑定响应事件处理程序
this.bindEvents()
xhr.open(this.options.method, this.url + this.addParam(), true)
// 设置 responseType
this.setResponseType()
// 设置跨域是否携带 cookie
this.setCookie()
// 设置超时
this.setTimeout()
// 发送请求
this.sendData()
}
// 绑定响应事件处理程序
bindEvents() {
const xhr = this.xhr
const { success, httpCodeError, error, abort, timeout } = this.options
// load
xhr.addEventListener(
'load',
() => {
if (this.ok()) {
success(xhr.response, xhr)
} else {
httpCodeError(xhr.status, xhr)
}
},
false
)
// error
// 当请求遇到错误时,将触发error事件
xhr.addEventListener(
'error',
() => {
error(xhr)
},
false
)
// abort
xhr.addEventListener(
'abort',
() => {
abort(xhr)
},
false
)
// timeout
xhr.addEventListener(
'timeout',
() => {
timeout(xhr)
},
false
)
}
// 检测响应的HTTP状态码是否正常
ok() {
const xhr = this.xhr
return (xhr.status >= 200 && xhr.status < 300) || xhr.status === 304
}
// 在地址上添加数据
addParam() {
const { params } = this.options
if (!params) return ''
return addURLData(this.url, serialize(params))
}
// 设置 responseType
setResponseType() {
this.xhr.responseType = this.options.responseType
}
// 设置跨域是否携带 cookie
setCookie() {
if (this.options.withCredentials) {
this.xhr.withCredentials = true
}
}
// 设置超时
setTimeout() {
const { timeoutTime } = this.options
if (timeoutTime > 0) {
this.xhr.timeout = timeoutTime
}
}
// 发送请求
sendData() {
const xhr = this.xhr
if (!this.isSendData()) {
return xhr.send(null)
}
let resultData = null
const { data } = this.options
// 发送 FormData 格式的数据
if (this.isFormData()) {
resultData = data
} else if (this.isFormURLEncodedData()) {
// 发送 application/x-www-form-urlencoded 格式的数据
this.setContentType(CONTENT_TYPE_FORM_URLENCODED)
resultData = serialize(data)
} else if (this.isJSONData()) {
// 发送 application/json 格式的数据
this.setContentType(CONTENT_TYPE_JSON)
resultData = serializeJSON(data)
} else {
// 发送其他格式的数据
this.setContentType()
resultData = data
}
xhr.send(resultData)
}
// 是否需要使用 send 发送数据
isSendData() {
const { data, method } = this.options
if (!data) return false
if (method.toLowerCase() === HTTP_GET.toLowerCase()) return false
return true
}
// 是否需要发送 FormData 格式的数据
isFormData() {
return this.options.data instanceof FormData
}
// 是否需要发送 application/x-www.form-urlencoded
isFormURLEncodedData() {
return this.options.contentType.toLowerCase().includes(CONTENT_TYPE_FORM_URLENCODED)
}
// 是否需要发送 application/json 格式的数据
isJSONData() {
return this.options.contentType.toLowerCase.includes(CONTENT_TYPE_JSON)
}
// 设置 Content-type
setContentType(contentType = this.options.contentType) {
if (!contentType) return
this.xhr.setRequestHeader('Content-Type', contentType)
}
// 获取 XHR 对象
getXHR() {
return this.xhr
}
}
export default Ajax
index.js
import Ajax from './ajax.js'
const ajax = (url, options) => {
return new Ajax(url, options).getXHR()
}
const get = (url, options) => {
return ajax(url, { ...options, method: 'GET' })
}
const getJSON = (url, options) => {
return ajax(url, { ...options, method: 'GET', responseType: 'json' })
}
const post = (url, options) => {
return ajax(url, { ...options, method: 'POST' })
}
export { ajax, get, getJSON, post }
defaults.js
// 常量
import { HTTP_GET, CONTENT_TYPE_FORM_URLENCODED } from './constants.js'
// 默认参数
const DEFAULTS = {
method: HTTP_GET,
// 请求头携带的数据
params: null,
// 有数据的情况
// params: {
// username: '小孙',
// age: 18
// }
data: null,
// 有数据的情况
// data: {
// username: '小孙',
// age: 18
// }
// data: FormData 数据
contentType: CONTENT_TYPE_FORM_URLENCODED,
responseType: '',
timeoutTime: 0,
withCredentials: false,
// 方法
success() {},
httpCodeError() {},
error() {},
abort() {},
timeout() {}
}
export default DEFAULTS
utils.js
// 工具函数
// 数据序列化成 urlencoded 格式的字符串
const serialize = param => {
const results = []
for (const [key, value] of Object.entries(param)) {
results.push(`${encodeURIComponent(key)} = ${decodeURIComponent(value)}`)
}
// ['username=小刘','age=18']
return results.join('&')
}
// 数据序列化成 JSON 格式的字符串
const serializeJSON = param => {
return JSON.stringify(param)
}
// 给 URL 添加参数
// www.baidu.com?search=js&
const addURLData = (url, data) => {
if (!data) return ''
const mark = url.includes('?') ? '&' : '?'
return `${mark}${data}`
}
export { serialize, addURLData, serializeJSON }
constants.js
// 常量
export const HTTP_GET = 'GET'
export const CONTENT_TYPE_FORM_URLENCODED = 'application/x-www-form-urlencoded'
export const CONTENT_TYPE_JSON = 'application/json'
使用Promise改造封装好的Ajax(学习向)
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>封装Ajax</title>
</head>
<body></body>
<script type="module">
import { ajax, get, getJSON, post } from './ajax/index.js'
const url = 'https://www.imooc.com/api/http/search/suggest?words=js'
// const xhr = ajax(url, {
// method: 'GET',
// params: { username: '小孙' },
// data: {
// age: 18
// },
// responseType: 'json',
// success(response) {
// console.log(response)
// },
// httpCodeError(err) {
// console.log('http code error', err)
// },
// error(xhr) {
// console.log('error', xhr)
// },
// abort(xhr) {
// console.log('abort', xhr)
// },
// timeout(xhr) {
// console.log('timeout', xhr)
// }
// })
// xhr.abort()
const p = getJSON(url, {
params: { username: '小刘' },
data: { age: 18 },
timeoutTime: 10
})
p.xhr.abort()
const { ERROR_HTTP_CODE, ERROR_REQUEST, ERROR_TIMEOUT, ERROR_ABORT } = p
p.then(response => {
console.log(response)
}).catch(err => {
switch (err.type) {
case ERROR_HTTP_CODE:
console.log(err.text)
break
case ERROR_REQUEST:
console.log(err.text)
break
case ERROR_TIMEOUT:
console.log(err.text)
break
case ERROR_ABORT:
console.log(err.text)
break
}
})
</script>
</html>
index.js
import Ajax from './ajax.js'
import {
ERROR_HTTP_CODE,
ERROR_REQUEST,
ERROR_TIMEOUT,
ERROR_ABORT,
ERROR_HTTP_CODE_TEXT,
ERROR_REQUEST_TEXT,
ERROR_TIMEOUT_TEXT,
ERROR_ABORT_TEXT
} from './constants.js'
const ajax = (url, options) => {
// return new Ajax(url, options).getXHR()
let xhr
const p = new Promise((resolve, reject) => {
xhr = new Ajax(url, {
...options,
...{
success(response) {
resolve(response)
},
httpCodeError(status) {
reject({
type: ERROR_HTTP_CODE,
text: `${ERROR_HTTP_CODE_TEXT}:${status}`
})
},
error() {
reject({
type: ERROR_REQUEST,
text: `${ERROR_REQUEST_TEXT}`
})
},
abort() {
reject({
type: ERROR_ABORT,
text: `${ERROR_ABORT_TEXT}`
})
},
timeout() {
reject({
type: ERROR_TIMEOUT,
text: `${ERROR_TIMEOUT_TEXT}`
})
}
}
}).getXHR()
})
p.xhr = xhr
p.ERROR_HTTP_CODE = ERROR_HTTP_CODE
p.ERROR_REQUEST = ERROR_REQUEST
p.ERROR_ABORT = ERROR_ABORT
p.ERROR_TIMEOUT = ERROR_TIMEOUT
return p
}
const get = (url, options) => {
return ajax(url, { ...options, method: 'GET' })
}
const getJSON = (url, options) => {
return ajax(url, { ...options, method: 'GET', responseType: 'json' })
}
const post = (url, options) => {
return ajax(url, { ...options, method: 'POST' })
}
export { ajax, get, getJSON, post }
constants.js
// 常量
export const HTTP_GET = 'GET'
export const CONTENT_TYPE_FORM_URLENCODED = 'application/x-www-form-urlencoded'
export const CONTENT_TYPE_JSON = 'application/json'
export const ERROR_HTTP_CODE = 1
export const ERROR_HTTP_CODE_TEXT = 'HTTP状态码异常'
export const ERROR_REQUEST = 2
export const ERROR_REQUEST_TEXT = '请求被阻止'
export const ERROR_TIMEOUT = 3
export const ERROR_TIMEOUT_TEXT = '请求超时'
export const ERROR_ABORT = 4
export const ERROR_ABORT_TEXT = '请求终止'
参考:
慕课的前端课程