使用Webpack构建微前端应用

news2024/12/28 5:02:26

英文社区对 Webpack Module Federation 的响应非常热烈,甚至被誉为“A game-changer in JavaScript architecture”,相对而言国内对此热度并不高,这一方面是因为 MF 强依赖于 Webpack5,升级成本有点高;另一方面是国内已经有一些成熟微前端框架,例如 qiankun。不过我个人觉得 MF 有不少实用性强,非常值得学习、使用的特性,包括:

  • 应用可按需导出若干模块,这些模块最终会被单独打成模块包,功能上有点像 NPM 模块;

  • 应用可在运行时基于 HTTP(S) 协议动态加载其它应用暴露的模块,且用法与动态加载普通 NPM 模块一样简单;

  • 与其它微前端方案不同,MF 的应用之间关系平等,没有主应用/子应用之分,每个应用都能导出/导入任意模块;

  • 等等。

而对于微前端的解释,大家可以查这篇文章。

简单示例

Module Federation 的基本逻辑是一端导出模块,另一端导入、使用模块,实现上两端都依赖于 Webpack 5 内置的 ModuleFederationPlugin 插件:

  1. 对于模块生成方,需要使用 ModuleFederationPlugin 插件的 expose 参数声明需要导出的模块列表;

  2. 对于模块使用方,需要使用 ModuleFederationPlugin 插件的 remotes 参数声明需要从哪些地方导入远程模块。

接下来,我们按这个流程一步步搭建一个简单的 Webpack Module Federation 示例,基本框架:

//MF-basic
├─ app-1
│  ├─ dist
│  │  ├─ ...
│  ├─ package.json
│  ├─ src
│  │  ├─ main.js
│  │  ├─ foo.js
│  │  └─ utils.js
│  └─ webpack.config.js
├─ app-2
│  ├─ dist
│  │  ├─ ...
│  ├─ package.json
│  ├─ src
│  │  ├─ bootstrap.js
│  │  └─ main.js
│  ├─ webpack.config.js
├─ lerna.json
└─ package.json

提示:为简化依赖管理,示例引入 lerna 实现 Monorepo 策略,不过这与文章主题无关,这里不做过多介绍。

其中,app-1app-2 是两个独立应用,分别有一套独立的 Webpack 构建配置,类似于微前端场景下的“微应用”概念。在本示例中,app-1 负责导出模块 —— 类似于子应用;app-2 负责使用这些模块 —— 类似于主应用。

我们先看看模块导出方 —— 也就是 app-1 的构建配置:

const path = require("path");
const { ModuleFederationPlugin } = require("webpack").container;

module.exports = {
  mode: "development",
  devtool: false,
  entry: path.resolve(__dirname, "./src/main.js"),
  output: {
    path: path.resolve(__dirname, "./dist"),
    // 必须指定产物的完整路径,否则使用方无法正确加载产物资源
    publicPath: `http://localhost:8081/dist/`,
  },
  plugins: [
    new ModuleFederationPlugin({
      // MF 应用名称
      name: "app1",
      // MF 模块入口,可以理解为该应用的资源清单
      filename: `remoteEntry.js`,
      // 定义应用导出哪些模块
      exposes: {
        "./utils": "./src/utils",
        "./foo": "./src/foo",
      },
    }),
  ],
  // MF 应用资源提供方必须以 http(s) 形式提供服务
  // 所以这里需要使用 devServer 提供 http(s) server 能力
  devServer: {
    port: 8081,
    hot: true,
  },
};

作用模块导出方,app-1 的配置逻辑可以总结为:

  1. 需要使用 ModuleFederationPluginexposes 项声明哪些模块需要被导出;使用 filename 项定义入口文件名称;

  2. 需要使用 devServer 启动开发服务器能力。

使用 ModuleFederationPlugin 插件后,Webpack 会将 exposes 声明的模块分别编译为独立产物,并将产物清单、MF 运行时等代码打包进 filename 定义的应用入口文件(Remote Entry File)中。例如 app-1 经过 Webpack 编译后,将生成如下产物:

