webpack 5 module Federation

Solving the difficult problems of implementing micro front end with webpack 5 module Federation

explain

webpack 5 adds the function of Module Federation, which can help to form multiple independent builds into an application, and different builds can be developed and deployed independently.

With the help of module Federation, we can realize micro front-end to a certain extent

concept

1. What is a micro front end?

  • Micro front end extends the concept of micro service to front-end development. Generally speaking, in a micro service architecture, there will be multiple back-end teams developing different business services, while the front-end usually has only one team to centrally maintain a SPA single page application. Over time, the SPA maintained by the front-end team will increase with the growth of business, Become difficult to maintain (project startup time, CI\CD time, etc.);
  • The micro front-end can help us split SPA applications into multiple applications that can be maintained and deployed independently according to the business like the back-end. On the one hand, we can update the front-end of which business is changed nearby; It can also help build a team in a single business field from front-end to back-end
  • It can be understood that micro front-end is an architectural style that aggregates multiple small-scale front-end applications that can be delivered independently into a whole

2. What kind of products are suitable for micro front-end?

  • Large front end projects
  • Cross application module sharing among multiple projects
  • There is a need to split multiple independent subsystems and deploy and maintain them independently

3. What can module Federation do?

  • Module Federation provides the ability to remotely load applications on other servers in the current application
  • Using module Federation, you can implement a decentralized application deployment group. Each application is deployed separately. Each application can refer to other applications or be referenced by other applications (js level)
  • Cross technology stack development can be realized with the help of third-party tools (react + vue 2)
  • It can be used to realize the smooth iteration of front-end projects (the old project is an application and the new project is an application, which are connected remotely, so that we can gradually enrich the new project to replace the content of the old project)
  • It can also help build a team in a single business field from front-end to back-end

4. What are local module and remote module, and what is host \ remote?

  • host: webpack build that consumes other remote (i.e. applications using remote modules)
  • remote: webpack construction consumed by the host (i.e. providing available modules for other applications)
  • Host and remote are relative. An application can be either host or remote. Therefore, the micro front end implemented by module Federation is decentralized.
  • The local module is the module in the current project during our local development
  • The remote module does not belong to the current build. It is a module provided by the remote application that is loaded in at runtime

5. Negative impact of module Federation

  • Loading logic such as remote modules at runtime may cause some performance problems
  • Local development needs to open multiple port services, which is more troublesome
  • Loading third-party dependencies on demand is difficult to achieve
  • Compared with the Traditional spa, the project structure is somewhat complex
  • Versioning during iterations requires more attention

Module Federation Api

// webpack.config.js
export default {
  plugins: [
    new ModuleFederationPlugin({
      name: "app_two", // Current app name
      filename: "remoteEntry.js", // Loading entry when the external application references the current application module
      exposes: {
        Search: "./src/Search" // Output to modules used by external applications
      },
      remotes: {
        appOne: 'appOne@http://localhost:3003/remoteEntry.js' / / the remote application address that the current application will use
      }
      shared: [ // The modules shared with the remote module are configured together with the remote module, so that the library will only be loaded once in the page to avoid repeated loading of third-party dependencies
        "react", 
        "react-dom"
      ]
    })
  ]
};

// /index.js note that the entry must dynamically introduce modules
import('./bootstrap');

// /bootstrap.js
ReactDOM.render(
  <App/>
  document.getElementById('root'),
);

// /App.js module using remote application import('remote application / remote application module ')
const AppOne = React.lazy(() => import('appOne/Button'));

shared description

/ **
 *Advanced configuration of modules that should be shared within the scope of sharing.
 * /
