”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 使用 Rspack 和 Modern.js 的微前端

使用 Rspack 和 Modern.js 的微前端

发布于2024-11-04
浏览:587

Photo by Markus Spiske on Unsplash

Introduction

Web developers are beginning to take a strong interest in the idea of Micro-Frontends. Remix and Next.js are two examples of React meta-frameworks that have a lot of useful capabilities, but they don't always give complete support for creating micro-frontends. By providing built-in support for micro-frontends, Modern.js fills this gap and enables developers to design modular, scalable, and flexible apps.

Modern.js Framework

ByteDance created Modern.js, a progressive web framework built on React. Giving developers an outstanding development experience and empowering applications to provide better user experiences simplifies the process of creating contemporary web applications. In addition to having built-in state management, data gathering, and routing capabilities, Modern.js offers all required setups and tools for React applications.

Key features of Modern.js include:

  • Rust Bundler: The primary bundler is Rspack, however, it can be switched to Webpack.
  • Progressive: Facilitates the incremental integration of features via templates and plugins.
  • Integration: Offers isomorphic CSR and SSR development in a unified development and production environment.
  • Out of the Box: Provides debugging tools, ESLint, default TypeScript support, and integrated build tools.
  • Ecology: Module packaging, micro-frontend support, and integrated state management.
  • Routing Modes: Allows for file-convention-based and self-controlled routing.
  • With integrated micro-frontend functionality, Modern.js facilitates scalable, maintainable, and flexible apps while streamlining the development process.

Rspack

Rspack is a high-performance JavaScript bundler written in Rust, designed to be compatible with the webpack ecosystem while providing significantly faster build speeds.

Why Rspack?
Rspack was created to solve performance issues at ByteDance, where large app projects had excessively long build and startup times. Rspack addresses these problems with:

  • Fast Dev Mode Startup: Ensures quick startup to maintain developer productivity.
  • Quick Builds: Reduces CI/CD pipeline build times, improving productivity and application delivery.
  • Flexible Configuration: Maintains webpack's flexibility, easing the transition for legacy projects.
  • Optimized Production Builds: Enhances production optimizations with Rust's multithreading capabilities.

Let's start

This guide will walk us through setting up a Modern.js project, enabling micro-frontend capabilities, and configuring multiple applications to work together seamlessly. We will use pnpm as our package manager and Rspack as the bundler. The shell app will act as the main application that dynamically loads micro-frontends.

Step-by-Step Instructions

1. Initialize the Repository
Start by initializing a new pnpm project in the root directory.

pnpm init

This will create a package.json file to manage dependencies and scripts for your project.

2. Create the Shell Application
Next, create the shell application using Modern.js.

npx @modern-js/create@latest shell-app

( Note: npx @modern-js/create@latest will also commit new changes. So you might have to manually remove .git folder and keep it only on the top level: rm -rf .git )

Select the following options:

  • Project type: Web App
  • Programming language: TS (TypeScript)
  • Package manager: pnpm

  • In the latest versions, Rspack is used as the default bundler.

3. Run the Shell Application
Navigate into the shell application directory and start the development server.

cd shell-app
pnpm start

Running the application ensures everything is set up correctly. You should see the default Modern.js application running in your browser.

4. Enable Micro-frontend Features
Enable micro-frontend features for the shell application.

pnpm run new

Select the following options:

  • Operation: Enable Features
  • Feature: Enable Micro Frontend

Enabling micro-frontend features configures the project to support dynamic loading and micro-frontend integration, allowing us to build modular and scalable applications.

5. Update modern.config.ts
Modify the configuration file to include necessary plugins and runtime settings.

// shell-app/modern.config.ts
import { appTools, defineConfig } from '@modern-js/app-tools';
import { garfishPlugin } from '@modern-js/plugin-garfish';

export default defineConfig({
  runtime: {
    router: true,
    state: true,
    masterApp: {},
  },
  plugins: [appTools(), garfishPlugin()],
});

