Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 396|回复: 0

[默认分类] 触碰jQuery:AJAX异步详解

[复制链接]
  • TA的每日心情
    开心
    2021-12-13 21:45
  • 签到天数: 15 天

    [LV.4]偶尔看看III

    发表于 2018-7-12 17:36:48 | 显示全部楼层 |阅读模式
    传送门:异步编程系列目录……
    示例源码:触碰jQuery:AJAX异步详解.rar
    AJAX 全称 Asynchronous javaScript and XML(异步的 JavaScript 和 XML)。它并非一种新的技术,而是以下几种原有技术的结合体。
    1)   使用CSS和XHTML来表示。
    2)   使用DOM模型来交互和动态显示。
    3)   使用XMLHttpRequest来和服务器进行异步通信。
    4)   使用javascript来绑定和调用。
    通过AJAX异步技术,可以在客户端脚本与web服务器交互数据的过程中使用XMLHttpRequest对象来完成HTTP请求(Request)/应答(Response)模型:
    1)   不需要用户等待服务端响应。在异步派发XMLHttpRequest请求后控制权马上就被返回到浏览器。界面不会出现白板,在得到服务器响应之前还可以友好的给出一个加载提示。
    2)   不需要重新加载整个页面。为XMLHttpRequest注册一个回调函数,待服务器响应到达时,触发回调函数,并且传递所需的少量数据。“按需取数据”也降低了服务器的压力。
    3)   不需要使用隐藏或内嵌的框架。在XHR对象之前,模拟Ajax通信通常使用hack手段,如使用隐藏的或内嵌的框架(<iframe>标签)。
    下面介绍下AJAX中的重要对象:XMLHttpRequest。

    XMLHttpRequest对象(XHR)
    XMLHttpRequest是一套可以在Javascript、VbScript、Jscript等脚本语言中通过http协议传送或接收XML及其他数据的一套API。
    XMLHttpRequest对象首次以ActiveX对象形式在微软Internet Explorer(IE) 5中以引入。其他浏览器制造商在认识到这一对象重要性后也纷纷实现了XMLHttpRequest对象,但是以一个本地JavaScript对象而不是作为一个ActiveX对象实现。而如今,由于安全性、标准等问题,微软已经在其IE 7中把XMLHttpRequest实现为一个本地JavaScript对象。



      
       API
       描述
      
      
        客服端请求
      
      
       open(method,url,async, bstrUser, bstrPassword)
       规定请求的类型、URL 以及是否异步处理请求。1)   method:请求的类型,例如:POST、GET、PUT及PROPFIND。大小写不敏感。2)   url:请求的URL地址,可以为绝对地址也可以为相对地址。3)   async[可选]:true(默认,异步)或 false(同步)。注释:当您使用async=false 时,JavaScript 会等到服务器响应就绪才继续执行。如果服务器繁忙或缓慢,应用程序会挂起或停止。此时,不需要编写onreadystatechange回调函数,把代码放到 send() 语句后面即可。4)   bstrUser[可选]:如果服务器需要验证,此处指定用户名,如果未指定,当服务器需要验证时,会弹出验证窗口。5)   bstrPassword[可选]:验证信息中的密码部分,如果用户名为空,则此值将被忽略。
      
      
       getRequestHeader(name)
       获取指定的相应头部信息
      
      
       setRequestHeader(name,value)
       自定义HTTP头部信息。需在open()方法之后和send()之前调用,才能成功发送请求头部信息。传送门:HTTP 头部详解
       
         
          
           Accept
           浏览器能够处理的媒体类型
          
          
           Accept-Charset
           浏览器申明自己接收的字符集
          
          
           Accept-Encoding
           浏览器申明自己接收的编码方法,通常指定压缩方法,是否支持压缩,支持什么压缩方法(gzip,deflate)
          
          
           Host
           客户端指定要请求的WEB服务器的域名/IP 地址和端口号
          
          
           Referer
           发出请求的页面的URI
          
          
           Content-Type
           标明发送或者接收的实体的MIME类型。传送门:1、HTTP Content-type对照表2、格式:Content-Type: [type]/[subtype]; parameter
          
          
           X-Requested-With
           非标准HTTP头,只为firefox3标注是否为ajax异步请求,null表示为同步请求。
          
         
        默认情况下,服务器对POST请求和提交Web表单不会一视同仁,将Content-Type头部信息设置为application/x-www-form-urlencoded (模拟表单提交)
      
      
       send(string)
       将请求发送到服务器。参数string仅用于POST请求;对于GET请求的参数写在url后面,所以string参数传递null。
      
      
       abort()
       调用此方法可取消异步请求,调用后,XHR对象停止触发事件,不允许访问任何与响应相关的属性;
      
      
        服务端响应
      
      
       onreadystatechange事件
       对于异步请求,如果需要对服务器获取和操作响应结果,则在send() 之前,需要为onreadystatechange属性指定处理方法。该函数用于对服务器响应进行处理。
      
      
       readyState
       存有XMLHttpRequest的状态。每当readyState改变时,就会触发onreadystatechange事件。从 0 到 4 发生变化:
       
         
          
           0(未初始化)
           对象已建立,但是尚未初始化(尚未调用open方法)
          
          
           1(初始化)
           对象已建立,尚未调用send方法
          
          
           2(发送数据)
           send方法已调用,但是当前的状态及http头未知
          
          
           3(数据传送中)
           已接收部分数据,因为响应及http头不全,这时通过responseBody和responseText获取部分数据会出现错误
          
          
           4(完成)
           数据接收完毕,此时可以通过responseXml和responseText获取完整的回应数据
          
         
         
      
      
       status(数字表示)
       返回当前请求的http状态码。传送门:HTTP状态码一览表(HTTP Status Code)
       
         
          
           1xx(临时响应)
           表示临时响应并需要请求者继续执行操作的状态代码。
          
          
           2xx (成功)
           表示成功处理了请求的状态代码。Eg:200
          
          
           3xx (重定向)
           表示要完成请求,需要进一步操作。通常,这些状态代码用来重定向。Eg:304
          
          
           4xx(请求错误)
           这些状态代码表示请求可能出错,导致服务器无法正常处理。Eg:404
          
          
           5xx(服务器错误)
           这些状态代码表示服务器在尝试处理请求时发生内部错误。这些错误可能是服务器本身的错误,而不是请求出错。Eg:500
          
         
         
      
      
       statusText(字符表示)
       返回当前请求的状态文本eg:OK  (status:200)
      
      
       responseText
       将响应信息作为字符串返回
      
      
       responseXML
       将响应信息格式化为Xml Document对象并返回
      
      
       responseBody(只有微软的IE支持)
       将响应信息正文以unsigned byte数组形式返回(二进制数据)
      
      
       responseStream(只有IE的某些版本支持)
       以Ado Stream对象(二进制流)的形式返回响应信息
      
      
       getResponseHeader(name)
       从响应信息中获取指定的http头
      
      
       getAllResponseHeaders()
       获取响应的所有http头
      
      
       overrideMimeType
       通常用于重写服务器响应的MIME类型。Eg,正常情况下XMLHttpRequest只接收文本数据,但我们可以重写MIME为“text/plain; charset=x-user-defined”,以欺骗浏览器避免浏览器格式化服务器返回的数据,以实现接收二进制数据。
      



    一个简单的ajax封装:
    1. var myAjax = {
    2.     // XMLHttpRequest IE7+, Firefox, Chrome, Opera, Safari ;  ActiveXObject IE6, IE5
    3.     xhr: window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"),
    4.     get: function (url, callback) {
    5.         this.xhr.open("get", url);
    6.         this.onreadystatechange(callback, this.xhr);
    7.         this.xhr.send(null);        
    8.     },
    9.     post: function (url, data, callback) {
    10.         this.xhr.open("post", url);
    11.         this.xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    12.         this.onreadystatechange(callback, this.xhr);
    13.         this.xhr.send(data);
    14.     },
    15.     onreadystatechange: function (func, _xhr) {
    16.         _xhr.onreadystatechange = function () {
    17.             if (_xhr.readyState == 4) {
    18.                 if (_xhr.status == 200) {
    19.                     func(_xhr.responseText);
    20.                 }
    21.             }
    22.         }
    23.     }
    24. }
    复制代码
    使用:
    1.             $("#btn_nowTime1").bind("click", null
    2.                 , function () {
    3.                     myAjax.post("AjaxHandler.ashx", "func=GetServerTime"
    4.                         , function (data) {
    5.                             if (data)
    6.                                 alert(data);
    7.                         }
    8.                     );
    9.                 });
    复制代码

    XMLHttpRequest Level 2
    XMLHttpRequest是一个浏览器接口,使得Javascript可以进行 HTTP (S) 通信。但是,这个接口一直没有标准化,每家浏览器的实现或多或少有点不同。HTML 5 的概念形成后,W3C 开始考虑标准化这个接口。2008年 2 月,提出了XMLHttpRequest Level 2 草案。
    1.   老版本的缺点
    老版本的XMLHttpRequest对象有以下几个缺点:
    1)   只支持文本数据的传送,无法用来读取和上传二进制文件。
    2)   传送和接收数据时,没有进度信息,只能提示有没有完成。
    3)   受到"同域限制"(Same Origin Policy),只能向同一域名的服务器请求数据。
    2.   新版本的功能
    新版本的XMLHttpRequest对象,针对老版本的缺点,做出了大幅改进。
    1)   可以设置 HTTP 请求的时限。
    2)   可以使用FormData对象管理表单数据。
    3)   可以上传文件。
    4)   可以请求不同域名下的数据(跨域资源共享,Cross-origin resource sharing,简称 CORS)。
    5)   可以获取服务器端的二进制数据。
    6)   可以获得数据传输的进度信息。
    3.   介绍几个XMLHttpRequest Leve2 新增的成员


      
        超时时限
      
      
       timeout
       设置ajax请求超时时限,过了这个时限,就自动停止 HTTP 请求。
      
      
       ontimeout事件
       当ajax超过timeout 时限时触发的回调函数。
      
      
        指定响应格式
      
      
       responseType
       (默认:“text”)在发送请求前,根据您的数据需要,将xhr.responseType设置为“text”、“arraybuffer”、“blob”或“document”。
      
      
       response
       成功发送请求后,xhr的响应属性会包含DOMString、ArrayBuffer、Blob 或 Document 形式(具体取决于responseTyp的设置)的请求数据。
      
      
        进度信息
      
      
       progress 事件
       在XMLHttpRequest对象传递数据的时候用来返回进度信息。它分成上传和下载两种情况。下载的 progress 事件属于XMLHttpRequest对象,上传的 progress 事件属于XMLHttpRequest.upload对象。即:xhr.onprogress = updateProgress;xhr.upload.onprogress = updateProgress;XHR还新增了与progress事件相关的五个事件:1)   load 事件:传输成功完成。2)   abort 事件:传输被用户取消。3)   error 事件:传输中出现错误。4)   loadstart事件:传输开始。5)   loadEnd事件:传输结束,但是不知道成功还是失败。
      


    4.   一个新功能实例
    1)   接收二进制数据(方法A:改写MIMEType)
    老版本的XMLHttpRequest对象,只能从服务器取回文本数据。但我们可以改写数据的MIMEType,将服务器返回的二进制数据伪装成文本数据,并且告诉浏览器这是用户自定义的字符集。
    关键代码如下:
    服务端
    1.         String str = "二进制数据获取";
    2.         MemoryStream _memory = new MemoryStream();
    3.         BinaryFormatter formatter = new BinaryFormatter();
    4.         formatter.Serialize(_memory, str);
    5.         _memory.Position = 0;
    6.         byte[] read = new byte[_memory.Length];
    7.         _memory.Read(read, 0, read.Length);
    8.         _memory.Close();
    9.         context.Response.ContentType = "text/plain";
    10.         // 服务器使用OutputStream输出二进制流
    11.         context.Response.OutputStream.Write(read, 0, read.Length);
    复制代码
    客服端
    1.             $("#btn_mime").bind("click", null
    2.                 , function () {
    3.                     $.ajax("AjaxHandler.ashx?func=GetBinaryData",
    4.                         {
    5.                             type: "get",
    6.                             dataType: "text",
    7.                             cache: false,
    8.                             mimeType: "text/plain; charset=x-user-defined",
    9.                             success: function (data) {
    10.                                 if (data) {
    11.                                     var byte = [];
    12.                                     for (var i = 0, len = data.length; i < len; ++i) {
    13.                                         var c = data.charCodeAt(i);
    14.                                         byte[byte.length] = c & 0xff;
    15.                                     }
    16.                                     alert(byte);
    17.                                 }
    18.                             }
    19.                         });
    20.                 });
    复制代码
    浏览器会把相应数据当做文本数据接收,所以我们还必须再一个个字节地还原成二进制数据。位运算"c & 0xff",表示在每个字符的两个字节之中,只保留后一个字节,将前一个字节扔掉。原因是浏览器解读字符的时候,会把字符自动解读成Unicode 的 0xF700-0xF7ff 区段。
    截图如下:(测试环境:Google Chrome 版本 26.0.1410.43)
    服务器端返回二进制数据:

    客服端输出:
    a)   使用mimeType: "text/plain; charset=x-user-defined"参数。
         
    b)   没有对服务器的MIME类型进行重写,导致返回信息被浏览器格式化后输出的二进制数据与服务器不同。并且不同浏览器格式化后输出的二进制数据都有差异。
         

    2)   接收二进制数据(方法B:responseType属性)
    在XMLHttpRequest Level2中,可以使用新增的responseType属性从服务器取回二进制数据。把responseType设为 blob,表示服务器传回的是二进制对象。
    1. var xhr = new XMLHttpRequest();
    2. xhr.open ("GET", "/path/to/image.png");
    3. xhr.responseType = "blob";
    复制代码
    接收数据的时候,用浏览器自带的 Blob 对象即可。注意,读取的xhr.response,而不是xhr.responseText。
    1. var blob = new Blob ([xhr.response], {type: "image/png"});
    复制代码
    还可以将responseType设为arraybuffer,把二进制数据装在一个数组里。然后再遍历这个数组。
    1. var xhr = new XMLHttpRequest ();
    2. xhr.open ("GET", "/path/to/image.png");
    3. xhr.responseType = "arraybuffer";
    4. var arrayBuffer = xhr.response;
    5. if (arrayBuffer) {
    6. var byteArray = new Uint8Array (arrayBuffer);
    7.     for (vari = 0; i<byteArray.byteLength; i++) {
    8.         // do something
    9.     }
    10. }
    复制代码
    5.   更多XMLHttpRequest Level 2新功能描述请看:
    1)   XMLHttpRequest 增强功能
    2)   XMLHttpRequest Level 2 使用指南
    3)   XMLHttpRequest2 新技巧

    jQuery框架的Ajax
        jQuery是一个快速、简单的JavaScript library,核心理念是write less,do more(写的更少,做的更多)。它简化了HTML 文件的traversing,事件处理、动画、Ajax 互动,从而方便了网页制作的快速发展。jQuery是为改变你编写JavaScript 的方式而设计的。更多jQuery科普知识请看:jQuery百度百科(Eg:模块,历史版本)
        下面介绍下jQuery框架中ajax相关API:
        版本Jquery-1.7.1.js。

    1.   jQuery.ajax( [url,] options )
        通过 HTTP 请求加载远程数据。
    返回值:$.ajax() 返回jqXHR对象(jqXHR对象:为XMLHttpRequest对象的超集)。可用于手动终止请求abort()、为ajax函数设置额外的回调函数等。

    ajax内部实现的两个重要对象:s对象和jqXHR对象。
    1)   s对象
    由默认设置jQuery.ajaxSettings对象、options参数集合和jQuery.ajaxSetup({})默认设置合并而成s对象。


      
       参数名
       描述
      
      
        可由ajax的options参数设置
      
      
       url
       (默认: 当前页地址) 要请求的目的URL地址。
      
      
       usernamepassword
       用于响应HTTP访问认证请求的用户名及密码
      
      
       type
       (默认: "GET") 请求方式 ("POST" 或 "GET")。注意:其它 HTTP 请求方法,如 PUT 和 DELETE 也可以使用,但仅部分浏览器支持。
      
      
       dataType
       预期服务器返回的数据类型。如果不指定,jQuery将自动根据 HTTP 包 MIME 信息来智能判断,比如 XML MIME 类型就被识别为 XML。随后服务器端返回的数据会根据这个值解析后,传递给回调函数。必须确保网页服务器报告的 MIME 类型与我们选择的dataType所匹配。比如说,XML的话,服务器端就必须声明 text/xml 或者 application/xml 来获得一致的结果。可用值:
       
         
          
           "xml"
           返回 XML 文档,可用jQuery处理。
          
          
           "html"
           返回纯文本 HTML 信息;包含的 script 标签会在插入dom时执行。
          
          
           "script"
           返回纯文本 JavaScript 代码,常常用于跨域请求。不会触发全局事件和局部事件;只支持GET方式(POST请求会自动转化为GET请求);默认不启用缓存(cache:false)
          
          
           "json"
           返回 JSON 数据。JSON 数据是一种能很方便通过 JavaScript 解析的结构化数据。
          
          
           "jsonp"
           JSONP 格式,用于跨域请求。
          
          
           "text"
           返回纯文本字符串
          
         
        其中,text 和 xml 类型返回的数据不会经过处理。数据仅仅简单的将XMLHttpRequest的responseText或responseHTML属性传递给 success 回调函数。如果指定了 script 或者jsonp类型,那么当从服务器接收到数据时,实际上是用了<script>标签而不是XMLHttpRequest对象。这种情况下,$.ajax() 不再返回一个XMLHttpRequest对象,并且也不会传递事件处理函数,比如beforeSend。
      
      
       contentType
       (默认: "application/x-www-form-urlencoded")标明发送或者接收的实体的MIME类型。当“非GET或HEAD请求”的HTTP请求时,会被设置为HTTP头请求信息。
      
      
       mimeType
       多用途互联网邮件扩展(MIME,Multipurpose Internet Mail Extensions);用于重写服务器端响应的MIME类型。
      
      
       data
       发送到服务器的数据。可以是一个查询字符串,比如 key1=value1&amp;key2=value2 ,也可以是一个映射,比如 {key1: "value1", key2: "value2"} 。如果使用了后者的形式,则数据在发送前会通过jQuery.param()函数转换成查询字符串。这个处理过程也可以通过设置processData选项为false来回避。
      
      
       processData
       (默认: true) 默认情况下,发送到服务器的数据(即data参数)将被转换为字符串以配合默认内容类型 "application/x-www-form-urlencoded"。如果要发送 DOM 树信息或其它不希望转换的信息,请设置为 false。jQuery中的处理方式:
    1. if ( s.data&&s.processData&&typeofs.data !== "string" ) {
    2.     s.data = jQuery.param(s.data, s.traditional );
    3. }
    复制代码
      
      
       async
       (默认: true) 默认设置下,所有请求均为异步请求。如果需要发送同步请求,请将此选项设置为 false。注意,同步请求将锁住浏览器,用户其它操作必须等待请求完成才可以执行。
      
      
       timeout
       设置请求超时时间(毫秒)。通过setTimeout(fn,time)实现。
      
      
       cache
       (默认: true)dataType为 script 和jsonp时默认为 false。设置为 false 将不缓存此页面。当使用GET或HEAD方式发送请求时要添加时间戳参数 (net Date()).getTime() 来保证每次发送的URL不同, 可以避免浏览器缓存.(只有GET和HEAD方式的请求浏览器才会缓存)jQuery中的处理方式:
    1. if ( s.cache === false ) {
    2.     var ts = jQuery.now(),
    3.     // rts = /([?&])_=[^&]*/尝试替换
    4.     ret = s.url.replace( rts, "$1_=" + ts );
    5.     // rquery = /\?/如果没有替换任何内容,则把时间戳加到url最后
    6.     s.url = ret + ( ( ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" );
    7. }
    复制代码
    示例:/AjaxHandler.ashx?func=GetBinaryData&_=1368424995535
      
      
       ifModified
       (默认: false) 仅在服务器数据改变时获取新数据。通过响应头If-Modified-Since、IF-None-Match和请求头Last-Modified、Etag提高GET或HEAD方式请求效率。(只有GET和HEAD方式的请求浏览器才会缓存)
      
      
       global
       (默认: true) 是否触发全局 AJAX 事件。设置为 false 将不会触发全局AJAX 事件:ajaxStart、ajaxSend、ajaxSuccess、ajaxError、ajaxComplete、ajaxStop。(比如请求频繁时可禁用全局AJAX事件提高效率)
      
      
       context
       (默认:true)  这个对象用于设置Ajax相关回调函数的上下文,让回调函数内this指向这个对象。如果不设定这个参数,那么回调函数中的this就指向调用本次AJAX请求时传递的options参数载体“s对象”。但对于全局Ajax事件来说,this都是指向全局事件所绑定的元素。
      
      
       jsonp
       指定获得jsonp回调函数名的参数名(默认为:callback)。这个值用来替代URL中"callback=?"里的"callback"部分,比如{jsonp:"onJsonPLoad"}会替换为将"onJsonPLoad=?"传给服务器。
      
      
       jsonpCallback
       为jsonp请求指定一个回调函数名。jsonpCallback参数一般为字符串,也可接收函数(该函数返回字符串)。默认情况下生成随机函数名:"jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ) + jQuery.now()
      
      
       crossDomain
       (默认:null)false:同域请求;true跨域请求。倘若crossDomain标识为null,则jQuery会自动根据本地url、端口来解析。可以根据需求直接赋值来提高性能。通常情况下由服务器自动解析即可,但如果你想在同一域中强制跨域请求(像JSONP一样),那么将crossDomain为true,这允许你将服务器端重定向到另一个域。
      
      
       scriptCharset
       只有当请求时dataType为"jsonp"或"script",并且type是"GET"才会用于修改charset。因为此时是动态创建<script>来完成脚本加载,但是如果js中的编码与页面的编码不一致时,js可能加载失败或者显示乱码或者IE下报某符号错误。设置此参数就相当于为<script>标签设置charset属性。
      
      
       hearders
       (默认:{})  设置HTTP请求头数据"{键:值}"。此设置发生在:jQuery所有影响HTTP头的参数(options)设置之后,beforeSend回调函数之前。
      
      
       statusCode
       (默认:{})  定义一组HTTP状态码与回调函数的映射,当响应的状态码有匹配statusCode则会触发对应回调函数。例如,如果响应状态是404,将触发以下警报:
    1. $.ajax({
    2.     statusCode: {404: function() {
    3.        alert("page not found");
    4.     }
    5. });
    复制代码
      
      
       traditional
       如果你想要用传统的方式来序列化数据,那么就设置为true。请参考$.param()深度递归详解
      
      
       xhrFields
       声明附加到XMLHttpRequest对象的自定义“key-value”数组。例如,如果需要的话,你可以用它来设置跨域的withCredentials为true,即:xhrFields: { withCredentials: true }   
      
      
       5个局部事件
       beforeSend、dataFilter、success、error、complete。(详见后面事件介绍部分)
      
      
        由ajax函数内部解析或内部提供
      
      
       dataTypes
       由dataType按空格拆分所得。
      
      
       isLocal
       根据协议确定当前url请求的是否为本地请求。jQuery中定义默认值为:
    1. isLocal:/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/.test(/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/.exec(url))
    复制代码
      
      
       hasContent
       非GET或HEAD请求为true,用于处理data和contentType参数。
      
      
       contents
       一个"{类型字符串:正则表达式}"的对象,倘若dataTypes[0]为“*”时,用contents中的正则表达式去匹配contentType,匹配成功则用“类型字符串”覆盖dataTypes[0]。jQuery内部定义如下:
    1. contents: {
    2.     xml: /xml/,
    3.     html: /html/,
    4.     json: /json/,
    5.     script: /javascript|ecmascript/
    6. }
    复制代码
      
      
       accepts
       浏览器能够处理的媒体类型,其值取决于dataTypes[0]参数。jQuery内部定义如下:
    1. accepts: {
    2.     xml: "application/xml, text/xml",
    3.     html: "text/html",
    4.     text: "text/plain",
    5.     json: "application/json, text/javascript",
    6.     script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript",
    7.         "*": allTypes    // dataTypes[0]匹配不上时取此值
    8. }
    复制代码
      
      
       responseFields
       jqXHR超集设置“数据类型:属性”对应关系,在返回响应数据时,用于确定创建哪个属性变量。jQuery中定义如下:
    1. responseFields: {
    2.     xml: "responseXML",
    3.     text: "responseText"
    4. }
    复制代码
      
      
       converters
       存储数据类型对应的转换器,根据dataTypes获取对应转换器,用于对响应数据response进行处理。该处理发生在dataFilter回调函数之后。
    1. converters: {
    2.     "* text": window.String,
    3.     "text html": true,
    4.     "text json": jQuery.parseJSON,
    5.     "text xml": jQuery.parseXML,
    6.     "text script": function( text ) {
    7.                     jQuery.globalEval( text );  // 执行脚本
    8.                     return text;
    9.                 }
    10. }
    复制代码
      
      
       
       
       
      


    2)   jqXHR对象
    为不同浏览器内置的XMLHttpRequest提供了一致的超集。对于XMLHttpRequest之外的传输机制,比如JSONP请求,jXHR对象也可以进行处理。

    超集与真子集:
    如果一个集合S2中的每一个元素都在集合S1中,且集合S1中可能包含S2中没有的元素,则集合S1就是S2的一个超集。 S1是S2的超集,则S2是S1的真子集,反之亦然。

        jqXHR对象我们常常使用如下成员,这些成员主要用于ajax的全局事件和局部事件,并且做为$.ajax()函数返回值返回。
    1. jqXHR:{
    2.     readyState
    3.     ,setRequestHeader: function( name, value )
    4.     ,getAllResponseHeaders: function()
    5.     ,getResponseHeader: function( key )
    6.     ,overrideMimeType: function( type )
    7.     ,abort: function( statusText )
    8.     ,responseText
    9.     ,responseXML
    10. }
    复制代码
    另外,jqXHR的全部成员如下:

        在图中我们看到一些陌生的函数,比如:done()、fail()、promise()、isResolve()、isRejected()、then()、always()、progress()等,都是jQuery的deferred对象API。
    开发网站的过程中,我们经常遇到某些耗时很长的javascript操作。其中,既有异步的操作(比如ajax读取服务器数据),也有同步的操作(比如遍历一个大型数组),它们都不是立即能得到结果的。
        通常的做法是,为它们指定回调函数(callback)。即事先规定,一旦它们运行结束,应该调用哪些函数。但是,在回调函数方面,jQuery的功能非常弱。为了改变这一点,jQuery开发团队就设计了deferred对象。
    简单说,deferred对象就是jQuery的回调函数解决方案。在英语中,defer的意思是"延迟",所以deferred对象的含义就是"延迟"到未来某个点再执行。 它解决了如何处理耗时操作的问题,对那些操作提供了更好的控制,以及统一的编程接口。
    更专业的资源:jQuery的deferred对象详解

    2.   jQuery Ajax事件
    jQuery框架中,伴随Ajax请求会触发若干事件,我们可以订阅这些事件并在其中处理我们的逻辑。在jQuery中有两种Ajax事件:局部事件和全局事件。
    1)   局部事件(回调函数),在$.ajax()方法的options参数中声明,可以用来设置请求数据和获取、处理响应数据。


      
       beforeSend
       该函数可在发送请求前修改XMLHttpRequest对象,如添加自定义 HTTP 头。签名:function (jqXHR,s) { }函数说明:传入jqXHR、s对象
      
      
       dataFilter
       在请求成功之后调用。若状态码为304(未修改)则不触发此回调。签名:function (data, dataType) { return newData; }函数说明:传入返回的数据、"dataType"参数的值。并且必须返回新的数据传递给success回调函数
      
      
       success
       请求成功时触发。签名:function (data,statusText,jqXHR) { }函数说明:传入返回的数据、描述状态的字符串”success”、jqXHR对象
      
      
       error
       请求失败时调用此函数。签名:function (jqXHR, textStatus, errorThrown) { }函数说明:传入jqXHR对象、描述状态的字符串”error”、错误信息
      
      
       complete
       请求完成后回调函数 (请求成功或失败之后均调用)签名:function (jqXHR, textStatus) { }函数说明:传入jqXHR对象、描述状态的字符串(可能值:"No Transport"、"timeout"、"notmodified"---304 "、"parsererror"、"success"、"error")
      


            定义方式例如:
    1. $.ajax({
    2.     // ...
    3.     beforeSend: function(){
    4.         // Handle the beforeSend event
    5.     },
    6.     complete: function(){
    7.         // Handle the complete event
    8.     }
    9.     // ...
    10. });
    复制代码
    2)   全局事件,每次Ajax请求都会触发,它会向DOM中的所有元素广播,你只需为DOM中任意元素bind好全局事件即会触发(若绑定多次,则会依次触发为事件注册的回调函数)。


      
       ajaxStart
       开始新的Ajax请求,并且此时jQuery对象上没有其他ajax请求正在进行。签名:function(e)函数说明:传入事件对象
      
      
       ajaxSend
       当一个Ajax请求开始时触发签名:function(e,jqXHR,s)函数说明:传入事件对象、jqXHR、s对象
      
      
       ajaxSuccess
       全局的请求成功签名:function(e,jqXHR,s,data)函数说明:传入事件对象、jqXHR、s对象、请求成功返回的相应数据
      
      
       ajaxError
       全局的发生错误时触发签名:function(e,jqXHR,s,errorData)函数说明:传入事件对象、jqXHR、s对象、请求失败返回的错误信息
      
      
       ajaxComplete
       全局的请求完成时触发签名:function(e,jqXHR,s)函数说明:传入事件对象、jqXHR、s对象
      
      
       ajaxStop
       当jQuery对象上正在进行Ajax请求都结束时触发。签名:function(e)函数说明:传入事件对象
      


    全局事件在jQuery中的声明方式:
    1. jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){
    2.             jQuery.fn[ o ] = function( f ){
    3.                 return this.on( o, f );
    4.         };
    5. });
    复制代码
        所以我们可以使用下面两种方式定义全局事件:
    1. // 可以用bind来绑定,用unbind来取消绑定。
    2. $("#loading").bind("ajaxSend", function(){ … });
    3. 或者:
    4. $("#loading").ajaxStart(function(){ … });
    复制代码
    3)   ajax方法完整的事件流
       
       
    4)   示例:$.ajax()触发的事件(局部事件和全局事件)
    1.             // 全局事件
    2.             $("#div_event").ajaxStart(function (e) {
    3.                 doAddEvent4textarea("txt_event", "触发ajaxStart回调函数");
    4.             });
    5.             $("#div_event").ajaxSend(function (e) {
    6.                 doAddEvent4textarea("txt_event", "触发ajaxSend回调函数");
    7.             });
    8.             $("#div_event").ajaxSuccess(function (e, jqXHR, s, data) {
    9.                 doAddEvent4textarea("txt_event", "触发ajaxSuccess回调函数");
    10.             });
    11.             $("#div_event").ajaxError(function (e, jqXHR, s, errorData) {
    12.                 doAddEvent4textarea("txt_event", "触发ajaxError回调函数");
    13.             });
    14.             $("#div_event").ajaxComplete(function (e, jqXHR, s) {
    15.                 doAddEvent4textarea("txt_event", "触发ajaxComplete回调函数");
    16.             });
    17.             $("#div_event").ajaxStop(function (e) {
    18.                 doAddEvent4textarea("txt_event", "触发ajaxStop回调函数");
    19.             });
    20.             // 局部事件
    21.             function bindLocalEvent(e) {
    22.                 var textareaid = e.data.textareaid;
    23.                 var global = e.data.global;
    24.                 $.ajax("AjaxHandler.ashx?func=btn_nowTime_long",
    25.                     {
    26.                         type: "get",
    27.                         dataType: "text",
    28.                         global: global,
    29.                         cache: false,
    30.                         beforeSend: function (jqXHR, s) {
    31.                             doAddEvent4textarea(textareaid, "触发beforeSend回调函数");
    32.                         },
    33.                         dataFilter: function (data, dataType) {
    34.                             doAddEvent4textarea(textareaid, "触发dataFilter回调函数");
    35.                         },
    36.                         success: function (data, statusText, jqXHR) {
    37.                             doAddEvent4textarea(textareaid, "触发success回调函数");
    38.                         },
    39.                         error: function (jqXHR, textStatus, errorThrown) {
    40.                             doAddEvent4textarea(textareaid, "触发error回调函数");
    41.                         },
    42.                         complete: function (jqXHR, textStatus) {
    43.                             doAddEvent4textarea(textareaid, "触发complete回调函数");
    44.                         }
    45.                     });
    46.             }
    47.             function doAddEvent4textarea(textareaid, txt) {
    48.                 var textarea = $("#" + textareaid);
    49.                 textarea.val(textarea.val() + "\r\n" + txt);
    50.             }
    复制代码
            效果图:
               
    5)   $.ajax()方法的全局事件典型用例
    你的页面存在多个甚至为数不少的ajax请求,但是这些ajax请求都有相同的消息机制。ajax请求开始前显示一个提示框,提示“正在读取数据”;ajax请求成功时提示框显示“数据获取成功”;ajax请求结束后隐藏提示框。
    a)   不使用全局事件的做法是:
    给$.ajax()加上beforeSend、success、complete回调函数,在回调函数中加上处理提示框。
    b)   使用全局事件的做法是:
    1. $(document).ajaxStart(onStart)
    2.                          .ajaxComplete(onComplete)
    3.                           .ajaxSuccess(onSuccess);
    4. function onStart(event) { //..... }
    5. function onComplete(event, xhr, settings) { //..... }
    6. function onSuccess(event, xhr, settings) { //..... }
    复制代码
    3.   jQuery ajax相关函数
    1)   jQuery.ajaxSetup({ })
    jQuery.ajax()函数中的所有的参数选项都可以通过jQuery.ajaxSetup()函数来全局设置默认值。
    2)   $.ajax()函数的封装
    a)   $("").load(url [, params] [, callback])
    请求远程的HTML文件代码(dataType: "html"),默认使用 GET 方式,如果传递了params参数则使用Post方式。在请求“成功”完成时将responseText属性值插入至DOM中。但不管请求是否成功完成“在最后”都会执行callback回调函数(即:complete:callback)。
    b)   jQuery.get(url [, data] [, callback] [, type] )
    通过HTTP GET请求载入数据,并在请求成功时执行回调函数(即:success: callback)。
    c)   jQuery.getJSON(url [, data] [, callback] )
    通过 HTTP GET 请求载入 JSON 数据。相当于: jQuery.get(url, [data],[callback], "json")
    可以通过使用JSONP 形式的回调函数来加载其他网域的JSON数据。
    d)   jQuery.getScript(url [, callback] )
    通过 HTTP GET 请求载入并执行一个 JavaScript 文件。相当于: jQuery.get(url, null, [callback], "script")
    可以跨域调用 JavaScript 文件。
    e)   jQuery.post(url [, data] [, callback] [, type] )
    通过 HTTP POST 请求载入信息,并在请求成功时执行回调函数(即:success: callback)。
    3)   对象序列化
    a)   jQuery.param(object,traditional)
    创建数组或对象的序列化表示,该序列化可在ajax请求时在URL查询字符串中使用。

    序列化过程中会使用encodeURIComponent()函数把字符串作为URI组件进行编码。
    encodeURIComponent() 方法不会对 ASCII 字母和数字进行编码,也不会对这些 ASCII 标点符号进行编码: - _ . ! ~ * " ( ) 。其他字符(比如:;/?:@&=+$,# 这些用于分隔 URI 组件的标点符号),都是由一个或多个十六进制的转义序列替换的。
    1. // 在param中会进行如下处理
    2. function( key, value ) {
    3.     // 如果value是函数,则取其函数返回值
    4.     value = jQuery.isFunction( value ) ? value() : value;
    5.     s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
    6. };
    复制代码

    对于 jQuery 1.4,$.param() 方法将会通过深度递归的方式序列化对象,以便符合现代化脚本语言的需求,比如 PHP、Ruby on Rails 等。你可以传递traditional = true 或在ajax功能中传递包含traditional的options参数。
               传送门:$.param()深度递归详解$.param() 示例
    b)   $("").serializeArray()
    可以将一个或多个表单元素(比如 input、 textarea等),或者 form 元素本身的jQuery对象序列化为JSON对象。(非 JSON 字符串。需要使用插件或者第三方库进行字符串化操作)
    特别说明,元素不能被禁用(禁用的元素不会被包括在内),并且元素应当有含有 name 属性。提交按钮的值也不会被序列化。文件选择元素的数据也不会被序列化。
               传送门:$("").serializeArray() 示例
    c)   $("").serialize()
    可以将一个或多个表单元素(比如 input、 textarea等),或者 form 元素本身的jQuery对象序列化为经过URL编码转换后的字符串,可直接用在URL查询字符串中。
           jQuery内部定义:
    1.         serialize: function() {
    2.                 return jQuery.param( this.serializeArray() );
    3.         }
    复制代码
    传送门:$("").serialize()示例

    $.ajax()中常见应用示例
    1.   cache和ifModified参数
    1)   cache参数:GET和POST最重要的区别(传送门)
    语义上,GET是获取指定URL上的资源,是读操作,重要的一点是不论对某个资源GET多少次,它的状态是不会改变的,在这个意义上,我们说GET是安全的(不是被密码学或者数据保护意义上的安全)。因为GET是安全的,所以GET返回的内容可以被浏览器,Cache服务器缓存起来。
    而POST的语意是对指定资源“追加/添加”数据,所以是不安全的,每次提交的POST,参与的代码都会认为这个操作会修改操作对象资源的状态,于是,浏览器在你按下F5的时候会跳出确认框,缓存服务器不会缓存POST请求返回内容。
    2)   ifModified参数:通过ifModified参数提高请求性能(即:“条件GET”:Last-Modified / If-Modified-Since和ETag / If-None-Match)
    当你请求的资源并不是一层不变的时候,即不能简单的一直使用客户端缓存时,你可能通过将cache设置为false来发送请求,这实际上是在url加上时间戳组合成新的url,每次发送新的请求,这明显加大了服务器的压力。
    对于这种情况,我们可以通过ifModified参数改进缓存方式(即:cache和ifModified都设置为true),仅在请求的数据改变时重新获取。通过这种方式请求的url不会改变,并且每次都会发送到服务器,只是会有检验方法验证是否需要重新获取数据从而节省带宽和开销。
    更多ETag描述(优点,解决了Last-Modified无法解决的一些问题,什么场合不应该被使用)
    过程如下:
    a)   将$.ajax()函数的cache和ifModified参数同时设置为true。
    b)   客户端请求服务端A,在服务端加上Last-Modified/ETag响应体一起返回。
    c)   客户端缓存接收到的Last-Modified/ETag响应体,并在下一次发生请求A时将缓存的Last-Modified/ETag值做为If-Modified-Since/IF-None-Match请求头一起发给服务器。
    d)   服务器接收If-Modified-Since/IF-None-Match后,就根据参数值检验自上次客服端请求之后资源是否有改动
                              i.      若还未改动则直接返回响应304和一个空的响应体。
                            ii.      若已改动则重新处理数据,返回最新的请求数据。
    e)   这样,既保证不向客户端重复发出资源,也保证当服务器有变化时,客户端能够得到最新的资源。
    这一过程中,我们只需要做:服务器返回Last-Modified/ETag响应头和在服务端检验数据是否失效并采取对应处理方式。其余步骤由jQuery框架的ajax()函数完成。
    关键代码如下:   
    客服端:
    1.             $("#btn_nowTime_long3").bind("click", null
    2.                  , function () {
    3.                          $.ajax("AjaxHandler.ashx?func=GetServerTime4Modified",
    4.                          {
    5.                              type: "get",
    6.                              dataType: "text",
    7.                              cache: true,
    8.                              ifModified: true,
    9.                              success: function (data) {
    10.                                  if (data)
    11.                                      alert(data);
    12.                              },
    13.                      });
    14.              });
    复制代码
    服务端:
    1.     if(!String.IsNullOrEmpty(context.Request.Headers["If-Modified-Since"]))
    2.     {
    3.         if (CheckResourceValidate())  // 检查资源有效性
    4.         {
    5.             // 如果资源有效,则直接返回304状态码,客户端回去到此状态码后会从缓存中取值。
    6.             context.Response.StatusCode = 304;
    7.             return;
    8.         }
    9.     }
    10.     // 请求数据
    11.     GetServerTimeAfter2Second();
    12.     context.Response.Cache.SetExpires(DateTime.Now.AddSeconds(5));
    13.     // 设置Last-Modified响应体
    14.     context.Response.Cache.SetLastModified(DateTime.Now);
    复制代码
    2.   跨域请求
    在JavaScript中,有一个很重要的安全性限制,被称为“Same-Origin Policy”(同源策略)。这一策略对于JavaScript代码能够访问的页面内容做了很重要的限制,即JavaScript只能访问与包含它的文档在同一域下的内容。所谓同源是指,域名(host),协议(protocol),端口(port)相同。   


      
       URL
       说明
       是否允许通信
       能否通过javascript解决
      
      
       http://www.a.com/a.jshttp://www.a.com/b.js
       同一域名下
       允许
       
      
      
       http://www.a.com/lab/a.jshttp://www.a.com/script/b.js
       同一域名下不同文件夹
       允许
       
      
      
       http://www.a.com:8000/a.jshttp://www.a.com/b.js
       同一域名,不同端口
       不允许
       能
      
      
       http://www.a.com/a.jshttps://www.a.com/b.js
       同一域名,不同协议(http和https)
       不允许
       不能
      
      
       http://www.a.com/a.jshttp://70.32.92.74/b.js
       域名和域名对应ip
       不允许
       能
      
      
       http://www.cnblogs.com/a.jshttp://www.a.com/b.jshttp://script.a.com/b.jshttp://a.com/b.js
       不同域名(host)
       不允许
       能
      


    1)  $.ajax()为我们提供了两种解决方案,不过都是只支持get方式,分别是jQuery的jQuery.ajax“jsonp”格式和jquery.getScript()(即jQuery.ajax “script”格式)方式。
    2)   $.ajax()跨域原理分析
    由于javascript的安全限制“同源策略”,所以我们无法使用XMLHttpRequest直接请求别的域名下的资源。不过拥有src属性和href属性的<script>\<img>\<iframe>和<link>\<a>标签不受同源策略影响。$.ajax()提供的两种解决方案正是应用了动态创建<script>的方式来实现(即:生成<script>标签,src引入脚本,然后执行,最后移除<script>标签)。
    3)   jQuery.ajax()的jsonp和script方式的异同点:
    a)   相同:都走$.ajax() script格式的流程;不会触发全局事件和局部事件;只支持GET方式(POST请求会自动转化为GET请求);默认不启用缓存(cache:false)
    b)   不同:jsonp方式可以通过jsonp和jsonpCallback参数指定一个特定回调函数。
    4)   示例部署说明:
        因为是跨域请求,所以需要在本机部署两个示例程序以模拟不同域之间的访问,并且在示例代码中需要修改“crossUrl”为目的域路径。
    5)   jsonp示例代码:
    客服端:
    1.             // jsonp方式跨域请求(dataType:jsonp)
    2.             $("#btn_cross_req1").bind("click", null
    3.                 , function () {
    4.                     $.ajax(crossUrl,
    5.                         {
    6.                             type: "get",
    7.                             dataType: "jsonp",
    8.                             jsonp: "jsonpParamName",
    9.                             jsonpCallback: "crossCallback",
    10.                             crossDomain: true,
    11.                         });
    12.             });
    13.         function crossCallback(data) {
    14.             alert("jsonp" + data);
    15.         }
    复制代码
            服务端:
    1.     context.Response.ContentType = "text/plain";
    2.     string jsonpCallbackName = reqCollection["jsonpParamName"];
    3.     context.Response.Write(String.Format("{0}("来自域:{1}的相应信息")"
    4.                     , jsonpCallbackName, context.Request.Url.Host));
    复制代码
        分析:
    a)   因jsonp和jsonpCallback参数而改变的url如下。(即默认为:callback=jQuery随机值,更改为:jsonpParamName=crossCallback)
    URL:http://192.168.1.100:6567/AjaxHandler.ashx?func=CrossRequest&jsonpParamName=crossCallback&_=1368360234428
    b)   服务器端获取到jsonp回调函数名后,返回一个函数表达式。
    6)   在XMLHttpRequest Level 2中新增了跨域访问方式、接收二进制等新功能,详细请看:XMLHttpRequest2 新技巧

    最后,再来一张示例截图吧!!!
    示例源码:触碰jQuery:AJAX异步详解.rar






    本篇博文到此结束,主要介绍内容是使用XMLHttpRequest实现ajax请求和XMLHttpRequest Level 2为我们所带来的改进,最后重点讲解了jQuery中通过$.ajax()方法实现ajax以及各个参数的详细介绍,并立举了经典示例说明了:跨域请求、ajax全局事件、ajax局部事件、xhr二进制数据处理、如何高效使用缓存……
    做为一个后台工程师,你是否想深入了解一些前台必须的技术呢?是的,我想了解,并且会慢慢把所了解到的技术以博文的方式整理分享给大家。
    谢谢大家查阅,如果觉得文章不错,还请多多帮推荐……




    文中出现比较多的相关资源链接,这里整理下方便大家日后快速找到链接:
    1)   jQuery相关
            jQueryAPI文档
            jQuery百度百科(Eg:模块,历史版本)
    Ajax 技术资源中心(IBM)
    这是有关 Ajax 编程模型信息的一站式中心,包括很多文档、教程、论坛、blog、wiki 和新闻。任何 Ajax 的新信息都能在这里找到。
    jQuery.ajax()中的预过滤器和分发机制函数inspectPrefiltersOrTransports详解
    jQuery的deferred对象详解
    $.param()深度递归详解
    2)   XMLHttpRequest Level 2 的新功能相关
            XMLHttpRequest 增强功能
            XMLHttpRequest Level 2 使用指南
            XMLHttpRequest2 新技巧
    3)   跨域请求相关
    JavaScript跨域总结与解决办法
    总结了5种js跨域方式:利用<iframe>标签和document.domain属性、动态创建scrip、利用<iframe>标签和location.hash属性、window.name实现的跨域数据传输、使用HTML5 postMessage、利用flash跨域
    域名和IP地址及域名解析
    xhr注入_百度百科
            说说JSON和JSONP
    4)   HTTP相关
    HTTP深入浅出 http请求
    1)   介绍了一次HTTP通信的7个步骤:建立TCP连接、Web浏览器向Web服务器发送请求命令、Web浏览器发送请求头信息、Web服务器应答、Web服务器发送应答头信息、Web服务器向浏览器发送数据、Web服务器关闭TCP连接
    2)   介绍HTTP请求格式
            HTTP GET和POST的区别
    HTTP 头部详解
            HTTP Content-type对照表
            格式:Content-Type: [type]/[subtype]; parameter
            HTTP状态码一览表(HTTP Status Code)   
        5)Jquery其他部分
            不定义JQuery插件,不要说会JQuery


    ================================================
    求职:ASP.NET高级工程师。(地点:广州,深圳)
    今晚准备简历,步上新的求职之旅。希望有好工作推荐的园友给点帮助。
    另外,我有关注 广州多益、3g门户网、广州39健康网 公司,如有内部信息或你是内部员工,我很希望能与你交流。QQ:369220123
    如果你想认识.net朋友的可以加入群:185718116(广深莞·NET技术)  173844862(.Net高级部落)

    ================================================
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-4-18 09:20 , Processed in 0.483330 second(s), 54 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表