Skip to content

原生 Ajax — 使用 xhr 对象(Day14 1/2) ​

本页对应 Day14_Ajax,共 6 段视频,约 1 小时 57 分。

学习目标 ​

  • 能够用自己的话说明 Ajax 的含义、与普通页面请求的区别,以及“无刷新获取数据”的基本原理。
  • 能够使用 XMLHttpRequest 完成一次 GET 请求的四步流程:创建对象、监听 onload、调用 open 初始化、调用 send 发送,并通过 responseText 将响应内容渲染到页面。
  • 能够使用 URL 查询字符串携带 GET 请求数据,并说明 GET 请求没有请求体。
  • 能够分别以 text/plain、application/x-www-form-urlencoded、application/json 作为请求体格式发送 POST/PUT 请求,正确设置 Content-Type,并验证后端接收与解析结果的差异。
  • 能够使用 FormData 作为请求体发送数据:既可通过 append 添加字段,也可由表单元素直接构造,并能上传文件且无需手动设置 Content-Type。

知识点 ​

Ajax 是什么:为什么需要它 ​

Ajax 全称为 Asynchronous JavaScript And XML,就是异步的 JavaScript 和 XML。名字里有 XML,是因为早期会用 XMLHttpRequest 这个对象收发数据;现在前后端传递数据更常用 JSON,但对象名仍保留下来。

为什么需要:以前发请求主要靠地址栏输入地址、点链接、提交表单,页面往往会刷新或跳转。若只想更新页面的一小块内容,整页刷新很浪费。怎么想:让页面里的 JS 主动发 HTTP 请求并接收响应,用户不参与也能发;需要时再由按钮、输入等事件触发。怎么做:用 XMLHttpRequest 创建对象,监听响应,初始化请求,再发送。易错点:Ajax 不是新语言,而是现有标准的组合;它发出去的仍然是 HTTP 请求,不是“另一种请求”。

js
const xhr = new XMLHttpRequest();
xhr.onload = function () {
  console.log(xhr.responseText);
};
xhr.open('GET', '/getData');
xhr.send();

基本使用流程:四步走与调试 ​

为什么需要:初学 Ajax 最容易“代码写了但请求没发”,所以要把流程固定下来。怎么想:先确认后端路由能被浏览器直接访问,再在页面里用 Ajax 请求同一地址;打开网络面板看请求是否出现。怎么做:四步是创建对象、监听 onload、open 初始化、send 发送。转写稿中第一次调试时网络里没有 /getData,原因就是漏了 send();补上后状态 200,响应内容出现。易错点:onload 是“请求完成并拿到响应”后触发;没发请求就不会触发。open 第三个参数默认异步,一般不用改。

js
const xhr = new XMLHttpRequest();
xhr.onload = function () {
  console.log('接收到后端响应', xhr.responseText);
};
xhr.open('GET', '/getData');
xhr.send();

把请求放进按钮点击事件里,就能由用户决定何时发;也可以打开页面就发。调试时重点看 Network 中的请求地址、方法、状态码、请求头和响应体。

GET 携带数据:只能拼查询字符串 ​

为什么需要:请求常常要带用户名、密码等数据。怎么想:GET 没有请求体,数据只能拼到 URL 的查询字符串里。怎么做:先拼 userName=...&userPWD=...,再接到地址后面。转写稿中先发空 GET,后端能收到请求但 URL 中提取不到数据;拼上查询串后,后端才拿到数据。易错点:GET 的 send() 不要塞请求体;查询串前要有 ?。

js
const qs = `userName=${nameInput.value}&userPWD=${pwdInput.value}`;
xhr.open('GET', `/addData?${qs}`);
xhr.send();

POST/PUT 携带数据:请求体与 Content-Type ​

