美文网首页
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高程)

    story 2005年,Jesse James Garrett 在一篇文章中介绍了一种他称为Ajax(Asynch...

  • 《JS高程》—— ajax

    Ajax (Asynchronous Javascript + XML) 05年由Jesse James Garr...

  • JS高程:读书摘要(十七)JSON & Ajax

    一、JSON JSON是一种数据格式,与XML相比,JSON 是在JavaScript中读写结构化数据的更好的方式...

  • JS重难点梳理

    重读JS高程,系统梳理下JS重难点JS重难点梳理之事件

  • JS中的Ajax

    JS中的Ajax Ajax简介 Ajax为 Asynchronous Javascript And XML 的缩写...

  • 封装ajax

    利用npm安装axios 在src目录下创建ajax.jsajax.js 将ajax.js 文件引入main.js...

  • ajax的封装-ajax.js

    ajax.js:

  • JS高程(一)

    复习 JavaScript 高程, 基本上 是 摘抄原文。 在 HTML 中 使用 JavaScript JS 包...

  • js高程学习

    定义函数的方式:函数声明函数表达式(匿名函数) 函数声明提升函数声明总是在执行代码之前 函数表达式定义:先赋值再用

  • JS高程(二)

    面向对象 的 程序设计 ECMAScript 中 有 两种 属性:数据属性和访问器属性。 数据属性 数...

网友评论

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

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