快速了解JavaScript的Ajax

2022-12-22 0 1,017

快速了解JavaScript的Ajax

简述

Ajax(Asynchronous JavaScript and XML,即触发器 JavaScript 和 XML)控制技术用作与伺服器互换统计数据并创下部份页面,同时实现更快的使用者新体验。

Ajax 的核心理念第一类是XMLHttpRequest,透过 XMLHttpRequest 能在不创下页面的情况下允诺某一 URLronous JavaScript+XML的简写,但Ajax通讯与统计数据库系统毫无关系,未必是XML文件格式。即使全力支持 HTTP 之外的协定(如file://FTP),虽然可能将受更多所致安全可靠等其原因的管制。

采用 Ajax 须要两个关键步骤:

建立 XMLHttpRequest 第一类。发出 HTTP 允诺。转交伺服器发回的统计数据。预览页面统计数据。

总而言之,Ajax 是透过 XMLHttpRequest 第一类发出 HTTP允诺,获得伺服器积极响应并处置。

建立 XMLHttpRequest

在大部份当代应用程序都能透过 XMLHttpRequest 缺省codelet:

let xhr = new XMLHttpRequest();

而在老版 IE5 和 IE6 中采用ActiveX 第一类建立:

let xhr = new ActiveXObject(“Microsoft.XMLHTTP”);

采用 XMLHttpRequest

codelet后,须要采用 XMLHttpRequest 第一类调用 open(type, url, async) 方法并设置三个参数:

type:表示允诺类型,如 “get”“post”等。url:表示允诺的url。async:表示允诺是否触发器。如true表示触发器允诺,false表示同步允诺,它会使JavaScript代码等待伺服器积极响应之后再继续执行。

允诺

XMLHttpRequest 采用 open() 方法初始化一个允诺,一共能转交五个参数:

open(method, url,async, username, password): void;method:字符串,表示要传入的HTTP方法,如/GET/POST/PUT/DELETE等。url:字符串,表示允诺发送目标URL。async:布尔值,表示允诺是否为触发器,默认为true。如果值为falsesend()方法只有等待收到伺服器返回结果,才会进行下一步操作。参数可选。如果 multipart 属性为 true 则这个必须为 true,否则将引发异常。注意,主线程上的同步允诺很容易破坏使用者新体验,应避免;实际上,许多应用程序已完全启用主线程上的同步XMLHttpRequest全力支持。在Worker中允许同步允诺。username:字符串,表示使用者名用作认证用途,默认为null。参数可选。password:字符串,表示密码用作认证用途,默认为null。参数可选。

因此,前两个参数是必须要添加的。允诺初始化后,就能调用send()方法发出 HTTP 允诺。如果是触发器允诺(默认触发器允诺),则此方法会在允诺发送后立即返回;如果是同步允诺,则此方法直接积极响应到达后才会返回。该方法有一个可选参数,作为允诺主体,允诺方法是GET/HEAD,则允诺体设置为null

send(body): void;

该方法的参数是 XMLHttpRequest 允诺中要发送的统计数据体,能设置的值为:

Document:发送 Document 类型的统计数据,但发送之前会被序列化。BodyInit:能是 Blob/BufferSource/FormData/URLSearchParams/ReadableStream<Uint8Array>/string 这几种类型的统计数据。null。如果没有指定值,默认值是 null

因此,建立完XMLHttpRequest第一类后,就会按顺序调用open()send()方法来发送允诺。而最常用的方法是GETPOST 方法。

GET

GET 允诺方法,主要用作查询伺服器信息。而查询的参数要正确编码并添加到URL后面,例如 example.jsp?username=Alpha&password=a123456,参数之间会用&符号分隔,然后才能传给 open() 方法。该方法最常见的错误是文件格式不对,因此能采用 encodeURIComponent() 函数对字符串进行编码。

let url = “example.jsp”; url = addParam(url, “username”, “Alpha”); url = addParam(url, “password”, “a123456”); xhr.open(get, url, true); function addParam(url, name, value) {    url += (url.indexOf(“?”) == -1 ? “?”:“&”);    url += encodeURIComponent(name) + “=” + encodeURIComponent(value);    return url; }

addParam() 函数保证 XMLHttpRequest 发送允诺的 URL 文件格式正确。

POST

POST 允诺方法,主要用作向伺服器发送统计数据。POST 允诺会在允诺体中携带要提交的统计数据。POST 允诺在传参上不用像 GET允诺那样将参数拼接在URL 后面,而是采用 send() 方法来携带要提交的统计数据。

默认情况下,对伺服器而言,POST 允诺与HTML表单提交是不一样的。伺服器逻辑须要读取原始 POST统计数据才能取得应用程序发送的统计数据。不过,还能采用XML模拟表单提交。为此,第一步须要把 Content-Type 头部设置为 “application/x-www-formurlencoded”,这是提交表单时采用的内容类型。第二步是建立对应文件格式的字符串。POST 统计数据此时采用与查询字符串相同的文件格式。如果页面中确实有一个表单须要序列化并透过XMLHttpRequest 发送到伺服器,则能采用 serialize()函数来建立相应的字符串,如下所示:

function submitData() {    let xhr = new XMLHttpRequest();    xhr.onreadystatechange = function() {        if (xhr.readyState == 4) {            if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {                console.log(xhr.responseText);           }else {                alert(“允诺失败: “+ xhr.status);           }       }   };    xhr.open(“post”, “example.jsp”, true);    xhr.setRequestHeader(“Content-Type”, “application/x-www-form-urlencoded”);    // ID为 user-info 的表单元素    let form = document.getElementById(“user-info”);    xhr.send(serialize(form)); }

注意:POST 允诺相比 GET 允诺要占用更多资源。从性能方面说,发送相同统计数据的统计数据,GET允诺比 POST 允诺快两倍。

积极响应

当伺服器收到允诺并积极响应后,XMLHttpRequest 第一类会有以下属性被填充统计数据:

status:积极响应的 HTTP 状态码。stautsText:积极响应的 HTTP 状态描述。response:积极响应的正文。responseText:作为积极响应体返回的文本。responseXML:如果积极响应的内容类型是 “text/xml”“application/xml”,那是包含积极响应统计数据的 XML DOM 文档。

一般都是透过 status 属性来确保积极响应是否成功返回。当 status 为 2xx 表示成功,为 304 表示资源未被修改,而是从缓存中取出的。这时的 responseTextresponseXML 属性中会有内容。因此,我们能采用 status 属性来判断积极响应是否有效,如下所示:

xhr.open(“get”, “example.txt”, false); xhr.send(null);if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {    console.log(xhr.responseText); }else {    console.log(“Request was unsuccessful: “ + xhr.status); }

response

XMLHttpRequest.response 属性返回积极响应的统计数据体(即 HTTP 积极响应的 body 部份)。返回的类型为 ArrayBufferBlobDocumentObjectDOMString中的一个。具体类型由XMLHttpRequest.responseType 类型决定。

当允诺尚未完成或尚未成功,该值为 null。但当 responseType 属性设置成 “text” 或空字符串(“”) 且当允诺状态在LOADING时,response属性包含到目前为止该允诺已经取得的内容。

let xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(){    if (xhr.readyState == 4) {        console.log(xhr.response);   } }

responseType

XMLHttpRequest.responseType 属性是一个字符串,返回积极响应统计数据的类型。允许手动设置返回统计数据的类型。如果设置为空字符串,则采用默认的“text”类型。

responseType设置为一个某一的类型时,需确保伺服器返回的类型和你所设置的类型是兼容的。如果不兼容,即使伺服器返回了统计数据,统计数据也会变成nullresponseType 属性全力支持以下几种值:

“”:当responseType为空字符串时,与 text相同,表示伺服器返回文本统计数据。“arraybuffer”:表示伺服器返回的是一个包含二进制统计数据的ArrayBuffer 第一类。“blob”:表示伺服器返回的是一个包含二进制统计数据的 Blob第一类。“document”:表示伺服器返回的是一个HTML DocumentXML Document,这取决于转交统计数据的 MIME 类型。“json”:表示将转交到的伺服器统计数据视为JSON来进行解析并获得。“text”:表示伺服器返回的是以DOMString第一类表示的文本。

上面几种类型之中,text类型适合大多数情况,而且直接处置文本也比较方便。document类型适合返回HTML/XML文档的情况,这意味着,对于那些打开 CORS 的网站,能直接用 Ajax 抓取页面,然后不用解析 HTML 字符串,直接对抓取回来的统计数据进行 DOM 操作。blob 类型适合读取二进制统计数据,比如图片文件。XMLHttpRequest.responseType属性要在调用 open() 方法之后,并且在调用 send() 方法之前调用。

let xhr = new XMLHttpRequest(); xhr.open(“GET”, “example.jsp”, true); xhr.responseType = “json”; xhr.send(null);

readyState

XMLHttpRequest.readyState属性返回一个无符号短整型数字,表示XMLHttpRequest第一类的当前状态。该第一类会返回以下某个值:

返回 0,未初始化(Uninitialized),状态为 UNSENT。表示代理被建立,但尚未调用 open()方法。返回1,状态为 Open 已打开(Open),状态为OPENED。表示 open() 方法已被调用,但尚未调用 send() 方法。返回 2,已发送(Sent),状态为HEADERS_RECEIVED。表示已调用 send() 方法,并且头部和状态已经可获得,但尚未收到积极响应。返回 3:转交中(Receiving),状态为LOADING。表示已经收到部份积极响应。返回 4:已完成(Complete),状态为DONE。表示允诺操作已完成。意味着统计数据传输已经彻底完成或失败。

通讯过程中,每当 XMLHttpRequest 第一类状态发送变化,readyState 属性值就会被改变,并会触发 onreadystatechange事件。并且XMLHttpRequest第一类调用abort()方法,终止允诺,也会造成 readyState属性变化。为保证兼容性,onreadystatechange事件应在open()方法之前赋值。如下所示:

let xhr = new XMLHttpRequest(); xhr.onreadystatechange =function() { if (xhr.readyState == 4) { if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) { console.log(xhr.responseText); } else{ alert(“Request was unsuccessful: “ + xhr.status); } } }; xhr.open(“get”, “example.txt”, true); xhr.send(null);

如果之前采用的是触发器允诺并想在积极响应之前取消,能调用 abort() 方法:

let xhr = newXMLHttpRequest(); xhr.open(“get”, “example.jsp”, true); setTimeout(function() { if (xhr) { xhr.abort(); xhr = null; } }, 5000); // 5秒后,终止 Ajax 允诺

调用这个方法后,XMLHttpRequest 第一类会停止触发事件,并阻止访问这个第一类上任何与积极响应相关的属性,而 readyState 属性变为 4status 属性变为 0 。中断允诺后,应该取消对 XMLHttpRequest 第一类的引用。由于内存问题,不推荐重用 XMLHttpRequest 第一类。

HTTP头部

在 HTTP 的每次允诺和积极响应中都会携带一些头部字段,默认允诺下,XMLHttpRequest 允诺会发送以下头部字段。

Accept:应用程序能处置的内容类型。Accept-Charset:应用程序能显示的字符集。Accept-Encoding:应用程序能处置的压缩编码类型。Accept-Language:应用程序采用的语言。Connection:应用程序与伺服器的连接类型。Cookie:页面中设置的 CookieHost:发送允诺的页面所在地域。Referer:发送允诺的页面的 URIUser-Agent:应用程序的使用者代理字符串。

XMLHttpRequest第一类透过一些方法暴露与允诺和积极响应相关的头部字段。

setRequestHeader()

XMLHttpRequest.setRequestHeader()方法是设置 HTTP 允诺头部字段的方法。此方法转交两个参数:第一个参数是头部属性名,第二个参数是头部属性值。为保证允诺头被发送,必须在open() 之后,send() 之前调用。如果多次调用,设置同一个字段的值会合并成单一的值发送。如下所示:

let xhr = newXMLHttpRequest(); xhr.open(“get”, “example.jsp”, true); xhr.setRequestHeader(“Content-Type”, “application/json”); xhr.setRequestHeader(“Custom-Header”, “Custom-Value”); xhr.send(null);

自定义头部要区别于应用程序正常发送的头部,否则会影响伺服器正常响应。默认头部在有些应用程序上能重写。而自定义一些header属性进行跨域允诺时,可能将会遇到 “not allowed by Access-Control-Allow-Headers in preflight response”,你可能将须要在你的服务端设置 “Access-Control-Allow-Headers”。

getResponseHeader()/getAllResponseHeaders()

XMLHttpRequest.getResponseHeader() 方法用作返回 HTTP 积极响应头中指定的属性值。如果在返回时,有多个一样的名字,那么返回的值是用逗号和空格分隔开的字符串。

XMLHttpRequest.getAllResponseHeaders() 方法返回大部份的积极响应头,是以 CRLF 分隔(回车+换行)的字符串,如果没收到伺服器回应,该属性为 null。以下是该方法返回的字符串样子:

Access-Control-Allow-Credentials: true Access-Control-Allow-Headers: * Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE Access-Control-Allow-Origin: * Access-Control-Max-Age: 3600 Content-Length: 0 Content-Type: application/json;charset=UTF-8 Date: Sun, 13 Dec 2020 11:46:23 GMT Server: Apache-Coyote/1.1

透过解析以上头部字段的输出,就能知道伺服器发送的大部份头部,而不须要单独去检查了。

withCredentials

XMLHttpRequest.withCredentials 属性是一个Boolean类型,用来指定跨域允诺时,是否应带有使用者信息(如Cookie和认证的HTTP头信息)。默认值是false,即向 sample.com 发送跨域允诺时,不会发送 sample.com 设置在本机上的 Cookie。

当须要跨域 AJAX请求发送Cookie 时,须要设置 XMLHttpRequest.withCredentialstrue。同源允诺无需设置。而想要这个属性生效,伺服器须要显示返回 Access-Control-Allow-Credentials 头信息。

Access-Control-Allow-Credentials: true

注意:脚本总是遵守同源策略,无法从 document.cookie或者 HTTP 积极响应的头信息中读取跨域的 Cookie,无论XMLHttpRequest.withCredentials 的属性值是truefalse

abort()

XMLHttpRequest.abort()方法用作终止已被发出的允诺。当一个允诺被终止,readyStatestatus 属性都会被置为 0

事件

XMLHttpRequest 第一类的事件基本上都和允诺进度有关,以下列出与 XMLHttpRequest 第一类有关的事件:

abort:允诺中止时(比如使用者取消)触发。如果发生错误导致中止,不会触发该事件。error:允诺错误时触发。load:允诺成功并完成积极响应时触发,不用检查 readyState 属性,可替代 readystatechange 事件。loadstart:允诺成功时并转交到积极响应的第一个字节时触发。也适用作 <img><video 元素。loadend:允诺成功并积极响应完成时,且在 errorabortload等事件之后触发。progress:在允诺转交到统计数据时候被周期性触发。timeout:允诺超时时触发。

触发的先后顺序为 loadstart > progress > error/abort/load > loadend

let xhr = new XMLHttpRequest(); xhr.onloadstart = function(event) { console.log(“允诺开始:”+ event); }; xhr.addEventListener(“progress”, function(event) { let divStatus = document.getElementById(“status”); divStatus.innerHTML = event.lengthComputable;if(event.lengthComputable) { divStatus.innerHTML = divStatus.innerHTML +“\n” + “Received “ + event.position + ” of “ + event.total + ” bytes”; } },false); xhr.onabort = function() { console.log(“允诺中止”); }; xhr.onload = function(event) { if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) { console.log(xhr.responseText); }else { console.log(“允诺失败:” + xhr.status); } }; xhr.onerror = function(event) { console.error(“允诺错误:” + event); }; xhr.onloadend = function(event) { console.log(“允诺完成:”+ event); }; xhr.ontimeout =function() { console.error(“允诺超时。”); }; xhr.open(“get”, “http://localhost:8088/servlets/ajaxGet”, true); xhr.timeout = 5000; // 5秒超时 xhr.send(null); setTimeout(function () {if (xhr) { xhr.abort(); xhr = null; } }, 4000); // 4 秒之后,终止AJAX允诺

其中,progress 事件触发会收到 event 第一类,该属性包含 lengthComputablepositiontotalSize 这三个属性:

lengthComputable:布尔值属性,表示进度信息是否可用。position:转交的字节数。total:积极响应的 Content-Length 头部定义的总字节数。

进度事件

进度事件定义了客户端/服务端通讯,用作测量如 HTTP 允诺(XMLHttpRequest或<img><audio><video><style><link>等底层资源的加载)等底层流程进度的事件。继承于 Event。原生提供ProgressEvent()缺省生成事件第一类。如下所示:

ProgressEvent(type, {lengthComputable: boolean, loaded: number, total: number});

缺省中有两个参数:

第一个参数返回的是字符串,表示事件的类型,这个参数是必须的。第二个参数是配置第一类,表示事件的属性,该参数可选。除了继承Event 接口的配置属性外,还能是下面的属性,属性可选。lengthComputable:布尔值,表示加载的总量是否能计算,默认是falseloaded:整数,表示已经加载的量,默认是 0total:整数,表示须要加载的总量,默认是 0

进度相关的事件都是继承自 ProgressEvent接口的,该接口用来描述外部资源加载的进度。而应用程序原生提供ProgressEvent() 缺省来生成事件实例。如下所示:

let event = new ProgressEvent(type, options);

而缺省中转交的两个参数:第一个参数是字符串,表示事件的类型,这个参数是必须的;第二个参数是配置第一类,表示事件的属性,该参数可选。配置第一类除了能采用Event 接口的配置属性,还能采用下面的属性,大部份这些属性都是可选的。

lengthComputable:布尔值,表示加载的总量是否能计算,默认是 falseloaded:整数,表示已经加载的量,默认是 0total:整数,表示须要加载的总量,默认是 0

属性

ProgressEvent 除了从 Event 继承的属性与方法外,还有三个与缺省中对应的三个属性。

ProgressEvent.lengthComputable:只读属性,是一个 Boolean标志,指示基础流程是否能计算总工作量和已经完成的工作量。换句话说,它告诉进度是否可测量。ProgressEvent.loaded:只读属性,表示一个 unsigned long long,代表基础流程已经完成的工作量。能透过属性和 ProgressEvent.total 来计算完成的工作量。采用 HTTP 下载资源时,这仅代表内容本身的一部份,而不代表头和其它开销。ProgressEvent.total:只读属性,表示一个 unsigned long long,表示基础流程正在执行的工作总量。采用 HTTP 下载资源时,这仅代表内容本身,而不代表头和其它开销。

如果 ProgressEvent.lengthComputablefalseProgressEvent.total 实际上是没有意义的。

XMLHttpRequest Level 2

XMLHttpRequest Level 2 是在 XMLHttpRequest Level 1的基础上进一步的扩展。但并非大部份应用程序都同时实现了XMLHttpRequest Level 2 的大部份功能,也可能将只同时实现了部份功能。

FormData

XMLHttpRequest Level 2 中新增了 FormData 类型,主要用作将统计数据编译成键值对,采用XMLHttpRequest来发送统计数据。也用作建立与表单类似文件格式的统计数据。建立一个FormData第一类,如下所示:

let data= new FormData();data.append(“username”, “Clark”); data.append(“account”, “addie”); let request = new XMLHttpRequest(): request.open(“post”, “http://example.com/submitform.jsp”); request.send(data);

append() 方法转交两个参数:键,相当于表单字段名;值,相当于表单字段名的值。当然,也能直接给 FormData 缺省传入表单元素,也能将表单中的统计数据作为键值对填充进去,也能附加额外的统计数据到FormData第一类中:

let form = document.querySelector(“form”); let data = new FormData(form); data.append(“serialnumber”, 134);

而采用 FormData之后,能不再须要给 XMLHttpRequest第一类显式地设置任何允诺头部。XMLHttpRequest第一类能够识别作为 FormData第一类传入的统计数据类型并自动配置相应的头部。

timeout

XMLHttpRequest.timeout 属性是一个表示发送统计数据后等待的毫秒数的一个整数,积极响应允诺时间超出该属性设置的毫秒数,就会中断该允诺。IE8 给 XMLHttpRequest 添加了该属性,并在 XMLHttpRequest Level 2 中添加了该特性。默认值为 0,当超时时,就会触发 timeout 事件。如下所示:

let xhr = new XMLHttpRequest(); xhr.onreadystatechange =function() { if (xhr.readyState === 4) { if ((xhr.status >= 200&& xhr.status < 300) || xhr.status === 304) { console.log(xhr.responseText); } else { console.log(“允诺失败:” + xhr.status); } } }; xhr.ontimeout = function() { console.log(“允诺超时。”); }; xhr.open(“get”, “http://localhost:8088/servlets/ajaxGet”, true); xhr.timeout = 1000; // 1秒超时 xhr.send(null);

上面的例子演示了设置超时timout属性值,超时后允诺中断触发ontimeout事件,并将 status 属性值置 0。但readyState属性值仍为 4,因此也会调用onreadystatechange 事件。

upload

XMLHttpRequest.upload 属性会返回一个 XMLHttpRequestUpload 第一类,代表上传进度。这个第一类不透明,但作为一个 XMLHttpRequestEventTarget,能透过对其绑定事件来追踪它的进度。

XMLHttpRequest.upload 属性能监听 loadstart/progress/abort/error/load/timeout/loadend 这几种事件。

<body> <progress id=“uploadprogress” min=“0” max=“100” value=“0”>0</progress> <br> <input type=“file” id=“upload_files” /> <br> <button onclick=“upload()”>上传</button> </body> <script> function upload() { let resource = document.getElementsByTagName(input)[0].files[0]; // 此处进行 ajax 上传 let xhr = newXMLHttpRequest();// 预置的 formdata 第一类,详情查看 ajax level 2, let data = new FormData(); data.append(“resource”, resource); xhr.open(“post”, “http://localhost:8088/servlets/upload”); xhr.onload =function() { if (xhr.status === 200) { console.log(“上传成功”); } else { console.log(“上传出错”); } }; xhr.onerror =function(e) { console.error(An error occurred while submitting the form. Maybe your file is too big); }; // 上传进度事件 xhr.upload.addEventListener(progress, function (ev) { if (ev.lengthComputable) { let complete = (ev.loaded / ev.total * 100 | 0); let progress = document.getElementById(uploadprogress); progress.value = complete; } }); xhr.send(data); }</script>

当进行上传操作时,如果要跨域上传,须要在伺服器设置 CORS。

跨源资源共享(CORS,Cross-Origin Resource Sharing)定义应用程序与伺服器如何同时实现跨源通讯。使得XMLHttpRequest 允诺克服了同源采用的管制。简单的授权访问,在伺服器设置允诺头:

Access-Control-Allow-Origin: * Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE Access-Control-Max-Age: 3600 Access-Control-Allow-Headers: Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers

overrideMimeType()

XMLHttpRequest.overrideMimeType() 方法用来重写 XMLHttpRequest 积极响应的 MIME类型。是 Firefox 首次引入,在XMLHttpRequest Level 2 中添加了该方法。因为积极响应返回的 MIME 类型决定了 XMLHttpRequest 第一类如何处置积极响应。例如伺服器实际发送的是 XML统计数据,但积极响应头中MIME类型设置的是text/plain,应用程序不会自动解析,responseXML属性值是null。此时调用overrideMimeType() 能保证将积极响应当成 XML 而不是纯文本来处置:

let xhr = new XMLHttpRequest(); xhr.open(“get”, “example.jsp”, true); xhr.overrideMimeType(“text/xml”); xhr.send(null);

注意:为了正确覆盖积极响应的 MIME 类型,该方法必须在 send() 方法之前调用。

小结

XMLHttpRequest,最早由微软发明,并在 IE5 中引入。之后,其它应用程序都同时实现了该第一类。W3C 虽然也引入了 Web 标准。虽然不同应用程序有些差异,但基本采用是相对较规范的,因此能放心采用。

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务