Ajax
0x01 Ajax
(1)概述
- 全称:Asynchronous Javascript And XML
- 功能:用于客户端与服务端之间收发数据,即前后端交互,是一个默认异步执行机制的功能
- 优势:
- 不需要插件的支持
- 不需要刷新页面获得数据的更新
- 减轻服务器和带宽的负担
- 缺陷:搜索引擎的支持度不够
(2)基础
-
在 Javascript 中有内置的构造函数用于创建 Ajax 对象
-
可以基于创建的 Ajax 对象发送请求和接收响应
-
最基本的 Ajax 请求步骤为:
- 创建 Ajax 对象
- 配置请求
- 发送请求
-
上述 Ajax 请求过程中,无法获取到响应结果,为解决此问题,需要以下两个条件
-
HTTP 请求成功,即 HTTP 状态码为 2xx
可以通过 Ajax 对象的
status
成员获取 -
Ajax 请求成功,即 Ajax 状态码为 4
可以通过 Ajax 对象的
readyState
成员获取状态码 说明 readyState === 0
初始化未完成 readyState === 1
参数配置完成 readyState === 2
请求发送完成 readyState === 3
正在解析响应 readyState === 4
响应解析完成
-
(3)示例
-
创建一个目录,其中新建 index.html 和 data.json
-
在 index.html 中创建 Ajax 对象
-
IE9 及以上
const xhr = new XMLHttpRequest();
-
IE9 以下
const xhr = new ActiveXObject("Microsoft.XMLHTTP");
-
-
配置 HTTP 请求的参数
const xhr = new XMLHttpRequest(); xhr.open("GET", "./data.json", true);
- 第一个参数:请求方法
- 第二个参数:请求 URL
- 第三个参数(可选):是否异步,默认
true
- 第四个参数(可选):用于认证的账号,默认空字符串
- 第五个参数(可选):用于认证的命名,默认空字符串
-
发送 HTTP 请求
const xhr = new XMLHttpRequest(); xhr.open("GET", "./data.json", true); xhr.send();
-
监听 HTTP 请求
const xhr = new XMLHttpRequest(); xhr.open("GET", "./data.json", true); xhr.send(); xhr.onreadystatechange = function() { console.log("state changed"); }
-
修改监听方法,获取 HTTP 响应内容
const xhr = new XMLHttpRequest(); xhr.open("GET", "./data.json", true); xhr.send(); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (/^2\d{2}$/.test(xhr.status)) { console.log(JSON.parse(xhr.responseText)); } else { console.log("error\n", xhr.responseText); } } };
-
修改监听方法,简化对响应解析完毕的监听
const xhr = new XMLHttpRequest(); xhr.open("GET", "./data.json", true); xhr.send(); xhr.onload = function () { if (/^2\d{2}$/.test(xhr.status)) { console.log(JSON.parse(xhr.responseText)); } else { console.log("error\n", xhr.responseText); } };
(4)请求方式
准备 JSON Server
在官网安装最新版 Node.js
使用命令
npm install -g json-server
安装 JSON Server在下面根目录下,使用命令
json-server --watch .\data.json --port 3000
开启 JSON Server
--watch
:实时监听--port
:分配端口修改 data.json
{ "users": [ { "id": 1, "name": "John", "age": 18 }, { "id": 2, "name": "Mary", "age": 19 } ] }
-
GET:获取
<body> <button onclick="getRequest()">GET</button> <script> const xhr = new XMLHttpRequest(); function getRequest() { xhr.open("GET", "http://localhost:3000/users?name=John"); xhr.send(); xhr.onload = function () { if (/^2\d{2}$/.test(xhr.status)) { console.log(JSON.parse(xhr.responseText)); } else { console.log("error\n", xhr.responseText); } }; } </script> </body>
-
POST:提交
<body> <button onclick="postRequest()">POST</button> <script> const xhr = new XMLHttpRequest(); function postRequest() { xhr.open("POST", "http://localhost:3000/users"); // Form 数据 // xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); // xhr.send(`name=Alex&age=20`); // JSON 数据 xhr.setRequestHeader("Content-Type", "application/json"); xhr.send(JSON.stringify({ name: "Alex", age: 20 })); xhr.onload = function () { if (/^2\d{2}$/.test(xhr.status)) { console.log(JSON.parse(xhr.responseText)); } else { console.log("error\n", xhr.responseText); } }; } </script> </body>
-
PUT:更新
<body> <button onclick="putRequest()">PUT</button> <script> const xhr = new XMLHttpRequest(); function putRequest() { xhr.open("PUT", "http://localhost:3000/users/1"); xhr.setRequestHeader("Content-Type", "application/json"); xhr.send(JSON.stringify({ age: 20 })); xhr.onload = function () { if (/^2\d{2}$/.test(xhr.status)) { console.log(JSON.parse(xhr.responseText)); } else { console.log("error\n", xhr.responseText); } }; } </script> </body>
- 在 URL 中指定 id
-
PATCH:修改
<body> <button onclick="patchRequest()">PATCH</button> <script> const xhr = new XMLHttpRequest(); function patchRequest() { xhr.open("PATCH", "http://localhost:3000/users/1"); xhr.setRequestHeader("Content-Type", "application/json"); xhr.send(JSON.stringify({ age: 20 })); xhr.onload = function () { if (/^2\d{2}$/.test(xhr.status)) { console.log(JSON.parse(xhr.responseText)); } else { console.log("error\n", xhr.responseText); } }; } </script> </body>
-
DELETE:删除
<body> <button onclick="deleteRequest()">DELETE</button> <script> const xhr = new XMLHttpRequest(); function deleteRequest() { xhr.open("DELETE", "http://localhost:3000/users/2"); xhr.send(); xhr.onload = function () { if (/^2\d{2}$/.test(xhr.status)) { console.log(JSON.parse(xhr.responseText)); } else { console.log("error\n", xhr.responseText); } }; } </script> </body>
-
其他请求方式
请求方式 主要功能 HEAD 获取服务器头信息 OPTION 获取服务器设备信息 CONNECT 保留请求方式
0x02 Fetch
-
Fetch 是 W3C 的新标准,是替代 XMLHttpRequest 实现 Ajax 的新方式
-
使用方法
-
GET
fetch("http://localhost:3000/users").then((res) => { res.json().then((res) => { console.log(res); }); });
// 简化写法 fetch("http://localhost:3000/users").then(res => { return res.json(); }).then(res => { console.log(res); });
// 携带参数 fetch("http://localhost:3000/users?name=John").then(res => { return res.json(); }).then(res => { console.log(res); });
-
POST
fetch("http://localhost:3000/users", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ name: "Mary", age: 19, }), }) .then(res => { return res.json(); }) .then(res => { console.log(res); });
-
-
请求失败处理
fetch("http://localhost:3000/use1rs") .then(res => { if (res.ok) { return res.json(); } else { return Promise.reject({ status: res.status, msg: res.statusText, }); } }) .then(res => { console.log(res); }) .catch(err => { console.error(err); });
-
将获取的数据在页面中展示
fetch("http://localhost:3000/users") .then(res => { return res.json(); }) .then(res => { document.body.appendChild(document.createElement("ul")); document.querySelector("ul").innerHTML = res .map(item => `<li>${item.name}-${item.age}</li>`) .join(""); });
0x03 Axios
(1)基础使用
-
基于 Promise 的 HTTP 库,可以用在浏览器和 Node.js 中
-
引入 Axios
<script type="text/javascript" src="https://unpkg.com/axios@1.6.7/dist/axios.min.js"></script>
-
使用方法
-
GET
axios .get("http://localhost:3000/users") .then(res => { console.log(res); }) .then(res => { console.log(res.data); }) .catch(err => { console.log(err.message); });
// 携带参数 axios .get("http://localhost:3000/users", { params: { name: "John", }, }) .then(res => { console.log(res.data); }) .catch(err => { console.log(err.message); });
-
POST
axios .post("http://localhost:3000/users", { name: "Mary", age: 19, }) .then(res => { console.log(res.data); }) .catch(err => { console.log(err.message); });
// 完整写法 axios({ method: "POST", url: "http://localhost:3000/users", headers: { "Content-Type": "application/json", }, data: { name: "Mary", age: 19, }, }) .then(res => { console.log(res.data); }) .catch(err => { console.log(err.message); });
-
(2)其他应用
-
拦截器:在发送请求或者响应请求前修改请求或者响应
axios.interceptors.request.use( config => { console.log("Start"); return config; }, error => { console.log("Error"); return Promise.reject(error); } ); axios.interceptors.response.use( response => { console.log("End"); return response; }, error => { console.log("Error"); return Promise.reject(error); } );
-
中断器:在发送请求或者响应请求过程中中断并取消请求
const controller = new AbortController(); axios.get("http://localhost:3000/users", { signal: controller.signal }).then(response => { console.log(response); }); controller.abort();
-
中断器应用示例
<body> <button onclick="func()">Click</button> <script> const controller = new AbortController(); axios .get("http://localhost:3000/users", { signal: controller.signal, }) .then(response => { console.log(response); }); function func() { controller.abort(); } </script> </body>
-
0x04 跨域
(1)同源策略
- 一个 URL 包括协议、域名、端口,上述三者完全一致时,称为同源
- 在
http://localhost:3000/...
中,http
是协议,localhost
是域名,3000
是端口
- 在
- 对于非同源的网页:
- 无法读取 Cookie、LocalStorage 等
- 无法接触 DOM
- 无法发送 Ajax 请求(浏览器会拒绝响应)
- 同源策略(Same-origin policy)是浏览器行为,用于保护本地数据不被污染
(2)跨域方法
- 所谓跨域是指避开浏览器的同源策略,访问其他域名的数据
a. JSONP
-
JSONP(JSON with Padding)是 JSON 的一种使用模式,使网页可以从其他域名获取数据
-
JSONP 只能发送 GET 请求,无法发送 POST 等其他请求
-
举例:自制网页引入百度搜索
<body> <input type="text" /> <ul></ul> <script> let input = document.querySelector("input"); let ul = document.querySelector("ul"); input.oninput = function () { if (input.value === "") { ul.innerHTML = ""; return; } let script = document.createElement("script"); script.src = `https://www.baidu.com/sugrec?...&wd=${input.value}&cb=custom`; document.body.appendChild(script); script.onload = function () { // script.parentNode.removeChild(script); script.remove(); }; }; function custom(data) { ul.innerHTML = data.g.map((item) => `<li>${item.q}</li>`).join(""); } </script> </body>
b. 允许跨域
- 当响应头中有
Access-Control-Allow-Origin: *
时,说明服务端允许跨域请求,此时可以从自制网页直接发送请求 - 此时如果依然被拒绝请求,则可以添加其他请求头,如
X-Host
、X-Client-Info
等,对应的值可以通过以下方法获取:- 打开浏览器的开发者工具
- 菜单栏中选择“网络”(Network)标签
- 选择 Fetch/XHR 过滤
- 查看相关请求中的头信息
c. Nginx
-
Nginx 是一个独立的反向代理服务器
Nginx 详细使用方法:Nginx | 博客园-SRIGT
-
使用方法(简述)
- 将网页作为静态资源存于 Nginx 中,其中页面请求的 URL 以
/api
开头,忽略协议和域名 - 修改相关配置文件,将页面的目录作为
location / { root }
,将目标页面的 api 路由作为location /api/
- 使用命令
nginx -c .\conf\xxx.conf
执行上述修改的配置文件(xxx 为配置文件的名称) - 访问 http://localhost/xxx.html(xxx 为页面文件的名称)
- 将网页作为静态资源存于 Nginx 中,其中页面请求的 URL 以
-End-