《关于我设置了跨域资源共享后,依然跨域这件事》

起因

我们公司有一个部门需要做微前端,其中有一个 JS 链接主应用和子应用都需要,但是在部署的是否发现了子应用的 JS 出现了跨域的情况。
image.png
而且子应用单独启动时,是没有任何问题的。

既然是跨域问题,那赶紧去 OSS 看看有没有设置跨域。
image.png
发现已经设置了跨域呀。
image.png

此时细心的小伙伴已经发现了问题,并想到了解决方案

解密

造成这个问题的原因有三个:

  • 微前端加载子应用过程
  • 云厂商策略
  • 浏览器缓存策略

微前端子应用过程

image.png
细心的小伙伴会发现,主应用的 JS 的 typescript,但是子应用的 JS 的 type 却是 fetch类型,这就涉及到微前端加载子应用的过程。

微前端的入口是一个 html 地址,其为了实现 JS 沙箱和 CSS 隔离,其基本流程是:

  1. 先获取 HTML 文本内容
  2. 分析 HTML 文本内容,获取外链的 JS 和 CSS 链接数组
  3. 遍历数组,通过 fetch 获取每个 JS 和 CSS 文本
  4. 通过 eval 函数执行 JS 文本,并通过代理 window 对象,实现沙箱;CSS 通过增加前缀增加隔离

我们以 qiankun 为例,看一下其获取 JS 链接的过程:
image.png

源码:github.com/kuitos/impo…

所以子应用的 type才是 fetch 而不是正常的 script

但如果你是觉得只是因为 fetch 才跨域的,但实际单独 fetch是没有任何问题滴。
image.png
image.png

云厂商策略

我测试了两家国内知名云厂商,发现都是如下策略:对于非跨域请求,则不返回跨域头,对于跨域请求才返回跨域头。
image.png
image.png
当然其目的当然是好的,因为跨域的情况下,在未设置 Access-Control-Max-Age或者其值过期的情况下,就算文件未改变的情况下,还是会导致发起请求,询问是否跨域,增加了请求量。

但是到这里我们还是不明白为什么两个同时请求时,就会 GG。
image.png

浏览器缓存策略

image.png
我们仔细观察报错的请求,发现 fetch 请求只有 Response Headers并没有 Request Headers,怎么回事呢?

原因也很简单,就是因为浏览器没有发起请求,此请求被认为其和上一个 JS 是同一个资源,所以返回了上一个请求的 Response。

真相大白

image.png
通过上述三点分析我们已经彻底明白了:

  • 首先是微前端的主应用先通过 script 请求到 JS,(云厂商 Response 里没有跨域头)
  • 然后微前端框架通过 fetch 方式获取子应用的 JS
  • 然后浏览器发现这个资源加载过了,于是就返回了 script 的 Response,但是由于没有跨域头,所以浏览器就报了跨域错误。

解决方案

1、子应用区分开发和生产环境

由于是同一份 JS,其实子应用没有必要重新加载一遍的,我们可以通过类似 html-webpack-plugin区分开发环境和生产环境。

  • 当子应用本地开发时,将 JS 添加到 HTML 模板中;
  • 当生产构建环境时,不将其打进,如此就可以完美解决上述问题。

2、资源无缓存

我们知道了,本质上其由于浏览器缓存策略引起的,那我们就可以增加 cache-control: no-cache 的方式,不允许浏览器利用缓存,也可以完美解决这个问题。
image.png

坏处:会导致流量增加

3、主应用 script 增加 crossorigin 属性

由于fetch 请求复用了第一次的响应结果导致的报错,如果第一次就让他返回带跨域头的结果不就可以了嘛,所以我们可以通过 crossorigin 头,让 script 返回跨域头信息。
image.png

4、增加 vary: origin

image.png
我们看到阿里云跨域设置中,有一个选择框就是让你选择是否增加 vary: origin 这个 header 头。

Vary 是一个HTTP响应头部信息,它决定了对于未来的一个请求头,应该用一个缓存的回复(response)还是向源服务器请求一个新的回复。它被服务器用来表明在 content negotiation algorithm(内容协商算法)中选择一个资源代表的时候应该使用哪些头部信息(headers).

那为什么 Vary: origin可以区分 fetch 请求 和 script 呢?

原来 script 是没有 origin 这个请求头的,而 fetch 的方式因为跨域,所以浏览器会强制加上 Origin
image.png
image.png
因为 vary: origin响应头的原因,所以导致 fetch 并能复用 script方式请求的 response,所以也避免了跨域。

结束语

虽然问题很小,但是引出来的知识还是挺多的,你学到了吗?
20210720140344_ee130.jpg

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享