专栏首页腾讯NEXT学位学员投稿 | iframe 解决跨域

学员投稿 | iframe 解决跨域

天气降温,大家注意保暖~~~

因为学习了腾讯课堂NEXT学院的前端进阶课程,所以打算把课程内容都总结一遍。有些都是很普通很常见的知识,但是为了巩固自己的知识面,梳理自己的知识树,所以每个知识点都会写成文章,所有文章都会放在公众号右下角的前端进阶课程总结中~~~也希望能够帮助到需要的人~~

跨域的东西, 简直不要接触太多,网上相关内容一抓一大把,但是突然学习到一个关于前端解决跨域的方式

就是 利用 iframe 

不管你有没有了解过,反正我没有

我觉得很有用并且容易忘,所以我记录下来哈哈哈

下面会分三块内容进行描述

1、基本原理

2、简单模拟

2、封装的函数

3、封装函数实战

解决场景

现在我们在 a.com 的域名下有一个页面

我们要请求 b.com 下的一个接口,很明显是会跨域的,无法直接请求

今天我们使用 iframe 来解决这个问题

基本原理

1、需要三个页面

两个同域(a.com)的页面,一个和接口同域的页面(b.com)

其中一个页面是父页面,也就是真正的内容页,展示数据的

另外两个作为子页面,是辅助父页面请求跨域数据用的,不会显示在页面中

2、利用全局变量 window.name 存储数据

父子页面利用 window.name 进行通信,但是前提是同域

当父子页面不同域的时候,父子无法访问对方 window.name 

但是,如果是同级页面切换,就算是不同域,window.name 也是可以访问的

3、数据请求

现在请求b.com的接口,所以只能在 b.com下的页面(这个是子页面)进行请求

请求成功后,保存在当前的window.name 中

4、页面跳转

上一步保存完数据之后,由 b.com 页面 跳到 a.com 页面

此时 a.com 页面就能通过 window.name 拿到 b.com 保存过的数据啦

5、给父页面传递数据

上面那个 a.com 页面是辅助页,拿到数据之后,需要传递给真正需要数据的父页面(同样是 a.com 下)

大概了解之后,我们来说一下简单流程

现在有 a.com 下的内容页 A,需要请求接口 b.com/xxxx,但是跨域

1、内容页 A 嵌入一个隐藏 iframe,iframe 加载 b.com 下的辅助页面 B

2、辅助页面B 开始请求接口 b.com/xxx,请求成功,存放到 window.name

3、隐藏 iframe 由页面B跳转到 a.com 下另一个辅助页(页面C)

4、页面C读取到B存放的 window.name,然后传递给父页面A

5、父页面A 拿到数据,用于展示在页面上

简单模拟

现在我启动了两个服务

1、localhost:3001 下有  a.html 和 c.html

a.html 是内容页,需要使用数据的终端页(以下简称A)

c.html 是辅助页(以下简称C)

2、localhost:3002 下有 b.html

b.html 也是辅助页,用于请求数据(以下简称B)

内容页 A

在 A 中,使用 iframe 嵌入了B,并且全局设置了一个函数 getData

这个函数的作用是,为了给C页面调用,传入接口的数据的

<body>
    我是A页面
    <script>        window.getData=function(data){                        console.log("获取到数据",data)        }        </script>    <iframe src="http://localhost:3002/b.html" ></iframe></body>

辅助页 B 

B 页面当然是用于请求接口了,这里使用定时器模拟接口,请求成功后跳转到 C

<body>

    我是B页面
    <script>
        console.log("B页面开始请求接口")        setTimeout(function(){                        window.name="我是B页面保存的数据"            location.href="http://localhost:3001/c.html"        },2000)        </script></body>

辅助页 C 

B请求完,跳到C之后,C拿到 window.name,然后调用 A 的方法 getData,并且把数据传过去

<body>
    我是C页面
    <script>// 调用页面A 的方法,并把 name 传过去        parent.getData(window.name)        </script></body>

