Ajax 复习笔记

1. 什么是 XML

可扩展标记语言,被设计用来传输和存储数据,全部是自定义标签,用来表示数据,目前被 JSON 替代。

2. AJAX 的优缺点

AJAX(Asynchronous JavaScript and XML)即"异步 JavaScript 和 XML",指一种创建交互式网页应用的网页开发技术。

优点:

  • 异步请求,不阻塞页面其他操作。
  • 按需取数据,减少不必要的数据传输。
  • 局部刷新,用户体验更好。
  • 可以与服务器保持连接(如 Comet、长轮询)。

缺点:

  • 破坏浏览器后退按钮的正常行为(因为是异步更新页面内容)。
  • 依赖 JavaScript,需要考虑浏览器兼容性。
  • 不利于 SEO(爬虫看不到异步加载的数据)。
  • 存在跨域问题。

3. 使用 XMLHttpRequest 发送网络请求

const xhr = new XMLHttpRequest();
xhr.open(method, url);
// 设置请求头, 一般不设置
xhr.setRequestHeader("Content-type", "..");
// 发送请求
// get 请求体为空, post 可以不为空
xhr.send(body);
// 响应请求
// xhr.responseXML 接受 xml 格式的响应数据
// xhr.responseText 接收 text 格式的响应数据
xhr.onreadystatechange = function () {
    if (xhr.readyState == 4 && xhr.status == 200) {
        const test = xhr.responseText;
    }
}

4. 避免缓存

在一些浏览器中(IE),由于缓存机制的存在,ajax 只会发送的第一次请求,剩余多次请求不会再发送给浏览器而是直接加载缓存中的数据。

浏览器的缓存是根据 url 地址来记录的,所以我们只需要修改 url 地址即可避免缓存问题:

xhr.open("get", "xx/x?t=" + Date.now());

5. AJAX 请求状态

使用 xhr.readyState 可以查看请求当前的状态:

readyState状态
0未初始化,尚未调用 open()
1open() 已调用
2send() 已调用
3正在接收响应
4响应接收完毕

6. 响应体内容转化

手动转化:

let data = JSON.parse(xhr.response);

自动转化:

xhr.responseType = "json";

如此设置的话,响应数据将会自动按照 JSON 格式转化为对象。

7. 超时设置、超时回调、错误回调与取消请求

xhr.timeout = 2000;
xhr.ontimeout = function () {}; // 超时回调函数
xhr.onerror = function () {};   // 错误回调函数
xhr.abort();                    // 取消请求

axios 配合 AbortController 或 CancelToken 实现取消请求:

// 方式一
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get("xxxx", { cancelToken: source.token });
source.cancel("主动取消请求");

// 方式二
const CancelToken = axios.CancelToken;
let cancel;
axios.get("xxxx", {
  cancelToken: new CancelToken(function executor(c) { cancel = c; })
});
cancel("主动取消请求");

8. 请求报文与响应报文结构

请求报文:

  • 请求行:请求类型 / URL(路径或查询字符串) / HTTP 协议版本,例如 GET 或 POST /s?ie=utf-8 HTTP/1.1
  • 请求头:名字: 值,例如 Host: atguigu.com、Cookie: name=guigu、Content-Type: ...
  • 空行
  • 请求体:GET 请求为空;POST 请求可以不为空,例如 username=admin&password=admin

响应报文:

  • 状态行:HTTP 协议版本 / 响应状态码 / 状态字符串,例如 HTTP/1.1 200 OK
  • 响应头:名字: 值,例如 Content-Type: text/html;charset=utf-8 等
  • 空行
  • 响应体:HTML / JSON 等内容

9. 用 Express 监听 GET/POST 请求并实现跨域

// 引入 express
const express = require("express");

// 创建应用对象
const app = express();

// 创建路由规则
// request 是对请求报文的封装
// response 是对响应报文的封装
// /server 是向服务器请求的 URL
app.get("/server", (request, response) => {
  // 设置响应头, 设置允许跨域
  response.setHeader("Access-Control-Allow-Origin", "*");
  // 设置响应体
  response.send("HELLO AJAX");
});

