原生ajax & 跨域问题

前言

其实一直对原生ajax和跨域的问题似懂非懂,理解的程度也不是很全面,所以打算整理一下知识点,现在一般交互使用现成的插件,也就是调用封装好的api实现交互,出现的问题对应也有解决的方式,,解决跨域问题也是只知道没有出现跨域问题,如何解决的了解的也不是很透彻,虽然现在可能有些方式用的不是很多了,但是了解清楚还是很重要的

原生ajax讲解 

ajax即"Asynchronous Javascript And XML"(异步的JavaScript和XML)
ajax的核心是XMLHTTPRequest对象(XHR)

先创建一个xhr对象

function createXHR(){
  let xhr
  if(window.XMLHttpRequest){
    xhr = new XMLHttpRequest()
  }else if(window.ActiveXObject){
    xhr = new ActiveXObject("Microsoft.XMLHTTP")
  }
  return xhr
}

XMLHttpRequest 1级

xhr对象的主要方法

void open(String method,String url,Boolean async)
method:请求方式
url:请求地址
async:是否异步,默认为true
username:可选参数,如果服务器需要验证,该参数指定用户名,如果未指定,当服务器需要验证时,会弹出验证窗口。
password:可选参数,验证信息中的密码部分,如果用户名为空则改制将被忽略。
 
void send(String body)
body:要发送的数据
 
void setRequestHeader(String header,String value)
header:请求头的key
value:请求头的value
 
String getAllResponseHeaders()
获得所有响应头,返回值:响应头数据
 
String getResponseHeader(String header)
获取响应头中指定header的值
 
void abort()
终止请求

xhr对象的主要属性

Number readyState 状态值,可以确定请求/响应过程的当前活动阶段
0:未初始化。未调用send()方法。
1:启动。已经调用open()方法,但尚未调用send()方法。
2:发送。已经调用send()方法,但尚未接收到响应。
3:接收。已经接收到部分响应数据。
4:完成。已经接收到全部的响应数据,而且可以在客户端使用了。
 
Function onreadystatechange 当readyState的值改变时自动触发执行其相应的(回调)函数。
String responseText 作为相应主体被返回的文本
XMLDocument responseXML 服务器返回的数据
Number status 状态码(200/304)
String statusText 状态文本(OK)

只要readyState属相的值由一个值变为另一个值,都会触发一次readystatechange事件。也就是说可以用xhr对象监听readystatechange事件,从而在其回调函数中进行逻辑操作。

xhr.onreadystatechange = function(){
  if(xhr.readyState == 4){
    if(xhr.status >= 200 && xhr.status <= 300 || xhr.status == 304){
      alert(xhr.responseText)
    }else{
      alert(xhr.status)
    }
  }
}

XMLHttpRequest 2级

FormData为序列化表单以及创建与表单格式相同的数据(用于通过xhr传输)提供了遍历。

let formdata = new FormData()
formdata.get(key)
formdata.getAll(key)
formdata.append(key,value)
formdata.set(key,value)
formdata.has(key)
formdata.delete(key)
formdata.entries()
formdata.keys()
formdata.values()

超时设定:xhr对象添加了timeout属性,表示请求在等待多少毫秒之后就终止。还可以监听timeout事件。(前提是请求超时终止)

xhr.timeout = 1000
xhr.ontimeout = function (){
 
}

overrideMimeType()方法,用于重写xhr响应的MIME类型。比如:服务器返回的MIME类型时text/plain,但是数据中实际包含的是XML。根据MIME类型,即使数据时XML,responseXML属性中仍然是null。通过此方法可以保证响应当做XML而非纯文本处理。

 进度事件

loadstart:在接收到响应数据的第一个字节时触发。
progress:在接收响应期间持续不间断触发。
error:在请求发生错误时触发。
abort:在因为调用abort()方法而终止连接时触发。
load:在接收到完整的响应数据时触发。
loadend:在通信完成或者触发error、abort、或load事件后触发。

实现一个进度指示器

xhr添加了progress事件,这个事件会在浏览器接收新数据期间周期性触发。而onprogress事件处理程序会接收到一个event对象,其target属性是xhr对象,但包含着三个额外属性:lengthComputable、position和totalSize。其中lengthComputable是一个表示进度信息是否可用的布尔值,position表示已经接收的字节数,totalSize表示根据Content-Length响应头部确定的预期字节数。

跨域问题

同源策略:同源策略/SOP(Same Origin Policy)是一种约定,它是浏览器最核心最基础的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSRF等攻击。

跨域:当协议、域名、端口号有一个或多个不同时,又希望可以访问并获取数据的现象称为跨域访问。

误区:同源策略限制下,访问不到后台服务器的数据,或访问到后台服务器的数据没有返回
正确:同源策略限制下,可以访问到后台服务器的数据,后台服务器会正常返回数据,只是被浏览器拦截了。

实现跨域的方式:写几种常见的方式

jsonp

只能发送get请求,不支持post、put、delete
不安全,很容易受到XSS攻击
function cbFn(data){
  alert(data.name + data.age)
}

let script = document.createElement('script')
script.src = 'http://www.zjy.com?callback=cbFn'
document.body.appendChild(script)

//请求'http://www.zjy.com?callback=cbFn,后端返回的应该是这样的数据格式"cbFn({name:'zhangsan',age:18})"

使用CORS跨域:个人理解就是后端解决,也不用前端操心

跨源资源共享/CORS(Cross-Origin Resource Sharing)
基本思想:使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应应该成功还是失败。
设置哪个源可以访问,也可以设置 固定源,参数为 * 时,允许任何人访问,但是不可以和 cookie 凭证的响应头共同使用
"Access-Control-Allow-Origin": *
 
想要获取 ajax 的头信息,需设置响应头
"Access-Control-Allow-Headers":"key"
 
处理复杂请求的头
"Access-Control-Allow-Methods": "PUT"

允许发送 cookie 凭证的响应头
"Access-Control-Allow-Credentials": true

允许前端获取哪个头信息
"Access-Control-Expose-Headers": "key"

处理 OPTIONS 预检的存活时间,单位 s
"Access-Control-Max-Age": 10

使用postMessage实现跨域

postMessage是H5的新的API,跨文档消息传送(cross-document messaging),简称为XMD。指的是来自不同域的页面见传递消息。
 
调用方式
window.postMessage(message,targetOrigin)
message:发送的数据
targetOrigin:发送的窗口的域
在对应的页面中用message事件接收,事件对象中有data、origin、source三个重要信息。
data:接收到的数据
origin:接收到数据源的域(数据来自哪个域)
source:接收到数据源的窗口对象(数据来自哪个窗口对象)

使用WebSocket实现跨域:WebSocket没有跨域限制

怎么使用去看WebSocket就可以了不过多说明了
或是我上面写的实现WebSocket心跳,基本上也用到几乎所有WebSocket中的API。
WebSocket的API比较简单,用法也相对简单

vue框架开发出现的跨域问题:这种方式应该是目前开发应用到最多的地方
可以在vue-cli脚手架config/index.js里面配置proxyTable,使用proxy代理。
其实这个配置项可以好好研究一下,一般开发的时候也不会注意到,里面还有哪些配置项,具体查一下用法就可以了

proxyTable:{
    '/api':{ //根据这个字段去匹配
         target:"http://www.zjy.com", //替换成的地址
         changeOrigin:true, //是否跨域
         pathRewrite:{ //重写路径
            "/api":""
         }
    }
}    

还有window.name、location.hash与iframe结合解决跨域问题
原文地址:https://www.cnblogs.com/zhenjianyu/p/12965498.html