//MF-basic
├─ app-1
│  ├─ dist
│  │  ├─ main.js
│  │  ├─ remoteEntry.js
│  │  ├─ src_foo_js.js
│  │  └─ src_utils_js.js
│  ├─ src
│  │  ├─ ...
  • main.js 为整个应用的编译结果,此处可忽略;

  • src_utils_js.jssrc_foo_js.js 分别为 exposes 声明的模块的编译产物;

  • remoteEntry.jsModuleFederationPlugin 插件生成的应用入口文件,包含模块清单、MF 运行时代码。

拓展1

devServer的配置项

devServer 是 Webpack 开发工具中的一个选项,主要用于提供一个快速的本地开发服务器,帮助开发人员在开发过程中更方便地构建和测试应用。使用 devServer 可以提高开发效率,提供热更新功能,并简化静态资源的提供。以下是 devServer 的一些关键特性和配置选项:

关键特性

  1. 热模块替换 (Hot Module Replacement, HMR):

    • 支持在运行时替换、添加或删除模块,而无需完全刷新页面。这使得开发者可以看到改动的即时效果,提高开发效率。
  2. 自动刷新页面:

    • 当文件发生变化时,devServer 可以自动刷新浏览器,确保开发者总是能看到最新的代码效果。
  3. 自定义路由:

    • 可以配置 devServer 的路由,例如重定向请求到特定页面,适用于单页应用(SPA)。
  4. Proxy:

    • 支持 API 代理,允许将开发环境中的请求代理到其他服务器,这在需要与后端 API 交互时非常有用。
  5. 错误页面:

    • 可以定制错误页面,方便开发者在出现问题时进行调试。

基本配置

Webpack 配置文件中,通常在 module.exports 对象中添加 devServer 配置。以下是一个基本的配置示例:

const path = require('path');

module.exports = {
    // 其他配置...
    devServer: {
        contentBase: path.join(__dirname, 'dist'), // 静态文件的目录
        compress: true, // 启用 gzip 压缩
        port: 9000, // 监听端口
        hot: true, // 启用热模块替换
        open: true, // 自动打开浏览器
        proxy: {
            '/api': 'http://localhost:5000' // 代理 API 请求
        },
        historyApiFallback: true, // 处理所有 404 请求,重定向到 index.html
    },
};
  • contentBase: 指定静态文件的根目录,默认是 public 目录。
  • compress: 布尔值,启用 gzip 压缩。
  • port: 指定开发服务器的端口。
  • hot: 启用热模块替换。
  • open: 在服务器启动后自动打开浏览器。
  • proxy: 配置代理,将请求代理到其他服务器,适用于跨域请求。
  • historyApiFallback: 启用后将所有 404 响应重定向到 index.html,适合单页应用。

接下来继续看看模块导入方 —— 也就是 app-2 的配置方法:

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { ModuleFederationPlugin } = require("webpack").container;

module.exports = {
  mode: "development",
  devtool: false,
  entry: path.resolve(__dirname, "./src/main.js"),
  output: {
    path: path.resolve(__dirname, "./dist"),
  },
  plugins: [
    // 模块使用方也依然使用 ModuleFederationPlugin 插件搭建 MF 环境
    new ModuleFederationPlugin({
      // 使用 remotes 属性声明远程模块列表
      remotes: {
        // 地址需要指向导出方生成的应用入口文件
        RemoteApp: "app1@http://localhost:8081/dist/remoteEntry.js",
      },
    }),
    new HtmlWebpackPlugin(),
  ],
  devServer: {
    port: 8082,
    hot: true,
    open: true,
  },
};

作用远程模块使用方app-2 需要使用 ModuleFederationPlugin 声明远程模块的 HTTP(S) 地址与模块名称(示例中的 RemoteApp),之后在 app-2 中就可以使用模块名称异步导入 app-1 暴露出来的模块,例如:

// app-2/src/main.js
(async () => {
  const { sayHello } = await import("RemoteApp/utils");
  sayHello();
})();

其中:

  • remoteEntry.jsapp-1 构建的应用入口文件;

  • src_utils_js.js 则是 import("RemoteApp/utils") 语句导入的远程模块。