app.post("/server", (request, response) => {
  response.setHeader("Access-Control-Allow-Origin", "*");
  response.send("HELLO AJAX POST");
});

// 可以接收任意类型的请求
app.all("/server", (request, response) => {
  response.setHeader("Access-Control-Allow-Origin", "*");
  // 设置可以接受任意的响应头键值对
  response.setHeader("Access-Control-Allow-Headers", "*");
  response.send("HELLO AJAX POST");
});

// 监听端口启动服务
app.listen(8000, () => {
  console.log("8000 端口监听,服务已启动");
});

10. jQuery 发送 AJAX 请求

get

$.get(url, [dataType], [callback 载入成功回调函数], [err 回调函数])

type: xml, html, script, json, text, _default

post

$.post(url, [dataType], [callback], [err 回调函数])

$.ajax()

$.ajax({
    type: "get",
    url: "",
    data: {...},
    dataType: "json",
    timeout: 2000,
    success: function (data, testStatus) {},
    error: function (XMLHttpRequest, testStatus, errorThrown) {
        // XMLHttpRequest, 错误信息, 捕获的错误对象
    },
    headers: {
        c: 300
    }
})

11. axios 发送 AJAX

npm i axios

创建实例:

const instance = axios.create({
    baseURL: "",
    timeout: 1000,
    headers: {}
});

实例方法:

  • axios#request(config)
  • axios#get(url[, config])
  • axios#delete(url[, config])
  • axios#head(url[, config])
  • axios#options(url[, config])
  • axios#post(url[, data[, config]])
  • axios#put(url[, data[, config]])
  • axios#patch(url[, data[, config]])

get:

axios.default.baseURL = "BASEurl";

axios.get(url, {
    params: { id: 100 },   // 与请求一起发送的 url 参数
    headers: { name: "liqingy" },
    timeout: 2000
}).then(res => {
    console.log(res.data);
    console.log(res.status);
    console.log(res.statusText);
    console.log(res.headers);
    console.log(res.config);
});

post:

axios.post(url,
    {
        // 作为请求体被发送的数据
        // 只能被应用于 put post patch
        username: "admin"
    },
    {
        params: { id: 100 }, // url 参数
        headers: { name: "liqingyang" } // 请求头信息
    }
).then(res => {}).catch(reason => {});

axios 函数形式发送 AJAX:

axios({
    url: "",
    method: "POST",
    params: { vip: 10, level: 30 },   // url 参数
    headers: { a: 100, b: 200 },      // 请求头信息
    data: { username: "admin", password: "admin" } // 请求体信息
}).then(res => {
    console.log(res.status);     // 响应状态码
    console.log(res.statusText); // 响应状态字符串
    console.log(res.headers);    // 响应头信息
    console.log(res.data);       // 响应体
});

12. jQuery 发送 JSONP

$.getJSON("url", 回调函数)

URL 中一定要有 callback=? 请求参数。该参数在页面中创建一个调用函数,并且发送给服务端,服务端利用 request.query.callback 可以直到该函数名,并且返回调用函数及实参。

前端:

$.getJSON("url/jquery-jsonp-server?callback=xixi", function (data) {
    $("#result").html(`
        名称: ${data.name}<br>
        校区: ${data.city}
    `);
});

服务器:

app.all("jquery-jsonp-server", (request, response) => {
    const data = { name: "liqingyang", city: ["beijing"] };
    let cb = request.query.callback;
    let str = JSON.stringify(data);
    response.send(`${cb}(${str})`);
});

13. 什么是跨域 以及 解决方法

同源策略:浏览器安全策略。同源要求协议、域名、端口号完全相同,违背同源策略就是跨域。

JSONP:只支持 get 请求。利用一些标签天生的跨域能力,比如 img、script、iframe、link。

动态地创建一个 img 标签,设置 img 的 src,将 src 添加到 body 中:

const img = document.createElement("img");
img.src = "url";
document.body.appendChild(img);

举例:客户端发送请求,服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。

首先,网页动态插入 <script> 元素,由它向跨源网址发出请求:

function addScriptTag(src) {
  var script = document.createElement("script");
  script.setAttribute("type", "text/javascript");
  script.src = src;
  document.body.appendChild(script);
}