为什么需要:POST、PUT 可以把数据放进请求体,适合提交表单、修改数据。怎么想:请求体只能写字符串(FormData 例外),并且要用请求头告诉后端“字符串是什么格式”。怎么做:查询字符串格式用 application/x-www-form-urlencoded;JSON 格式先用 JSON.stringify 转字符串,再设 application/json。转写稿中 POST 默认 Content-Type 是 text/plain,后端不解析,拿到的是字符串;设置表单格式或 JSON 格式后,后端才解析成对象。PUT 与 POST 写法基本一样,只改请求方法。易错点:setRequestHeader 必须放在 open 之后、send 之前;请求体不要直接 send({}),会变成 [object Object]。

js
xhr.open('POST', '/addData');
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(`userName=${nameInput.value}&userPWD=${pwdInput.value}`);
js
const data = { userName: nameInput.value, userPWD: pwdInput.value };
xhr.open('POST', '/addData');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify(data));

FormData:提交表单和文件更省事 ​

为什么需要:手动拼查询串、逐个取表单值很麻烦;上传文件更不能用普通字符串。怎么想:FormData 可以装键值对,也可以直接包住表单元素;发送时浏览器会自动设置 multipart/form-data 相关请求头。怎么做:可 new FormData() 后 append,也可 new FormData(formEl) 自动收集表单控件。传文件时用 fileInput.files[0],不是 value。易错点:用 FormData 时不要手动乱设 Content-Type;若表单按钮会默认提交,要阻止默认行为,例如表单上写 onsubmit="return false"。

js
const fd = new FormData();
fd.append('userName', nameInput.value);
fd.append('userPWD', pwdInput.value);
xhr.open('POST', '/upload');
xhr.send(fd);
js
const form = document.querySelector('form');
const fd = new FormData(form);
xhr.open('POST', '/upload');
xhr.send(fd);

一、Ajax 是什么:让 JS 自己发 HTTP 请求 ​

为什么需要:以前在浏览器里发请求,主要靠地址栏输入地址、点链接、提交表单,这些都要用户操作,而且常常伴随页面刷新。Ajax 的价值是让页面里的 JS 程序自己发请求、自己接响应,用户不参与也可以完成。

怎么想:Ajax 不是新语言,而是把现有标准组合起来的使用方式。它发的仍然是 HTTP 请求,只不过发起者从“用户操作”变成了“开发者写的 JS 代码”。所以要先有一个后端地址配合,前端再用 XMLHttpRequest 去请求它。

怎么做:Ajax 请求围绕 XMLHttpRequest 对象展开,常简写为 xhr:

js
const xhr = new XMLHttpRequest();

易错点:XMLHttpRequest 是浏览器里的 DOM 对象,只能在浏览器环境用,不要拿到 Node 后端里用。页面也要通过服务器地址访问,不要随便双击 HTML 打开,否则容易出现跨域等问题。

二、xhr 基本流程:创建 → 监听 → 初始化 → 发送 ​

为什么需要:Ajax 请求是异步的,请求发出去后不能立刻拿响应,所以要先把“收到响应后做什么”安排好,再发请求。

怎么想:把流程拆成四步:创建 xhr 对象、监听响应成功事件、初始化请求、正式发送。监听要提前写,因为请求一旦发出去,响应回来时要有回调接住。

怎么做:

js
const xhr = new XMLHttpRequest();

xhr.onload = function () {
  console.log('接收到后端响应', xhr.responseText);
};

xhr.open('GET', '/getData');
xhr.send();

调试时打开浏览器开发者工具的 Network 面板,刷新页面后能看到页面本身和 Ajax 发出的请求。xhr.responseText 拿到的是响应体文本。

易错点:onload 不写或写在 send() 后面都可能来不及;open() 至少要给请求方式和 URL;GET 请求一般没有请求体,send() 不传参数。

三、GET 携带数据:只能拼到 URL 查询字符串里 ​

为什么需要:GET 请求没有请求体,但前端仍可能要把用户名、密码、搜索关键字等带给后端。

怎么想:GET 的数据放在 URL 的查询字符串里,也就是 ?key=value&key2=value2 的形式。后端从 URL 的 query 中取。