总结一下,MF 中的模块导出/导入方都依赖于 ModuleFederationPlugin 插件,其中导出方需要使用插件的 exposes 项声明导出哪些模块,使用 filename 指定生成的入口文件;导入方需要使用 remotes 声明远程模块地址,之后在代码中使用异步导入语法 import("module") 引入模块。

这种模块远程加载、运行的能力,搭配适当的 DevOps 手段,已经足以满足微前端的独立部署、独立维护、开发隔离的要求,在此基础上 MF 还提供了一套简单的依赖共享功能,用于解决多应用间基础库管理问题。

拓展2

ModuleFederationPlugin 插件

ModuleFederationPlugin 是 Webpack 5 引入的一个强大功能,旨在实现微前端架构(Micro Frontends)。它允许多个独立构建的应用程序(或模块)在运行时动态共享和加载彼此的代码,从而实现更灵活的代码复用和跨团队协作。

核心概念

  1. 微前端:将一个大型应用拆分成多个小型、独立的应用,每个应用可以独立开发、部署和维护。

  2. 共享模块:通过 ModuleFederationPlugin,不同的应用可以共享某些公共依赖,从而减少重复代码和整体包的大小。

  3. 动态加载:应用可以在运行时加载其他应用的模块,而无需在构建时将它们打包在一起。

使用方法

1. 配置主应用

首先,我们需要在主应用(Host)中配置 ModuleFederationPlugin。以下是一个简单的配置示例:

// webpack.config.js (主应用)
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
const path = require('path');

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: 'auto', // 适应于动态加载
  },
  plugins: [
    new ModuleFederationPlugin({
      name: 'app1', // 主应用的名称
      filename: 'remoteEntry.js', // 远程入口文件
      exposes: {
        // 指定要暴露的模块
        './Component': './src/Component', 
      },
      shared: {
        // 共享模块
        react: { singleton: true, eager: true },
        'react-dom': { singleton: true, eager: true },
      },
    }),
  ],
};

在这个示例中,配置了一个名为 app1 的主应用,暴露了一个名为 Component 的模块,并且共享了 reactreact-dom

2. 配置远程应用

接下来,我们需要配置一个远程应用(Remote),它将从主应用中加载模块。

// webpack.config.js (远程应用)
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
const path = require('path');

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'remoteEntry.js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: 'auto',
  },
  plugins: [
    new ModuleFederationPlugin({
      name: 'app2', // 远程应用的名称
      filename: 'remoteEntry.js',
      remotes: {
        app1: 'app1@http://localhost:3001/remoteEntry.js', // 指定主应用的远程入口
      },
      shared: {
        react: { singleton: true, eager: true },
        'react-dom': { singleton: true, eager: true },
      },
    }),
  ],
};

在这个示例中,远程应用 app2 可以从主应用 app1 中加载暴露的模块。确保两个应用都在不同的端口运行(例如,app1 在 3001 端口,app2 在 3002 端口),然后你可以在 app2 中加载和使用 app1 中的模块。

3. 加载远程模块

在远程应用中,我们可以使用动态导入来加载主应用暴露的模块。

// src/index.js (远程应用)
import('app1/Component').then((Component) => {
  // 使用加载的组件
  const App = Component.default;
  // 渲染组件
});

依赖共享

上例应用相互独立,各自管理、打包基础依赖包,但实际项目中应用之间通常存在一部分公共依赖 —— 例如 Vue、React、Lodash 等,如果简单沿用上例这种分开打包的方式势必会出现依赖被重复打包,造成产物冗余的问题,为此 ModuleFederationPlugin 提供了 shared 配置用于声明该应用可被共享的依赖模块。

例如,改造上例模块导出方 app-1 ,添加 shared 配置:

module.exports = {
  // ...
  plugins: [
    new ModuleFederationPlugin({
      name: "app1",
      filename: `remoteEntry.js`,
      exposes: {
        "./utils": "./src/utils",
        "./foo": "./src/foo",
      }, 
      // 可被共享的依赖模块
+     shared: ['lodash']
    }),
  ],
  // ...
};