window.onload = function () {
  addScriptTag("http://example.com/ip?callback=foo");
}

function foo(data) {
  console.log("Your public IP address is: " + data.ip);
}

上面代码通过动态添加 <script> 元素,向服务器 example.com 发出请求。注意,该请求的查询字符串有一个 callback 参数,用来指定回调函数的名字,这对于 JSONP 是必需的。

服务器收到这个请求以后,会将数据放在回调函数的参数位置返回:

foo({ "ip": "8.8.8.8" });

由于 <script> 元素请求的脚本,直接作为代码运行。这时,只要浏览器定义了 foo 函数,该函数就会立即调用。作为参数的 JSON 数据被视为 JavaScript 对象,而不是字符串,因此避免了使用 JSON.parse 的步骤。

CORS 跨域资源共享:支持 get/post 请求,完全在服务器中进行处理。

CORS 新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。

CORS 通过设置一个响应头来告诉浏览器,该请求允许跨域:

// 服务器设置响应头
res.set("Access-Control-Allow-Origin", "url 或者 *");

WebSocket:WebSocket 是一种通信协议,使用 ws://(非加密)/ wss://(加密)作为协议前缀,该协议不实行同源策略。

WebSocket 发送请求的头信息有一个字段 Origin,表示该请求的请求源,即发自哪个域名。正是因为这个 WebSocket 才没有实行同源策略,因为服务器可以根据这个判断可否许可本次通信。

Cookie 是服务器写入浏览器的一小段信息,只有同源的网页才能共享。但是,两个网页一级域名相同,只是二级域名不同,浏览器允许通过设置 document.domain 共享 Cookie。

举例来说,A 网页是 http://w1.example.com/a.html,B 网页是 http://w2.example.com/b.html,那么只要设置相同的 document.domain,两个网页就可以共享 Cookie:

document.domain = "example.com";

另外,服务器也可以在设置 Cookie 的时候,指定 Cookie 的所属域名为一级域名,比如 .example.com:

Set-Cookie: key=value; domain=.example.com; path=/

这样的话,二级域名和三级域名不用做任何设置,都可以读取这个 Cookie。

补充

补充:本篇是 Ajax 复习笔记,覆盖 XML、AJAX 优缺点、XHR 用法、jQuery AJAX、跨域方案、Cookie、请求/响应报文结构、Express 跨域监听、响应体转化、超时取消、axios 与 JSONP 实现等。

关键速记:

  • XHR 五个 readyState:0 未初始化 → 1 open() 已调用 → 2 send() 已调用 → 3 接收到部分响应 → 4 完成。
  • HTTP 状态码范围:2xx 成功、3xx 重定向、4xx 客户端错误、5xx 服务端错误。
  • 跨域三种方案:
    • JSONP:利用 <script> 跨域特性,仅支持 GET,需服务端配合 callback。
    • CORS:服务端设置 Access-Control-Allow-Origin,浏览器自动放行,简单请求直接发,预检请求先 OPTIONS。
    • WebSocket / PostMessage / Nginx 反代:其他常见方案。
  • 避免缓存:URL 加时间戳 ?t=Date.now(),或设置请求头 Cache-Control: no-cache。
  • 请求报文三段:请求行(方法 + URL + HTTP 版本)、请求头、空行、请求体(POST 携带表单或 JSON)。
  • Express 监听:app.get/post/all("/server", handler),跨域通过 Access-Control-Allow-Origin 响应头实现。
  • axios 实例:axios.create({ baseURL, timeout, headers }),全局默认可用 axios.defaults.baseURL。
  • 超时 / 取消:
    • xhr.timeout = 2000; xhr.ontimeout = () => {}
    • xhr.abort()
    • axios 配合 AbortController 或 CancelToken。
  • jQuery JSONP:$.getJSON(url + "?callback=fn", cb),服务端用 request.query.callback 拼成函数调用返回。

补充:现代项目几乎都用 axios / fetch,XHR 主要用于面试复习和理解底层原理;现代工程实践里 axios + 服务端 RESTful 接口是主流,JSONP 几乎只在维护老项目或解决特殊跨域场景时才会用。

来源整理自:我的有道云笔记