This configuration enables the router and sets up the shell app as a main application using the garfishPlugin.
(Garfish, developed by the ByteDance team, is a micro-frontend framework similar to single-spa. Modern.js handles the rest under the hood, so we don't need to focus much on it.)

6. Create Custom Entry and Update App.tsx
Remove the default routes folder and create a new App.tsx file in the src directory.

// shell-app/src/App.tsx
import { Suspense } from 'react';
import { defineConfig } from '@modern-js/runtime';
import {
  BrowserRouter as Router,
  Routes,
  Route,
} from '@modern-js/runtime/router';
import styled from '@modern-js/runtime/styled';

const AppContainer = styled.div`
  text-align: center;
  padding: 20px;
  background-color: #f5f5f5;
`;

const Title = styled.h1`
  color: #333;
`;

const Loading = styled.div`
  font-size: 1.2em;
  color: #666;
`;

const App: React.FC = () => {
  return (
    
      
        Loading...}>
          
            Welcome to the Shell Application}
            />
          
        
      
    
  );
};

export default defineConfig(App, {
  masterApp: {
    manifest: {
      getAppList: async () => {
        // here we will add our mfe
        return [];
      },
    },
  },
});

Here, we set up the main component for the shell application, configuring it as the master application that will manage micro-frontends.

7. Create the First Micro-frontend Application
Navigate back to the root directory and create the first micro-frontend.

cd ..
npx @modern-js/create@latest mfe1

Select the following options:

  • Project type: Web App
  • Programming language: TS (TypeScript)
  • Package manager: pnpm

8. Enable Micro-frontend Feature for mfe1
Enable micro-frontend features for the first micro-frontend application.

cd mfe1
pnpm run new

Select the following options:

  • Operation: Enable Features
  • Feature: Enable Micro Frontend

9. Update modern.config.ts for mfe1
Update the configuration file for the micro-frontend.

// mfe1/modern.config.ts
import { appTools, defineConfig } from '@modern-js/app-tools';
import { garfishPlugin } from '@modern-js/plugin-garfish';

export default defineConfig({
  server: {
    port: 8081,
  },
  deploy: {
    microFrontend: true,
  },
  plugins: [appTools(), garfishPlugin()],
});

This configuration sets the port and enables micro-frontend features.

10. Create Custom Entry and Update App.tsx for mfe1
Remove the default routes folder and create a new App.tsx file in the src directory.

// mfe1/src/App.tsx
import React from 'react';
import {
  BrowserRouter as Router,
  Routes,
  Route,
  Link,
} from '@modern-js/runtime/router';
import styled from '@modern-js/runtime/styled';

const Container = styled.div`
  font-size: 1.2em;
  color: #333;
`;

const AppContainer = styled.div`
  text-align: center;
  padding: 20px;
  background-color: #f5f5f5;
`;

const NavBar = styled.nav`
  margin-bottom: 20px;
  a {
    margin: 0 10px;
    color: #007acc;
    text-decoration: none;
  }
`;

const Contact: React.FC = () => {
  return Contact Page;
};

const Home: React.FC = () => {
  return Home Page;
};

const About: React.FC = () => {
  return About Page;
};

const App: React.FC = () => {
  return (
    
      
        
          Home
          About
          Contact
        
        
          } />
          } />
          } />
        
      
    
  );
};

export default App;

Here, we set up the micro-frontend application with its routes and navigation.

11. Update the Root package.json
Add a start script in the root package.json to run both the shell app and the micro-frontend simultaneously.

{
  "name": "micro-frontends-with-modern.js-and-rspack",
  "version": "1.0.0",
  "description": "",
  "scripts": {
     "start": "pnpm --filter shell-app --filter mfe1 --parallel start"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

12. Update App.tsx in shell-app to Load mfe1
Modify the App.tsx file in the shell application to dynamically load and integrate the micro-frontend.

// shell-app/src/App.tsx
import React, { Suspense } from 'react';
import { useModuleApps } from '@modern-js/plugin-garfish/runtime';
import { defineConfig } from '@modern-js/runtime';
import {
  BrowserRouter as Router,
  Routes,
  Route,
  Link,
} from '@modern-js/runtime/router';
import styled from '@modern-js/runtime/styled';

const AppContainer = styled.div`
  text-align: center;
  padding: 20px;
  background-color: #f5f5f5;
`;

const Title = styled.h1`
  color: #333;
`;

const Loading = styled.div`
  font-size: 1.2em;
  color: #666;
`;

const App: React.FC = () => {
  const { MApp } = useModuleApps();
  return (
    
      
        Welcome to the Shell Application
        Loading...}>
          
          
            
                  
MFE1
> } />
); }; export default defineConfig(App, { masterApp: { manifest: { getAppList: async () => { return [ { name: 'mfe1', entry: 'http://localhost:8081/index.js', activeWhen: path => path.includes('mfe1'), }, ]; }, }, }, });