接下来,还需要修改模块导入方 app-2,添加相同的 shared 配置:

module.exports = {
  // ...
  plugins: [
    // 模块使用方也依然使用 ModuleFederationPlugin 插件搭建 MF 环境
    new ModuleFederationPlugin({
      // 使用 remotes 属性声明远程模块列表
      remotes: {
        // 地址需要指向导出方生成的应用入口文件
        RemoteApp: "app1@http://localhost:8081/dist/remoteEntry.js",
      },
+     shared: ['lodash']
    }),
    new HtmlWebpackPlugin(),
  ],
  // ...
};

之后,运行页面可以看到最终只加载了一次 lodash 产物,而改动前则需要分别从导入/导出方各加载一次 lodash

添加 shared 后改动前

注意,这里要求两个应用使用 版本号完全相同 的依赖才能被复用,假设上例应用 app-1 用了 lodash@4.17.0 ,而 app-2 用的是 lodash@4.17.1,Webpack 还是会同时加载两份 lodash 代码,我们可以通过 shared.[lib].requiredVersion 配置项显式声明应用需要的依赖库版本来解决这个问题:

module.exports = {
  // ...
  plugins: [
    new ModuleFederationPlugin({
      // ...
      // 共享依赖及版本要求声明
+     shared: {
+       lodash: {
+         requiredVersion: "^4.17.0",
+       },
+     },
    }),
  ],
  // ...
};

上例 requiredVersion: "^4.17.0" 表示该应用支持共享版本大于等于 4.17.0 小于等于 4.18.0 的 lodash,其它应用所使用的 lodash 版本号只要在这一范围内即可复用。requiredVersion 支持 Semantic Versioning 2.0 标准,这意味着我们可以复用 package.json 中声明版本依赖的方法。

requiredVersion 的作用在于限制依赖版本的上下限,实用性极高。除此之外,我们还可以通过 shared.[lib].shareScope 属性更精细地控制依赖的共享范围,例如:

module.exports = {
  // ...
  plugins: [
    new ModuleFederationPlugin({
      // ...
      // 共享依赖及版本要求声明
+     shared: {
+       lodash: {
+         // 任意字符串
+         shareScope: 'foo'
+       },
+     },
    }),
  ],
  // ...
};

在这种配置下,其它应用所共享的 lodash 库必须同样声明为 foo 空间才能复用。shareScope 在多团队协作时能够切分出多个资源共享空间,降低依赖冲突的概率。

以下为我总结后的app1和app2webpack.config.js配置项

app1:

const path = require('path');
const { ModuleFederationPlugin } = require('webpack').container;
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  mode: 'development',
  devtool: false,
  entry: {
    main: path.resolve(__dirname, './src/main.js'),
  },
  output: {
    path: path.resolve(__dirname, './dist'),
    publicPath: 'http://localhost:8081/dist/',
    filename: '[name].js', // 初始文件名模板
    chunkFilename: 'src_[name]_js.js', // 动态分块文件名模板
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env', '@babel/preset-react'],
            plugins: [
              '@babel/plugin-transform-runtime',
              '@babel/plugin-syntax-dynamic-import'
            ]
          }
        }
      }
    ],
  },
  plugins: [
    new ModuleFederationPlugin({
      name: 'app1',
      filename: 'remoteEntry.js',
      exposes: {
        './utils': './src/utils',
        './foo': './src/foo',
      },
      shared: {
        lodash: {
          singleton: true, // 确保只有一个实例
          requiredVersion: '^4.17.21', // 指定所需的版本范围
          import: 'lodash', // 指定导入的模块路径
          strictVersion: false, // 是否严格匹配版本
        },
      },
    }),
    new HtmlWebpackPlugin(),
  ],
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        foo: {
          test: /[\\/]src[\\/]foo\.js$/,
          name: 'foo',
          chunks: 'all',
        },
        utils: {
          test: /[\\/]src[\\/]utils\.js$/,
          name: 'utils',
          chunks: 'all',
        },
      },
    },
  },
  devServer: {
    port: 8081,
    hot: true,
  },
};