没错,这就完成了 iframe 解决跨域的问题,但是实际上并不会这么做,肯定是封装得更加适用一些

详情就看下面吧

封装函数

经过上面的说明,我们首先要明确我们的目的

1、iframe

2、两个辅助页

3、数据回调

所以我们封装的函数必须要满足这几个东西

首先,封装一个函数创建 iframe 插入 body 中,并且转到传入的 url

function createIframe (url) {  
  var doc = document  var iframe = doc.createElement('iframe')
  iframe.src = url
  iframe.frameborder = '0'
  iframe.style.display = 'none'

  doc.body.appendChild(iframe)
}

很简单,都能看懂

之后,我们需要在 url 上拼接参数,所以我们需要在封装一个 url 相关的函数

为什么需要拼接参数?

比如请求的接口需要某些参数,这些参数是父页面提供的,所以就只能把这些参数放到 iframe 的 url 上以便通信

拼接 url 函数

function parseUrl (url, param) {      return url + (url.indexOf('?') === -1 ? '?' : '&') + serialize(param)}

当时放在 url 上的参数,是要经过转义的,比如不能出现中文,所以需要多一个函数用于格式化参数

function serialize (data) {      var ret = []      Object.keys(data).forEach(item => {        ret.push(encodeURIComponent(item) + '=' + encodeURIComponent(data[item]))    })      return ret.join('&')}

那么现在就万事俱备,只欠主菜了,马上来看

function cross (option, callback) {  
    var funcName = getFunctionName()      var data = option.data || {}  
    window[funcName] = function (response) {        callback(response)            window[funcName] = null    }  
    var url = parseUrl(option.targetUrl, {              data:option.data,                        url:option.url,                        parentFuc:funcName,                        skipUrl:option.skipUrl    })

    createIframe(url)

}

在这个函数中,解决了我们一开始提到的两个问题

1、数据回调

2、两个辅助页

很明显,这个函数是给我们的父页面调用的啦

首先,cross 函数接收两个参数,options 和 callback,一个个解释

options

是一个对象,包含下面四个字段

targetUrl

用于请求接口,跟接口同域那个辅助子页面

skipUrl

请求接口成功后,跳转到的那个子页面

data

请求接口时需要传递的参数

url

需要进行请求的接口名

callback

很明显,这个回调,也就是接口请求完成,跳转完成之后会触发的

但是并不是直接触发,而是包装了一层,你看其中这段

var funcName = getFunctionName()window[funcName] = function (response) {   callback(response)
}

主要作用还是像我们模拟中的一样,在父页面中注册一个函数,给子页面传递数据用

但是这个函数注册就有点意思了,因为是全局的,非常害怕全局污染和重名,所以这里弄了一个随机生成函数名的函数 getFunctionName,如下

function getFunctionName () {      return ('iframe_' + Math.random())           .replace('.', '')}

但是,函数名是随机的,子页面怎么知道我要调用那个函数?

子页面:我不知道函数名字啊!!!!

所以需要把函数名字也传递

怎么传递?

放到 url 上,然后子页面就可以直接从 url 上拿参数,并取其中的字段 parentFunc ,就可以知道函数名啦

优化

随着请求越来越多,生成的全局随机函数肯定会越来越多,并且是一次性的,不会再被使用,留着根本没用,还占用资源

所以我们最好手动清除他,所以你看到在设置全局函数时,回调执行完之后就执行设置为 null

window[funcName] = function (response) {    callback(response)      window[funcName] = null}

没错,封装的函数我们已经看完了,我们还是有必要来看下怎么使用的啊

封装函数的实践

在这里我先出一个大王函数,用于获取链接参数的,直接得到对象的

作用如下

然后就到我们的正文了

首先,我们的内容页率先登场,并且调用 cross 表示调用接口

