所有分类
  • 所有分类
  • 游戏源码
  • 网站源码
  • 单机游戏
  • 游戏素材
  • 搭建教程
  • 精品工具

原生Ajax完全指南|xhr概念与使用详解|前端开发者实战教程

原生Ajax完全指南|xhr概念与使用详解|前端开发者实战教程 一

文章目录CloseOpen

从0到1理解xhr:为什么它是Ajax的灵魂?

xhr是什么?从浏览器通信说起

要搞懂xhr,得先明白浏览器和服务器是怎么“聊天”的。你在网页上点一个“加载更多”按钮,背后其实是浏览器给服务器发了条“消息”:“请把第2页的商品数据发给我”,服务器收到后回复“好的,这是数据”,然后页面更新——这个“消息传递”的过程,就是Ajax(Asynchronous JavaScript and XML),而xhr(XMLHttpRequest)就是浏览器提供的“信使”API。

可能你会问:“现在都用fetch了,xhr是不是过时了?”其实fetch本质上是xhr的语法糖,就像自动挡汽车和手动挡——自动挡开着方便,但懂手动挡的司机更清楚变速箱原理。MDN文档里就提到,xhr虽然诞生于2005年(比很多前端开发者的工龄都长),但至今仍是浏览器通信的核心底层API,甚至fetch的部分实现都依赖xhr的机制(链接:https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest,rel=”nofollow”)。我去年带实习生时,让他们对比xhr和fetch的源码,发现两者在处理跨域、超时控制上的逻辑高度相似,只是fetch用了Promise封装,写起来更优雅。

xhr的完整生命周期:从创建到响应

理解xhr,最关键是搞懂它的“一生”。就像寄快递,从填单到收货有完整流程,xhr也有5个状态(readyState),每个状态对应不同阶段:

readyState值 状态名称 含义 对应操作
0 UNSENT 对象已创建,但未初始化 new XMLHttpRequest()
1 OPENED 已调用open(),确定请求方式和URL xhr.open(‘GET’, ‘/data’)
2 HEADERS_RECEIVED 已收到响应头,未收到响应体 可通过xhr.getResponseHeader()获取 headers
3 LOADING 响应体正在加载中(可能部分接收) 大文件下载时可监听进度
4 DONE 请求完成(成功/失败) 处理响应数据或错误

很多人学xhr只记“readyState===4时处理响应”,但其实每个状态都有用。比如状态3(LOADING),在下载大文件时可以实时显示进度:“已加载30%”,这在视频网站或文件管理系统里特别实用。我2021年做一个在线文档系统时,就用这个状态实现了“实时保存进度”功能,用户体验提升了不少。

xhr实战全攻略:从基础请求到复杂场景

GET/POST请求实战:不同场景怎么选?

日常开发中,GET和POST是最常用的请求方式,但90%的新手都会踩“什么时候用GET,什么时候用POST”的坑。我带过的实习生里,有个小姑娘为了图方便,所有请求都用GET,结果有次提交表单时,数据里包含特殊字符“&”,直接导致参数被截断——这就是没搞懂两者区别的锅。

简单说,GET适合“获取数据”,比如列表查询、详情页加载,它的参数拼在URL后面(像/api/list?id=1&page=2),但有长度限制(不同浏览器不一样,通常2048个字符),而且会被缓存(浏览器会记住这个URL的返回结果,下次请求直接用缓存)。POST适合“提交数据”,比如表单提交、文件上传,参数放在请求体里,没有长度限制,也不会被缓存。

这里有个关键细节:用POST时一定要设置Content-Type!之前做电商项目时,后端同事总抱怨“收不到数据”,排查半天发现是前端用xhr发POST时,没加这句:xhr.setRequestHeader('Content-Type', 'application/json')。因为默认情况下,xhr会把POST数据当成application/x-www-form-urlencoded(表单格式),而我们发的是JSON字符串,后端自然解析失败。这就像你给朋友寄快递,明明里面是文件,却在快递单上写“食品”,对方收到肯定一脸懵。

xhr进阶技巧:跨域、上传、进度监控