app2:

const path = require('path');
const { ModuleFederationPlugin } = require('webpack').container;
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  mode: 'development',
  devtool: false,
  entry: {
    main: path.resolve(__dirname, './src/main.js'),
  },
  output: {
    path: path.resolve(__dirname, './dist'),
    publicPath: 'http://localhost:8082/dist/', // 确保与 app2 的启动端口一致
    filename: '[name].js',
    chunkFilename: 'src_[name]_js.js',
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env', '@babel/preset-react'],
            plugins: [
              '@babel/plugin-transform-runtime',
              '@babel/plugin-syntax-dynamic-import'
            ]
          }
        }
      }
    ],
  },
  plugins: [
    new ModuleFederationPlugin({
      name: 'app2',
      filename: 'remoteEntry.js',
      remotes: {
        app1: 'app1@http://localhost:8081/dist/remoteEntry.js', // 指向 app1 的 remoteEntry.js
      },
      shared: {
        lodash: {
          singleton: true,
          requiredVersion: '^4.17.21',
          import: 'lodash',
          strictVersion: false,
        },
      }, // 如果需要共享库,可以在这里配置
    }),
    new HtmlWebpackPlugin(),
  ],
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        // 可以根据需要添加缓存组
      },
    },
  },
  devServer: {
    port: 8082,
    hot: true,
    open: true,
  },
};

拓展3

shared 配置

常见配置选项

  • singleton:

    • 类型boolean
    • 说明: 如果设置为 true,Webpack 会确保只会有一个实例被加载,适用于像 React 这样的库,它们通常应该只存在一个实例。
  • eager:

    • 类型boolean
    • 说明: 如果设置为 true,Webpack 会在应用加载时立即加载这个共享模块,而不是在第一次使用时异步加载。这对于某些共享库(如 React)可能是有益的。
  • requiredVersion:

    • 类型string
    • 说明: 用于指定所需的版本范围。Webpack 会根据这个版本来判断是否可以共享该模块。如果不兼容,则会引发警告或错误。
  • strictVersion:

    • 类型boolean
    • 说明: 如果设置为 true,则要求共享模块的版本必须完全匹配 requiredVersion 指定的版本。这样可以避免因版本不匹配导致的问题。
  • import:

    • 类型string
    • 说明: 可以用于指定在其他应用中使用时共享模块的导入路径。例如,你可以使用此配置来导入不同的模块路径。
  • shareScope:

    • 类型string
    • 说明: 用于指定共享模块的作用域,通常在微前端架构中使用。

完整的微前端应用

Module Federation 是一种非常新的技术,社区资料还比较少,接下来我们来编写一个完整的微前端应用,帮助你更好理解 MF 的功能与用法。微前端架构通常包含一个作为容器的主应用及若干负责渲染具体页面的子应用,分别对标到下面示例的 packages/hostpackages/order 应用:

//MF-micro-fe
├─ packages
│  ├─ host
│  │  ├─ public
│  │  │  └─ index.html
│  │  ├─ src
│  │  │  ├─ App.js
│  │  │  ├─ HomePage.js
│  │  │  ├─ Navigation.js
│  │  │  ├─ bootstrap.js
│  │  │  ├─ index.js
│  │  │  └─ routes.js
│  │  ├─ package.json
│  │  └─ webpack.config.js
│  └─ order
│     ├─ src
│     │  ├─ OrderDetail.js
│     │  ├─ OrderList.js
│     │  ├─ main.js
│     │  └─ routes.js
│     ├─ package.json
│     └─ webpack.config.js
├─ lerna.json
└─ package.json

示例代码:MF-micro-fe,可以辅助阅读

先看看 order 对应的 MF 配置:

module.exports = {
  // ...
  plugins: [
    new ModuleFederationPlugin({
      name: "order",
      filename: "remoteEntry.js",
      // 导入路由配置
      exposes: {
        "./routes": "./src/routes",
      },
    }),
  ],
};

注意,order 应用实际导出的是路由配置文件 routes.js。而 host 则通过 MF 插件导入并消费 order 应用的组件,对应配置:

