如何操作页面内嵌的iframe,和iframe传递消息?
1、 通过 postMessage 的方式进行监听双方的message,当然这种前提是需要你有 iframe嵌入页面的源代码可以进行监听,下面看代码 使用iframe 页面
<template>
<div>
<iframe ref="iframedom" src="http://localhost:8080/" width="600px" height="400px" frameborder="0"></iframe>
<div>
<div v-for="item in temArr" :key="item">
<p>{{ item }}</p>
<button @click="interTo(item)">插入</button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
// 在使用webpack 构建的项目 中使用 postMessage 会出现 自动触发 一次 message 的监听事件
// 解决方案: 在vite 构建的不会出现,但我们保险起见也还是一样的操作
/**
* let _window = iframedom.value?.contentWindow
let _document = iframedom.value?.contentDocument
_window.postMessage(item,'http://localhost:8080/') 我们可以给 postMessage 的第一个参数 设置为一个对象然后,对象里面设置type标识,这样就可以避免自动触发 message 事件
_window.postMessage({type:'inserInto',data:item},'http://localhost:8080/')
*/
// 在使用 postMessage 的时候,我们最好使用 https 的方式,这样就可以避免被劫持
// postMessage 通信的时候,我们最好使用 域名的方式,这样就可以避免被劫持
// postMessage 有一定的局限性 ,例如我们需要使用 三方库的情况 ,我们无法使用 postMessage 来进行通信
// iframe 里面任何一个dom=> 获取到操作元素的dom => 修改元素的样式 => 修改元素的属性 => 修改元素的文本内容
import {ref, onMounted} from 'vue'
const iframedom = ref(null)
const temArr = ['hello', '大家好', 'buzhidao', '我是谁', '我在哪', '我为什么在这里']
const interTo = (item:string)=>{
if(iframedom.value){
let _window = iframedom.value?.contentWindow
let _document = iframedom.value?.contentDocument
_window.postMessage({type:'inserInto',data:item},'http://localhost:8080/')
}
}
onMounted(()=>{
// 这是接收 iframe 的传输的数据
window.addEventListener('message', (e) => {
if(e.data.type==='save'){
console.log(e.data.data)
}
})
})
</script>
iframe页面
<template>
<div id="app">
<textarea class="textarea" name="" v-model="texts" id="" cols="30" rows="10" @blur="blurText"></textarea>
<button @click="save">保存</button>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from "vue";
const texts = ref('')
// 光标位置
const cursorIndex = ref(0)
const save = () =>{
// window.top // 获取父级窗口
// 将数据传递给父级
window.parent.postMessage({type:'save',data:texts.value},'*')
}
const blurText = (el) =>{
cursorIndex.value = el.target.selectionStart
}
onMounted(()=>{
window.addEventListener('message', (e) => {
if(e.data.type==='inserInto'){
// texts.value = e.data.data
texts.value = texts.value.slice(0, cursorIndex.value) + e.data.data + texts.value.slice(cursorIndex.value)
}
})
})
</script>
2、 通过直接操作DOM 元素,我们不能修改代码,或者调用的iframe 是 第三方的时候使用
<template>
<div>
<iframe ref="iframedom" src="http://localhost:8080/" width="600px" height="400px" frameborder="0"></iframe>
<div>
<div v-for="item in temArr" :key="item">
<p>{{ item }}</p>
<button @click="interTo(item)">插入</button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import {ref, onMounted} from 'vue'
const iframedom = ref(null)
const temArr = ['hello', '大家好', 'buzhidao', '我是谁', '我在哪', '我为什么在这里']
const interTo = (item:string)=>{
if(iframedom.value){
let _document = iframedom.value && iframedom.value.contentDocument
if(_document){
let textAreaDom = _document.querySelector('.textarea')
let _oldVal = textAreaDom.value
let _postion = textAreaDom.getAttribute('data-position')
let _newVal = _oldVal.slice(0,Number(_postion)) + item + _oldVal.slice(Number(_postion))
textAreaDom.value = _newVal
}
}
}
onMounted(()=>{
// dom=> 获取 操作
if(iframedom.value){
iframedom.value.addEventListener('load',()=>{
console.log(iframedom.value, 'iframedom.value')
let _document = iframedom.value?.contentDocument;
console.log(_document, '-_document')
if(_document){
let textAreaDom = _document.querySelector('.textarea')
textAreaDom.addEventListener('blur',(e)=>{
console.log(textAreaDom.value)
textAreaDom.setAttribute('data-position', e.target.selectionStart + '')
})
}
})
}
})
</script>
<style scoped>
</style>
3、 无法嵌入百度页面 — X-Frame-Options响应限制
百度使用了 X-Frame-Options 响应头来限制其在 iframe 中的嵌套。X-Frame-Options 是一种安全策略,可以由网站的服务器设置,用于控制页面是否允许在 iframe 中嵌套。当网站设置了 X-Frame-Options 为 DENY 或 SAMEORIGIN,浏览器将不允许将该页面在 iframe 中加载,以防止点击劫持等安全问题。
DENY: 禁止所有页面在 iframe 中嵌套,无论来源域名是什么。
SAMEORIGIN: 允许同源页面在 iframe 中嵌套,但禁止不同源的页面进行嵌套。
allow-from XXX.com: 允许被指定域名的网站嵌套。
4、http无法嵌入https
解决方案:
(1)使用 HTTPS 协议: 主页面也迁移到 HTTPS 协议,这样就不会涉及到 HTTP 和 HTTPS 跨域的问题。
(2)服务器代理: 在服务器端设置代理,让服务器请求 HTTPS 页面的内容,然后将结果传递给 HTTP 页面的前端,由前端进行展示。
(3)使用 Subresource Integrity (SRI) : 如果目标 HTTPS 页面提供了 SRI 支持,可以使用 SRI 来加载和验证脚本和样式。
最佳做法是将页面迁移到 使用 HTTPS 协议
5、cookie设置共享问题
(1)浏览器限制了通过 iframe 中的页面使用 set-cookie 标头来设置 Cookie。这是出于安全考虑,防止跨域 Cookie 污染攻击。当在 iframe 中加载一个来自不同域的页面时,该页面无法通过设置 set-cookie 标头来在主页面的域中设置 Cookie。
(2)这种限制是由同源策略 (Same-Origin Policy) 引起的,它要求网页只能访问来自相同域的资源。Cookie 是一种用于跟踪会话状态和存储用户数据的机制,在跨域的情况下,Cookie 可能被恶意网站滥用,因此浏览器禁止了在跨域 。
解决方案: 1、使用同域代理: 在服务器端设置代理,让服务器请求目标域的资源,然后将结果传递给前端,由前端处理 Cookie。这样可以避免跨域 Cookie 问题。
2、使用 token: 通过在请求中使用 token 来进行身份验证和会话管理,而不依赖于 Cookie。比如我直接嵌入掘金的页面,使用情况是属于正常的
3、CORS: 可以考虑设置目标网站服务器 CORS 响应头,以允许特定域名的请求访问资源。
这是目前在使用中和查看的一些问题,可能不全面,毕竟还没在真正项目中使用过,在项目中就得具体问题具体分析了,各位网友多多指教!