使用 Nx 改进 React 微前端
我们在技术读书俱乐部中介绍的第一篇文章之一是《微前端》,这是一种在许多独立和自治的团队中扩展前端开发的方法。
虽然文章内容清晰,但示例却略显不足。它利用create-react-app 的额外包来启用 Webpack 构建,并且没有提供同时运行所有微前端应用的机制。示例虽然易于理解,但对于实际场景而言却缺乏信心。
在尝试了不同的工具和方法之后,我认为我已经构建了一个更好的微前端框架,可以提升整体开发者体验。本文将引导您了解这种方法。
您可以在此处找到完整的示例。
Monorepos 与 Nx
微前端的主要缺点之一是复杂性。它不再将所有应用程序代码都集中维护,而是分散在多个应用程序中,并由不同的团队管理。这会使共享资源的协作变得困难且繁琐。
将每个微前端放在同一个代码库 (monorepo) 中是管理这种复杂性的简单方法之一。谷歌就曾使用这种技术来管理其数十亿行代码库,并依靠自动化和工具来权衡利弊。
与其使用 create-react-app 来引导微前端,不如使用Nx。Nx 是一个构建框架,提供管理多应用程序 monorepo 的工具,非常适合微前端。
以下是 Nx 帮助管理微前端的几种方法:
- 脚本编排:使用单个命令同时运行多个微前端的服务器/构建。
- 方便地共享通用组件和代码库,而无需引入大量的 Webpack 开销。
- 管理一致的依赖版本。
- 根据依赖关系图,对跨微前端的受影响更改运行构建和测试。
Nx 当然不是唯一支持 Monorepos 的工具,但我发现它非常适合微前端,因为它内置了 React 支持,并且功能强大。Lerna是一个值得关注的替代方案,它内置的功能较少,但灵活性更高。
详细示例
Nx 只需要进行少量配置更改即可支持微前端,并且您不需要诸如之类的弹出工具的帮助react-app-rewired
。
- 创建一个包含两个 React 应用程序(一个容器、一个微前端)的新 Nx 工作区。
- 扩展 Nx 的默认 React Webpack 配置以禁用分块并生成资产清单。
- 按照 Thoughtworks 文章中描述的方式实现常规的微前端组件。
- 用一个
npm start
脚本将所有内容结合在一起。
1.创建Nx工作区
首先创建一个新的 Nx 工作区:
npx create-nx-workspace@latest micronx
? What to create in the new workspace...
> empty
Use Nx Cloud?
> No
进入新micronx
目录并创建两个 React 应用:一个容器和一个微前端。务必选择styled-components
(或其他 CSS-in-JS 解决方案),以便组件 CSS 包含在微前端的 JS 包中。
cd ./micronx
npm install --also=dev @nrwl/react
# Container application
nx g @nrwl/react:app container
> styled-components
> No
# Micro frontend
nx g @nrwl/react:app dashboard
> No
到目前为止,您已经创建了一个包含两个独立 React 应用的 monorepo:容器和仪表板。每个 React 应用都可以通过各自的nx run <app>:serve
脚本独立运行,但目前还没有任何机制可以让它们协同工作。
下一步将添加一些配置更改,允许您将仪表板应用程序动态加载为微前端。
2.修改微前端Webpack配置
Nx 将其大部分相关配置存储在workspace.json
项目根目录下的文件中。
您需要修改workspace.json
以将微前端的 Webpack 配置指向一个新文件webpack.config.js
。此新文件包含支持动态加载微前端所需的配置更新。
请注意,您不需要对容器执行此操作,因为容器不是微前端。
// workspace.json
"projects": {
"dashboard": {
"targets": {
"build": {
// ...
"webpackConfig": "webpack.config.js"
}
}
}
}
现在您需要webpack.config.js
在项目的根目录中创建该文件。
修改后的 Webpack 配置扩展了@nrwl/react的默认代码,以避免丢失任何功能。按照 Thoughtworks 的示例,需要进行两项修改才能支持常规微前端:
- 禁用分块,以便容器应用程序每个微前端加载一个包。
- 添加
WebpackManifestPlugin
以将生成的 JS 输出映射到一个简单的导入路径(取自react-scripts webpack 配置)。
npm install --also=dev webpack-manifest-plugin
// webpack.config.js
const reactWebpackConfig = require('@nrwl/react/plugins/webpack')
const { WebpackManifestPlugin } = require('webpack-manifest-plugin')
function getWebpackConfig(config) {
config = reactWebpackConfig(config)
// Disable chunking
config.optimization = {
...config.optimization,
runtimeChunk: false,
splitChunks: {
chunks(chunk) {
return false
},
},
}
// Enable asset-manifest
config.plugins.push(
new WebpackManifestPlugin({
fileName: 'asset-manifest.json',
publicPath: '/',
generate: (seed, files, entrypoints) => {
const manifestFiles = files.reduce((manifest, file) => {
manifest[file.name] = file.path
return manifest
}, seed)
const entrypointFiles = entrypoints.main.filter(
fileName => !fileName.endsWith('.map'),
)
return {
files: manifestFiles,
entrypoints: entrypointFiles,
}
},
}),
)
return config
}
module.exports = getWebpackConfig
运行nx run dashboard:serve
并访问http://localhost:4200/asset-manifest.json。请注意,仪表板应用程序现在只有一个入口点:main.js
。
{
"files": {
"main.js": "/main.js",
"main.js.map": "/main.js.map",
"polyfills.js": "/polyfills.js",
"polyfills.js.map": "/polyfills.js.map",
"assets/.gitkeep": "/assets/.gitkeep",
"favicon.ico": "/favicon.ico",
"index.html": "/index.html"
},
"entrypoints": ["main.js"]
}
3. 添加微前端组件
正确配置 Nx 后,下一步是按照 Thoughtworks 示例并引入所有微前端功能。
以下链接与文章内容一致,但是为了完整性而包含的。
4. 将所有东西联系在一起
最后一步是将微前端和容器一起提供服务。添加concurrently
并修改启动脚本,以便在特定端口上提供仪表板服务。
"start": "concurrently \"nx run container:serve\" \"nx run dashboard:serve --port=3001\""
运行npm start
后您就会得到微前端。
使用 Nx
服务微前端
Nx 缺乏同时服务多个应用程序的开箱即用功能,这就是我concurrently
在上面的例子中求助的原因。话虽如此,使用 Nx CLI 可以轻松运行单个微前端。
- 通过独立开发微前端
nx run <project>:serve
。 - 看看它们如何通过融入整个应用程序
npm start
。
生成器
Nx 附带了一些生成器,可以帮助你搭建应用程序。特别是库生成器,它使得共享 React 组件变得非常容易:
nx g lib common
这将在项目libs/
目录中创建一个新的 React 库,其中包含一系列预配置的构建设置。它还包含一个方便的 TypeScript 路径别名,使导入库变得简单:
// apps/dashboard/src/app/app.tsx
import { ComponentA, ComponentB } from '@micronx/common'
Nx 通过跟踪项目的依赖关系图,为这种代码共享方式提供了额外的好处。您可以通过运行来展示各种代码库与每个依赖应用程序之间的关系nx dep-graph
。
在内部,Nx 使用此依赖关系图来减少引入更改时需要运行的构建/测试次数。如果您对 进行更改apps/dashboard/
并运行nx affected:test
,Nx 将仅针对 Dashboard 微前端运行测试。随着项目依赖关系图的复杂性不断增加,此功能将变得非常强大。
优化
微前端策略的独特之处在于生产 JS 包中常见供应商依赖项和共享代码库的重复。
Thoughtwork 的文章在“公共内容”部分中谈到了这一点,主张将公共依赖项标记为Webpack 外部依赖项,以防止它们包含在每个应用程序的最终捆绑包中。
module.exports = (config, env) => {
config.externals = {
react: 'React',
'react-dom': 'ReactDOM',
}
return config
}
一旦Nx 将其 React 工具升级到 Webpack 5,微前端项目将可以通过模块联合 (Module Federation)获得一种新的代码优化方法。此策略允许将共享代码库 ( libs/
)构建到容器应用程序中,从而消除微前端 bundles 中的另一个常见依赖项。