module.exports = {
  // ...
  plugins: [
    // 模块使用方也依然使用 ModuleFederationPlugin 插件搭建 MF 环境
    new ModuleFederationPlugin({
      // 使用 remotes 属性声明远程模块列表
      remotes: {
        // 地址需要指向导出方生成的应用入口文件
        RemoteOrder: "order@http://localhost:8081/dist/remoteEntry.js",
      },
    })
  ],
  // ...
};

注意,order 应用实际导出的是路由配置文件 routes.js。而 host 则通过 MF 插件导入并消费 order 应用的组件,对应配置:

module.exports = {
  // ...
  plugins: [
    // 模块使用方也依然使用 ModuleFederationPlugin 插件搭建 MF 环境
    new ModuleFederationPlugin({
      // 使用 remotes 属性声明远程模块列表
      remotes: {
        // 地址需要指向导出方生成的应用入口文件
        RemoteOrder: "order@http://localhost:8081/dist/remoteEntry.js",
      },
    })
  ],
  // ...
};

之后,在 host 应用中引入 order 的路由配置并应用到页面中:

import localRoutes from "./routes";
// 引入远程 order 模块
import orderRoutes from "RemoteOrder/routes";

const routes = [...localRoutes, ...orderRoutes];

const App = () => (
  <React.StrictMode>
    <HashRouter>
      <h1>Micro Frontend Example</h1>
      <Navigation />
      <Routes>
        {routes.map((route) => (
          <Route
            key={route.path}
            path={route.path}
            element={
              <React.Suspense fallback={<>...</>}>
                <route.component />
              </React.Suspense>
            }
            exact={route.exact}
          />
        ))}
      </Routes>
    </HashRouter>
  </React.StrictMode>
);

export default App;

通过这种方式,一是可以将业务代码分解为更细粒度的应用形态;二是应用可以各自管理路由逻辑,降低应用间耦合性。最终能降低系统组件间耦合度,更有利于多团队协作。除此之外,MF 技术还有非常大想象空间,国外有大神专门整理了一系列实用 MF 示例:Module Federation Examples,感兴趣的读者务必仔细阅读这些示例代码。

拓展4

什么是沙箱技术

沙箱”是一种用于安全和隔离的技术,通常用于运行不可信或潜在危险的代码而不影响主系统或其他应用程序的环境。在软件开发、网络安全和虚拟化等领域,沙箱的概念被广泛应用。以下是沙箱的一些关键特征和应用场景:

关键特征

  1. 隔离性:

    • 沙箱环境与主系统或其他应用程序是隔离的。运行在沙箱中的代码无法直接访问主系统的资源,确保主系统不会受到不良代码的影响。
  2. 安全性:

    • 沙箱可以限制代码的权限和行为,防止其执行恶意操作,如访问敏感数据、修改系统文件等。
  3. 可控性:

    • 在沙箱中运行的代码可以被监控和控制,可以轻松地对其进行调试和分析。
  4. 资源限制:

    • 沙箱可以限制代码的资源使用,例如 CPU、内存和网络访问,防止代码过度消耗系统资源。

应用场景

  1. Web 浏览器:

    • 现代浏览器使用沙箱技术来隔离网页和插件,防止恶意网页对用户系统的攻击。例如,浏览器的“安全沙箱”可以防止 JavaScript 访问本地文件系统。
  2. 虚拟机和容器:

    • 通过虚拟机(如 VMware、VirtualBox)和容器(如 Docker),可以创建沙箱环境来运行应用程序。每个虚拟机或容器都是一个独立的执行环境,提供了隔离和安全性。
  3. 移动应用:

    • 移动操作系统(如 Android 和 iOS)使用沙箱来限制应用程序的访问权限,确保一个应用程序无法影响其他应用程序或系统。
  4. API 测试:

    • 在开发和测试 API 时,可以使用沙箱环境来模拟生产环境,以安全地测试代码而不影响实际数据。
  5. 恶意软件分析:

    • 安全研究人员使用沙箱环境分析和研究恶意软件的行为,帮助识别威胁而不危及主系统的安全。

