AJAX简介
AJAX(Asynchronous JavaScript And XML)即异步的 JS 和 XML,发送异步请求,无刷新获取数据
作用:进行前后端交互,前后端交互初步概念
组成:
- XMLHttpRequest/Fetch API (异步的与服务器交换数据)
- JavaScript/DOM (信息显示/交互)
- CSS (给数据定义样式)
- XML/JSON (作为转换数据的格式)
优点:
- 无需刷新页面而与服务器端进行通信,获取数据。
- 根据事件动态获取所需数据,提高效率。
缺点:
- 跨域问题
- SEO不友好
- 无浏览历史
XML可扩展标记语言,用于传输和存储数据
结构上与html标签结构类似,但XML内都是自定义标签
<student> <name>chuckle</name> <age>19</age></student>发请求
浏览器内建有 XMLHttpRequest 构造函数,通过操控其构造出的对象,就能发送AJAX请求
xhr的 readyState 的属性记录了当前响应处于哪个过程,其值的的变化会触发 readystatechange 事件 0 未调用open方法,1 调用了open还未调用send,2 发送了请求还未收到响应,3 收到了部分响应,4 响应都接收完了
基本属性和方法:
- open(请求方法, URL, 是否异步) 初始化
- send(请求体) 发送请求
- setRequestHeader() 设置请求头
- abort() 取消请求
- timeout 设置超时时间,超时触发timeout事件
响应相关:
- status 响应状态码
- statusText 状态字符串
- getAllResponseHeaders() 获取所有响应头
- response 响应体
- responseType 设置响应类型,自动转换
- responseXML 接收 xml 格式的响应数据
- responseText 接收文本格式的响应数据
GET请求
const xhr = new XMLHttpRequest();xhr.responseType = 'json';// 第三个参数为是否异步,默认truexhr.open('GET', 'https://kpb.qcqx.cn/api', true);xhr.send();xhr.onreadystatechange = () => { if(xhr.readyState === 4 && ((xhr.status >= 200 && xhr.status <= 300) || xhr.status === 304)){ let text = xhr.responseText; console.log(xhr.status); console.log(xhr.statusText); console.log(xhr.getAllResponseHeaders()); console.log(xhr.response); console.log(text); }};POST请求
const xhr = new XMLHttpRequest();xhr.responseType = 'json';xhr.open("POST", "https://kpb.qcqx.cn/api/login", true);// 设置请求头,需在open后send前设置xhr.setRequestHeader( "content-type", "application/x-www-form-urlencoded");xhr.send("username=qx&password=123");xhr.onreadystatechange = () => { if ( xhr.readyState === 4 && ((xhr.status >= 200 && xhr.status <= 300) || xhr.status === 304) ) { console.log(xhr.status); console.log(xhr.statusText); console.log(xhr.getAllResponseHeaders()); console.log(xhr.response); }};超时和错误处理
xhr.timeout 设置超时时间,超时会触发timeout事件 出错(网络错误)会触发error事件
xhr.timeout = 2000; // 两秒超时xhr.ontimeout = ()=>{ console.log('连接超时');}xhr.onerror = ()=>{ console.log('出错了');}取消请求
abort() 可以取消一个请求
const xhr = new XMLHttpRequest();xhr.open('GET', 'https://kpb.qcqx.cn/api', true);xhr.send();xhr.abort();// 取消请求进度
6个进度事件:
loadstart在接收到响应数据的第一个字节时触发progress在接收响应期间持续不断地触error在请求发生错误时触发abort在因为调用abort()方法而终止连接时触发load在接收到完整的响应数据时触发loadend在通信完成或者触发error、abort或load事件后触发timeout超时发生时触发
// 下载进度xhr.onprogress = (e)=>{ // loaded 已经下载的字节数,total 总字节数 console.log((e.loaded / e.total * 100).toFixed(2) + '%');}// 上传进度xhr.upload.onprogress = (e)=>{ console.log((e.loaded / e.total * 100).toFixed(2) + '%');}JQ-AJAX
使用JQ发送AJAX请求
get和post方法:
- $.get(url, [data], [callback], [type])
- $.post(url, [data], [callback], [type]) url:请求的 URL 地址。 data:请求携带的参数。 callback:载入成功时回调函数。 type:设置返回内容格式
通用方法 ajax:
$.ajax({ url: '', type: 'GET', // 请求头 headers: { a: 100 } // 数据类型 dataType: 'json', // 请求体数据 data: { username: 'qx', password: '123' }, // 请求超时时间 timeout: 2000, // 是否异步处理,默认true async: true, // 请求成功回调 success: (data)=>{ console.log(data) }, // 失败回调 error: (err)=>{ console.log(err) }})Axios
Axios 是一个简单的基于 promise 的 HTTP 客户端,适用于浏览器和 node.js。 Axios 在具有非常可扩展的接口的小包中提供了一个简单易用的库。
使用:
npm install axios<script src="https://cdn.bootcdn.net/ajax/libs/axios/1.3.6/axios.min.js"></script>常用方法: 用于发送请求:
axios(config): 通用/最本质的发任意类型请求的方式axios(url[, config]): 可以只指定 url 发 get 请求axios.request(config): 等同于 axios(config)axios.get(url[, config]): 发 get 请求axios.post(url[, data, config]): 发 post 请求axios.delete(url[, config]): 发 delete 请求axios.put(url[, data, config]): 发 put 请求axios.patch(url[, data, config]): 发 patch 请求
基本使用
get请求:axios.get()
// 设置baseURLaxios.defaults.baseURL = 'https://kpb.qcqx.cn';axios.get('/api', { // url参数,查询字符串 params: {}, // 请求头 headers: { token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InF4IiwiX2lkIjoiNjQ0NWZhMGYzNTBhYjM2NDM2NDEzNjcwIiwiaWF0IjoxNjgyNDIzOTExLCJleHAiOjE2ODI0MzM5OTF9.pbwr6YjZS-b52xBNISi1O4QemT0Rd-w8oyVI9GpSa6U' },}).then(res=>{ // 响应对象 console.log(res); // 响应体对象 console.log(res.data);})post请求:axios.post()
axios.post('/api/login', { // 第二个参数请求体 username: 'qx', password: '123' },{ // 第三个参数其余配置 }).then(res=>{ // 响应对象 console.log(res); // 响应体对象 console.log(res.data);})通用方法 axios()
axios({ url: '/api/login', method: 'post', // url参数,查询字符串 params: {}, // 请求头 headers: {}, // 请求体 data: { username: 'qx', password: '123' }, // 请求超时 timeout: 5000}).then(res=>{ // 响应对象 console.log(res); // 响应体对象 console.log(res.data);})响应结果
- config 配置对象,里面有请求方法、请求头、请求url等
- data 响应体
- headers 响应头
- request axios发送请求时创建的原生的AJAX请求对象 XMLHttpRequest
- status 响应码
- statusText 响应状态字符串
{data: Array(4), status: 200, statusText: 'OK', headers: i, config: {…}, …}config: {transitional: {…}, adapter: Array(2), transformRequest: Array(1), transformResponse: Array(1), timeout: 0, …}data: (4) [{…}, {…}, {…}, {…}]headers: i {cache-control: 'no-cache', content-length: '299', content-type: 'application/json; charset=utf-8', expires: '-1', pragma: 'no-cache'}request: XMLHttpRequest {onreadystatechange: null, readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …}status: 200statusText: "OK"[[Prototype]]: Objectconfig配置对象
- url 给谁发送请求
- method 请求方法
- baseURL 设置url的基础结构
- params url参数,查询字符串
- headers 请求头
- data 请求体
- timeout 请求超时
- transformRequest 对请求的数据进行处理后再发送
transformRequest: [function (data, headers) { return data; }],- transformResponse 对响应体进行预处理
transformResponse: [function (data) { return data; }],- paramsSerializer url参数序列化,设置url参数的格式
paramsSerializer: { encode?: (param: string): string => { // 自定义操作并返回转换后的字符串 }, // url参数序列化 serialize?: (params: Record<string, any>, options?: ParamsSerializerOptions ), indexes: false // 数组索引格式 null-无括号,false(默认)-空括号,true-带索引的括号 },// /api?a=100&b=200// /api/a/100/b/200// /api/a.100/b.200- withCredentials: 跨域请求是否携带cookie,默认false
- responseType: 响应体结果类型,默认json,array、buffer、document、json、text、stream
- responseEncoding: 响应结果的字符集,默认utf8
- xsrfHeaderName和xsrfCookieName 防止跨站请求攻击
xsrfCookieName: 'XSRF-TOKEN', // defaultxsrfHeaderName: 'X-XSRF-TOKEN', // default在客户端第一次发送get请求时,服务器响应数据会携带一个会话cookie(XSRF-TOKEN)一同发送到客户端在后续请求发送前,http服务会从cookie中读取一个token(默认为XSRF-TOKEN)并且将其设置到HTTP头部(X-XSRF-TOKEN)一同发送到服务器。然后,服务器端会判断HTTP头部是否携带X-XSRF-TOKEN值,如果该值与之前发送的会话cookie值相同,就可以判定为来自己同一domain请求,否者会拦截该请求- maxContentLength: 2000 定义node.js中允许的http响应内容的最大大小
- maxBodyLength: 2000 定义http请求内容的最大大小
- validateStatus 请求成功的响应码范围
validateStatus: function (status) { return status >= 200 && status < 300; // default }- maxRedirects: 21 node.js中允许最大重定向数
- proxy: 设置代理
proxy: { protocol: 'https', host: '127.0.0.1', hostname: '127.0.0.1' //如果同时定义了“host”,则优先于“host” port: 9000, auth: { username: 'mikeymike', password: 'rapunz3l' } },- cancelToken 用于取消、打断请求,后面有详细介绍
- signal 使用AbortController取消Axios请求
全局默认配置
一些重复的配置项可以使用 axios.defaults 进行全局配置
// 全局baseURLaxios.defaults.baseURL = 'http://127.0.0.1';// 设置headers中默认tokenaxios.defaults.headers.token = "adra2nh12h4jh91";创建实例对象
axios.create(config) 创建实例对象,传入配置项
当需要大量发送请求时,可以创建一个实例对象,分别传入所需的基础配置项,可以减少代码的书写量,功能和axios几乎一样
const example = axios.create({ baseURL: 'http://127.0.0.1'});发请求:
example({ url: '/api'}).then(res=>{ console.log(res)})拦截器
拦截器是一些函数,分为请求拦截器和响应拦截器,实例对象能单独添加
可以添加多个拦截器,响应拦截器按代码位置先后倒序执行,响应拦截器顺序执行(请求拦截器进入的是堆栈,响应拦截器进入的是队列)
**作用:**对请求的参数和内容进行检测(判断token,设置请求头)。对响应结果进行预处理(失败提醒、数据格式化)
// 请求拦截器axios.interceptors.request.use(function () {/*...*/})// 响应拦截器axios.interceptors.response.use(function () {/*...*/})// 实例对象添加拦截器const instance = axios.create();instance.interceptors.request.use(function () {/*...*/});// 移除拦截器const myInterceptor = axios.interceptors.request.use(function () {/*...*/});axios.interceptors.request.eject(myInterceptor);// 清除请求或响应的所有拦截器const instance = axios.create();instance.interceptors.request.clear();instance.interceptors.response.clear();请求拦截器
对请求的参数和内容进行检测(判断token,设置请求头)
axios.interceptors.request.use(function (config) { // 在发送请求之前做些什么 return config; }, function (error) { // 对请求错误做些什么 return Promise.reject(error); });响应拦截器
对响应结果进行预处理(失败提醒、数据格式化)
axios.interceptors.response.use(function (response) { // 对响应数据做点什么 return response; }, function (error) { // 对响应错误做点什么 return Promise.reject(error); });取消请求
**1、**使用 cancelToken 中断axios请求(v0.22.0后弃用),两种方式:
//创建取消令牌的生成器对象const CancelToken = axios.CancelToken//获取令牌对象const source = CancelToken.source()
// const source = axios.CancelToken.source()axios.get('/test',{ cancelToken: source.token})
//取消请求source.cancel()let cancel;
axios.get('/user/12345', { cancelToken: new axios.CancelToken(function(c) { // 执行器函数接收一个取消函数作为参数 cancel = c; })});
// 取消请求cancel();**2、**使用 AbortController : AbortController 接口表示一个控制器对象,允许根据需要中止一个或多个 Web 请求
通过AbortController创建一个信号对象signal,然后将signal传递给config的signal属性,调用AbortController对象的abort()方法来中止请求
// 创建AbortController对象const controller = new AbortController();// 获取信号对象const { signal } = controller;const config = { method: 'get', url: "http://localhost:3000/posts", signal: signal}axios(config).then((result) => { console.log(result); console.log(result.data);}).catch((err) => { console.log(err);});// 取消请求controller.abort();Fetch
get请求:
const options = { method: "GET", headers: { token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InF4IiwiX2lkIjoiNjQ0NWZhMGYzNTBhYjM2NDM2NDEzNjcwIiwiaWF0IjoxNjgyNDIzOTExLCJleHAiOjE2ODI0MzM5OTF9.pbwr6YjZS-b52xBNISi1O4QemT0Rd-w8oyVI9GpSa6U", },};
fetch("https://kpb.qcqx.cn/api", options) .then((response) => response.json()) .then((response) => console.log(response)) .catch((err) => console.error(err));post请求:
const options = { method: "POST", headers: { token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InF4IiwiX2lkIjoiNjQ0NWZhMGYzNTBhYjM2NDM2NDEzNjcwIiwiaWF0IjoxNjgyNDIzOTExLCJleHAiOjE2ODI0MzM5OTF9.pbwr6YjZS-b52xBNISi1O4QemT0Rd-w8oyVI9GpSa6U", "content-type": "application/x-www-form-urlencoded", }, // 请求体 body: new URLSearchParams({ matter: "吃饭", data: "2023-04-24", type: "支出", account: "20", remark: "在食堂吃的", }),};
fetch("https://kpb.qcqx.cn/api", options) .then((response) => response.json()) .then((response) => console.log(response)) .catch((err) => console.error(err));使用AbortController打断fetch请求,在执行fetch请求时,可以通过AbortController创建一个信号对象signal,然后将signal作为配置选项传递给fetch()方法,调用AbortController对象的abort()方法来中止请求。
// 创建AbortController对象const controller = new AbortController();// 获取信号对象const { signal } = controller;// 将信号对象传入fetch配置项fetch(url, { signal }) .then(response => response.json())
// 调用abort() 中止 fetch 请求controller.abort();fetch 没有超时设置,所以需要通过 abort 和 setTimeout 来实现超时
下载进度
fetch 没有 progress 事件,需获取数据流手动计算下载进度。
fetch(url).then(async res => { // 克隆响应对象 const response = res.clone(); // 获取数据流 const reader = res.body.getReader(); // 获取数据总字节大小 const total = +res.headers.get('Content-Length'); // 接收到的数据大小 let loaded = 0; // 读取数据 while (true) { const { done, value } = await reader.read(); if (done) { break; } loaded += value.length; console.log((loaded / total * 100).toFixed(2) + '%'); } return response.text();}).then(data => { console.log(data);}).catch(err => { console.error(err);});跨域
**同源策略:**浏览器的一种安全策略,同源即协议、域名、端口号必须完全相同。违背同源策略就是跨域
CORS(Cross-Origin Resource Sharing),跨域资源共享。官方的解决跨域方 案,在后端进行设置。
CORS通过设置一个响应头来告诉浏览器,该请求允许跨域,浏览器收到该响应以后就会对响应放行。
express设置cors:
// 请求来源白名单const allowedOrigins = ["127.0.0.1:5500", undefined];app.use((req, res, next) => { const origin = req.headers.origin; // 判断请求来源是否在白名单内 if ( (origin && allowedOrigins.some((item) => origin.includes(item))) || allowedOrigins.includes(origin) ) { // 设置允许跨域的域名,*代表允许任意域名跨域 res.header('Access-Control-Allow-Origin', origin); // 允许的header类型,如下设置允许自定义header、允许Content-Type为非默认值等,按需删改 res.header('Access-Control-Allow-Headers', "*, Origin, X-Requested-With, Content-Type, Accept, Authorization"); // 跨域允许的请求方式 res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, PATCH ,OPTIONS'); // 跨域的时候是否携带cookie // 需要与 XMLHttpRequest.withCredentials 或 Fetch API 的 Request() 构造函数中的 credentials 选项结合使用 res.header("Access-Control-Allow-Credentials", true); if (req.method.toLowerCase() == 'options') { res.send(200); // 让options请求快速结束 } else { next(); } } else { res.status(403).send('Forbidden'); }})**jsonp:**非官方的解决跨域方法
利用一些天生具有跨域能力的标签发请求,如:img link iframe script。
步骤:
- 新建一个script标签
- 设置 script 的 src,设置回调函数
- 将 script 添加到 body 中
- 服务端将数据扔进回调函数中返回
- script接收到响应会自动解析运行回调函数,从而获取到数据
<script src="https://127.0.0.1:3000/test"></script>// 后端部分const data = { name: 'chuckle'}app.all('/test', (req,res)=>{ res.end(`console.log(${data.name})`);})JQ实现jsonp
$.getJSON("/api?callback=?", function(data) { // 获得数据 console.log(data);})// 后端设置const data = { name: 'chuckle'}app.all('/api', (req,res)=>{ // 将数据转化为字符串 let str = JSON.stringify(data); // 接收回调函数 let cb = req.query.callback; // 返回数据data res.end(`${cb}(${str})`);})
评论