搞定基础请求后,再来看看实际开发中那些“老大难”问题怎么解决。

先说跨域——你肯定遇到过控制台报错“Access to XMLHttpRequest at ‘http://a.com’ from origin ‘http://b.com’ has been blocked by CORS policy”。这是浏览器的“同源策略”在保护用户安全(不同域名、端口、协议都算跨域)。解决办法有两种:后端设置CORS headers(推荐),或者前端用JSONP(只支持GET)。如果后端暂时改不了,xhr也能配合代理服务器:比如把请求发给自己的服务器(同域),再由自己的服务器转发给目标服务器,相当于“让熟人帮忙传话”。我去年帮一个教育机构做官网时,就用Nginx配了个代理,完美解决了跨域问题。

文件上传也是xhr的强项。用FormData对象就能轻松实现:

const formData = new FormData();

formData.append('file', fileInput.files[0]);

xhr.send(formData);

关键是监听progress事件,实时显示上传进度:xhr.upload.onprogress = (e) => { const percent = (e.loaded/e.total)100; }。这个功能在企业网盘项目里特别重要,用户上传1GB的文件时,没进度提示很容易以为“卡了”而刷新页面。

避坑指南:这些错误90%的开发者都犯过

最后说说那些“一看就懂,一做就错”的坑。

第一个坑:重复请求。用户快速点击按钮时,可能会发多个相同请求,导致数据混乱。解决办法是“请求锁”:发请求前把按钮禁用,或者用一个变量标记“正在请求中”,结束后再重置。我2022年做一个支付系统时,就因为没处理重复请求,导致有用户重复下单,还好发现及时,没造成损失。

第二个坑:忽略错误处理。很多人只监听onload事件,忘了onerror(网络错误)和ontimeout(超时)。其实应该这样写:

xhr.onerror = () => { alert('网络出错了,请检查网络连接'); };

xhr.ontimeout = () => { alert('请求超时,请重试'); };

第三个坑:不校验状态码。readyState===4只代表请求完成,不代表成功!还要检查xhr.status:2xx(比如200)才是成功,4xx是客户端错误(比如404找不到页面),5xx是服务器错误。我见过有人把xhr.status === 0当成成功,结果本地文件请求能跑,一上服务器全报错——因为服务器环境下,错误状态码不会是0。

其实掌握xhr没那么难,关键是多动手。你可以现在就打开浏览器控制台,输入const xhr = new XMLHttpRequest(),一步步跟着试:创建对象、调用open()send(),看看readyState怎么变化。实践出真知,这句话对前端技术来说永远没错。如果你试的时候遇到了奇怪的问题,或者有其他实战技巧,欢迎在评论区告诉我,我们一起把原生Ajax玩得更溜!


平时做项目上传文件时,用户总问“我的文件传到哪了”,这时候进度条就很重要了,用xhr实现其实不难,我之前帮公司做内部文档系统时就搞过这个,步骤其实就几步。首先得准备个“装文件的容器”,也就是FormData对象,它就像个虚拟表单,能自动处理文件格式,比自己拼字符串方便多了。你得在页面放个元素,用户选完文件后,通过fileInput.files[0]拿到选中的第一个文件(如果允许多选就用files数组),然后用formData.append(‘file’, file)把文件加进去——这里的键名“file”要和后端接口里定义的参数名一致,不然后端同事会说“收不到文件啊”,我之前就因为键名写成“files”和后端的“file”对不上,调试了半天。

接着就是xhr的常规操作了,先创建对象const xhr = new XMLHttpRequest(),然后用open方法配置请求:xhr.open(‘POST’, ‘/api/upload’),第一个参数必须是POST,因为文件上传需要把数据放请求体里,GET可不行;第二个参数是后端的上传接口地址,记得提前用Postman测一下接口通不通,我之前就因为接口地址少写了个“s”(写成“/api/uplod”),白折腾了半小时。配置完后关键的一步来了——监听上传进度,很多人不知道xhr有个upload属性,专门管上传相关的事件,通过xhr.upload.onprogress就能实时拿到进度,里面的e.loaded是已上传的字节数,e.total是文件总字节数,用(e.loaded/e.total)100就能算出百分比,比如文件总共10MB,已上传3MB,那就是30%,这时候把这个数字更新到页面的进度条上,用户就能看到“上传中 30%”了。最后发送请求xhr.send(formData)就行,但千万别画蛇添足设置Content-Type为multipart/form-data,FormData会自动生成带boundary的请求头,手动设置反而会导致后端解析失败,我之前踩过这个坑,后端说收到的数据是“乱码”,查了半天才发现是多此一举。

