driver.get调用iframe中的页面_【JS应用】Iframe 解决跨域

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

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

就是 利用 iframe

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

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

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

1、基本原理

2、简单模拟

2、封装的函数

3、封装函数实战

如果觉得排版不好看,可以看原版 【JS应用】Iframe 解决跨域 ,或者拉到最后关注公众号吧

解决场景

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

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

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

基本原理

1、需要三个页面

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

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

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

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

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

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

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

3、数据请求

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

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

4、页面跳转

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

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

5、给父页面传递数据

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

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

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

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

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

3、隐藏 iframe 由页面B 跳转到 http://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>

不过说真的,这种方法也就作为一个备胎方法,大家熟悉熟悉知道一下就好了,毕竟项目中也不太可能用到

不过面试可能会问哈哈,多掌握点总是没错的

最后

鉴于本人能力有限,难免会有疏漏错误的地方,请大家多多包涵,如果有任何描述不当的地方,欢迎后台联系本人,领取红包

10f34f62c485cb80a8d5e5b5d78096db.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值