HTML 语义元素 - iframe

291 阅读3分钟

由来

以前使用多窗口页面(frames)来创建网站是一种比较流行的手段,人们将一个大的网站拆分成多个 HTML 页面,每个页面独立的放到一个 <frame> 元素中,再通过 <frameset> 元素将这些 frame 元素包含在一起

  • bigpipe
  • 解决网速较慢提前显示一些已经加载好的内容

特点

和父文档完全隔离的 CSS 和 JS

每次子应用进入都是一次浏览器上下文重建、资源重新加载的过程

浏览上下文.png

内部发生的页面跳转导航,不会对父窗口产生任何影响

iframe 内的路由变化,iframe 的 src 值不会变

history共享.jpg

会创建一个新的 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()
    • 同一页面跳转
  • 使用 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-Options

    Content-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)