怎么做:

js
const qs = `username=${nameInput.value}&userPWD=${pwdInput.value}`;
xhr.open('GET', '/addData?' + qs);
xhr.send();

易错点:GET 不要幻想往请求体里塞数据;拼接时注意 ? 和 &;中文和特殊字符实际开发中通常还要 encodeURIComponent,转写稿中演示的是基础拼接。

四、POST 携带数据:请求体与 Content-Type 要配对 ​

为什么需要:POST 可以把数据放到请求体里,适合提交表单、JSON 等数据。但后端要看请求头里的 Content-Type 才知道怎么解析请求体。

怎么想:请求体只能写字符串。字符串格式不同,Content-Type 就不同。默认常是 text/plain,后端可能不解析;如果是表单格式,要告诉后端是 application/x-www-form-urlencoded;如果是 JSON 字符串,要告诉后端是 application/json。

怎么做:表单格式:

js
const qs = `username=${nameInput.value}&userPWD=${pwdInput.value}`;
xhr.open('POST', '/addData');
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(qs);

JSON 格式:

js
const data = {
  username: nameInput.value,
  userPWD: pwdInput.value
};
xhr.open('POST', '/addData');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify(data));

易错点:setRequestHeader 必须放在 open() 之后、send() 之前;send() 里不能直接传对象,否则会变成 [object Object];请求体格式和 Content-Type 要对得上。调试时可在 Network 的请求头看 Content-Type,在载荷里看请求体。

五、PUT:和 POST 一样属于“有请求体”的请求 ​

为什么需要:实际接口里除了 GET、POST,还会用 PUT 表示修改资源,用 DELETE 表示删除。Ajax 也能发这些请求方式。

怎么想:PUT 在 xhr 使用上和 POST 很像:可以带请求体,也可以设置 Content-Type。区别更多是语义:POST 常用于新增,PUT 常用于修改。

怎么做:

js
xhr.open('PUT', '/addData');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify(data));

易错点:不要以为 PUT 是特殊语法;它仍然走 open、setRequestHeader、send。请求方式要和后端路由匹配。

六、FormData:提交表单数据和文件更省事 ​

为什么需要:用字符串拼表单数据很麻烦,传文件更不可能靠普通字符串完成。FormData 可以把表单字段和文件一起放进请求体。

怎么想:有两种常见方式:一是创建空 FormData 再用 append 添加;二是直接把表单元素传给 new FormData(form),自动收集表单控件。文件不能取 input.value,那只是客户端假路径,要取 input.files[0]。

怎么做:

js
const fd = new FormData();
fd.append('username', nameInput.value);
fd.append('avatar', avatarInput.files[0]);

xhr.open('POST', '/upload');
xhr.send(fd);

或:

js
const form = document.querySelector('form');
const fd = new FormData(form);
xhr.open('POST', '/upload');
xhr.send(fd);

易错点:用 FormData 时不要手动设置 multipart/form-data 的 boundary,浏览器会自动设置;文件必须用 files;如果表单按钮会真的提交,记得阻止默认行为,例如表单上加 onsubmit="return false"。

七、调试与总结 ​

Ajax 调试优先看 Network:请求方式、URL、请求头、请求体、响应体都能看到。GET 的数据在 URL;POST/PUT 的数据常在请求体;Content-Type 决定后端怎么解析。最怕前后端都“猜”:前端按后端要求设置请求方式、地址、请求体格式和请求头,问题就会少很多。

先保留本节总结代码 ​

text
1. 设置请求报文
   请求体:字符串、formData
   请求体字符串三种格式: 无格式、querystring、json

2. 解析响应报文
   响应状态码  响应状态描述
   响应体
   响应体是json格式的字符串
   设置响应超时
   

3. 监听进度事件
   readystatechange
   loadstart  
   progress
   load
   error
   loadend

