💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、豆包、星火、月之暗面及文生图、文生视频 广告
目录 [TOC] # 1 简介 Ajax是一系列web技术开发的合集,这系列技术包括以下: * 展现层的HTML(或者XHTML), CSS , * 动态显示和数据交互的DOM对象 * 数据的载体形式, JSON或者XML(基本不用了) * 实现异步通信的XMLHttpRequest 对象,这是Ajax的核心 * 把这些技术整合起来的JavaScript 利用Ajax。web应用可以通过XMLHttpRequest实例发送数据, JavaScript 解耦数据接口获取数据更新展现层,而不用重新加载整个页面。 # 2 XMLHttpRequest 发送请求的步骤 XMLHttpRequest是Ajax的实现的核心。看下使用他的步骤 1. 实例化XMLHttpRequest对象获得一个实例。 2. 通过实例open(可以说初始化)一个请求, 设置发送类型和接口以及同步 3. 如有需要配置请求头,各种事件(error,timeout等) 4. 调用实例的send方法,发送http/https的请求 5. 服务器回调,客户端接收,并做响应处理 # 3 实例代码 先看实例代码 ## 3.1get 方法 <pre>//步骤一:获得实例 var xhr = new XMLHttpRequest(); /步骤二:.初始化请求 xhr.open("POST",“server.php”) //步骤三: 设置报头,各种事件 xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); //这是必不可少的,告诉服务器传输的类型 xhr.timeout = 2000; // 超时时间,单位是毫秒,必须设置,否则ontimeout无效 xhr.onload = function () { // 请求完成。在此进行处理。 }; xhr.ontimeout = function (e) { // XMLHttpRequest 超时。在此做某事。 }; //步骤四:发送请求 var param = {参数,一般为JSON格式} xhr.send(param) //步骤五:.readyState改变时,监听请求是否完成,完成时处理服务器返回的数据 xhr.onreadystatechange = function() { //请求完成 if(xhr.readyState === 4 ) { //请求成功,处理数据 if(xhr.status === 200) { //解析数据 var date = JSON.parse(xhr.responseText) //do something.... }else{ //请求失败,返回http状态码。 alert(xhr.status) } } } </pre> ## 3.2 步骤二解析 步骤二调用`open()`进行初始化,参数为 <pre>open( 请求方法:一般为get,post。当然也有put,delete, url: 请求后台地址, async: 是否执行异步操作,默认为true,不要作死随便改成false。 )</pre> 如果请求方法为`get`并需要发送参数。参数在url后由`?`开头,多个参数由`&`拼接 例如 xhr.open("GET",“server.php?参数名=参数值&参数名二=参数值二"); 参数名由前后端约定 POST传参在步骤四`send()`进行 ## 3.3 步骤三解析 这个步骤可以做各种事件, 比如 * `XMLHttpRequestEventTarget.onabort` 当请求失败时调用该方法 * `XMLHttpRequestEventTarget.onerror` 当请求发生错误时调用该方法,输出 * `XMLHttpRequestEventTarget.onload` 当一个HTTP请求正确加载出内容后返回时调用。 * `XMLHttpRequestEventTarget.onloadstart` 当一个HTTP请求开始加载数据时调用。可以用来显示loading图片 * `XMLHttpRequestEventTarget.onprogress` 间歇调用该方法用来获取请求过程中的信息。 * `XMLHttpRequestEventTarget.ontimeout` 当时间超时时调用;只有通过设置XMLHttpRequest对象的timeout属性来发生超时时,这种情况才会发生。 * `XMLHttpRequestEventTarget.onloadend` 当内容加载完成,不管失败与否,都会调用该方法,可以用来隐藏loading图片 注意: 所有相关的事件绑定必须在调用send()方法之前进行. 步骤三除了做各种事件, 还有一个比较事情,就是设置请求头。 ### 3.3.1 setRequestHeader 参数为 `setRequestHeader( DOMString header, DOMString value );` header需要设置为`"Content-Type"`,字符串格式。 `Content-Type`是非常重要的内容,在请求中,需要通过`Content-Type`准确告诉服务器请求时携带数据的MEMI类型。否则服务器不能真确处理携带数据。在响应中,服务器也需要在后端文件头部设置`Content-Type`,告诉客户端响应的数据类型。 格式一般为`Content-Type: [type]/[subtype]; parameter` *type*有下面的形式 * <b>Text</b>:用于标准化地表示的文本信息,文本消息可以是多种字符集和或者多种格式的; * <b>Multipart</b>:顾名思义,携带的数据由不同类型数据组成,比如头像上传时请求中有图片和用户id的文本信息; * <b>Application</b>:用于传输应用程序数据或者二进制数据; * <b>Message</b>:用于包装一个E-mail消息; * <b>Image</b>:用于传输静态图片数据; * <b>Audio</b>:用于传输音频或者音声数据; * <b>Video</b>:用于传输动态影像数据,可以是与音频编辑在一起的视频数据格式。 *subtype* 作为一个补充信息, 标准的文本编码格式`application/x-www-form-urlencoded` 比如后端返回的是JSON格式信息,就是 `Content-Type:application/json` JavaScript文件则是`Content-Type:application/x-javascript` 图片,混合文本信息的: `Content-Type:multipart/form-data` 如果是单独图片,则是`Content-Type:Image/jpg` *parameter*更多时候是用来指定text/plain和text/htm 等文字的 编码形式 比如`Content-Type: application/json;charset=utf-8` 下面是一个在控制台netWorkg工具截下的headers。 ![](https://box.kancloud.cn/1e70c9e84f1e20b9af7115e5577105a9_651x605.png) form Data是POST请求携带的数据,编码为标准格式。而服务器响应的则是json格式。另外get请求一般不需要额外设置Content-Type,浏览器会默认用 `text/plain` ## 3.4步骤四解析 post请求携带的数据通过send()发送。 ## 3.5步骤五解析 ### 3.5.1 readyState readyState返回的是请求的状态,一共五种状态 | 值 |状态 |描述 | | --- | --- | --- | | 0 | UNSENT (未打开) | open()方法还未被调用. | | 1 | OPENED (未发送) | send()方法还未被调用. | | 2 | HEADERS_RECEIVED (已获取响应头) | send()方法已经被调用, 响应头和响应状态已经返回. | | 3 | LOADING (正在下载响应体) | 响应体下载中,responseText已经获取了部分数据 | | 4 | DONE (请求完成 | 整个请求过程已经完毕 | ### 3.5.2 onreadystatechange 每当readyState的值改变时,onreadystatechange事件会被触发。当readyState的值为4时,说明请求完成,响应体也下载完毕。就能进行响应操作了。 ### 3.5.3 status status 输出服务器返回的HTTP状态码。常见的200请求成功, 304响应体文件已经缓存本地,不需要再下载响应体。在404请求地址不存在。5xx是服务器出现问题 更多参见[http状态码详解](http://tool.oschina.net/commons?type=5) ### 3.5.4 responseText 可以读取responseText 获得后端返回的响应体, 响应体是JSON字符串格式,需要用JSON.parse()方法解析才能使用。 # 4 跨域 ## 4.1什么是跨域 使用Ajax,很多人会遇到跨域的问题,跨域是的根本原因是浏览器`同源策略`引起的。所谓同源是指三个相同 * 协议相同 * 域名相同 * 端口相同 举例来说,`http://www.example.com/dir/page.html`这个网址,协议是http://,域名是`www.example.com`,端口是80(默认端口可以省略) * `http://www.example.com/dir2/other.html`:同源 * `http://example.com/dir/other.html`:不同源(域名不同) * `http://v2.www.example.com/dir/other.html`:不同源(域名不同) * `http://www.example.com:81/dir/other.html`:不同源(端口不同) 如果没有同源策略,浏览器毫无安全可言。 Ajax本身不能跨域,需要借助其他技术实现。 ## 4.2 JSONP jsonp是经典跨域解决方案,对服务区改造小 jsonp是JSON with padding的简写,原理是利用script的src属性不受同源策略限制。(具有src属性的如img,iframe,srcipt都不受同源策略的影响),要点就是允许用户传递一个callback参数给服务器,然后服务器返回数据时会将callback参数作为函数名包裹住json数据,这样客户端可以定制自己的函数来处理返回的数据。 光看上面文字理解不全面,自己动手试验才能深刻理解 ### 4.2.1创建跨域环境 首先要创建一个跨域的环境:需要用到本地服务器。推荐[phpstudy](http://www.phpstudy.net/)。不需要繁琐的配置。 打开phpstudy面板-->站点域名管理。网站文件夹都放在了 phpstydy目录下的`X盘:\phpstudy\WWW`下面,我这里默认端口为80。新增一个网站目录为`X盘:\phpstudy\WWW2`,网站端口为1122。 端口不同也算跨域。保存后重启服务器,这样就有2个端口的网站。 ![](https://box.kancloud.cn/e20a7df2911e02ad31c28140614a23d7_547x502.png) ### 4.2.2 发送跨域请求 在网页输入`localhost:1122即可打开新端口的网站`,向`localhost:80`的后台文件请求数据 <pre>function addScriptTag(src) { var script = document.createElement('script'); script.setAttribute("type","text/javascript"); script.src = src; document.body.appendChild(script); } window.onload = function () { var url = 'http://localhost:80/server.php' addScriptTag(url+'callback=foo'); } function foo(data) { console.log('Your public IP address is: ' + data.ip); }; </pre> 服务器端返回的数据。以php为例:server.php <pre> echo "foo({ip: 1.1.1.2})"; </pre> 注意,开头说的服务器改造,就是后端返回数据时,将JSON数据包含在前后端约定的方法名中、如例子中的`foo`。如果不这么做,前端会虽然能拿到数据,但会报错。`Uncaught SyntaxError: Unexpected token :` 我们可以在客户端的foo方法对客户端返回的数据做处理。 ### 4.2.3 JSONP 优缺点 优点: 简单,函数回调在本地处理; 缺点: 1、安全性(存在注入漏洞,如CSRF,XSS); 2、如果出现错误,不会像http请求那样有状态码; 3、只能使用get请求; ### 4.2.4 cros跨域 另外一个常用的可以方法是CROS,墙裂推荐 阅读这篇文章 * [跨域资源共享 CORS 详解 阮一峰](http://www.ruanyifeng.com/blog/2016/04/cors.html) # 5 封装Ajax和jsonp ## 5.1 代码 <pre>function ajax(params) { params = params || {}; params.data = params.data || {}; // 判断是ajax请求还是jsonp请求 var json = params.jsonp ? jsonp(params) : json(params); // ajax请求 function json(params) { // 请求方式,默认是GET params.type = (params.type || 'GET').toUpperCase(); // 避免有特殊字符,必须格式化传输数据 params.data = formatParams(params.data); var xhr = null; // 实例化XMLHttpRequest对象 if (window.XMLHttpRequest) { xhr = new XMLHttpRequest(); } else { // IE6及其以下版本 xhr = new ActiveXObjcet('Microsoft.XMLHTTP'); } // 监听事件,只要 readyState 的值变化,就会调用 readystatechange 事件 xhr.onreadystatechange = function () { // readyState属性表示请求/响应过程的当前活动阶段,4为完成,已经接收到全部响应数据 if (xhr.readyState == 4) { var status = xhr.status; // status:响应的HTTP状态码,以2开头的都是成功 if (status >= 200 && status < 300) { var response = ''; // 判断接受数据的内容类型 var type = xhr.getResponseHeader('Content-type'); if (type.indexOf('xml') !== -1 && xhr.responseXML) { response = xhr.responseXML; //Document对象响应 } else if (type === 'application/json') { response = JSON.parse(xhr.responseText); //JSON响应 } else { response = xhr.responseText; //字符串响应 } // 成功回调函数 params.success && params.success(response); } else { params.error && params.error(status); } } }; // 连接和传输数据 if (params.type == 'GET') { // 三个参数:请求方式、请求地址(get方式时,传输数据是加在地址后的)、是否异步请求(同步请求的情况极少); xhr.open(params.type, params.url + '?' + params.data, true); xhr.send(null); } else { xhr.open(params.type, params.url, true); //必须,设置提交时的内容类型 xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); // 传输数据 xhr.send(params.data); } } // jsonp请求 function jsonp(params) { //创建script标签并加入到页面中 var callbackName = params.jsonp; var head = document.getElementsByTagName('head')[0]; // 设置传递给后台的回调参数名 params.data['callback'] = callbackName; var data = formatParams(params.data); var script = document.createElement('script'); head.appendChild(script); //创建jsonp回调函数 window[callbackName] = function(json) { head.removeChild(script); clearTimeout(script.timer); window[callbackName] = null; params.success && params.success(json); }; //发送请求 script.src = params.url + '?' + data; //超时处理 if(params.time) { script.timer = setTimeout(function() { window[callbackName] = null; head.removeChild(script); params.error && params.error({ message: '超时' }); }, time); } }; //格式化参数 function formatParams(data) { var arr = []; for (var name in data) { // encodeURIComponent() :用于对 URI 中的某一部分进行编码 arr.push(encodeURIComponent(name) + '=' + encodeURIComponent(data[name])); } // 添加一个随机数参数,防止缓存 arr.push('v=' + random()); return arr.join('&'); } // 获取随机数 function random() { return Math.floor(Math.random() * 10000 + 500); } } </pre> ## 5.2 使用 类似于jquery的$.ajax用法,参数接受一个对象 <pre> ajax({ url: 'test.php', // 请求地址 type: 'GET', // 请求类型,默认"GET",还可以是"POST" data: {'b': '异步请求'}, // 传输数据 jsonp: '回调方法名' success: function (res) { // 请求成功的回调函数 console.log(JSON.parse(res)); }, error: function (error) { } // 请求失败的回调函数 }); </pre> # 6 参考 [前端必备HTTP技能之Ajax技术详解](http://www.jianshu.com/p/610a05e51fef) [Ajax原生设计方案](https://github.com/GerryIsWarrior/ajax) [NDM XMLHttpRequest](https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest) [Content-type的说明即HTTP请求头的类型整理](http://www.jb51.net/web/85379.html) [原生JS实现AJAX、JSONP及DOM加载完成事件](http://www.html-js.com/article/1882?utm_source=caibaojian.com) [跨域原理以及解决方案](http://www.cnblogs.com/bojuetech/p/5895767.html)