declare interface SharedConfig  {
 / **
  *Include the provided and backup modules directly after the asynchronous request. This also allows this shared module to be used in the initial load. All possible shared modules also need to be urgently.
  * /
 eager?: boolean;
/ **
  *Modules with shared scope shall be provided. If the shared module cannot be found in the shared scope or the version is invalid, it also acts as a fallback module. The default is the attribute name.
  * /
 import?: DevTool;
/ **
  *Package name, used to determine the required version from the description file. Required only if the package name cannot be automatically determined on request.
  * /
 packageName?: string;
/ **
  *Version requirements from modules in the shared scope.
  * /
 requiredVersion?: DevTool;
/ **
  *Find the module under this key in the shared scope.
  * /
 shareKey?: string;
/ **
  *Share scope name.
  * /
 shareScope?: string;
/ **
  *Only a single version of a module is allowed to be shared within the shared scope (disabled by default).
  * /
 singleton?: boolean;
/ **
  *If the version is invalid, the shared module will not be accepted (yes by default. If the local backup module is available and the shared module is not a single instance, otherwise no,Invalid if the required version is not specified).
  * /
 strictVersion?: boolean;
/ **
  *The version of the module provided. The lower matching version will be replaced, but the higher version will not be replaced.
  * /
 version?: DevTool;
}
```

### Problems encountered in development

#### 1. How to emulate the service discovery in microservices to realize the runtime management of different application versions?
> Because the reference to "remote dependent application" is in build When packaged, the fixed value packaged into the code( url),In order to distinguish whether there is an update by file name, we need to remoteEntry.[contenthash].js add hash,;At this time, if, "If the "remote dependent application" has a version update, the application using the "remote dependent application" should also be updated (otherwise, it will be the resources of the current period),In this way, we can't achieve our goal of "publishing only changed applications".

> In order to achieve this goal, it is best to remoteEntry Exact address of( url)Inject when the project is running,This avoids the dilemma of changing one application and updating other applications
> [Reference case](https://github.com/module-federation/module-federation-examples/tree/master/advanced-api/dynamic-remotes-synchronous-imports)

> In addition, you can also use `__webpack_init_sharing__ __webpack_share_scopes__` Implement "dynamic remote container" [reference resources](https://github.com/module-federation/module-federation-examples/blob/master/advanced-api/dynamic-remotes-runtime-environment-variables/host/src/components/System.js) through this scheme, we can realize: when running on the project line, we can dynamically decide which remote application modules to render

#### 2. What impact does module Federation have on Tree shaking

- Because each application in the module Federation is packaged separately, there is no way to integrate all applications Tree shaking(Can only be applied individually Tree-shaking );
- This will result in redundant references to a dependency between applications, such as multiple applications Antd of Button Components will be packaged in each application Button assembly
- If the dependency is extracted as a public dependency, it can only be referenced in full, which also leads to excessive code volume (all that can become a public dependency need to be full, for example react react-dom Etc.);
- Considering the above problems, we can use a more troublesome scheme: add another "library application", which is specially used to meet the needs Tree-shaking For the packaging of dependencies, all references to this dependency should point to the project. The trouble is to determine whether automatic addition can be realized through scripts, and TS What about type check in the project
-   be careful: Federation module not available webpack of `module.noParse` To handle the parsing of the tool library, otherwise an error will be reported for the module reference

#### 3. What impact does module Federation have on context usage?

  - use context, We can avoid passing down through intermediate elements props
  - Context Automatic transfer is not possible between modules (Applications) in the module Federation
  - Therefore, a new one needs to be provided at the entrance of each application Provider To bring the same as the upper layer context Data transmission

#### 4. What impact does module Federation have on state management?
 
  - And context Similar, for example redux Such a state management tool is also used internally context realization
  - with redux For example, one SPA In the project, we will use react-redux Provided Provider To wrap the business component, and then connect take store The state in is released into the wrapped component (and its subcomponents).
  - For each application in the module Federation, even if one application acts as another containing Provider Sub components of the application, store It will not be passed on because the two applications are built separately.
  - In order that all applications in the module Federation share a set of States, we can pass through the top level of each application Provider Wrap it up and pass it in to each application store In this way, the connection can be realized

#### 5. How to use Typescript type check in module Federation? Will eslint have an impact?
If our project uses lerna Make one monorepo Warehouse, each sub package Represents an application that is expected to be used TS Write, and TS The configuration should be the same, so I configure it as follows
  - There will be only the outermost layer of the whole warehouse tsconfig.json as well as .eslintrc.js Profile, all children package The configuration of outer layer will be used in
  - .eslintrc.js Correct reference in ts The path of the configuration file
  - Because the module references between applications will be similar webpack alias The referenced remote module is cross package and also needs ts Provide attribute tips, so we want to give tsconfig.json Configure path resolution in. Otherwise ts Module location not found
  - If the module is requested as follows @module/library/antd Is mapped to /modules/library/antd/index On this file, ts of paths Some path resolution support will be provided, but there is no way to analyze it more carefully (for example, there is no way to @module/library/antd Resolve to /modules/library/src/antd.ts)Therefore, when we develop, we should not be in each sub domain package Create in src Folder, but directly expand and write, so that we don't have to change frequently when adding remote modules tsconfig.json File

```js
  // tsconfig.json configuration
{
    "compilerOptions": {
        "baseUrl": ".",
        "paths": {
          "@shell/*": ["./shells/*"],
          "@module/*": ["./modules/*"]
        },
    }
}

