前端初学者们都应该知道得前端通信方式

一,什么是同源策略及其限制

  1. 概念:同源策略限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键安全机制。

  2. 什么是源:协议、域名与端口。这三者任何一个不一样的话,就算是跨域。

  3. 什么是限制:不是一个源的文档,没有权限去操作另一个源的文档。

Cookie、LocalStorage 和 IndexDB无法读取。

Dom无法获得

Ajax请求不能发送

常见跨域场景

URL                                      说明                    是否允许通信
http://www.domain.com/a.js
http://www.domain.com/b.js         同一域名,不同文件或路径           允许
http://www.domain.com/lab/c.js

http://www.domain.com:8000/a.js
http://www.domain.com/b.js         同一域名,不同端口                不允许
 
http://www.domain.com/a.js
https://www.domain.com/b.js        同一域名,不同协议                不允许
 
http://www.domain.com/a.js
http://192.168.4.12/b.js           域名和域名对应相同ip              不允许
 
http://www.domain.com/a.js
http://x.domain.com/b.js           主域相同,子域不同                不允许
http://domain.com/c.js
 
http://www.domain1.com/a.js
http://www.domain2.com/b.js        不同域名                         不允许

二,前端端如何通信

  1. Ajax
  1. WebSocket(不受同源策略限制)
  1. CORS

三,如何创建ajax

  1. XMLHttpRequest对象的工作流程
  1. 兼容性处理:IE下面的兼容性处理
  1. 事件的触发条件:事件的触发动作
  1. 事件的触发顺序:每个事件的触发顺序

下面是个简单的实现ajax的例子

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ajax</title>
</head>
<body>
    <div id="myDiv"></div>
    <script type="text/javascript">
        function ajax(url,method){
            var xmlhttp;  
            if (window.XMLHttpRequest){// 兼容 IE7+, Firefox, Chrome, Opera, Safari  
                xmlhttp=new XMLHttpRequest();  
            }  
            else{// 兼容 IE6, IE5  
                xmlhttp=newActiveXObject("Microsoft.XMLHTTP");  
            } 

            xmlhttp.open(method,url,true);  
            xmlhttp.send();


            xmlhttp.onreadystatechange=function(){  
                if(xmlhttp.readyState==4 &&xmlhttp.status==200){  
                    document.getElementById("myDiv").innerHTML=xmlhttp.responseText;  
                }  
            } 
        }

        ajax(" http://www.easy-mock.com/mock/59ba1ac1e0dc663341a981b5/demo/list",'get');
    </script>
</body>
</html>

注意一:xmlhttp.readyState:一共有5中请求状态,从0 到 4 发生变化。**

0: 请求未初始化

1: 服务器连接已建立

2: 请求已接收

3: 请求处理中

4: 请求已完成,且响应已就绪

注意二:xmlhttp.status响应状态码。这个也是面试比较爱问的,这个必须知道4个以上,比较常见的有:

200 "OK"

403 (禁止) 服务器拒绝请求。

404 (未找到) 服务器找不到请求的网页。

408 (请求超时) 服务器等候请求时发生超时。

500 (服务器内部错误) 服务器遇到错误,无法完成请求。

注意三:xmlhttp.open方法open 的参数要牢记,很多面试官爱问这样的细节

method:请求的类型;GET 或 POST

url:文件在服务器上的位置

async:true(异步)或 false(同步)

注意四:post请求一定要设置请求头的格式内容

xmlhttp.open("POST","ajax_test.html",true);  
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");  
xmlhttp.send("fname=Henry&lname=Ford"); 

注意五:服务器响应处理

responseText 获得字符串形式的响应数据。

responseXML 获得XML 形式的响应数据。

知识介绍 : Ajax详细介绍

四,跨域通信的几种方式

  • JSONP

通常为了减轻web服务器的负载,我们把js、css,img等静态资源分离到另一台独立域名的服务器上,在html页面中再通过相应的标签从不同域名下加载静态资源,而被浏览器允许,基于此原理,我们可以通过动态创建script,再请求一个带参网址实现跨域通信。

js原生实现方式

<script>
var script = document.createElement('script');
script.type = 'text/javascript';

script.src = 'https://api.asilu.com/geo/&callback=jsonp';//这个是获取当前经纬度的接口
document.head.appendChild(script);//创建并添加script标签到<head>下

// 回调执行函数
function jsonp(res) {
    console.log(res);//打印jsonp返回的信息
}
</script>

jq的Ajax实现方式

<script type="text/javascript">
    $.ajax({
        url: 'https://api.asilu.com/geo/',
        type: 'get',
        dataType: 'jsonp',  // 请求方式为jsonp
        success:function(ret){
            console.log(ret);
        }
    });
</script>

优点:它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;它的兼容性更好,在更加古老的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持;并且在请求完毕后可以通过调用callback的方式回传结果。

缺点:它只支持GET请求;安全问题(请求代码中可能存在安全隐患);

  • Hash

