由来
以前使用多窗口页面(frames)来创建网站是一种比较流行的手段,人们将一个大的网站拆分成多个 HTML 页面,每个页面独立的放到一个 <frame> 元素中,再通过 <frameset> 元素将这些 frame 元素包含在一起
- bigpipe
- 解决网速较慢提前显示一些已经加载好的内容
特点
和父文档完全隔离的 CSS 和 JS
每次子应用进入都是一次浏览器上下文重建、资源重新加载的过程
内部发生的页面跳转导航,不会对父窗口产生任何影响
iframe 内的路由变化,iframe 的 src 值不会变
会创建一个新的 viewport
页面上的某些元素的定位规则会受到相应的影响
- width, height, padding, margin 属性的值是百分比
- position: fixed 出现问题
- vw, vh, vmin, vmax
优点
- 程序调入静态页面比较方便
- 主页面和子页面分离
缺点
- 样式/脚本需要额外链入会增加请求
- 搜索引擎无法侦测
- 比其它 DOM 元素速度更慢
- 会共享连接池
问题
iframe 阻塞页面加载 onload 事件
js 动态插入 iframe
iframe 嵌入网页会出现资源无法加载的情况(防盗链)
<meta name="referrer" content="never">
iframe 内外系统的通信、数据同步等需求
使用 postMessage
应用的 cookie 要透传到根域名都不同的子应用中实现免登效果
设置 cookie 的 domain 和 path 为同一个
界面样式风格不一样
出现上下、左右多个滚动条
导航失效
按钮、window.history.go 和 window.history.back
-
使用 this.$router.push() 地址栏的链接不变,iframe 的 src 不变,但是 iframe 的内容发生变化
- 通过监听 popstate 事件
- 浏览器后退按钮会对 iframe 窗口的后退
- History.back()、History.forward()、History.go()
- 同一页面跳转
- 通过监听 popstate 事件
-
使用 this.$router.go(-1) 来进行跳转,地址栏链接改变,iframe 的 src 改变,iframe 的内容也发生变化。
- window.go()
- 返回上一页面
-
使用 this.$router.href() 可以进行跳转,且地址栏发生改变
window.location.href
浏览器刷新 iframe url 状态丢失
通过监听锚点
reload 后退两次问题
跨域通信
未跨主域
-
获取 iframe 的相关节点,有些信息无法获得
在没有父级的时候 parent.window = window
-
document.domain
修改 document.domain 为同一个域
-
监听 location hash
跨主域
使用 postMessage
// 父页面
var iframe = document.createElement("iframe");
iframe.src = "./ifame-child.html";
iframe.id = "Yiframe";
iframe.style = "height: 100%; width: 100%; border: 0;";
document.getElementById("content").append(iframe);
document.querySelector("#Yiframe").onload = function (event) {
// console.log("onload", event);
// 跨主域
// 父向子
// 父:
document.getElementById("send").onclick = function () {
document
.querySelector("#Yiframe")
.contentWindow.postMessage(
"parentMessage",
"http://localhost:5000/html/iframe/ifame-child"
);
};
// 子向父
// 父:
window.onmessage = function (e) {
console.log("onmessage", e);
};
console.log(document.querySelector("#Yiframe").contentWindow.location.href);
};
// 子页面
window.onmessage = function (e) {
console.log("onmessage", e, document.domain);
// 可以获得父级window对象(必须是同域)
// e.source;
// 检查 postMessage 的发送页面的源。
// e.origin;
};
document.getElementById("send").addEventListener("click", function () {
parent.window.postMessage(
"childMessage",
"http://localhost:5000/html/iframe/iframe-parent"
);
// window.location.href="https://www.baidu.com"
});
安全
点击劫持
同源策略
同源策略 <iframe> 元素可以通过 JS 获取窗口对象进行任意操作
HTTPS 禁止主窗口和 <iframe> 之间任何可能的 JS 相互调用
sandbox 属性
永远不应该同时添加 allow-scripts 和 allow-same-origin
配置 CSP 指令
-
Content-Security-Policy使用 frame-ancestors 会覆盖 X-Frame-OptionsContent-Security-Policy: frame-ancestors <source>; // 类似 X-Frame-Options DENY Content-Security-Policy: frame-ancestors 'none'; -
responseHeader 使用 X-Frame-Options
- X-Frame-Options: DENY 不允许任何站点引用
- X-Frame-Options: SAMEORIGIN 仅允许同源站点引用
- X-Frame-Options: ALLOW-FROM example.com 允许某个站点引用
用途
在线编辑器
- html、js 通过 post 请求发送
- css 通过 postMessage
无刷新文件上传
浏览器多页面间的通信
音乐播放器
服务端向浏览器推送数据
Comet 叫做 Ajax Push, Reverse Ajax, HTTP Server Push
-
长轮询(Long polling)
-
永久帧(Forever Frame)
- 当前文档内创建一个
<iframe>元素,其文档所指向的地址会返回一个 HTTP 1.1 的 trunked 编码 文档。根据 trunked 编码文档的特性,服务器可以将整个文档分成多个部分发送给浏览器端 - 在 IE 和 Firefox 下,采用这样的方案会让浏览器的进度条一直显示加载中
- Google 通过采用类型为 htmlfile 的 ActiveXObject
- 当前文档内创建一个
-
XHR 流(XMLHttpRequest Streaming)