微前端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日

相关文章

  • javascript asp教程第六课– response方法

    下面是详细讲解“javascript asp教程第六课– response方法”的完整攻略: 一、什么是response对象? 在 ASP 中,response 对象代表向客户端发送输出时使用的方法和属性。它允许 ASP 页面向客户端浏览器发送文本、HTML、XML 或任何其他类型的数据。下面是response对象的一些常用方法: Write(strTex…

    JavaScript 2023年5月28日
    00
  • JavaScript在IE中“意外地调用了方法或属性访问”

    当在IE浏览器中运行Javascript代码时,可能会出现“意外调用方法或属性访问”的问题。这个问题的主要原因是在IE中,当我们访问未定义的JavaScript变量时,会默认将其添加到全局作用域中,这可能会导致一些意料之外的影响。 例如,下面的代码中使用了一个未定义的变量test,这时在IE中,会自动将该变量添加到全局作用域中,可能会与其他已定义的变量发生冲…

    JavaScript 2023年5月28日
    00
  • JS实现加载和读取XML文件的方法详解

    JS实现加载和读取XML文件的方法详解 在Web开发中,我们有时会需要从服务器端获取XML文件,然后在前端进行解析和操作。本文将详细讲解JS实现加载和读取XML文件的方法,以及对XML文件进行解析和操作的技巧。 加载XML文件 加载XML文件主要有两种方式,一种是使用AJAX技术,另一种是使用XMLHttpRequest对象。下面分别进行讲解。 AJAX方式…

    JavaScript 2023年5月27日
    00
  • JavaScript深入理解作用域链与闭包详情

    JavaScript深入理解作用域链与闭包攻略 作用域链 在JavaScript中,作用域链是一种机制,它决定了变量和函数的访问权限。每个函数都有一个作用域链,它是由函数创建时所处的环境和所有父级环境的变量对象所组成。当函数执行时,它的作用域链会被用来解析变量和函数的引用。 示例一:作用域链的基本概念 var globalVar = ‘global’; fu…

    JavaScript 2023年6月10日
    00
  • 详解原生JS动态添加和删除类

    下面就详细讲解一下“详解原生JS动态添加和删除类”的完整攻略。 概述 在前端开发中,动态改变元素的样式是十分常见的需求,其中动态添加类名和动态删除类名就是两种常见的实现方式。通过动态改变元素的类名,可以轻松实现样式的交互效果和动画效果。 动态添加类名 方法一:使用Element.classList方法 Element.classList是DOM API提供的…

    JavaScript 2023年6月10日
    00
  • 如何利用javascript接收json信息并进行处理

    我们来讲一下如何利用JavaScript接收JSON信息并进行处理。 1. 什么是JSON? JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它以易于阅读和编写的方式在人类和计算机之间传输数据。JSON是基于JavaScript的一个子集,因此,当我们使用JavaScript处理JSON数据时,处理起来非常高效。 …

    JavaScript 2023年5月27日
    00
  • 使用JavaScript通过前端发送电子邮件

    下面是“使用JavaScript通过前端发送电子邮件”的完整攻略: 1.准备工作 要通过前端使用JavaScript发送电子邮件,你需要使用邮件服务提供商的API,本文以SendGrid为例进行说明。在使用SendGrid之前,你需要完成以下准备工作: 注册SendGrid账户并创建API密钥 创建一个用于发送邮件的HTML表单 2.获取API密钥 在Sen…

    JavaScript 2023年6月10日
    00
  • js获取指定字符前/后的字符串简单实例

    当我们在开发 Javascript 程序时,有时候需要从一个字符串中截取出前面或后面一段字符串,这就需要使用字符串的截取操作了。在 Javascript 中,我们可以使用一些简单的方法来获取指定字符前/后的字符串。 获取指定字符后的字符串 在 Javascript 中,我们可以使用字符串的 substring() 方法来获取指定字符后的字符串。这个方法接受两…

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