这张“总结”不是让你背名词,而是把原生 AJAX 的完整链路压缩成三块:发请求时怎么组织报文、收到响应后怎么读报文、整个过程有哪些事件可监听。下面按“为什么需要 → 怎么想 → 怎么做 → 易错点”展开。

设置请求报文:先分清 URL 和请求体 ​

为什么需要:AJAX 的本质仍然是 HTTP 请求。服务器不关心请求是地址栏发的、表单发的,还是 JS 发的,它只认 HTTP 请求报文。所以前端要做的,是把请求方式、URL、请求头、请求体组织正确。

怎么想:先问数据放哪里。GET 没有请求体,数据只能拼到 URL 查询字符串里;POST、PUT 等可以把数据放进请求体。请求体只有两类:字符串或 FormData。字符串又分三种常见格式:无格式、querystring、json。

怎么做:原生 AJAX 基本四步:

js
const xhr = new XMLHttpRequest();

xhr.onload = function () {
  console.log('接收到后端响应', xhr.responseText);
};

xhr.open('GET', '/getData'); // 默认异步
xhr.send(); // GET 不传请求体

POST 发 querystring:

js
const qs = `userName=${nameInput.value}&userPWD=${pwdInput.value}`;
xhr.open('POST', '/addData');
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(qs);

POST 发 json:

js
const data = { userName: nameInput.value, userPWD: pwdInput.value };
xhr.open('POST', '/addData');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify(data));

用 FormData:

js
const fd = new FormData();
fd.append('userName', nameInput.value);
fd.append('userPWD', pwdInput.value);
xhr.open('POST', '/upload');
xhr.send(fd); // Content-Type 自动设置

易错点:setRequestHeader 必须放在 open 之后、send 之前;请求体不能直接传对象,否则会变成 [object Object];默认 text/plain 时后端通常不按对象解析;FormData 不需要手动设置 Content-Type,浏览器会自动加边界。

解析响应报文:状态码、状态描述、响应体 ​

为什么需要:请求发出去不代表成功,必须读响应。响应报文里有关心三件事:状态码、状态描述、响应体。

怎么想:onload 表示请求完成并拿到响应。此时先看 xhr.status,200 说明正常;响应体用 xhr.responseText 取,它本质是字符串。如果后端返回 JSON 字符串,还要 JSON.parse 转对象。

怎么做:

js
xhr.onload = function () {
  console.log(xhr.status, xhr.statusText);
  const text = xhr.responseText;
  // 若后端返回 JSON 字符串:
  // const data = JSON.parse(text);
};

调试时打开浏览器“网络”面板,看请求方式、请求头、请求体、响应状态和响应内容。转写稿里反复强调:改 HTML 后刷新浏览器即可;node 后端只是静态文件搬运工,通常不用重启。

易错点:只看 onload 不看状态码;把 responseText 当对象直接用;请求体格式和 Content-Type 对不上,导致后端解析失败。笔记还列出“设置响应超时”,要把它作为响应处理的一部分记住,具体超时 API 后续再展开。

监听进度事件:知道请求走到哪一步 ​

为什么需要:只写 onload 只能知道成功结束,上传下载进度、失败、结束都需要事件。

怎么想:loadstart 开始;progress 进行中;load 成功拿到响应;error 失败;loadend 无论成功失败都会结束;readystatechange 在状态变化时多次触发。最常用仍是 load,调试时可配合其他事件观察顺序。

怎么做:

js
xhr.addEventListener('loadstart', () => console.log('开始'));
xhr.addEventListener('progress', e => console.log('进度', e.loaded, e.total));
xhr.addEventListener('load', () => console.log('成功', xhr.responseText));
xhr.addEventListener('error', () => console.log('失败'));
xhr.addEventListener('loadend', () => console.log('结束'));

易错点:把 load 和 loadend 混为一谈;失败时仍去读响应体;忘记事件要绑定在 xhr 上。把这三块串起来,才是完整的原生 AJAX 使用流程。

课堂画面 ​

课堂画面 1

课堂画面 2

课堂画面 3

