特点
- 默认可以加载不同域的网页,但通信存在跨域限制
-
不允许网页被 iframe 跨域加载
- 设置响应头 & meta(即csp):X-Frame-Options
- SAMEORIGIN:表示网页只允许在相同域名的 iframe 中显示
- DENY:表示网页不允许在任何 iframe 中显示
- ALLOW-FROM uri:表示网页允许在指定的 uri 中显示
- 设置响应头 & meta(即csp):Content-Security-Policy
- Content-Security-Policy: frame-ancestors ‘self’ https://allowed-site.com
- 只允许在同一源站点的 iframe 中加载网页和其他指定允许的站点
<meta http-equiv="Content-Security-Policy" content="frame-ancestors 'self'">
- https 的网页不可以嵌套 http 的网页
- 同源情况下在主页面中可以访问 cookie、localStorage 但无法访问 iframe 中页面的 sessionStorage
- 如果 iframe 中页面的 cookie 设置了 HttpOnly 属性,那么主应用无法获取
- iframe 中设置的 cookie 会覆盖主应用的
属性
- allow:允许使用的权限策略,详情
- height、width
- name
- 用于定位嵌入的浏览上下文的名称
- window.open() 方法的 windowName 参数值。
- <a> 标签与 <form> 标签的 target 属性值
- <input> 标签和 <button> 标签的 formtarget 属性值
<a href="http://www.jd.com" target="_jd">京东</a>
<iframe src="" width="100%" height="400px" name="_jd"></iframe>
- referrerpolicy:在获取 iframe 资源时如何发送 referrer 首部,详情
- sandbox:在 iframe 框架中的内容启用一些额外的限制条件,比如:allow-scripts 允许嵌入的浏览上下文运行脚本,详情
- src:同源网站
- srcdoc:该属性是一段 HTML 代码,这些代码会被渲染到 iframe 中。如果浏览器不支持 srcdoc 属性,则会渲染 src 属性表示的内容。
api
- window.frames:获取所有 iframe (类数组)
- iframe DOM对象.contentWindow:获取内联框架的 window 对象
- window.frames[ 0 ] 与 document.getElementsByTagName( “iframe” )[ 0 ].contentWindow 是相同的
- iframe DOM对象.contentDocument:获取 <iframe> 内部的 document 元素
- 等同于 contentWindow.document
- window.parent:引用父窗口对象
- window.top:获取顶级窗口对象
- iframe.onload 生命周期,初始直接访问的文档和加载完成的文档并不是一个
let oldDoc = iframe.contentDocument;
iframe.onload = function() {
let newDoc = iframe.contentDocument;
alert(oldDoc == newDoc);
};
跨域通信
- 发送消息:接受消息 window对象.postMessage(message, targetOrigin, [transfer]);
- targetOrigin:“*”(表示无限制)或者一个 URI。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配 targetOrigin 提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。
iframe.contentWindow.postMessage(message, targetOrigin, [transfer]);
- 接收消息
- data:信息。
- origin:调用 postMessage 时消息发送方窗口的 origin . 这个字符串由 协议、“😕/“、域名、“ : 端口号”拼接而成。
- source:对发送消息的窗口对象的引用; 可以使用此来在具有不同 origin 的两个窗口之间建立双向通信
window.addEventListener("message", receiveMessage, false);
缺点
- 页面样式风格不统一,同步样式存在问题
- 阻塞顶层页面的 onload 事件
- 与主页面共享连接池
- 顶层页面和内层页面共享连接池,在 chrome 下同时只能发送 6 个 http 请求,iframe 的嵌入会影响主页面的资源加载。
- 允许嵌入跨域网页存在点击劫持的风险
- 主应用的后退按钮无法回退 iframe 中的页面 ,也就是 url 不同步
- 不利于SEO,搜索引擎的爬虫无法解读iframe的页面。
解决方案
url 不同步
- 通过 js 操作iframe 路由栈
- iframe使用相对路径
```javascript
<iframe src="comments.html"></iframe>
跨域通信
- postMessage
- document.domain
- iframe为子域时,在父页面和子页面中都设置 document.domain 属性为相同的父域名。例如,在 “www.example.com” 的页面和 “blog.example.com” 的页面中都设置 document.domain = “example.com”
document.domain = "example.com";
var myVar = "Hello, world!";
document.domain = "example.com";
console.log(window.parent.myVar); /
ui 不同步
- 通过全屏iframe,将浏览器窗口全部交给子应用,代表框架:擎天
iframe 重复加载
- 利用 JavaScript 动态加载 iframe: 仅在需要时才通过 JavaScript 创建和插入 iframe 标签,避免页面初次加载时不必要的 iframe 加载。
function loadIframe() {
const iframe = document.createElement("iframe");
iframe.src = "https://example.com";
document.body.appendChild(iframe);
}
- 异步加载 iframe: 使用 loading=“lazy” 属性可以让浏览器延迟加载不在当前视口内的 iframe
<iframe src="https://example.com" loading="lazy"></iframe>
- 使用懒加载策略: 对于位于视口外的 iframe,需要在其进入视口时再进行加载
function handleIntersection(entries) {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const iframe = entry.target;
iframe.src = iframe.dataset.src;
observer.unobserve(iframe);
}
});
}
const observer = new IntersectionObserver(handleIntersection);
const iframes = document.querySelectorAll("iframe");
iframes.forEach((iframe) => observer.observe(iframe));