<body>
    <script>

        cross({            
            // 请求数据的页面            targetUrl: 'http://127.0.0.1:3002/b.html',                        // 当区域跳转的页面            skipUrl: 'http://127.0.0.1:3001/c.html',                        // 需要请求的接口            url:"http://localhost:3002/getUserInfo"
            data: {} // 参数        }, data= >{                        console.log("接收到数据",data)        });    });        </script></body>

接着,到了我们要请求接口的B 页面

拿到 url 上的请求接口和请求参数,调用 ajax 

调用成功后,拿到 url 上的跳转链接 skipUrl,直接跳过去,并且带上参数 parsetFunc

因为C 页面中,需要知道函数名,所以这里必须带上

<body>
    <script>

        var param = parseQueryString(location.href)        ajax({                        url:${param.url},                        data:${param.data},            success(data){                                // 数据保存在 window.name                window.name = data                                // 跳转到父页面配置的子页面,并且把配置的函数名带上                location.href = `${param.skipUrl}?parentFunc = ${param.parentFunc}`;
            }        })        </script></body>

那么现在就只剩下我们的C页面了

从url 上获取到函数名,然后拿到父页面的 window(也就是 parent)

直接调用,并且传入 window.name

这样,整个流程就走完了

<body>
    <script>

        var param = parseQueryString(location.href)        parent[param.parentFuc](window.name);        </script></body>

不过说真的,这种方法也就作为一个备胎方法,因为iframe的确存在能耗高,安全问题...很多小伙伴嫌弃它,但是它毕竟是解决跨域问题的一种思路,面试可能会问哦,多掌握点总是没错的。

最后

鉴于本人能力有限,难免会有疏漏错误的地方,请大家多多包涵。

双十一福利

原价7799,限时价直降2100,

次日返现200,老带新再返275!

全年抄底价5225

点击阅读原文,即可报名课程

本文分享自微信公众号 - 腾讯NEXT学院(Next_Academy)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-11-08

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 双十一狂欢的背后和NODE.JS不得不说的故事

    腾讯NEXT学位
  • 使用 CSS 追踪用户

    腾讯NEXT学位
  • 干货|小程序性能优化的几点实践技巧

    “ 我们都知道,性能的好坏直接影响用户的体验。本文首先论述下如何评判一个小程序页面的性能情况,之后通过具体的案例重点讲解下几点实践技巧,最后再讲讲key值在渲...

    腾讯NEXT学位
  • 优秀原型设计欣赏:美食类App原型制作-Kitchen Stories

    题材有Mockplus(摹客)团队提供,仅供参考学习。

    奔跑的小鹿
  • 【Mockplus教程】创建

    新建页面可以使用三种方式来实现: 1 点击右边页面树顶部的“+”按钮; 2 在右边页面面板空白处右击,在弹出菜单中选择“新建页面”; 3 直接使用快捷键...

    奔跑的小鹿
  • keepAlive页面缓存以及新页面不刷新问题(activated方法)

    用到的是 keepAlive 的生命周期中的activated方法,该方法在keepAlive激活时调用。所以你页面中需要的刷新都写在这里面就完成了。

    程序员不务正业
  • web跨域解决方案

    阅读目录 什么是跨域 常用的几种跨域处理方法: 跨域的原理解析及实现方法 总结 摘要:跨域问题,无论是面试还是平时的工作中,都会遇到,本文总结处理跨域问题的几种...

    hbbliyong
  • JSP 防止网页刷新重复提交数据

    if session("ok")=true then    response.write "错误,正在提交"    response.end end if

    阳光岛主
  • 操作系统第六篇【存储器管理】

    Java3y
  • 优秀原型设计欣赏:阅读类App原型制作-Another Read

    Another Read是一款阅读学习App,它主要是带给你最具创意的作家与插图画家出版的儿童读物。在这款App中,你可以根据喜好来左滑或者右滑书籍,喜欢的书籍...

    奔跑的小鹿

扫码关注云+社区

领取腾讯云代金券