Ajax衍生的一系列问题

54 阅读4分钟

Ajax衍生的一系列问题

同源策略

MDN上的概念同源策略是一个重要的安全策略,它用于限制一个origin的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。

要求:

  • 协议相同
  • 域名相同
  • 端口相同

虽然这些限制是必要的,但是有时很不方便,合理的用途也受到影响。

Ajax请求

同源政策规定,**AJAX请求只能发给同源的网址 **,否则就报错。

接下来让我们通过一个简单的例子理解一下ajax吧

简单手写一个简易的Ajax

function ajax(url) {
  const p = new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()
    xhr.open('GET', url, true)
    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4) {
        if (xhr.status === 200) {
          // 请求成功
          resolve(JSON.parse(xhr.responseText))
        } else if (xhr.status === 404) {
          reject(new Error('404 not found'))
        }
        // ...
      }
    }
    xhr.send(null)
  })
  return p
}

const url = './user.json'
ajax(url)
  .then(res => console.log(res))
  .catch(err => console.log(err))

xhr.readyStatew五种状态

  • 0 未初始化:还没有调用send()方法
  • 1 载入:已调用send()方法,正在发送请求
  • 2 载入完成:send()方法执行完成,已经接收到全部响应内容
  • 3 交互:正在解析响应内容
  • 4 完成:响应内容解析完成,可以在客户端调用了

xhr.status状态码

状态码会在http模块详细说,这里就先大概写一个:

  • 2xx:表示成功处理请求,如200
  • 3xx:需要重定向,浏览器直接跳转,如301、302、304
  • 4xx:客户端请求错误,如404、403
  • 5xx:服务器端错误

那么为了解决跨域通信,我们就得了解几种前端的方法

跨域通信的几种方式

JSONP

在CORS和postMessage以前,我们一直都是通过JSONP来做跨域通信的。

JSONP的原理:通过<script>标签的异步加载来实现的。比如说,实际开发中,我们发现,head标签里,可以通过<script>标签的src,里面放url,加载很多在线的插件。这就是用到了JSONP。

JSONP的实现:

比如说,客户端这样写:

<script src="http://www.huabyte.com/?data=name&callback=jsonp"></script>

上面的src中,data=name是get请求的参数,jsonp是和后台约定好的函数名。 服务器端这样写:

jsonp({
  data: {}
})

于是,本地要求创建一个jsonp 的全局函数,才能将返回的数据执行出来。

WebSocket

Websocket的推荐链接:www.ruanyifeng.com/blog/2017/0…

CORS

CORS 可以理解成是既可以同步、也可以异步的Ajax。

跨域时,浏览器会拦截Ajax请求,并在http头中加Origin。

fetch 是一个比较新的API,用来实现CORS通信。用法如下:

// url(必选),options(可选)
fetch('/api/url/', {
    method: 'get',
}).then(function (response) {  //类似于 ES6中的promise

}).catch(function (err) {
  // 出错了,等价于 then 的第二个参数,但这样更好用更直观
});

CORS的推荐链接:www.ruanyifeng.com/blog/2016/0…

Hash

url的#后面的内容就叫Hash。Hash的改变,页面不会刷新。这就是用 Hash 做跨域通信的基本原理。

补充:url的?后面的内容叫Search。Search的改变,会导致页面刷新,因此不能做跨域通信。

使用举例:

场景:我的页面 A 通过iframe或frame嵌入了跨域的页面 B。

现在,我这个A页面想给B页面发消息,怎么操作呢?

(1)首先,在我的A页面中:

//伪代码
var B = document.getElementsByTagName('iframe');
B.src = B.src + '#' + 'jsonString';  //我们可以把JS 对象,通过 JSON.stringify()方法转成 json字符串,发给 B

(2)然后,在B页面中:

// B中的伪代码
window.onhashchange = function () {  //通过onhashchange方法监听,url中的 hash 是否发生变化
    var data = window.location.hash;
};

postMessage()方法

在之前实习时遇到了这个情况当时特地写了一篇记录:使用 iframe + postMessage实现跨域通信

H5中新增的postMessage()方法,可以用来做跨域通信。既然是H5中新增的,那就一定要提到。

场景:窗口 A (http:A.com)向跨域的窗口 B (http:B.com)发送信息。步骤如下。

(1)在A窗口中操作如下:向B窗口发送数据:

// 窗口A(http:A.com)向跨域的窗口B(http:B.com)发送信息
Bwindow.postMessage('data', 'http://B.com'); //这里强调的是B窗口里的window对象

(2)在B窗口中操作如下:

// 在窗口B中监听 message 事件
Awindow.addEventListener('message', function (event) {   //这里强调的是A窗口里的window对象
    console.log(event.origin);  //获取 :url。这里指:http://A.com
    console.log(event.source);  //获取:A window对象
    console.log(event.data);    //获取传过来的数据
}, false);