课堂画面 4

课堂画面 5

课堂画面 6

代码示例 ​

示例一:原生 Ajax 基本流程(xhr 四步走) ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>01 Ajax 基本流程</title>
  <style>
    #books {
      width: 600px;
      min-height: 200px;
      padding: 20px;
      border: 1px solid #ccc;
    }
  </style>
</head>
<body>
  <h1>Ajax 基本流程</h1>
  <button id="btn">加载</button>
  <div id="books"></div>

  <script>
    // 后端约定:GET /getData 返回一段文本(带随机数)
    const btn = document.querySelector('#btn')
    const books = document.querySelector('#books')

    btn.onclick = function () {
      // 第 1 步:创建 XMLHttpRequest 对象(DOM 对象,只能在浏览器里用)
      const xhr = new XMLHttpRequest()

      // 第 2 步:监听"成功接收到响应"的事件(必须提前绑定)
      xhr.onload = function () {
        console.log('成功接收到后端的响应', xhr)
        // 通过 responseText 拿到响应体(一定是字符串)
        books.innerHTML += xhr.responseText + '<br>'
      }

      // 第 3 步:请求初始化 —— 请求方式、请求地址、是否异步(默认 true)
      xhr.open('get', '/getData')

      // 第 4 步:正式发送请求,GET 没有请求体,send() 不传参
      xhr.send()
    }
  </script>
</body>
</html>

