美文网首页
Ajax (cover JS高程)

Ajax (cover JS高程)

作者: zhaochengqi | 来源:发表于2018-06-03 14:45 被阅读55次

    story

    2005年,Jesse James Garrett 在一篇文章中介绍了一种他称为Ajax(Asynchronous JavaScript + XML)的技术,
    实际上Garrett提到的这种技术已经存在很长时间,之前人们通常把这种技术叫做 远程脚本 (remote scripting),而且早在1998年就有人采用不同手段实现了这种C/S的通信。
    在更早的时候,JS需要借助 Java applet 或 Flash 等中间层向服务器发送请求。
    XHR的出现则是将浏览器原生通信能力提供给开发者,简化了操作

    XMLHttpRequests

    XMLHttpRequests(XHR)对象是Ajax技术的核心,是由微软首先引入的一个特性,其他浏览器提供商后来都提供了相同的实现。

    IE7+ 才支持原生XHR, 之前的XHR对象是通过MSXML库的ActiveX对象实现的。

    • 使用
    ////只想支持IE7+
    var xhr = new XMLHttpRequest();
    
    //URL相对于执行代码的当前页面
    //最后一个参数表示是否异步发送请求
    //启动一个请求准备发送
    xhr.open('get', 'api/test', false)
    
    //参数为请求要发送的数据
    //不发送数据也需要传入null以兼容部分浏览器
    xhr.send(null)
    
    //本次请求是同步的,js代码会等到服务器响应之后再继续执行。响应结果会自动填充到XHR对象中
    console.log(xhr)
    
    • 读取结果
    //status         响应的HTTP状态 'status:200'
    //responseText   作为响应主体被返回的文本
    //statusText     HTTP状态说明 'statusText:"OK"'
    if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
      console.log(xhr.responseText)
    }
    
    • 异步

      通过检测 xhr.readyState 判断请求/响应过程的当前活动阶段;

      0:未初始化,尚未调用open()方法

      1:启动,已调用open()方法,但尚未调用send()方法

      2:发送,已调用send()方法,但尚未接收到响应

      3:接收,已接收到部分响应数据

      4:完成,已接受到全部响应数据,而且已经可以在客户端使用了

      只要 readyState 的值变化,都会触发一次 readystatechange 事件

      必须在调用open()之前指定readystatechange 处理程序(兼容不同浏览器)

    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function(){
      console.log(xhr)
    }
    xhr.open('get', 'api/test', true)
    xhr.send(null)
    
    • 取消

      在接收到响应之前还可以调用abort()方法来取消异步请求

      调用后xhr对象会停止触发事件,而且也不再允许访问任何与响应有关的对象属性

      终止请求之后应该对xhr对象进行解引用操作

    • HTTP 头部信息

      使用 setRequestHeader 方法可以设置自定义的请求头部信息;
      必须在调用 open() 方法之后且调用 send() 方法之前调用;
      有的浏览器允许重写默认头部信息,有的则不允许这么做。

      xhr.setRequestHeader('User-Agent', 'Frank');
      //chrome: index.js:11 Refused to set unsafe header "User-Agent"
      
      xhr.setRequestHeader('content-Type', 'application/x-www-form-urlencode')
      //success: content-Type:application/x-www-form-urlencode
      

      使用 getResponseHeader 方法可以取得相应的响应头部信息;调用 getAllResponseHeaders 方法可以取得一个包含所有头部信息的长字符串

      xhr.onreadystatechange = function(){
        if(xhr.readyState === 4) {
          console.log(xhr.getAllResponseHeaders())
        }
      }
      /*
        content-security-policy: default-src 'self'
        x-content-type-options: nosniff
        x-powered-by: Express
        vary: Accept-Encoding
        content-type: text/html; charset=utf-8
        date: Fri, 01 Jun 2018 07:43:50 GMT
        connection: keep-alive
        content-length: 147
      */
      

    Get

    xhr.open('get', 'api/test', false) 传入open方法的URL末尾的查询字符串必须经过正确的编码才行
    。查询字符串中每个参数名和值都必须使用 encodeURIComponent 进行编码,然后才能放到URL的末尾

    Post

    // 序列化发送表单数据
    // 如果不设置content-Type头部信息,在PHP服务器数据就不会出现在 $_POST ,必须借助$HTTP_RAW_POST_DATA
    xhr.setRequestHeader('content-Type', 'application/x-www-form-urlencode')
    xhr.send(serialize(form))
    

    XMLHttpRequest 2级

    • FormData

      FormData 为序列化表单以及创建与表单格式相同的数据提供了便利,体现在不必明确地在xhr对象上设置请求头部,xhr能够识别传入的数据类型是 FormData 的实例,并配置适当的头部信息。

      var data = new FormData();
      data.append('name', 'frank')
      xhr.send(data)
      
      //序列化表单,相对上述serialize方法
      xhr.send(new FormData(form))
      
    • 超时

    var xhr = new XMLHttpRequest()
    
    xhr.onreadystatechange = function(){
      try {
        //据说请求终止后访问xhr.status属性可能会导致错误。请求终止的时候readyState已经变为4
        console.log(xhr)
      }catch (e) {
        console.log(e)
      }
    }
    
    xhr.open('get', 'api/test', true);
    //将超时设置为3s
    xhr.timeout = 3000;
    xhr.ontimeout = function(){
      console.log('timeout: 3000ms')
    }
    xhr.send(null);
    
    • overrideMimeType() 方法
    xhr.open('get', 'api/test', true);
    //必须在send之前调用
    xhr.overrideMimeType('text/xml')
    xhr.send(null);
    
    • 进度事件

      • loadstart 接收到响应数据第一个字节时触发
      • progress 接收响应期间持续触发
      • error 请求发生错误时
      • abort 调用abort()终止请求时
      • load 接收到完整响应数据时
      • loadend 通信完成或error abort load事件后触发

      load

      最初为了简化交互模型而引入来替代readystatechange事件

      xhr.onload = function(){
        //只要接收到服务器响应,不管状态如何都会触发
        if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
          console.log(xhr.responseText)
        }
      }
      

      progress

      onprogress 事件处理程序会收到一个event对象,其target属性是XHR对象,但包含三个额外的属性:

      • lengthComputable 表示进度信息是否可用
      • position 已经接受的字节数 ??chrome中没有,只有loaded
      • totalSize 根据Content-Length响应头确定的预期字节数 ??chrome中没有,只有total
      xhr.onprogress = function(ev){
        console.log(ev)
      }
      

    CORS

    CORS(Cross-Origin Resource Sharing,跨域源资源共享):使用自定义的HTTP头部让浏览器与服务器进行沟通,访问跨域资源

    比如发送一个跨域请求,需要附加一个额外的Origin头部,其中包含请求页面的源信息(协议、域名、端口),以便服务器根据这个头部信息来决定是否给予响应

    IE引入 XDR(XDomainRequest)类型实现安全可靠的跨域通信,其他浏览器都通过XHR实现了对 CORS 的原生支持。当尝试打开跨域资源时,无需编写额外代码就可以触发这一行为。open时传入绝对URL即可

    Origin: http://www.example.com

    如果服务器接受这个请求,就在 Access-Control-Allow-Origin 头部中回发相同的源信息(如果是公共资源,可以回发 “*”)

    Access-Control-Allow-Origin: http://www.example.com

    如果没有这个头部,或者有这个头部但源信息不匹配,浏览器就会驳回请求

    跨域XHR对象有一些安全限制

    • 不能使用setRequestHeader设置自定义头部
    • 不能发送和接受cookie
    • 调用getAllResponseHeaders方法总会返回空字符串

    所以最好使用相对URL访问本地资源

    • Preflighted Requests

      Preflighted Requests: 透明服务器验证机制

      CORS 使用这种机制支持开发者使用自定义的头部、GET或POST之外的方法,以及不同类型的主体内容。
      在使用下列高级选项来发送请求时,就会向服务器发送一个Preflight请求,这种请求使用 OPTIONS 方法,发送下列头部

      • Origun
      • Access-Control-Request-Method
      • Access-Control-Request-Headers

      发送这个请求后,服务器可以决定是否允许这种类型的请求。通过在响应中发送如下头部与浏览器沟通

      • Access-Control-Allow-Origin
      • Access-Control-Allow-Methods
      • Access-Control-Allow-Headers
      • Access-Control-Max-Age: 将这个Preflight请求缓存多长时间(秒)
    • 带凭据的请求

      默认情况下跨域请求不提供凭据(cookie、HTTP认证、客户端SSL证明),通过将 withCredentials 属性设为 true,可以指定某个请求应该发送凭据。
      如果服务器接受带凭据的请求,会使用如下HTTP头部来响应

      Access-Control-Allow-Credentials: true

      如果response中没有这个头部,那么浏览器不会把响应交给JavaScript(于是responseText是空字符串,status值为0,触发onerror),

      服务器还可以在 Preflight 响应中发送这个头部,表示允许源发送带凭据的请求。

      IE10及更早版本不支持

    其他跨域技术

    在CROS出现之前,实现跨域ajax需要利用DOM中能够执行跨域请求的功能,在不依赖XHR对象的情况下发送请求。

    1. 图形Ping

      <img> .一个网页可以从任何网页中加载图像,不需要考虑跨域问题。

      图像Ping是与服务器进行简单、单向的跨域通信的一种方式。
      请求的数据是通过查询字符串形式发送的,而响应可以是任意内容,但通常是像素图或204响应

      浏览器得不到任何具体数据,但是通过监听 load 和 error事件,可以知道响应是什么时候接收到的

    1. JSONP

      JSON(JSON with padding):填充式JSON或参数式JSON。

      通过 script 元素,指定src为跨域URL,在请求完成后,响应内容(eg:callback({data:'Hello world'}))会立即执行

      <script src='example.net?callback=callback'></script>

      <script>callback(data){this.data = data}</script>

      JSONP确认请求是否失败并不容易,因为script 元素的 onerror 事件并没有被所有浏览器支持

    2. Comet

      Comet是Alex Russell发明的一个词,指一种服务器向页面推送数据的技术。实现Comet有两种方式:

      1. 长轮询

        页面发起一个到服务器的请求,然后服务器一直保持连接打开,直到有数据可发送。发送完数据后,浏览器关闭连接,随即继续发起一个新的请求,这一过程在页面打开期间一直持续不断。

      2. HTTP流不同于轮询,因为它在页面的整个生命周期内只使用一个HTTP连接:浏览器向服务器发送一个请求,而服务器保持连接打开,然后周期性地向浏览器发送数据。(所有服务端语言都支持打印输出到缓存然后刷新-将输入缓存中的内容一次性全部发送到客户端,这是实现HTTP流的关键所在)

        页面通过监听 readystatechange 事件以及readyState的值是否为3,就可以利用XHR对象实现HTTP流。
        因为responseText中保存的是所有数据,所以后续接受到的数据需要对比此前收到的数据,决定从什么位置取得新数据。

        var xhr = new XMLHttpRequest();
        var received = 0;
        xhr.open('get', url, true);
        xhr.onreadystatechange = function(){
          var result;
          if(xhr.readyState == 3){
            result = xhr.responseText.substring(received);
            received += result.length;
            //处理数据
          }else if(xhr.readyState == 4){
            //请求结束
          }
        }
        

        Comet连接的管理是很容易出错的,需要时间不短改进才能达到完美。社区认为Comet是未来Web的一个重要组成部分,为了简化这一技术,又为Comet创建了两个新的接口

        SSE(服务器发送事件)

        SSE(Server-Sent Events)简化了Comet的实现。SSE支持短轮询、长轮询和HTTP流,而且能在断开连接时自动确定何时重新连接。

        服务器通过这个连接可以发送任意数量的数据。服务器响应的MIME类型必须是 text/event-stream,而且是JavaScript API能解析的。

        1. SSE API

          var source = new EventSource(url); //创建一个新的EventSource对象,URL必须同源

          source.readyState // 0:正连接到服务器;1:打开了连接;2:关闭了连接

          source.open //在连接时触发

          source.message //从服务器接收到新事件时触发,服务器发回的数据保存在event.data中

          source.error //无法建立连接时触发

          source.close // 强制立即断开并不再重新连接。默认情况下,EventSource对象会保持与服务器的活动连接。如果连接断开,还会重新连接。

        2. 事件流

          服务器响应的MIME类型为 text/event-stream 。响应的格式是纯文本

          data:foo
          
          data:bar
          
          data:foo
          data:bar
          

          假设服务器返回上述响应,事件流中的message事件分别为

          1. event.data 为foo (只有包含data:的数据行后面有空行时才会触发message事件)
          2. event.data 为bar
          3. event.data 为foo\nbar (对于多个连续的以data:开头的数据行,将作为多段数据解析,每个值之间以一个换行符分隔)
          data:foo
          id:1
          

          通过id前缀可以给特定事件指定一个关联ID,这个ID行位于data:行前或行后皆可

          设置了ID后,EventSource对象会跟踪上一次触发的事件。如果连接断开了,会向服务器发送一个包含名为Last-Event-ID的特殊HTTP头部请求,以便服务器知道下一次该触发哪个事件。
          在多次连接的事件流中,这种机制可以确保浏览器以正确的顺序收到连接的数据段。

        3. Web Sockets

        Web Sockets的目标是在一个单独的持久连接上提供全双工、双向通信。在JavaScript中创建了Web Sockets之后,浏览器会发送一个HTTP请求以发起连接。在取得服务器响应之后,建立的连接的连接会使用HTTP升级,从HTTP协议交换为Web Socket协议。这也意味着使用标准的HTTP服务器无法实现web socket,只有支持这种协议法人专门服务器才能正常工作

        Web Sockets 使用了自定义的协议: ws:// wss://

        1. Web Sockets API

          创建连接

          var socket = new WebSocket('ws://example.com/test') //必须传入绝对URL,不限制同源

          实例化WebSocket对象后,浏览器会马上尝试创建连接。

          与XHR类似,WebSocket 也有一个表示当前状态的 readyState 属性,其值永远从0开始

          • WebSocket.OPENING(0): 正在建立连接
          • WebSocket.OPEN(1): 已建立连接
          • WebSocket.CLOSING(2): 正在关闭连接
          • WebSocket.CLOSE(3): 已关闭连接

          关闭连接

          socket.close();//调用后readyState的值立即变为2,关闭后变为3

          传送数据

          socket.send('Hello world');//只能发送纯文本数据

          socket.onmessage = function(event){this.data = event.data}//返回的同样是字符串

          相关事件

          • open: 成功建立连接时触发
          • error: 发生错误时触发
          • close: 连接关闭时触发

          WebSocket 对象不支持DOM2 级事件侦听,因此必须使用DOM 0 级语法分别定义每个事件的处理程序
          在这三个事件中,只有close事件的event对象有额外的信息

          socket.onclose = function(event){
            console.log(event.wasClean) //连接是否已经明确地关闭了
            console.log(event.code) //服务器返回的数值状态码
            console.log(event.reason) //服务器发回的字符串信息
          }
          

    相关文章

      网友评论

          本文标题:Ajax (cover JS高程)

          本文链接:https://www.haomeiwen.com/subject/fwdhsftx.html