The useModuleApps hook returns sub-app components and gives us full control of routing.

13. Run Both Applications
Navigate back to the root directory and run the start script to launch both applications.

pnpm start

This command will start both the shell-app and mfe1 concurrently, allowing you to access mfe1 inside the shell-app and navigate its subroutes.

Vertical vs Horizontal splits for micro-frontends

We looked at the vertical split of micro-frontends in the previous section. The horizontal divide with module federation will be discussed in the following section. What do these splits indicate, though?

A horizontal split is a pattern in which various application components—manufactured by separate teams—are merged into a single view or path. Every team is in charge of particular features or portions of the page, which are then smoothly integrated. Because teams may operate independently without impacting the program as a whole, this enables parallel development and speedier deployment.

On the other hand, a vertical split separates the application into discrete micro-frontends, each in charge of a separate feature or area of the system as a whole. These micro-frontends operate as distinct, feature-rich programs that can be accessed via several URLs. Because each micro-frontend can be designed, deployed, and updated individually, this approach improves modularity and scalability.

Micro-frontends with Rspack and Modern.js

Adding mfe2 with Module Federation for Horizontal Split

This section will create mfe2 using Module Federation, focusing on a horizontal split. This means mfe2 will handle a specific part of the application's UI, such as a shared component or feature, that can be dynamically loaded and shared across different parts of the shell application.