关键行与易错点

  • new XMLHttpRequest() 是 XML DOM 规范里的对象,所以 Ajax 名字里带 X;它只能在前端(浏览器)运行,不能在后端 Node 里用。
  • xhr.open(请求方式, 请求地址, 是否异步):第三个参数默认就是异步,一般不用写;地址写 /getData 这种以 / 开头的相对路径,浏览器会自动补上当前页面的 IP 和端口。
  • xhr.send() 的参数就是请求体。GET 没有请求体,所以不传参;传了也没有意义。
  • onload 必须在 send() 之前绑定,否则请求回来时没人接。转写稿里反复强调:光有 open 不 send,请求根本发不出去,事件也不会触发。
  • 页面必须通过服务器地址访问(如 http://127.0.0.1:8080/config01),不能双击打开 HTML 文件,否则会有跨域问题。
  • 调试一定要在开发者工具的 Network(网络)面板里看:页面本身一个请求,Ajax 一个请求,一共两个。

示例二:GET / POST 携带数据与请求体内容类型 ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>02 Ajax 携带数据</title>
</head>
<body>
  <table>
    <tr><td>用户名:</td><td><input type="text" id="userName"></td></tr>
    <tr><td>密码:</td><td><input type="password" id="userPwd"></td></tr>
  </table>

  <button id="btnGet">GET 发送</button>
  <button id="btnPostForm">POST 表单格式</button>
  <button id="btnPostJson">POST JSON 格式</button>
  <button id="btnPut">PUT JSON 格式</button>

  <script>
    // 后端约定:
    // GET  /addData —— 从 URL 查询字符串取数据(req.query)
    // POST /addData —— 从请求体取数据(需 body-parser 中间件)
    // PUT  /addData —— 同上
    const userName = document.querySelector('#userName')
    const userPwd  = document.querySelector('#userPwd')

    // 对象可以提前创建好,只要保证 open 和 send 在点击时才执行
    const xhr = new XMLHttpRequest()
    xhr.onload = function () {
      alert(xhr.responseText) // 后端返回的是字符串
    }

    // 1) GET:数据只能拼在 URL 的查询字符串里
    document.querySelector('#btnGet').onclick = function () {
      const qs = `userName=${userName.value}&userPwd=${userPwd.value}`
      xhr.open('get', '/addData?' + qs)
      xhr.send() // GET 没有请求体
    }

    // 2) POST + 表单格式(和 form 表单提交的默认类型一致,后端最认这个)
    document.querySelector('#btnPostForm').onclick = function () {
      const qs = `userName=${userName.value}&userPwd=${userPwd.value}`
      xhr.open('post', '/addData')
      // 设置请求头必须在 open 之后、send 之前
      xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
      xhr.send(qs) // 请求体是查询字符串,注意前面没有问号
    }

    // 3) POST + JSON 格式
    document.querySelector('#btnPostJson').onclick = function () {
      const data = { userName: userName.value, userPwd: userPwd.value }
      xhr.open('post', '/addData')
      xhr.setRequestHeader('Content-Type', 'application/json')
      xhr.send(JSON.stringify(data)) // 请求体必须是字符串,不能直接 send 对象
    }

    // 4) PUT:和 POST 完全一样,只是请求方式不同
    document.querySelector('#btnPut').onclick = function () {
      const data = { userName: userName.value, userPwd: userPwd.value }
      xhr.open('put', '/addData')
      xhr.setRequestHeader('Content-Type', 'application/json')
      xhr.send(JSON.stringify(data))
    }
  </script>
</body>
</html>

关键行与易错点

  • 携带数据只有两条路:URL 查询字符串(所有请求方式都行)和请求体(只有 POST / PUT 等有请求体的方式才行,GET 不行)。
  • setRequestHeader('Content-Type', ...) 的位置是死规定:open 之后、send 之前。写在 open 前会被初始化归零,写在 send 后请求已经发出去了,都没用。
  • 三种请求体内容类型要和请求体的实际格式对上:
    • 不设置(默认 text/plain):后端不做任何处理,你写什么它拿到的就是什么字符串;
    • application/x-www-form-urlencoded:请求体写成 k=v&k=v,后端能解析成对象;
    • application/json:请求体必须是 JSON 格式字符串,用 JSON.stringify() 转。
  • send() 里只能传字符串(或 FormData)。直接 send(对象) 会变成 [object Object],后端一定解析失败。
  • 实际工作中请求地址、请求方式、请求体格式都由后端告诉你,不要自己猜;这里出问题,多半是"前后端两个人都没对 Content-Type"。

示例三:FormData 作为请求体(含文件上传) ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>03 FormData 作为请求体</title>
</head>
<body>
  <form id="myForm" onsubmit="return false">
    <table>
      <tr><td>用户名:</td><td><input type="text" id="userName" name="userName"></td></tr>
      <tr><td>密码:</td><td><input type="password" id="userPwd" name="userPwd"></td></tr>
      <tr><td>头像:</td><td><input type="file" id="avatar" name="avatar"></td></tr>
    </table>
  </form>

  <button id="btn1">方式一:new FormData() + append</button>
  <button id="btn2">方式二:new FormData(form 元素)</button>

  <script>
    // 后端约定:POST /upload 接收 multipart/form-data(后端用 multer 处理)
    const userName = document.querySelector('#userName')
    const userPwd  = document.querySelector('#userPwd')
    const avatar   = document.querySelector('#avatar')

    const xhr = new XMLHttpRequest()
    xhr.onload = function () {
      alert(xhr.responseText)
    }

    // 方式一:创建空 FormData,再一个个 append
    document.querySelector('#btn1').onclick = function () {
      const fd = new FormData()
      fd.append('userName', userName.value)
      fd.append('userPwd', userPwd.value)
      // 文件不能靠 input.value 拿(那是客户端的假路径),要用 files[0]
      fd.append('avatar', avatar.files[0])

      xhr.open('post', '/upload')
      // 不要手动设置 Content-Type,浏览器会自动加上 multipart/form-data 和 boundary
      xhr.send(fd)
    }

    // 方式二:直接把 form 元素作为参数,自动收集所有带 name 的表单控件
    document.querySelector('#btn2').onclick = function () {
      const fd = new FormData(document.querySelector('#myForm'))
      xhr.open('post', '/upload')
      xhr.send(fd)
    }
  </script>
</body>
</html>

关键行与易错点

  • new FormData() 得到的真的是一个对象,可以直接作为 send() 的参数,这是唯一一种请求体不是字符串的情况。
  • 用 new FormData(form元素) 时,只有带 name 属性的控件才会被收集,所以 input 一定要写 name。
  • 上传文件只能取 input.files[0](类数组),input.value 拿到的是客户端本地路径,传给后端毫无意义。
  • 绝对不要手动 setRequestHeader('Content-Type', 'multipart/form-data'):浏览器会自动生成并附带 boundary 分隔符,手动设置反而会让后端解析失败。
  • 用 form 包住按钮时,button 默认是提交按钮,点一下页面就刷新了。要么写 onsubmit="return false" 阻止默认行为,要么把 button 写成 type="button"。
  • 有请求体的方式(POST / PUT)才能用 FormData,GET 想都别想。

常见错误 ​

  1. GET 携带数据时把参数写进 send() → GET 没有请求体,send() 中的内容不会按预期成为查询参数 → 将参数拼到 URL 查询字符串中,并确认后端按 query 读取。
  2. POST/PUT 请求体是 JSON 字符串,但未设置或设错 Content-Type → 后端按默认纯文本处理,拿到的是字符串而非对象 → 请求体用 JSON.stringify(),并在 open() 之后、send() 之前设置 Content-Type: application/json。
  3. 在 send() 之后或 open() 之前调用 setRequestHeader() → 请求已发出或初始化被覆盖,请求头不生效 → 固定顺序:创建 xhr → 监听事件 → open() → 设置请求头 → send()。
  4. 使用 FormData 时手动设置 Content-Type → 丢失 boundary,后端无法正确解析 multipart 数据 → 使用 FormData 作为请求体时不要手动设置 Content-Type,交给浏览器自动生成。
  5. 文件上传时读取 input.value 当文件提交 → value 只是客户端路径字符串,不是文件内容 → 读取 input.files[0],再 formData.append() 到请求体。
  6. 页面直接双击打开后发 Ajax → 页面来源与接口地址不同源,触发跨域限制 → 通过本地服务器访问页面,并让页面与接口保持同源或按后端要求处理跨域。

练习 ​

  1. 写一个 GET 请求:从两个输入框取用户名、密码,拼成查询字符串,请求 /addData,将 responseText 显示到页面指定容器中。
  2. 写一个 POST 请求:提交同样两个字段,分别用查询字符串格式和 JSON 字符串格式各实现一次,并正确设置对应 Content-Type。
  3. 用 FormData 提交表单中的用户名、密码和一个文件,要求不手动设置 Content-Type,并在 Network 面板检查请求体与请求头。
  4. 将“创建 xhr、监听 load、open、设置头、send”封装成一个函数,支持传入方法、URL、请求体和请求头;调用它完成一次 GET 和一次 JSON POST。
  5. 在表单提交场景中,用按钮触发 Ajax 提交,要求阻止表单默认提交行为,并说明如果阻止失败会出现什么现象。

原视频对照 ​

#视频时长
1ajax介绍08:51
2ajax基本使用流程21:50
3ajax请求携带数据-GET方式17:41
4ajax请求携带数据-POST方式31:16
5ajax请求携带数据-PUT方式12:28
6FormData对象作为请求体25:49

小结 ​

  1. 先确认接口契约:请求方式、URL、参数位置、请求体格式、响应格式,再写代码。
  2. GET 的数据位置优先看 URL;POST/PUT 才重点处理请求体,二者不要混用。
  3. 请求体格式与 Content-Type 必须成对匹配,这是前后端联调最常见的问题点。
  4. 有文件或表单数据时优先考虑 FormData,能减少手工拼接和请求头设置。
  5. 调试 Ajax 要看 Network 的请求头、载荷/请求体、响应内容和状态码,不要只看 console.log。
  6. responseText 始终按字符串处理;若后端返回 JSON,需要显式解析并处理解析失败。
  7. 页面必须通过服务器访问,Ajax 才更接近真实运行环境,也更容易暴露跨域和路径问题。