• micro-app-源码解析4-数据通信篇-终篇


    沙箱篇官方讲解地址

    https://github.com/micro-zoe/micro-app/issues/21

    1. 带着问题进行阅读

    在做框架的时候,没法避免的遇见父子通信和兄弟通信的问题,但是如何实现通信这是个每个框架需要解决的问题。
    
    • 1

    1.1 如何选择父子通信的方式

    最好的方式应该是像普通元素一样,通过micro-app元素传递数据。但自定义元素无法支持对象类型的属性,只能传递字符串,例如 会转换为

    所以选择通过改写原型链上setAttribute方法处理对象类型属性,对传递的数据进行拦截,加上发布订阅实现父子的通信。

    // /src/index.js
    
    // 记录原生方法
    const rawSetAttribute = Element.prototype.setAttribute
    
    // 重写setAttribute
    Element.prototype.setAttribute = function setAttribute (key, value) {
      // 目标为micro-app标签且属性名称为data时进行处理
      if (/^micro-app/i.test(this.tagName) && key === 'data') {
        if (toString.call(value) === '[object Object]') {
          // 克隆一个新的对象
          const cloneValue = {}
          Object.getOwnPropertyNames(value).forEach((propertyKey) => {
            // 过滤vue框架注入的数据
            if (!(typeof propertyKey === 'string' && propertyKey.indexOf('__') === 0)) {
              cloneValue[propertyKey] = value[propertyKey]
            }
          })
          // 数据拦截后对子应用发送数据
          BaseAppData.setData(this.getAttribute('name'), cloneValue)
        }
      } else {
        rawSetAttribute.call(this, key, value)
      }
    }
    
    
    • 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

    1.2 为什么选择发布订阅

    微前端各个应用本身是独立运行的,通信系统不应该对应用侵入太深,所以我们采用发布订阅系统。

    1.3 兄弟间如何通信

    子应用要发给其他子应用的数据先发给基座,基座进行分发。

    发布订阅系统很灵活,但太过于灵活可能会导致数据传输的混乱,必须定义一套清晰的数据流。所以我们要进行数据绑定,基座应用一次只能向指定的子应用发送数据,子应用只能发送数据到基座应用,至于子应用之间的数据通信则通过基座应用进行控制,这样数据流就会变得清晰

    2.原理详解

    2.1 父亲应用对子应用进行传值

    2.1.1 组件的data 属性传值

    因为元素不能传递对象,所以最终的结果还是走了发布订阅。通过修改原生的setAttribute方法,当改变属性的时候,进行获取值,然后将获取的值放入发布订阅当中。进行传递。
    请添加图片描述

    // /src/index.js
    
    // 记录原生方法
    const rawSetAttribute = Element.prototype.setAttribute
    
    // 重写setAttribute
    Element.prototype.setAttribute = function setAttribute (key, value) {
      // 目标为micro-app标签且属性名称为data时进行处理
      if (/^micro-app/i.test(this.tagName) && key === 'data') {
        if (toString.call(value) === '[object Object]') {
          // 克隆一个新的对象
          const cloneValue = {}
          Object.getOwnPropertyNames(value).forEach((propertyKey) => {
            // 过滤vue框架注入的数据
            if (!(typeof propertyKey === 'string' && propertyKey.indexOf('__') === 0)) {
              cloneValue[propertyKey] = value[propertyKey]
            }
          })
          // 发送数据
          BaseAppData.setData(this.getAttribute('name'), cloneValue)
        }
      } else {
        rawSetAttribute.call(this, key, value)
      }
    }
    
    
    • 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
    2.1.2 发布订阅传值

    发布订阅功能的代码实现

    // /src/data.js
    
    // 基座应用的数据通信方法集合
    export class EventCenterForBaseApp {
      /**
       * 向指定子应用发送数据
       * @param appName 子应用名称
       * @param data 对象数据
       */
      setData (appName, data) {
        eventCenter.dispatch(formatEventName(appName, true), data)
      }
    
      /**
       * 清空某个应用的监听函数
       * @param appName 子应用名称
       */
      clearDataListener (appName) {
        eventCenter.off(formatEventName(appName, false))
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    2.2 子应用对父亲应用进行传值

    子应用对父亲的传值,通过标签本身的属性[data],通过datachange方法来进行传值的

    核心代码

        const app = appInstanceMap.get(this.appName)
        if (app?.container) {
          // 子应用以自定义事件的形式发送数据
          const event = new CustomEvent('datachange', {
            detail: {
              data,
            }
          })
    
          app.container.dispatchEvent(event)
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2.3 父子应用传值的基本实现代码

    
    // /src/data.js
    
    // 基座应用的数据通信方法集合
    export class EventCenterForBaseApp {
      /**
       * 向指定子应用发送数据
       * @param appName 子应用名称
       * @param data 对象数据
       */
      setData (appName, data) {
        eventCenter.dispatch(formatEventName(appName, true), data)
      }
    
      /**
       * 清空某个应用的监听函数
       * @param appName 子应用名称
       */
      clearDataListener (appName) {
        eventCenter.off(formatEventName(appName, false))
      }
    }
    
    // 子应用的数据通信方法集合
    export class EventCenterForMicroApp {
      constructor (appName) {
        this.appName = appName
      }
    
      /**
       * 监听基座应用发送的数据
       * @param cb 绑定函数
       */
      addDataListener (cb) {
        eventCenter.on(formatEventName(this.appName, true), cb)
      }
    
      /**
       * 解除监听函数
       * @param cb 绑定函数
       */
      removeDataListener (cb) {
        if (typeof cb === 'function') {
          eventCenter.off(formatEventName(this.appName, true), cb)
        }
      }
    
      /**
       * 向基座应用发送数据
       * @param data 对象数据
       */
      dispatch (data) {
        const app = appInstanceMap.get(this.appName)
        if (app?.container) {
          // 子应用以自定义事件的形式发送数据
          const event = new CustomEvent('datachange', {
            detail: {
              data,
            }
          })
    
          app.container.dispatchEvent(event)
        }
      }
    
      /**
       * 清空当前子应用绑定的所有监听函数
       */
      clearDataListener () {
        eventCenter.off(formatEventName(this.appName, true))
      }
    }
    
    
    • 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
  • 相关阅读:
    《精神与爱欲》爱源于母性,且超越性别
    大二Web课程设计——张家界旅游网站设计与实现(HTML+CSS+JavaScript)
    用HTML+CSS做一个漂亮简单的个人网页(第二篇)
    武汉凯迪正大—全自动变比测试仪
    猿创征文|SpringBoot概述及在idea中创建方式
    深度学习笔记_2、多种方法定义神经网络
    java毕业生设计医院新型冠状病毒疫苗接种管理系统计算机源码+系统+mysql+调试部署+lw
    创建和删除目录( mkdir函数 和 rmdir函数 )
    Codeup刷题笔记-简单模拟
    护眼灯有效果吗?五款好用热门的护眼台灯推荐
  • 原文地址:https://blog.csdn.net/weixin_43856422/article/details/128148458