Ajax (cover JS高程)

2018-06-03  本文已影响55人  zhaochengqi

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)
}
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
  console.log(xhr)
}
xhr.open('get', 'api/test', true)
xhr.send(null)

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级

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);
xhr.open('get', 'api/test', true);
//必须在send之前调用
xhr.overrideMimeType('text/xml')
xhr.send(null);

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访问本地资源

其他跨域技术

在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) //服务器发回的字符串信息
        }
        
上一篇 下一篇

猜你喜欢

热点阅读