微前端qiankun沙箱实现源码解读

我们来详细讲解一下“微前端qiankun沙箱实现源码解读”的完整攻略。

什么是微前端

首先,我们需要知道什么是微前端。简单地说,微前端是一种前端架构模式,它将大型Web应用程序分解为较小的、易于管理的模块,这些模块可以独立地开发、测试和部署。每个模块可以由不同的团队开发,并且可以以不同的速度进行更新和发布。这种模式使得公司可以更加灵活地开发和部署前端应用程序。

qiankun框架

qiankun是一个完整的微前端解决方案,它使用了类似于iframe的方式来加载子应用程序,并且提供了沙箱机制来隔离子应用程序之间的全局变量污染和CSS冲突等问题。在qiankun的实现中,一个主应用程序可以加载多个子应用程序,并且可以将它们形成一个整体应用程序。qiankun的实现还涉及到一些复杂的技术,例如浏览器端路由、应用程序通信、外部样式加载、主应用程序与子应用程序之间的上下文等。

qiankun沙箱机制

qiankun的沙箱机制是其最核心的功能之一。它的主要目的是确保每个子应用程序在运行时都被隔离,避免全局变量的污染和CSS冲突等问题。在实现中,qiankun通过以下方式来实现沙箱机制:

  1. 拦截和修改子应用程序的JS代码

qiankun通过拦截和修改子应用程序的JS代码来避免全局变量污染和命名冲突问题。在这个过程中,qiankun会将子应用程序的所有全局变量和函数都保存到一个独立的命名空间中,确保子应用程序中的变量和函数与主应用程序和其他子应用程序的变量和函数保持隔离。

  1. 隔离CSS样式

qiankun还使用了类似于Shadow DOM的方式来隔离CSS样式。在实现中,qiankun会将子应用程序中的CSS代码进行重新编译,并添加唯一的命名空间标识符。这样,子应用程序中的CSS样式就不会与主应用程序和其他子应用程序的CSS样式产生冲突。

  1. 提供沙箱容器

qiankun还提供了沙箱容器来隔离子应用程序的DOM节点。在实现中,qiankun会在主应用程序中为每个子应用程序创建一个独立的DOM节点,子应用程序只能对自己的DOM节点进行操作,不能访问其他子应用程序的DOM节点。

qiankun沙箱实现源码解读

了解了qiankun沙箱机制的基本原理之后,我们可以进一步深入地阅读qiankun的源码,以了解其实现细节。下面我们将重点讲解qiankun的沙箱实现源码。

示例一:拦截和修改子应用程序的JS代码

在qiankun中,拦截和修改子应用程序的JS代码是通过emitLifeCycles函数来实现的。这个函数用于处理子应用程序的生命周期钩子函数,它会在子应用程序的生命周期钩子函数被调用之前进行一些必要的处理。

下面是emitLifeCycles函数的主要实现代码:

// 钩子函数名称
const QIANKUN_HOOKS = [
  'bootstrap',
  'mount',
  'unmount',
];

// 发射生命周期方法
export function emitLifeCycles(app, oldApp, config, callback) {
  // 遍历所有的钩子
  QIANKUN_HOOKS.forEach(hook => {
    app[hook] &&
      setTimeout(() => {
        // 如果旧应用存在,需要传递 oldApp 参数
        hook === 'unmount'
          ? app[hook]({ ...config, ...{ beforeUnmount: () => callback(oldApp) } })
          : app[hook](config);
      });
  });
}

在这个函数中,我们可以看到针对“bootstrap”、“mount”和“unmount”三个生命周期钩子的操作。在该函数中,没有涉及到修改子应用程序的代码的内容,那么子应用是如何被修改的呢?

首先,我们需要了解qiankun的loadMicroApp函数,这个函数是用于加载子应用程序的核心函数。在这个函数中,qiankun使用了一种特殊的代码处理方式,它通过调用被加载的子应用程序的bootstrap函数,并将该函数的执行结果进行分析,以获取子应用程序中的所有全局变量和函数,并将它们添加到qiankun的命名空间中。具体的实现代码可以如下展示:

