微前端是微服务的对应物。微服务是一种将单体后端应用分割成更小的服务的架构,而微前端可以用来在前端实现同样的目的。但它们还没有像微服务那样流行。
对于我的最后一个客户,我为微前端React与Webpack的方案做了一个实验性的秒杀。这里我想分享一下我的成果。完成的实验性微前端应用可以在这里找到。
React前台
我们将从这个带有Webpack的高级React设置开始。你还需要安装React Router。让我们一步一步地浏览React组件。这是我们的src/index.js根入口点:
import React from 'react';import ReactDOM from 'react-dom';
import App from './App';
const title = 'My React Micro Frontend';
ReactDOM.render( <App title={title} />, document.getElementById('app'));
在这里,我们在src/App/index.js 中有一个 App 组件:
import React from 'react';import { BrowserRouter as Router, Routes, Route, Link,} from 'react-router-dom';
import * as routes from '../constants/routes';import Programming from '../Programming';import Installation from '../Installation';
const App = ({ title }) => ( <Router> <h1>{title}</h1>
<ul> <li> <Link to={routes.PROGRAMMING}>Programming</Link> </li> <li> <Link to={routes.INSTALLATION}>Installation</Link> </li> </ul>
<Routes> <Route path={routes.PROGRAMMING} element={<Programming />} /> <Route path={routes.INSTALLATION} element={<Installation />} /> </Routes> </Router>);
export default App;
App组件负责使用React路由器进行路由,因此显示带有链接的导航,并根据路由渲染一个编程或安装组件。这两个组件将是我们的微型前端。但以后会有更多关于这方面的内容。
为了完整起见,这是src/constants/routes.js文件:
export const PROGRAMMING = '/';export const INSTALLATION = '/installation';
每个微型前端组件,这里是安装和编程,都存在于它们自己的文件夹中。一个在src/Installation/index.js,一个在src/Programming/index.js:
// src/Installation/index.js
import React from 'react';
const Installation = () => ( <div style={{ backgroundColor: 'yellow', padding: '20px' }}> <h1>Installation</h1> </div>);
export default Installation;
// src/Programming/index.js
import React from 'react';
const Programming = () => ( <div style={{ backgroundColor: 'green', padding: '20px' }}> <h1>Programming</h1> </div>);
export default Programming;
文件夹结构应该与此相似。
- src/-- App--- index.js-- constants--- routes.js-- Installation--- index.js-- Programming--- index.js
到目前为止,所有的组件几乎都是相互耦合的。App组件渲染了安装和编程组件。让我们转移到我们的Webpack设置,用这些React组件启用微前端架构。
Webpack微前端
我们将从package.json文件开始,并将所有的层级下移到我们的Webpack配置文件中。之前我们只有一个脚本来启动这个React应用程序。现在我们用另外两个命令来扩展它,以启动我们的一个微前端。
package.json
{ ... "scripts": { "start": "webpack serve --config build-utils/webpack.config.js --env env=dev", "start:programming": "webpack serve --config build-utils/webpack.config.js --env env=dev --env micro=Programming", "start:installation": "webpack serve --config build-utils/webpack.config.js --env env=dev --env micro=Installation", ... }, ...}
与之前的启动脚本相比,唯一改变的是这些新的--env micro 标志。这就是我们如何在Webpack中区分哪个应用程序应该作为微型前端启动。我们的build-utils/webpack.config.js文件看起来像这样。
const webpackMerge = require('webpack-merge');
const commonConfig = require('./webpack.common.js');
const getAddons = addonsArgs => { ... };
module.exports = ({ env, addon }) => { const envConfig = require(`./webpack.${env}.js`);
return webpackMerge(commonConfig, envConfig, ...getAddons(addon));};
注意:环境配置取决于另一个env 标志,它被传入以评估开发或生产构建之间。getAddons 功能是可选的,如果你有Webpack附加组件。再看看如何用Webpack设置构建过程和附加组件。
现在我们把这个实现改为如下。
...
module.exports = ({ env, micro, addon }) => { const envConfig = require(`./webpack.${env}.js`); const commonConfig = require('./webpack.common.js')(micro);
return webpackMerge(commonConfig, envConfig, ...getAddons(addon));};
这个变化假定我们的build-utils/webpack.common.js文件不再输出一个配置对象,而是一个返回配置对象的函数。基本上取决于micro 标志,这个函数返回一个合适的配置。我们在这里做的是普通Webpack配置,但如果在开发或生产Webpack配置文件中需要这个标志的话,它也可以同样工作。
现在在build-utils/webpack.common.js文件中,我们只需要调整两件事。我们将以下对象进行了转换。
module.exports = { entry: './src/index.js', ...};
到一个函数,它返回一个对象,有micro 标志作为参数,并根据我们是否要返回一个微型前端,返回适当的入口点文件。如果没有micro 标志,我们返回渲染App组件的标准src/index.js 文件,如果有micro 标志,我们返回我们源文件夹中的一个动态文件。
module.exports = micro => ({ entry: micro ? `./src/${micro}/standalone.js` : './src/index.js', ...});
我们还没有这个standalone.js文件。我们需要在源文件夹中为我们的微型前端提供这些新的入口文件。这发生在接下来。
React微前端
让我们来看看第一个微型前端的standalone.js文件,它是src/Installation/standalone.js。
import React from 'react';import ReactDOM from 'react-dom';
import Installation from '.';
const InstallationStandalone = () => { const props = {};
return <Installation isStandalone {...props} />;};
ReactDOM.render( <InstallationStandalone />, document.getElementById('app'));
这个文件将之前在App组件中使用的常规安装组件,包装成另一个React组件(这里是InstallationStandalone)。然后这个新的包装组件被用来用React DOM渲染一切。
这个新的包装组件(InstallationStandalone)的重要之处在于,你可以向安装组件提供任何不再来自App组件的信息。以前,App组件可能会向安装组件提供数据。现在这些数据不再可用了,因为安装组件必须自己渲染。这就是InstallationStandalone组件发挥作用的地方,它以道具的形式提供这些数据。
我们可以对第二个微型前端standalone.js文件采用同样的方法,即src/Programming/standalone.js。注意isStandalone 标志,这有助于我们以后在微型前端组件(这里是Programming)中识别它是作为微型前端独立呈现还是作为更大的单体的一部分。
import React from 'react';import ReactDOM from 'react-dom';
import Programming from '.';
const ProgrammingStandalone = () => { const props = {};
return <Programming isStandalone {...props} />;};
ReactDOM.render( <ProgrammingStandalone />, document.getElementById('app'));
isStandalone 标志可以在每个组件中使用。我们将使用它来渲染一个指向其他微前端组件的链接,但只有在该组件本身不是微前端的情况下。在src/Installation/index.js中,我们会这样做。
import React from 'react';import { Link } from 'react-router-dom';
import * as routes from '../constants/routes';
const Installation = ({ isStandalone }) => ( <div style={{ backgroundColor: 'yellow', padding: '20px' }}> <h1>Installation</h1>
{!isStandalone && ( <ul> <li> <Link to={routes.PROGRAMMING}>Back to Programming</Link> </li> </ul> )} </div>);
export default Installation;
而在src/Programming/index.js中,我们这样做了。
import React from 'react';import { Link } from 'react-router-dom';
import * as routes from '../constants/routes';
const Programming = ({ isStandalone }) => ( <div style={{ backgroundColor: 'green', padding: '20px' }}> <h1>Programming</h1>
{!isStandalone && ( <ul> <li> <Link to={routes.INSTALLATION}>Back to Installation</Link> </li> </ul> )} </div>);
export default Programming;
现在你可以尝试运行你新的微前端npm脚本了。npm start ,用App组件创建整个单片机应用程序,而其他新的npm脚本只创建微前端。
npm run start:programmingnpm run start:installation
你可以单独运行这两个微前端。如果它们被单独运行,它们的独立包装组件被用来在HTML中渲染,并提供通常来自App组件的额外道具。
你所看到的只是关于如何用Webpack和React创建一个微型前端架构的第一波高潮。还有很多事情需要考虑:
- 也应该有用于测试和构建的微型前端脚本。
- 每个微型前端文件夹都应该有自己的package.json文件,以便在没有monolith的情况下执行其脚本?
- 如果有的话,它是否应该从单体中列出所有的依赖关系,或者只是复制过来?
- 所有的测试都应该从单片机上执行,还是转移到微型前端的package.json文件中?
- 如何将微前端和单片机分离到它们自己的版本控制系统中?
无论如何,如果你正在寻找如何用React创建一个微型前端,我希望这个攻略能帮助你了解如何实现它。