前端基础知识:Promise|跨域(jsonp、cookie)

2021-02-0209:53:23WEB前端开发Comments1,912 views字数 8118阅读模式

Promise 必须为以下三种状态之一:等待态(Pending)、执行态(Fulfilled)和拒绝态(Rejected)。一旦Promise 被 resolve 或 reject,不能再迁移至其他任何状态(即状态 immutable)。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

基本过程:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

  1. 初始化 Promise 状态(pending)
  2. 执行 then(..) 注册回调处理数组(then 方法可被同一个 promise 调用多次)
  3. 立即执行 Promise 中传入的 fn 函数,将Promise 内部 resolve、reject 函数作为参数传递给 fn ,按事件机制时机处理
  4. Promise中要保证,then方法传入的参数 onFulfilled 和 onRejected,必须在then方法被调用的那一轮事件循环之后的新执行栈中执行。

真正的链式Promise是指在当前promise达到fulfilled状态后,即开始进行下一个promise.文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

跨域


不同地址,不同端口,不同级别,不同协议都会构成跨域。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

跨域方案

window.postMessage文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

window.postMessage是html5的功能,是客户端和客户端直接的数据传递,既可以跨域传递,也可以同域传递。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

栗子:假如有一个页面,页面中拿到部分用户信息,点击进入另外一个页面,另外的页面默认是取不到用户信息的,你可以通过window.postMessage把部分用户信息传到这个页面中。(需要考虑安全性等方面。)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

发送信息文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

//弹出一个新窗口
var domain = 'http://haorooms.com';
var myPopup = window.open(`${domain}/windowPostMessageListener.html`,'myWindow');

//周期性的发送消息
setTimeout(function(){
    var message = {name:"站点",sex:"男"}; //你在这里也可以传递一些数据,obj等
    console.log('传递的数据是  ' + message);
    myPopup.postMessage(message, domain);
},1000);

要延迟一下,我们一般用计时器setTimeout延迟再发用。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

接受的页面文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

//监听消息反馈
window.addEventListener('message',function(event) {
    if(event.origin !== 'http://haorooms.com') return;
    //这个判断一下是不是我这个域名跳转过来的
    console.log('received response: ', event.data);
}, false);

如下图,接受页面得到数据文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

前端基础知识:Promise|跨域(jsonp、cookie)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

如果是使用iframe,代码应该这样写:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

//捕获iframe
var domain = 'http://haorooms.com';
var iframe = document.getElementById('myIFrame').contentWindow;

//发送消息
setTimeout(function(){ 
    var message = {name:"站点",sex:"男"};
    console.log('传递的数据是:  ' + message);
    iframe.postMessage(message, domain); 
},1000);

接受数据文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

//响应事件
window.addEventListener('message',function(event) {
    if(event.origin !== 'http://haorooms.com') return;
    console.log('message received:  ' + event.data,event);
    event.source.postMessage('holla back youngin!',event.origin);
},false);

上面的代码片段是往消息源反馈信息,确认消息已经收到。下面是几个比较重要的事件属性:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

**source – 消息源,消息的发送窗口/iframe。
origin – 消息源的URI(可能包含协议、域名和端口),用来验证数据源。
data – 发送方发送给接收方的数据。**文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

CORS跨域资源共享文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

CORS跨域和jsonp跨域的优势:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

CORS与JSONP相比,无疑更为先进、方便和可靠。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

1、 JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

2、 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

3、 JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

CORS的使用文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

CORS要前后端同时做配置。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

1、首先我们来看前端。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

纯js的ajax请求。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

<script type="text/javascript">
    var xhr = new XMLHttpRequest(); 
    // ie6以下用new ActiveXObject("Microsoft.XMLHTTP");可以做能力判断。
    xhr.open("GET", "/haorooms", true);
    xhr.send();
</script>

以上的haorooms是相对路径,如果我们要使用CORS,相关Ajax代码可能如下所示:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

<script type="text/javascript">
    var xhr = new XMLHttpRequest();
    //ie6以下用new ActiveXObject("Microsoft.XMLHTTP");可以做能力判断。
    xhr.open("GET", "http://www.haorooms.com/CORS", true);
    xhr.send();
</script>

当然,你也可以用jquery的ajax进行。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

2、后端或者服务器端的配置文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

下面我们主要介绍Apache和PHP里的设置方法。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

Apache:Apache需要使用mod_headers模块来激活HTTP头的设置,它默认是激活的。你只需要在Apache配置文件的 < Directory >, < Location>, < Files >或< VirtualHost>的配置里加入以下内容即可:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

Header set Access-Control-Allow-Origin *

PHP:只需要使用如下的代码设置即可。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

<?php  
 header("Access-Control-Allow-Origin:*");  