// 拦截并标记子应用程序的调用函数
const hijackEffects = (app) => {
  ['pushState', 'replaceState'].forEach(type => {
    window.history[type] = (...args) => {
      window.history.__d = true;
      window.history[type](...args);
    };
  })

  // 保存子应用程序的原始对象
  const rawSetTimeout = window.setTimeout;
  const rawClearTimeout = window.clearTimeout;
  const rawSetInterval = window.setInterval;
  const rawClearInterval = window.clearInterval;
  const rawAddEventListener = window.addEventListener;
  const rawRemoveEventListener = window.removeEventListener;

  // 监听setTimeout、clearTimeout、setInterval、clearInterval和addEventListener等函数
  window.setTimeout = ((...args) => {
    const handler = rawSetTimeout(...args);
    recordWindowAccess('setTimeout');
    return handler;
  }) as any;

  // 同理,hook其他函数
  //......

  // 返回子应用程序的可访问变量和方法的列表
  return {
    // 全局对象
    get global() {
      return window;
    },
    // 钩子
    get hooks() {
      return hijackers;
    },
    // WebSocket 对象
    get WebSocket() {
      return WebSocket;
    },
    // 触发历史记录
    execScript(code: string) {
      return eval(code);
    }
  };
};

// 加载微应用
export function loadMicroApp(app, configuration: any = {}) {
  // 启用生命周期阶段中的强制沙盒环境切换,目前仅限在 usePublicPath 为 true 时使用,后续可能会用于样式隔离 & 预处理、事件隔离
  const { sandbox, singular } = configuration;
  // 设置 sandbox 注入方式及默认值
  const { strictStyleIsolation, experimentalStyleIsolation, useEmbedSandbox } = sandbox || {};

  // 对于一条 jest 测试用例(例如 singularMode.test.js),循环引用的问题会导致主应用和微应用的 window 对象不一致
  // 这会导致微应用注册失败,因此需要确保主应用和微应用使用同一个 window 对象
  if (window[__qiankun__]) {
    app._container = document.getElementById(window[__qiankun__]);
    return render(app);
  }

  // 创建全局命名空间
  window.__$app_instance_of = window.__$app_instance_of || [];
  window.__$app_global_variable = window.__$app_global_variable || {};
  window.__$app_function = window.__$app_function || {};

  // 创建唯一命名空间
  const prefix = genNamespace(className);
  window.__$app_instance_of[prefix] = new Set();
  window.__$app_global_variable[prefix] = {};
  window.__$app_function[prefix] = {};

  // 创建沙盒环境,以隔离命名空间
  const sandboxDocument = createSandboxContainer(document, strictStyleIsolation, experimentalStyleIsolation, useEmbedSandbox, appName);
  const { proxy, obj } = createSandbox(sandboxDocument, prefix, className, singular, true);

  // 拦截并标记全局变量和方法
  const exposed = hijackEffects(app);

  // 调用微应用程序的 bootstrap 方法
  const bootstrap = sanitizeHtml(app.bootstrap({ ...exposed, ...configuration, ...{ rewriteURL: rewriteSandBoxFetch(appName) } }));

  // 处理 bootstrap 方法返回的状态信息,以获取微应用程序中的全局变量和方法,并将它们添加到全局命名空间中
  const jsSandbox = new JSSandbox(bootstrap, proxy);

  // 创建 rendering 沙盒
  const renderSandbox = createSandboxWithSpecialProps(obj, exposed, getAppWrapperGetter(sandboxDocument), className, appName, app);

  // 渲染微应用程序
  render({ ...app, _container: sandboxDocument.body, _proxy: (renderSandbox.proxy as any)._proxy, _cache: {} });

  // 添加微应用程序到容器中
  mountMicroApp(app, configuration, false, jsSandbox, exposed, renderSandbox, instance, callback);

  return null;
}

通过以上的代码,我们可以看出,在加载子应用程序时,qiankun会先为子应用程序创建一个独立的命名空间,然后使用该命名空间来保存全局变量和函数,并将其与其他子应用程序和主应用程序进行隔离。在调用bootstrap函数时,qiankun会获取由子应用程序导出的所有全局变量和函数,并将其添加到命名空间中,然后将修改后的bootstrap函数的执行结果返回给加载函数,以进一步处理。由此,我们可以看出qiankun是如何通过拦截和修改子应用程序的JS代码来实现沙箱机制的。

示例二:隔离CSS样式

qiankun在隔离CSS样式方面,使用了类似于Shadow DOM的方式。在子应用程序中,所有的CSS样式都被编译为CSS Modules,并在原样式的基础上添加了独一无二的前缀,以区分不同子应用程序的样式。在qiankun的实现中,这个过程是通过withScopedStyle函数来实现的。

以下是withScopedStyle函数的主要实现代码:

const withScopedStyle = (id, css) => {
  const uniqId = `qiankun-${id}`;
  return `
    ${CSS_PREFIX} [class*="${uniqId}"] {
      ${css
        .replace(/([\s\S]*)?(.*)/, '$1')
        .replace(/&/g, `.${CSS_PREFIX} [class*="${uniqId}"]`)}
    }`;
};

在该函数中,我们可以看到如何将原CSS样式编译为带有独立前缀的样式,同时,我们还可以看到局部作用域是如何被实现的(通过将子应用程序的class设置为一个唯一标识符)。