// remote module / modules / library / antd / index tsx
export { Button } from 'antd';
export { Row } from 'antd';

// Calling remote module in host
import { Button,Row } from '@module/library/antd';
```

#### 6. What impact does module Federation have on code split?
 
  - Runtime chunk Will not be extracted (will cause parsing errors) runtimeChunk It is used to extract the runtime code and avoid other problems caused by runtime code changes chunk hash Change, which affects the browser cache.

#### 7. What requirements does the module Federation have for style files?
 
  - Each general application build Will generate their own main-style.css If used css-module You don't have to worry about style overrides and references
  - If it is global(Or not css-module) The style of has an overlay problem, so it is recommended to use css-module (dev Development and build Both environments will have style coverage and consistent behavior, which can ensure our performance in development, build What is the performance after the generation environment)
  - If it is "library application" (for example antd)You need to pay attention to the packaging of. Received babel-plugin-import The impact of plug-ins is similar in "library application" export {Button} from 'antd'; This writing method will lead to the failure of style file output [ISSUE](https://github.com/ant-design/babel-plugin-import/issues/569)
  - At present, there is no better scheme, so we can only use the scheme below first

```js
// Error1: will not output the style file
// export { Button, Row } from 'antd'; 
 
// Error2: cannot export {button} directly after import {Button}; Webpack will report an error
// import { Button, Row } from 'antd'; 
// export { Button, Row }; 


// Error3: export default can be implemented, but it is inconvenient to use
// For example, you can only use it this way:
//     import antd from '@module/library/antd';
//     const {Button} = antd;
// export default { Button, Row }; 

// The realizable scheme is a little cumbersome when writing library applications, but it is simple to use. Example: import {Button} from '@module/library/antd';
import { Button as _Button, Row as _Row } from 'antd';
 
export const Button = _Button;
export const Row = _Row;
```

#### 8. What impact does module Federation have on routing?

  - Basically no impact, react-router of Hash Routing can be used normally

#### 9. Combination of DLL and module Federation?

  - The two can be used in combination 
  - But because DLL It is packaged separately from other business codes and used together. There will be a problem: DLL In the packaging module chunkid moduleId In the business code packaging module chunkid Repeated, which will lead to module identification error. The solution is to DLL packaged  moduleId chunkId Generated by( deterministic => named), Reduce the possibility of duplication

#### 10. Can the combination of react technology stack and Vue technology stack be realized

  - With the help of vuera Can be realized in react Reference in project vue Component, or in Vue Introduction into the project React Components, but superior vuera This project lacks maintenance and is currently only in vue 2.x The pilot test was successful.
  - Combined with module Federation, we can realize a remote application through Vue The technology stack is implemented (maintained in a separate warehouse), which can ensure the cleanness of other remote applications

Keywords: webpack5

Added by phprocker on Sat, 05 Mar 2022 04:30:53 +0200