hash改变,但是其的域名不改变。可以利用iframe。例子 ==> a与b跨域相互通信,通过中间页c来实现。 三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。</font>

  • postMessage :

postMessage是HTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,它可用于解决以下方面的问题

  1. 页面和其打开的新窗口的数据传递

  2. 多窗口之间消息传递

  3. 页面与嵌套的iframe消息传递

  4. 上面三个场景的跨域数据传递

在这里你需要通过集成管理工具配置两个不同的域,fjw.me下的b页面,demo.me下面的a页面。(如下图)

image
//A.HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>b.html</title>
</head>
<body>
    <h1>b.html</h1>
    <iframe id="iframe" src="http://fjw.me/"></iframe>

    <script type="text/javascript">
        var iframe = document.getElementById('iframe');

        iframe.onload = function() {
            var data = {
                name: 'aym'
            };
            // 向fjw.me传送跨域数据,通过postMessage发送数据
            iframe.contentWindow.postMessage(JSON.stringify(data), 'http://fjw.me/index.html');
        };

        // 接受fjw.me返回数据
        window.addEventListener('message', function(e) {
            alert('data from domain2 ---> ' + e.data);
        }, false);
    </script>   
</body>
</html>
//B.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>
    <div>123456</div>
    <script type="text/javascript">
        // 接收demo.me的数据
        window.addEventListener('message', function(e) {
            alert('data from domain1 ---> ' + e.data);

            var data = JSON.parse(e.data);

            if (data) {
                data.number = 16;
                // 处理后再发回demo.me
                window.parent.postMessage(JSON.stringify(data), 'http://demo.me/b.html');
            }
        }, false);
    </script>
</body>
</html>

结果如图:

image
image
  • WebSocket:

WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现。

原生WebSocket API使用起来不太方便,我们使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。

例子如下,本例子是参考官方的一个简易聊天室,地址是http://socket.io/get-started/chat/,目录结构如下图所示。

image
//服务器的后台node.js
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var port = process.env.PORT || 3000;

app.get('/', function(req, res){
  res.sendFile(__dirname + '/index.html');
});

io.on('connection', function(socket){
  socket.on('chat message', function(msg){
    io.emit('chat message', msg);
  }); //监听socket是否连接,如果连接的话,就发送数据,chat-message
  socket.on('disconnect', function(){
    console.log('user disconnected');
  }); //监听socket是否断开,断开时执行相应的方法
});

http.listen(port, function(){
  console.log('listening on *:' + port);
});
//前端客户端
<!doctype html>
<html>
  <head>
    <title>Socket.IO chat</title>
    <style>
      * { margin: 0; padding: 0; box-sizing: border-box; }
      body { font: 13px Helvetica, Arial; }
      form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
      form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
      form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
      #messages { list-style-type: none; margin: 0; padding: 0; }
      #messages li { padding: 5px 10px; }
      #messages li:nth-child(odd) { background: #eee; }
      #messages { margin-bottom: 40px }
    </style>
  </head>
  <body>
    <ul id="messages"></ul>
    <form action="">
      <input id="m" autocomplete="off" /><button>Send</button>
    </form>
    <script src="https://cdn.socket.io/socket.io-1.2.0.js"></script>
    <script src="https://code.jquery.com/jquery-1.11.1.js"></script>
    <script>
      $(function () {
        var socket = io();//建立一个socket
        $('form').submit(function(){
          socket.emit('chat message', $('#m').val());//发送socket到服务器。
          $('#m').val('');
          return false;
        });
        socket.on('chat message', function(msg){
          $('#messages').append($('<li>').text(msg));
          window.scrollTo(0, document.body.scrollHeight);
          //接受服务器传回来的数据,新建一个li标签
        });
      });
    </script>
  </body>
</html>

具体效果如下图所示,可以在两个浏览器之间通信。

image
  • 5,CORS:

普通跨域请求:只服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求:前后端都需要设置。参考阮一峰老师的这一篇CORS的文章

文章的地址:跨域资源共享 CORS 详解

5.1, CORS的通信过程

整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

5,2, CORS的浏览器限制

目前,所有浏览器都支持该功能,IE浏览器不能低于IE10

5.3, 具体流程

第一步:浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。

代码如下:

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

上面的头信息中,Origin字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。

如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。如下所示:

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

意思分别是:

5.3.1,Access-Control-Allow-Origin

该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。

5.3.2,Access-Control-Allow-Credentials

该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。

5.3.3,Access-Control-Expose-Headers

该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader('FooBar')可以返回FooBar字段的值。

还有我们说到上面说到,CORS请求默认不发送CookieHTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials字段。

Access-Control-Allow-Credentials: true

前端也要设置:

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;//这个要开起来

5.4, 优点

CORS与JSONP相比,无疑更为先进、方便和可靠。

  1. JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。
  1. 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。
  1. JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS)。

文章参考

  1. 前端常见跨域解决方案(全)
  1. Ajax 是什么? 如何创建一个Ajax?
  1. Ajax 知识体系大梳理
  1. 跨域资源共享 CORS 详解
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343