以上的配置的含义是允许任何域发起的请求都可以获取当前服务器的数据。当然,这样有很大的危险性,恶意站点可能通过XSS攻击我们的服务器。所以我们应该尽量有针对性的对限制安全的来源,例如下面的设置使得只有www.haorooms.com这个域才能跨域访问服务器的API。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

Access-Control-Allow-Origin: http://www.haorooms.com

通过jsonp跨域文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

jsonp跨域也需要前后端配合使用。一般后端设置callback ,前端给后台接口中传一个callback 就可以。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

例如前端代码:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

<script type="text/javascript">
    function dosomething(jsondata){
        //处理获得的json数据
    }
</script>
<script src="http://haorooms.com/data.php?callback=dosomething"></script>

后台代码:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

<?php
  $callback = $_GET['callback'];//得到回调函数名
  $data = array('a','b','c');//要返回的数据
  echo $callback.'('.json_encode($data).')';//输出
?>

假如你用ajax方式进行jsonp跨域:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

$.ajax({  
    type : "get",  
    url : "跨域地址",  
    dataType : "jsonp",//数据类型为jsonp  
    jsonp: "callback",
    //服务端用于接收callback调用的function名的参数【后台接受什么参数,我们就传什么参数】
    success : function(data){  
        //结果处理
    },  
    error:function(data){  
          console.log(data);
    }  
});

通过修改document.domain来跨子域文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

我们只需要在跨域的两个页面中设置document.domain就可以了。修改document.domain的方法只适用于不同子域的框架间的交互。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

栗子:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

1.在页面 http:// www.haorooms.com/a.html 中设置document.domain文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

<iframe id = "iframe" src="http://haorooms.com/b.html" onload = "test()"></iframe>
<script type="text/javascript">
    document.domain = 'haorooms.com';//设置成主域
    function test(){
       alert(document.getElementById('iframe').contentWindow);
       //contentWindow 可取得子窗口的 window 对象
    }
</script>

2、在页面http:// haorooms.com/b.html 中设置document.domain文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

<script type="text/javascript">
    document.domain = 'haorooms.com';
    //在iframe载入这个页面也设置document.domain,使之与主页面的document.domain相同
</script>

使用window.name来进行跨域文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

原理:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

方法:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

假如有三个页面。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

a.com/app.html:应用页面。
a.com/proxy.html:代理文件,一般是一个没有任何内容的html文件,需要和应用页面在同一域下。
b.com/data.html:应用页面需要获取数据的页面,可称为数据页面。

1、在应用页面(a.com/app.html)中创建一个iframe,把其src指向数据页面(b.com/data.html)。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

数据页面会把数据附加到这个iframe的window.name上,data.html代码如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

<script type="text/javascript">
   window.name = 'I was there!';    
   // 这里是要传输的数据,大小一般为2M,IE和firefox下可以大至32M左右
   // 数据格式可以自定义,如json、字符串
</script>

2、在应用页面(a.com/app.html)中监听iframe的onload事件,在此事件中设置这个iframe的src指向本地域的代理文件(代理文件和应用页面在同一域下,所以可以相互通信)。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

app.html部分代码如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

<script type="text/javascript">
    var state = 0, 
    iframe = document.createElement('iframe'),
    loadfn = function() {
        if (state === 1) {
            var data = iframe.contentWindow.name; // 读取数据
            alert(data);
        } else if (state === 0) {
            state = 1;
            iframe.contentWindow.location = "http://a.com/proxy.html";    
            // 设置的代理文件
        }  
    };
    iframe.src = 'http://b.com/data.html';
    if (iframe.attachEvent) {
        iframe.attachEvent('onload', loadfn);
    } else {
        iframe.onload  = loadfn;
    }
    document.body.appendChild(iframe);
</script>

3、获取数据以后销毁这个iframe,释放内存;这也保证了安全(不被其他域frame js访问)。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

<script type="text/javascript">
    iframe.contentWindow.document.write('');
    iframe.contentWindow.close();
    document.body.removeChild(iframe);
</script>

webpack解决跨域问题文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

webpack也可以解决前端跨域问题,只需要安装webpack 的http-proxy-middleware模块就可以文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

npm install http-proxy-middleware --save-dev

配置如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

module.exports = {
  devtool: 'cheap-module-source-map',
  entry: './app/js/index.js'
  output: {
  path: path.resolve(__dirname, 'dev'),
    // 所有输出文件的目标路径
    filename: 'js/bundle.js',
    publicPath: '/',
    chunkFilename: '[name].chunk.js'
  },
  devServer: {
    contentBase: path.resolve(__dirname, 'dev'),
    publicPath: '/',
    historyApiFallback: true,
    proxy: {
      // 请求到 '/device' 下的请求都会被代理到target:http://debug.haorooms.com中
      '/device/*': {
        target: 'http://debug.haorooms.com',
        secure: false, // 接受运行在https上的服务
        changeOrigin: true
      }
    }
  }
}