总结

通过以上的讲解,我们可以看出qiankun框架的沙箱机制是如何实现的。它通过拦截和修改子应用程序的JS代码,并使用独立命名空间来隔离全局变量和函数。同时,它还使用了类似于Shadow DOM的方式来隔离CSS样式,确保子应用程序的样式不会与其他子应用程序和主应用程序的样式产生冲突。这些技术的结合使得qiankun可以实现完整的微前端解决方案,并且可以确保子应用程序之间的隔离。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:微前端qiankun沙箱实现源码解读 - Python技术站

(0)
上一篇 2023年6月11日
下一篇 2023年6月11日

相关文章

  • jQuery使用animate创建动画用法实例

    下面是详细讲解jQuery使用animate创建动画的攻略。 什么是jQuery animate? jQuery animate是一种创建动画的方式,它可以让元素以自然且流畅的方式进行动画效果,比如让元素缓慢地移动、旋转等。 animate()方法的语法 animate()方法的语法如下: jQuery(selector).animate(styles, s…

    JavaScript 2023年6月10日
    00
  • JS学习之一个简易的日历控件

    下面是针对“JS学习之一个简易的日历控件”的完整攻略。 介绍 这是一篇教程,讲解如何使用JavaScript实现一个简易的日历控件。通过阅读教程,您将学会以下内容: 理解日历的基本概念和操作 创建一个日历控件的HTML结构 通过JavaScript实现控件的基本功能和逻辑 通过这个教程,您将同时学习到HTML和JavaScript的知识,提高自己的前端开发技…

    JavaScript 2023年5月27日
    00
  • JavaScript高级程序设计(第3版)学习笔记11 内建js对象

    下面是《JavaScript高级程序设计(第3版)学习笔记11 内建js对象》的学习攻略。 常用内建对象 JavaScript中内建对象众多,本章介绍的是一些常用的内建对象。 Boolean对象 Boolean对象只有两种可能的实例,即true和false,如果将其他数据类型转换为Boolean类型,规则是:除了””、0、NaN、null和undefined…

    JavaScript 2023年5月18日
    00
  • 常用的js验证和数据处理总结

    下面详细讲解”常用的JS验证和数据处理总结”: 栏目介绍 本栏目主要针对常用的JS验证和数据处理进行总结,并提供一些示例说明,方便开发者在开发过程中快速应用。 表单数据验证 常用的表单验证有:非空验证、数字验证、电子邮件验证、电话号码验证、URL验证、正则验证。 针对非空验证可以使用下面的代码片段: function checkNotNull(str){ i…

    JavaScript 2023年6月10日
    00
  • js类定义函数时用prototype与不用的区别示例介绍

    当我们定义一个 JavaScript 的对象时,可以使用构造函数对其进行初始化,也可以使用 prototype 扩展对象,JavaScript 中的类的定义可以使用 prototype 与不使用 prototype 两种方式。 使用 prototype 的方式,代码可读性好,易于维护。同时可以减少对象的内存占用,避免过多的类定义,同时可以节省执行时间。 不使…

    JavaScript 2023年6月11日
    00
  • JavaScript web网页入门级开发详解

    JavaScript Web网页入门级开发详解 本攻略旨在协助初学者学习JavaScript Web网页开发,包括以下主题: HTML基础:学会构建网页基本结构 CSS基础:学会美化网页样式 JavaScript基础:学会如何编写JavaScript代码 jQuery:学会用jQuery进行Web开发 示例项目:两个实例帮助你理解如何将知识应用到实际项目中 …

    JavaScript 2023年5月17日
    00
  • javascript中打印当前的时间实现思路及代码

    JavaScript中打印当前时间,是一个比较简单但又非常有用的功能。本文将会详细讲解如何实现此功能。 实现思路 我们可以使用JavaScript内置的Date对象来获取当前的时间信息,然后通过一些方法将其格式化为我们所需要的形式,并将时间信息输出到控制台或者网页中。 具体的实现步骤如下: 创建一个Date对象,它可以获取当前的系统时间。 使用Date对象的…

    JavaScript 2023年5月27日
    00
  • js 点击a标签 获取a的自定义属性方法

    获取 <a> 标签的自定义属性是 JavaScript 中常见的需求之一,可以使用以下步骤和示例来实现: 步骤 首先,需要给 <a> 标签添加自定义属性,例如添加 data-* 属性,其中 * 替换为具体的属性名,例如 data-link。 接着,在 JavaScript 中,可以通过获取对应 <a> 标签的 DOM 元素…

    JavaScript 2023年6月11日
    00
合作推广
合作推广
分享本页
返回顶部