1. Create the Second Microfrontend Application
Navigate back to the root directory and create the second micro-frontend ( since it's module-federation we can use not only Modern.js ):

cd ..
npx @modern-js/create@latest mfe2

Select the following options:

  • Project type: Web App
  • Programming language: TS (TypeScript)
  • Package manager: pnpm

2. Update modern.config.ts for mfe2
Update the configuration file to include module federation settings.
We added some additional configurations to make module federation work with Modern.js. You can use a regular React app with Rspack instead of Modern.js as well. Additional examples you can find here module-fedration examples.

// mfe2/modern.config.ts
import { appTools, defineConfig } from '@modern-js/app-tools';

export default defineConfig({
  server: {
    port: 8082,
  },
  source: {
    enableAsyncEntry: true,
  },
  plugins: [appTools({ bundler: 'rspack' })],
  tools: {
    rspack: (config, { rspack, appendPlugins }) => {
      // just to remove noise form ts
      if (config.output) {
        config.output.publicPath = 'auto';
        config.output.uniqueName = 'mfe2';
      } else {
        config.output = {
          publicPath: 'auto',
          uniqueName: 'mfe2',
        };
      }

      if (config.optimization) {
        delete config.optimization.splitChunks;
        delete config.optimization.runtimeChunk;
      }

      appendPlugins([
        new rspack.container.ModuleFederationPlugin({
          name: 'mfe2',
          library: { type: 'var', name: 'mfe2' },
          filename: 'static/js/remoteEntry.js',
          exposes: {
            './MFE2Component': './src/MFE2Component.tsx',
          },
          shared: {
            react: { singleton: true },
            'react-dom': { singleton: true },
          },
        }),
      ]);
    },
  },
});

3. Create MFE2Component
Create a new component that will be exposed via module federation.

// src/MFE2Component.tsx
import React from 'react';
import styled from '@modern-js/runtime/styled';

const Container = styled.div`
  font-size: 1.2em;
  color: #007acc;
`;

const MFE2Component: React.FC = () => {
  return This is MFE2 Component!;
};

export default MFE2Component;

4. Update the Shell App modern.config.ts
We need to add module fedration configuration in the Shell App to consume what mfe2 produce.

import { appTools, defineConfig } from '@modern-js/app-tools';
import { garfishPlugin } from '@modern-js/plugin-garfish';

export default defineConfig({
  runtime: {
    router: true,
    state: true,
    masterApp: {},
  },
  source: {
    // automatically generated asynchronous boundary via Dynamic Import, allowing the page code to consume remote modules generated by the module federation.
    enableAsyncEntry: true,
  },
  output: {
    disableTsChecker: true,
  },
  tools: {
    rspack: (config, { rspack, appendPlugins }) => {
      appendPlugins([
        new rspack.container.ModuleFederationPlugin({
          name: 'host',
          remotes: {
            mfe2: 'mfe2@http://localhost:8082/static/js/remoteEntry.js',
          },
          shared: {
            react: { singleton: true },
            'react-dom': { singleton: true },
          },
        }),
      ]);
    },
  },
  plugins: [
    appTools({
      bundler: 'rspack',
    }),
    garfishPlugin(),
  ],
});

5. Update the Shell App to Load mfe2
Update the App.tsx in the shell-app to dynamically load mfe2.

// shell-app/src/App.tsx
...
import MFE2Component from 'mfe2/MFE2Component';
...
 Welcome to the Shell Application
 

In this updated App.tsx, we've added a link to mfe2 and included it in the manifest configuration.

6. Run Both Applications
Navigate back to the root directory, and update the script.

// package.json
...
"scripts": {
    "start": "pnpm --filter shell-app --filter mfe1  --filter mfe2 --parallel start"
  },
...

Run the start script to launch all applications.

pnpm start

This command will start both the shell-app, mfe1, and mfe2 concurrently, allowing you to access mfe2 inside the shell-app and navigate its subroutes.

Conclusion

We have barely scratched the surface of Modern.js by concentrating on just a couple of its features: integrated micro-frontend capabilities and module federation using Rspack. This is merely a simple illustration to help you get ideas for using Modern.js and creating micro-frontends for your applications.

? Find the code for this article on GitHub.

版本声明 本文转载于:https://dev.to/kyrylobashtenko/micro-frontends-with-rspack-and-modernjs-2l2d?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • HTML格式标签
    HTML格式标签
    HTML 格式化元素 **HTML Formatting is a process of formatting text for better look and feel. HTML provides us ability to format text without us...
    编程 发布于2025-04-09
  • 如何干净地删除匿名JavaScript事件处理程序?
    如何干净地删除匿名JavaScript事件处理程序?
    删除匿名事件侦听器将匿名事件侦听器添加到元素中会提供灵活性和简单性,但是当要删除它们时,可以构成挑战,而无需替换元素本身就可以替换一个问题。 element? element.addeventlistener(event,function(){/在这里工作/},false); 要解决此问题,请考虑...
    编程 发布于2025-04-09
  • 如何使用Regex在PHP中有效地提取括号内的文本
    如何使用Regex在PHP中有效地提取括号内的文本
    php:在括号内提取文本在处理括号内的文本时,找到最有效的解决方案是必不可少的。一种方法是利用PHP的字符串操作函数,如下所示: 作为替代 $ text ='忽略除此之外的一切(text)'; preg_match('#((。 &&& [Regex使用模式来搜索特...
    编程 发布于2025-04-09
  • 如何使用node-mysql在单个查询中执行多个SQL语句?
    如何使用node-mysql在单个查询中执行多个SQL语句?
    在node-mysql node-mysql文档最初出于安全原因最初禁用多个语句支持,因为它可能导致SQL注入攻击。要启用此功能,您需要在创建连接时将倍增设置设置为true: var connection = mysql.createconnection({{multipleStatement:...
    编程 发布于2025-04-09
  • 如何配置Pytesseract以使用数字输出的单位数字识别?
    如何配置Pytesseract以使用数字输出的单位数字识别?
    Pytesseract OCR具有单位数字识别和仅数字约束 在pytesseract的上下文中,在配置tesseract以识别单位数字和限制单个数字和限制输出对数字可能会提出质疑。 To address this issue, we delve into the specifics of Te...
    编程 发布于2025-04-09
  • \“(1)vs.(;;):编译器优化是否消除了性能差异?\”
    \“(1)vs.(;;):编译器优化是否消除了性能差异?\”
    答案: 在大多数现代编译器中,while(1)和(1)和(;;)之间没有性能差异。编译器: perl: 1 输入 - > 2 2 NextState(Main 2 -E:1)V-> 3 9 Leaveloop VK/2-> A 3 toterloop(next-> 8 last-> 9 ...
    编程 发布于2025-04-09
  • 如何将MySQL数据库添加到Visual Studio 2012中的数据源对话框中?
    如何将MySQL数据库添加到Visual Studio 2012中的数据源对话框中?
    在Visual Studio 2012 尽管已安装了MySQL Connector v.6.5.4,但无法将MySQL数据库添加到实体框架的“ DataSource对话框”中。为了解决这一问题,至关重要的是要了解MySQL连接器v.6.5.5及以后的6.6.x版本将提供MySQL的官方Visual...
    编程 发布于2025-04-09
  • 如何简化PHP中的JSON解析以获取多维阵列?
    如何简化PHP中的JSON解析以获取多维阵列?
    php 试图在PHP中解析JSON数据的JSON可能具有挑战性,尤其是在处理多维数组时。 To simplify the process, it's recommended to parse the JSON as an array rather than an object.To do...
    编程 发布于2025-04-09
  • 如何限制动态大小的父元素中元素的滚动范围?
    如何限制动态大小的父元素中元素的滚动范围?
    在交互式接口中实现垂直滚动元素的CSS高度限制,控制元素的滚动行为对于确保用户体验和可访问性是必不可少的。一种这样的方案涉及限制动态大小的父元素中元素的滚动范围。问题:考虑一个布局,其中我们具有与用户垂直滚动一起移动的可滚动地图div,同时与固定的固定sidebar保持一致。但是,地图的滚动无限期...
    编程 发布于2025-04-09
  • 如何克服PHP的功能重新定义限制?
    如何克服PHP的功能重新定义限制?
    克服PHP的函数重新定义限制在PHP中,多次定义一个相同名称的函数是一个no-no。尝试这样做,如提供的代码段所示,将导致可怕的“不能重新列出”错误。 但是,PHP工具腰带中有一个隐藏的宝石:runkit扩展。它使您能够灵活地重新定义函数。 runkit_function_renction_re...
    编程 发布于2025-04-09
  • 如何为PostgreSQL中的每个唯一标识符有效地检索最后一行?
    如何为PostgreSQL中的每个唯一标识符有效地检索最后一行?
    postgresql:为每个唯一标识符提取最后一行,在Postgresql中,您可能需要遇到与在数据库中的每个不同标识相关的信息中提取信息的情况。考虑以下数据:[ 1 2014-02-01 kjkj 在数据集中的每个唯一ID中检索最后一行的信息,您可以在操作员上使用Postgres的有效效率: ...
    编程 发布于2025-04-09
  • 在程序退出之前,我需要在C ++中明确删除堆的堆分配吗?
    在程序退出之前,我需要在C ++中明确删除堆的堆分配吗?
    在C中的显式删除 在C中的动态内存分配时,开发人员通常会想知道是否需要手动调用“ delete”操作员在heap-exprogal exit exit上。本文深入研究了这个主题。 在C主函数中,使用了动态分配变量(HEAP内存)的指针。当应用程序退出时,此内存是否会自动发布?通常,是。但是,即使在这...
    编程 发布于2025-04-09
  • 如何使用Python理解有效地创建字典?
    如何使用Python理解有效地创建字典?
    在python中,词典综合提供了一种生成新词典的简洁方法。尽管它们与列表综合相似,但存在一些显着差异。与问题所暗示的不同,您无法为钥匙创建字典理解。您必须明确指定键和值。 For example:d = {n: n**2 for n in range(5)}This creates a dicti...
    编程 发布于2025-04-09
  • 为什么使用Firefox后退按钮时JavaScript执行停止?
    为什么使用Firefox后退按钮时JavaScript执行停止?
    导航历史记录问题:JavaScript使用Firefox Back Back 此行为是由浏览器缓存JavaScript资源引起的。要解决此问题并确保在后续页面访问中执行脚本,Firefox用户应设置一个空功能。 警报'); }; alert('inline Alert')...
    编程 发布于2025-04-09
  • 为什么尽管有效代码,为什么在PHP中捕获输入?
    为什么尽管有效代码,为什么在PHP中捕获输入?
    在php ;?>" method="post">The intention is to capture the input from the text box and display it when the submit button is clicked.但是,输出...
    编程 发布于2025-04-09

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3