在微前端中的应用

在微前端架构中,沙箱可以用于隔离不同微前端应用之间的代码和状态,确保它们不会相互影响。虽然 Module Federation 本身没有内置的沙箱功能,但可以借助其他技术(如 iframe 或 Web Workers)来实现沙箱效果,增强微前端应用的安全性。

Module Federation 实现的微前端架构并未提供沙箱能力,可能会造成一些问题,如全局命名冲突、CSS 风格冲突、恶意代码注入、CSS 风格冲突等。而qiankun是拥有js沙箱技术的,感兴趣的可以前往官网学习。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2266750.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[bug]java导出csv用Microsoft Office Excel打开乱码解决

[bug]java导出csv用Microsoft Office Excel打开乱码 ‍ 现象 首先这个csv文件用macbook自带的 "Numbers表格" 软件打开是不乱码的, 但是使用者是Windows系统,他的电脑没有"Numbers表格"工具, ​​ 他用Microsoft Office Excel打开之后出现乱码,如下图…

关于分布式数据库需要了解的相关知识!!!

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///计算机爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于关于分布式数据库方面的相关内容&a…

tortoisegit推送失败

tortoisegit推送失败 git.exe push --progress -- "origin" testLidar:testLidar /usr/bin/bash: gitgithub.com: No such file or directory fatal: Could not read from remote repository. Please make sure you have the correct access rights and the reposit…

moviepy将图片序列制作成视频并加载字幕 - python 实现

DataBall 助力快速掌握数据集的信息和使用方式&#xff0c;会员享有 百种数据集&#xff0c;持续增加中。 需要更多数据资源和技术解决方案&#xff0c;知识星球&#xff1a; “DataBall - X 数据球(free)” -------------------------------------------------------------…

清空DNS 缓存

如果遇到修改了host文件&#xff0c;但是IP和域名的映射有问题的情况&#xff0c;可以尝试刷新DNS缓存。 ipconfig/flushdns win建加R建&#xff0c;然后输入cmd&#xff0c;然后回车 然后回车&#xff0c;或者点击确定按钮。 出现如下所示标识清空DNS 缓存成功。

2024最新鸿蒙开发面试题合集(二)-HarmonyOS NEXT Release(API 12 Release)

上一篇面试题链接&#xff1a;https://mp.csdn.net/mp_blog/creation/editor/144685078 1. 鸿蒙简单介绍和发展历程 HarmonyOS 是新一代的智能终端操作系统&#xff0c;为不同设备的智能化、互联与协同提供了统一的语言。带来简洁&#xff0c;流畅&#xff0c;连续&#xff0…

Yocto 项目 - 共享状态缓存 (Shared State Cache) 机制

引言 在嵌入式开发中&#xff0c;构建效率直接影响项目的开发进度和质量。Yocto 项目通过其核心工具 BitBake 提供了灵活而强大的构建能力。然而&#xff0c;OpenEmbedded 构建系统的传统设计是从头开始构建所有内容&#xff08;Build from Scratch&#xff09;&#xff0c;这…

idea 8年使用整理

文章目录 前言idea 8年使用整理1. 覆盖application配置2. 启动的时候设置编辑空间大小&#xff0c;并忽略最大空间3. 查询类的关系4. 查看这个方法的引用关系5. 查看方法的调用关系5.1. 查看被调用关系5.2. 查看调用关系 6. 方法分隔线7. 选择快捷键类型8. 代码预览插件9. JReb…

04软件测试需求分析案例-用户登录

通读文档&#xff0c;提取信息&#xff0c;提出问题&#xff0c;整理为需求。 从需求规格说明、设计说明、配置说明等文档获取原始需求&#xff0c;通读原始需求&#xff0c;分析有哪些功能&#xff0c;每种功能要完成什么业务&#xff0c;业务该如何实现&#xff0c;业务逻辑…

【MySQL】踩坑笔记——保存带有换行符等特殊字符的数据,需要进行转义保存