使用如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

fetch('/device/space').then(res => {
  // 被代理到 http://debug.haorooms.com/device/space
  return res.json();
});

fetch('device/space').then(res => {
  // http://localhost:8080/device/space 访问本地服务
  return res.json();
});

// 注:使用的url 必须以/开始 否则不会代理到指定地址

cookie跨域文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

业务场景文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

1、百度www域名下面登录了,发现yun域名下面也自然而然登录了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

2、淘宝登录了,发现天猫也登录了,淘宝和天猫是完全不一样的2个域名。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

栗子文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

  1. 同一个主域下面的跨域问题,类似www.baidu 和yun.baidu

cookie属性:
path文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

cookie 一般都是由于用户访问页面而被创建的,但是并不是只有在创建 cookie 的页面才可以访问这个cookie。在默认情况下,出于安全方面的考虑,只有与创建 cookie 的页面处于同一个目录或在创建cookie页面的子目录下的网页才可以访问。那么此时如果希望其父级或者整个网页都能够使用cookie,就需要进行路径的设置。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

path表示cookie所在的目录,haorooms.com默认为/,就是根目录。 在同一个服务器上有目录如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

/post/,/post/id/,/tag/,/tag/haorooms//tag/id/

现假设一个文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

cookie1的path为/tag/,cookie2的path为/tag/id/,文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

那么tag下的所有页面都可以访问到cookie1,而/tag/和/tag/haorooms/的子页面不能访问cookie2。 这是因为cookie2能让其path路径下的页面访问。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

让这个设置的cookie 能被其他目录或者父级的目录访问的方法:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

document.cookie = "name = value; path=/";

domain文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

domain表示的是cookie所在的域,默认为请求的地址,文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

如网址为 http://www.haorooms.com/post/... ,那么domain默认为www.haorooms.com。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

而跨域访问,文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

如域A为love.haorooms.com,域B为resource.haorooms.com,文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

那么在域A生产一个令域A和域B都能访问的cookie就要将该cookie的domain设置为.haorooms.com;文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

如果要在域A生产一个令域A不能访问而域B能访问的cookie就要将该cookie的domain设置为resource.haorooms.com。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

这样,我们就知道为什么www.百度 和yun.baidu共享cookie,我们只需要设置domain为.baidu.com就可以了【注:点好是必须的】文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

  1. 天猫和淘宝是如何共享cookie的?

cookie跨域解决方案一般有如下几种:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

1、nginx反向代理

反向代理(Reverse Proxy)方式是指以代理服务器来接受Internet上的连接请求,然后将请求转发给内部网络上的服务器;并将从服务器上得到的结果返回给Internet上请求连接的客户端,此时代理服务器对外就表现为一个服务器。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

反向代理服务器对于客户端而言它就像是原始服务器,并且客户端不需要进行任何特别的设置。客户端向反向代理 的命名空间(name-space)中的内容发送普通请求,接着反向代理将判断向何处(原始服务器)转交请求,并将获得的内容返回给客户端,就像这些内容 原本就是它自己的一样。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

nginx配置如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

upstream web1{
     server  127.0.0.1:8089  max_fails=0 weight=1;
}
upstream web2 {
     server 127.0.0.1:8080    max_fails=0 weight=1;
}

    location /web1 {
        proxy_pass http://web1;
        proxy_set_header Host  127.0.0.1;
        proxy_set_header   X-Real-IP        $remote_addr;
        proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;

        proxy_set_header Cookie $http_cookie;
        log_subrequest on;
    }

    location /web2 {
        proxy_pass http://web2;
        proxy_set_header Host  127.0.0.1;
        proxy_set_header   X-Real-IP        $remote_addr;
        proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
        proxy_set_header Cookie $http_cookie;
        log_subrequest on;
    }

3、nodejs superagent

package.json中的模块依赖:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

前端基础知识:Promise|跨域(jsonp、cookie)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

调用superagent api请求:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

前端基础知识:Promise|跨域(jsonp、cookie)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

其实本质也是jsonp的方式。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

同一域下,不同工程下的cookie携带问题

cookie跨域访问之后,可以成功的写入本地域。本地的前端工程在请求后端工程时,有很多是ajax请求,ajax默认不支持携带cookie,所以现在有以下两种方案:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

(1). 使用jsonp格式发送
(2). ajax请求中加上字段 xhrFields: {withCredentials: true},这样可以携带上cookie

前端基础知识:Promise|跨域(jsonp、cookie)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

服务器需要配置:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

Access-Control-Allow-Credentials: true

localStorage跨域文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html

文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/20920.html
  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/gcs/20920.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定