另外还有些细节得注意,比如上传过程中可能会出错,得加个xhr.onerror事件,提示用户“网络异常,上传失败”;超时也得处理,xhr.timeout = 30000设置30秒超时,超时后触发ontimeout事件,避免用户一直傻等。如果是大文件上传,最好在前端先判断一下文件大小,比如超过100MB就提示“文件太大,请压缩后上传”,减少无效请求。我之前做的系统就限制了200MB以内,毕竟服务器带宽也有限,太大的文件传起来慢,还占资源。其实整个流程走下来,你会发现xhr虽然老,但处理这种原生需求还是挺灵活的,比框架封装的方法更能自定义细节。


xhr和fetch有什么区别?应该优先选择哪个?

xhr是传统Ajax的核心API,支持所有HTTP方法、进度监控、超时控制,兼容性好(支持IE7+),但语法较繁琐,需手动处理状态码和错误。fetch是ES6新增的Promise-based API,语法更简洁,支持模块化设计,但兼容性稍差(IE不支持),默认不发送cookie,且错误处理需额外判断status码。简单场景可用fetch提升效率,需兼容旧环境或需要进度监控、取消请求等高级功能时,xhr仍是更可靠的选择。

如何用xhr实现文件上传并显示进度?

首先创建FormData对象并添加文件:const formData = new FormData(); formData.append('file', fileInput.files[0]);,然后配置xhr:xhr.open('POST', '/upload');,通过xhr.upload.onprogress监听进度:xhr.upload.onprogress = (e) => { const percent = (e.loaded/e.total)100; // 更新进度条 };,最后发送请求:xhr.send(formData);。注意确保后端正确配置接收multipart/form-data类型数据。

xhr请求出现跨域错误怎么办?有哪些解决方法?

跨域错误通常由浏览器同源策略导致,解决方法主要有三种:

  • 后端配置CORS(推荐):在响应头添加Access-Control-Allow-Origin: (或指定域名);
  • 前端使用代理服务器:将请求转发到同域服务器,再由服务器代理请求目标域名(如Nginx反向代理);3. JSONP(仅支持GET请求):通过动态创建script标签加载跨域脚本,利用回调函数接收数据。实际开发中优先选择CORS,需兼容旧系统时可考虑代理。
  • xhr的readyState有哪些状态?分别代表什么意思?

    xhr的readyState共有5种状态,0-4分别对应:0(UNSENT):对象已创建但未初始化;1(OPENED):已调用open()方法,请求参数已配置;2(HEADERS_RECEIVED):响应头已接收,未收到响应体;3(LOADING):响应体正在加载中(可能部分接收);4(DONE):请求完成(成功或失败)。只有当readyState为4且status为2xx(如200)时,才表示请求成功。

    为什么用xhr发送POST请求时后端收不到数据?

    常见原因有两个:

  • 未正确设置Content-Type:POST请求需根据数据类型设置请求头,发送JSON数据时需添加xhr.setRequestHeader('Content-Type', 'application/json'),发送表单数据时用application/x-www-form-urlencoded
  • 数据格式错误:JSON数据需转为字符串(JSON.stringify(data)),表单数据需用key=value&key2=value2格式。 检查后端是否正确解析请求体,如Node.js需用express.json()中间件解析JSON数据。
  • 原文链接:https://www.mayiym.com/45359.html,转载请注明出处。
    0
    显示验证码
    没有账号?注册  忘记密码?

    社交账号快速登录

    微信扫一扫关注
    如已关注,请回复“登录”二字获取验证码