• 让iframe为项目增加更多可能性


    最近在研究微前端。我觉得从理论上来说,iframe是微前端最理想的组合技术。使用iframe能够将一个页面内嵌到另一个页面中,并且和链接集成一样具有松散的耦合和高鲁棒性。iframe具有极强的隔离性,其中发生的一切只会影响到自身 —— 实际上目前大多数在线编辑平台都是iframe技术实现的。

    iframe难题

    可是慢慢地,你会发现iframe的负面效果极其糟糕,以至于足以让人忽略其高隔离和易于实现的优势:

    • 性能开销:从浏览器角度来说,向页面添加一个iframe是一项开销巨大的操作。每个iframe都会创建一个新的浏览器上下文,这会导致额外的内存和CPU消耗;
    • 破坏无障碍可访问性标准:iframe破坏了页面的语义话,因为它属于另一个页面。我们可以设置iframe样式使其和页面其它部分“无缝衔接”,但是屏幕阅读器并不会被我们“欺骗”;
    • SEO不友好:爬虫会将使用了iframe的页面当作两个不同的页面进行索引。依然从浏览器角度来说,iframe内外的内容看起来在同一个浏览器窗口中,实际它们不在同一个文档中;

    如果你打算在项目中使用不止一个的iframe,请测试足够的用例以保证它们对性能的影响。

    除了上面说的,iframe还有一个致命缺陷:缺乏可靠的iframe自动高度的解决方案。

    但是笔者觉得这在某些情况下是可以尝试的。现在举一个场景实例:
    wd-商品选择

    在微店商家营销活动设置中,有一个商品选择功能。他会弹出来一个选择框。这时候我们注意到:只有一个商品的浮层其实用不了十个商品那么高的高度。这时候我们需要“响应式”height
    在我司“天生支持”微前端的脚手架的架构下,商品选择是作为“通用业务组件”方式实现的,你可以理解为远程组件。然后在当前项目中以iframe形式引入
    iframe使用

    为了避免常见组件封装的一些缺点。比如:回调函数需要以v-bind形式单独再处理、暴露方法名改动文档同步不及时等等。我们采用了之前文章中提过的“大组件调用”方式。(实际上,“通用业务组件”的概念就和笔者提的“大组件”不谋而合)

    //通用组件,无敏感代码。使用时保留下面一行即可。
    // from 营销team@weidian
    import ModalPC from './index.js';
    
    let ins = null;
    
    // 初始化选择器
    export function initModal(cfg) {
      if (ins) {
        // 已有实例
        console.warn("已有实例化的商品选择器。"); // ignore-console
      } else {
        /**
          * @description
          * 对回调函数进行了包装
          */
        ins = new ModalPC({
          url: cfg.url,
          callback: (msg) => {
            let data = msg.data;
            // 页面加载以后做数据传输用途
            if (data.type === 'mkt-load') {
              ins && ins.sendMsg({
                //...
              });
            } else {
              cfg.callback && cfg.callback(msg);
            }
          }
        });
      }
    }
    
    export function closeModal() {
      if (ins) {
        ins.close();
        ins = null;
      }
    }
    
    export function getInstance() {
      return ins
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    在index.js中:

    //通用组件,无敏感代码。使用时保留下面一行即可。
    // from 营销team@weidian
    /**
     * @description
     * PC模态窗组件 - 
     * 
     */
    export default class ModalPC {
      constructor(param) {
        // 基础信息
        //...
        
        this.domWrapper = null;
        this.domWindow = null;
        this.domIframe = null;
        
        this.init(param);
      }
    
      // 初始化方法
      init(param) {
        // 缓存配置项参数
        Object.assign(this.cfg, param);
        let status = this.initOption(param);
        if (status) {
          this.initDom();
          this.bindEvent(); // 页面事件绑定
          this.render(); // 组件渲染
        }
      }
    
      // 配置项初始化
      initOption(opt) {
      }
    
      // dom结构初始化
      initDom() {
        // 容器初始化
        let domWrapper = this.doc.createElement('div'); // 模态窗整体容器
        let domWindow = this.doc.createElement('div'); // 窗口容器
        let domIframe = this.doc.createElement('iframe');
    
        domWrapper.setAttribute('style', `
          position: fixed;
          top: 0;
          right: 0;
          bottom: 0;
          left: 0;
          z-index: 99999;
          background: rgba(0,0,0,.5);
        `);
    
        domIframe.setAttribute('width', '100%');
        domIframe.setAttribute('height', '100%');
        domIframe.setAttribute('frameborder', 0);
        domIframe.src = this.url;
    
        domWrapper.append(domIframe);
        // 容器缓存
        this.domWrapper = domWrapper;
        this.domWindow = domWindow;
        this.domIframe = domIframe;
    
      }
      close() {
        // 组件注销
        this.win.removeEventListener('message', this.transportMsgFn)
        this.doc.body.removeChild(this.domWrapper);
      }
    
      // 父作用域向iframe中传值
      sendMsg(msg) { this.domIframe.contentWindow.postMessage(msg, "*") }
      //...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    可以看到,我们是用一个div包裹了iframe,在iframe中又是一个div包裹整个元素。那我们是不是可以通过控制这两个“外层元素”里任意一个去控制里面的iframe呢?

    我认为,最外层的元素(iframe也好、div也好)应该具备一个最大值max-height,然后有一个动态style去根据内容展示适当的高度:

    // 设置列表的高度
        _setListHeight() {
          if (this.listHeight) return;
          this.$nextTick(() => {
            let _dom = this.$refs.itemListWrapper; //需要动态高度的元素
            this.listHeight = Math.floor(_dom.clientHeight);
          });
        },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这段代码在获取商品列表数据后调用。
    yigeshangpin

    n个商品

    iframe通信

    如果你用iframe构建微前端应用。那必然首要考虑iframe和页面的通信(数据传递)。

    监听事件:

    import MessageType from "./message-type";
    
    /**
     * 主应用,
     */
    class MainApp {
      constructor() {
        this.registerEvents();
      }
    
      // 注册事件
      registerEvents() {
        window.addEventListener("message", (e) => {
          try {
            const { type, data } = e.data;
            const arg = { data, originEvent: e };
            if (type === MessageType.CHECK_COOKIE) {
              app.checkCookie(arg);
            }
          } catch (err) {
            console.error("主应用接收到消息失败", err);
          }
        });
      }
    }
    
    let app = null;
    const start = ({ onCheckCookie }) => {
      app = new MainApp();
      app.checkCookie = onCheckCookie;
    };
    
    export default {
      start,
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    // message-type.js
    const MESSAGE_TYPE = {
      CHECK_COOKIE: "CHECK_COOKIE", // 验证 cookie
    };
    export default MESSAGE_TYPE;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    通知事件:

    import MessageType from "./message-type";
    
    let _targetOrigin = "*";
    
    const setup = ({ targetOrigin }) => {
      _targetOrigin = targetOrigin;
    };
    
    // 通知事件
    const notify = (type, data) => {
      top.postMessage(
        {
          type,
          data,
          info: { //单独拿出来
            data.version,
            data.name,
          },
        },
        _targetOrigin
      );
    };
    
    // 验证 cookie 是否过期
    const checkCookie = (data) => {
      notify(MessageType.CHECK_COOKIE, data);
    };
    
    // 是否 iframe
    const isIframe = () => {
      return window.top !== window;
    };
    
    export default {
      setup,
      notify,
      checkCookie,
      isIframe,
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    // index.js
    export { default as MainApp } from '监听事件文件路径';
    export { default as MicroApp } from '通知事件文件路径';
    
    • 1
    • 2
    • 3
  • 相关阅读:
    React高手必学:自定义Hooks,轻松飞升技术巅峰!
    Mycat2分库分表
    C#非托管泄漏中HEAP_ENTRY的Size对不上是怎么回事?
    Python - 深度学习系列36 重塑实体识别3
    通过 SingleFlight 模式学习 Go 并发编程
    Python项目实战:基于2D或3D的区域增长算法
    【AI应用探讨】— Gemma2模型应用场景
    mysql连接查询
    Jenkins+Rancher2.7部署构建
    java - 数据结构,双向链表 - LinkedList
  • 原文地址:https://blog.csdn.net/qq_43624878/article/details/126562131