问题描述 从DBeaver中导出了部分业务数据的 insert sql&#xff0c;明明在开发、测试环境都可以一把执行通过&#xff0c;却在预发环境执行前的语法检查失败了&#xff0c;提示有SQL语法错误。 这条SQL长这样&#xff0c;default_sql是要在odps上执行的sql语句&#xff0c;提…

windos挂载目录到linux

验证环境麒麟V10 1: 在windows任意目录设置共享文件夹 2&#xff1a;记住网络路径\LAPTOP-86JV6NT1\gantie13_sdk 在linux中替换为本机ip级相对路径 比如本级ip是192.168.23.23&#xff0c;linux环境需要ping通本地地址 3&#xff1a; sudo apt-get install cifs-utils sud…

springboot494基于java的综合小区管理系统(论文+源码)_kaic

摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统综合小区管理系统信息管理难度大&#xff0c;容错率低&am…

数据分析篇001

目录 一、数据是什么&#xff1f; 二、数据能做什么&#xff1f; 三、数据应用四步骤 第一步---搭建数据体系 第二步---积累数据资产 第三步---完成数据分析 第四步---实现数据应用 四、数据的三种性质 变异性 规律性&#xff08;以正态分布为例&#xff09; 客观性…

ModbusTCP从站转Profinet主站案例

一. 案例背景 在复杂的工业自动化场景中&#xff0c;企业常常会采用不同品牌的设备来构建生产系统。西门子SINAMICS G120变频器以其高性能、高精度的速度和转矩控制功能&#xff0c;在电机驱动领域应用广泛。施耐德M580可编程逻辑控制器则以强大的逻辑控制和数据处理能力著称&…

flask后端开发(8):Flask连接MySQL数据库+ORM增删改查

目录 数据库初始化数据库连接创建数据库表添加用户查询用户更新用户删除 在Flask中&#xff0c;很少会使用pymysql直接写原生SQL语句去操作数据库&#xff0c;更多的是通过SQLAichemy提供的ORM技术&#xff0c;类似于操作普通Python对象一样实现数据库的增删改查操作&#xff0…

【社区投稿】自动特征auto trait的扩散规则

自动特征auto trait的扩散规则 公式化地概括&#xff0c;auto trait marker trait derived trait。其中&#xff0c;等号右侧的marker与derived是在Rustonomicon书中的引入的概念&#xff0c;鲜见于Rust References。所以&#xff0c;若略感生僻&#xff0c;不奇怪。 marker …

【Go】context标准库

文章目录 1. 概述1.1 什么是 Context1.2 设计原理1.3 使用场景1.4 Context 分类核心:Context接口2. 源码解读4个实现emptyCtxTODO 和 BackgroundcancelCtxWithCancelcancelCtx.propagateCancel 构建父子关联parentCancelCtx 获取父上下文中的内嵌cancelCtxcanceltimerCtxWithT…

【视觉惯性SLAM:四、相机成像模型】

相机成像模型介绍 相机成像模型是计算机视觉和图像处理中的核心内容&#xff0c;它描述了真实三维世界如何通过相机映射到二维图像平面。相机成像模型通常包括针孔相机的基本成像原理、数学模型&#xff0c;以及在实际应用中如何处理相机的各种畸变现象。 一、针孔相机成像原…

CAPL_构建基于UDS的刷写学习—04 思路的构建_第一部分

前言与导读&#xff1a; 基于前几篇文章我们已经梳理了HEX文件、S19文件的读取和UDS关键的0x34/0x36/0x37等服务的结构。 基于此&#xff0c;我们差不多就完成了前期的知识储备了&#xff0c;那么完成最终的目的——使用capl实现我们还需要解决以下几个问题。 1、文件如何读…

分布式 IO 模块助力冲压机械臂产线实现智能控制

在当今制造业蓬勃发展的浪潮中&#xff0c;冲压机械臂产线的智能化控制已然成为提升生产效率、保障产品质量以及增强企业竞争力的关键所在。而分布式 IO 模块的应用&#xff0c;正如同为这条产线注入了一股强大的智能动力&#xff0c;开启了全新的高效生产篇章。 传统挑战 冲压…