文章目录
一、Ajax 基础
1. 认识 Ajax
(1) Ajax 是什么
- Ajax 是 Asynchronous JavaScript and XML(异步 JavaScript 和 XML)的简写
- Ajax 中的异步:可以异步地向服务器发送请求,在等待响应的过程中,不会阻塞当前页面,浏览器可以做自己的事情。直到成功获取响应后,浏览器才开始处理响应数据
- XML(可扩展标记语言)是前后端数据通信时传输数据的一种格式
XML 现在已经不怎么用了,现在比较常用的是 JSON - Ajax 其实就是浏览器与服务器之间的一种异步通信方式
- 使用 Ajax 可以在不重新加载整个页面的情况下,对页面的某部分进行更新
(2) 搭建 Ajax 开发环境
- Ajax 需要服务器环境,非服务器环境下,很多浏览器无法正常使用 Ajax
- VSCode:Live Server
- Windows:phpStudy
- Mac:MAMP
2. Ajax 的基本用法
(1) XMLHttpRequest
- Ajax 想要实现浏览器与服务器之间的异步通信,需要依靠 XMLHttpRequest,它是一个构造函数
- 不论是 XMLHttpRequest,还是 Ajax,都没有和具体的某种数据格式绑定
(2) Ajax 的使用步骤
1)创建 xhr 对象:
const xhr = new XMLHttpRequest();
2)监听事件,处理响应:
当获取到响应后,会触发 xhr 对象的 readystatechange 事件,可以在该事件中对响应进行处理
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);
}
};
- readystatechange 事件也可以配合 addEventListener 使用,不过要注意,IE6~8 不支持
addEventListener
xhr.addEventListener('readystatechange', () => {}, fasle);
- 为了兼容性,readystatechange 中不使用 this,而是直接使用 xhr
- 由于兼容性的原因,最好放在 open 之前
- readystatechange 事件监听 readyState 这个状态的变化,它的值从 0 ~ 4,一共 5 个状态:
0:未初始化。尚未调用 open()
1:启动。已经调用 open(),但尚未调用 send()
2:发送。已经调用 send(),但尚未接收到响应
3:接收。已经接收到部分响应数据
4:完成。已经接收到全部响应数据,而且已经可以在浏览器中使用了
3)准备发送请求:
调用 open 并不会真正发送请求,而只是做好发送请求前的准备工作
xhr.open(
'HTTP 方法 GET、POST、PUT、DELETE',
'地址 URL https://www.imooc.com/api/http/search/suggest?words=js ./index.html ./index.xml ./index.txt',
true
);
4)发送请求:
调用 send() 正式发送请求
send() 的参数是通过请求体携带的数据
xhr.send(null);
(3) 使用 Ajax 完成前后端通信
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);
console.log(typeof xhr.responseText);
}
};
xhr.open('GET', url, true);
xhr.send(null);
3. GET 请求
(1) 携带数据
- GET 请求不能通过请求体携带数据,但可以通过请求头携带
const url =
'https://www.imooc.com/api/http/search/suggest?words=js&username=mike&age=18';
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);
// 不会报错,但不会发送数据
xhr.send('sex=male');
(2) 数据编码
- 如果携带的数据是非英文字母的话,比如说汉字,就需要编码之后再发送给后端,不然会造成乱码问题
- 可以使用 encodeURIComponent() 编码
const url = `https://www.imooc.com/api/http/search/suggest?words=${encodeURIComponent(
'前端'
)}`;
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);
4. POST 请求
(1) 携带数据
- 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=mike&age=18');
// 不能直接传递对象,需要先将对象转换成字符串的形式
xhr.send({
username: 'mike',
age: 18
});
// [object Object]
(2) 数据编码
- 可以使用 encodeURIComponent() 编码
xhr.send(`username=${encodeURIComponent('张三')}&age=18`);
二、JSON
1. 认识 JSON
(1) JSON 是什么
- JSON 全称是 JavaScript Object Notation
- JSON:Ajax 发送和接收数据的一种格式
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('GET', url, true);
xhr.send(null);
// {"code":200,"data":[{"word":"jsp"},{"word":"js"},{"word":"json"},{"word":"js \u5165\u95e8"},{"word":"jstl"}]}
// HTML/XML
(2) 为什么需要 JSON
- JSON 有 3 种形式,每种形式的写法都和 JS 中的数据类型很像,可以很轻松的和 JS 中的数据类型互相转换
- JS -> JSON -> PHP / Java
- PHP / Java -> JSON -> JS
2. JSON 的 3 种形式
(1) 简单值形式
- JSON 的简单值形式就对应着 JS 中的基础数据类型:数字、字符串、布尔值、null
- 注意事项:
① JSON 中没有 undefined 值
② JSON 中的字符串必须使用双引号
③ JSON 中是不能注释的
(2) 对象形式
- JSON 的对象形式就对应着 JS 中的对象
- 注意事项:
① JSON 中对象的属性名必须用双引号,属性值如果是字符串也必须用双引号
② JSON 中只要涉及到字符串,就必须使用双引号
③ 不支持 undefined
(3) 数组形式
- JSON 的数组形式就对应着 JS 中的数组
[1, "hi", null]
- 注意事项:
① 数组中的字符串必须用双引号
② JSON 中只要涉及到字符串,就必须使用双引号
③ 不支持 undefined
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);
console.log(typeof xhr.responseText);
}
};
// xhr.open('GET', './plain.json', true);
// xhr.open('GET', './obj.json', true);
xhr.open('GET', './arr.json', true);
xhr.send(null);
3. 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(xhr.responseText);
console.log(typeof xhr.responseText);
console.log(JSON.parse(xhr.responseText));
console.log(JSON.parse(xhr.responseText).data);
}
};
// xhr.open('GET', './plain.json', true);
// xhr.open('GET', './obj.json', true);
// xhr.open('GET', './arr.json', true);
xhr.open(
'GET',
'https://www.imooc.com/api/http/search/suggest?words=js',
true
);
xhr.send(null);
(2) JSON.stringify()
- JSON.stringify() 可以将 JS 的基本数据类型、对象或者数组转换成 JSON 格式的字符串
console.log(
JSON.stringify({
username: 'mike',
age: 18
})
);
const xhr = new XMLHttpRequest();
xhr.open(
'POST',
'https://www.imooc.com/api/http/search/suggest?words=js',
true
);
xhr.send(
JSON.stringify({
username: 'mike',
age: 18
})
);
(3) 使用 JSON.parse() 和 JSON.stringify() 封装 localStorage
import { get, set, remove, clear } from './storage.js';
set('username', 'mike');
console.log(get('username'));
set('zs', {
name: '张三',
age: 18
});
console.log(get('zs'));
remove('username');
clear();
三、跨域
1. 跨域是什么
- 同域,不是跨域
const url = './index.html';
// const url = 'http://127.0.0.1:5500/.../index.html';
http://127.0.0.1:5500
- 不同域,跨域,被浏览器阻止
const url = 'https://www.imooc.com';
const xhr = new XMLHttpRequest();
// Access to XMLHttpRequest at 'https://www.imooc.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
- 向一个域发送请求,如果要请求的域和当前域是不同域,就叫跨域
- 不同域之间的请求,就是跨域请求
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);
2. 什么是不同域,什么是同域
- https(协议): //www.imooc.com(域名):443(端口号)/course/list(路径)
- 协议、域名、端口号,任何一个不一样,就是不同域
- 与路径无关,路径一不一样无所谓
- 不同域:
https://www.imooc.com:443/course/list
http://www.imooc.com:80/course/list - 不同域:
http://www.imooc.com:80/course/list
http://m.imooc.com:80/course/list
http://imooc.com:80/course/list - 同域:
http://imooc.com:80
http://imooc.com:80/course/list
3. 跨域请求为什么会被阻止
- 阻止跨域请求,其实是浏览器本身的一种安全策略–同源策略
- 其他客户端或者服务器都不存在跨域被阻止的问题
4. 跨域解决方案
- ① CORS 跨域资源共享
- ② JSONP
- 优先使用 CORS 跨域资源共享,如果浏览器不支持 CORS 的话,再使用 JSONP
(1) CORS 跨域资源共享
// const url = 'https://www.imooc.com';
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('GET', url, true);
xhr.send(null);
- Access-Control-Allow-Origin: *
表明允许所有的域名来跨域请求它,*
是通配符,没有任何限制 - 只允许指定域名的跨域请求:
Access-Control-Allow-Origin: http://127.0.0.1:5500 - 使用 CORS 跨域的过程:
① 浏览器发送请求
② 后端在响应头中添加 Access-Control-Allow-Origin 头信息
③ 浏览器接收到响应
④ 如果是同域下的请求,浏览器不会额外做什么,这次前后端通信就圆满完成了
⑤ 如果是跨域请求,浏览器会从响应头中查找是否允许跨域访问
⑥ 如果允许跨域,通信圆满完成
⑦ 如果没找到或不包含想要跨域的域名,就丢弃响应结果 - CORS 的兼容性:
IE10 及以上版本的浏览器可以正常使用 CORS
(2) JSONP
- JSONP 的原理:
script 标签跨域不会被浏览器阻止
JSONP 主要就是利用 script 标签,加载跨域文件 - 使用 JSONP 实现跨域:
// 服务器端准备好 JSONP 接口
// https://www.imooc.com/api/http/jsonp?callback=handleResponse
// 手动加载 JSONP 接口或动态加载 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);
};
// handleResponse({
// code: 200,
// data: [
// {
// word: 'jsp'
// },
// {
// word: 'js'
// },
// {
// word: 'json'
// },
// {
// word: 'js 入门'
// },
// {
// word: 'jstl'
// }
// ]
// });
四、XHR 对象
1. XHR 的属性
(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);
// 可以用来替代 responseText
console.log('response:', xhr.response);
// console.log(JSON.parse(xhr.responseText));
}
};
xhr.open('GET', url, true);
xhr.responseType = '';
xhr.responseType = 'text';
xhr.responseType = 'json';
xhr.send(null);
IE6~9 不支持,IE10 开始支持
(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 = 10000;
xhr.send(null);
IE6~7 不支持,IE8 开始支持
(3) withCredentials 属性
- 指定使用 Ajax 发送请求时是否携带 Cookie
- 使用 Ajax 发送请求,默认情况下,同域时,会携带 Cookie;跨域时,不会携带 Cookie
xhr.withCredentials = true;
最终能否成功跨域携带 Cookie,还要看服务器同不同意
// 同域
// const url = './index.html';
// 跨域
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.withCredentials = true;
xhr.send(null);
IE6~9 不支持,IE10 开始支持
2. 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.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);
}
};
xhr.open('POST', url, true);
// 请求头中的 Content-Type 字段用来告诉服务器,浏览器发送的数据是什么格式的
// xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
// xhr.send('username=mike&age=18');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(
JSON.stringify({
username: 'mike'
})
);
// xhr.send(null);
3. XHR 的事件
(1) load 事件
- 响应数据可用时触发
const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
const xhr = new XMLHttpRequest();
// xhr.onload = () => {
// if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
// console.log(xhr.response);
// }
// };
// 与 addEventListener 搭配使用
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);
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');
},
false
);
xhr.open('GET', url, true);
xhr.send(null);
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');
},
false
);
xhr.open('GET', url, true);
xhr.send(null);
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');
},
false
);
xhr.open('GET', url, true);
xhr.timeout = 10;
xhr.send(null);
IE8 开始支持
五、Ajax 进阶
1. FormData
<body>
<form
id="login"
action="https://www.imooc.com/api/http/search/suggest?words=js"
method="POST"
enctype="multipart/form-data"
>
<input type="text" name="username" placeholder="用户名" />
<input type="password" name="password" placeholder="密码" />
<input id="submit" type="submit" value="登录" />
</form>
<script>
// 1.使用 Ajax 提交表单
// 2.FormData 的基本用法
// 通过 HTML 表单元素创建 FormData 对象
// const fd = new FormData(表单元素);
// xhr.send(fd);
// 通过 append() 方法添加数据
// const fd = new FormData(表单元素);
// fd.append('age', 18);
// fd.append('sex', 'male');
// xhr.send(fd);
// IE10 及以上可以支持
const login = document.getElementById('login');
// console.log(login.username);
// console.log(login.password);
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
);
</script>
</body>