在前一部分中,我讨论了转向MFE解决方案的动机以及解决方案相关的一些标准。在这一部分中,我将介绍我们如何在Outbrain实现它。
正如我在前一部分中提到的,其中一个标准是能够与我们当前的技术echo系统集成的解决方案,并且不需要对我们当前维护的应用程序进行什么更改。
进入Angular惰性加载特性模块
Angular有一个内建的模块概念,它基本上是一个声明对象,用来指定封装在一个模块中的所有组件、指令、服务和其他模块。
@NgModule({ imports: [CommonModule], declarations: [ WelcomeComponent], bootstrap: [], entryComponents: [] })export class AppB_Module {}
通过将模块文件指定为Webpack入口点,我们可以将整个Angular模块(包括css和html)打包为一个单独的js文件。
entry: {
'appB_module': './app/appB.prod.module.ts'
}
使用Angular的延迟加载机制,我们可以动态加载这个js文件并引导到当前的应用程序中。
const routes: Routes = [
{
path: appB,
loadChildren: '/appB/appB_Module#AppB_Module'
}
]
这是朝着将应用程序分离为一个小型应用程序的目标迈出的一大步。
从功能模块转移到迷你应用程序
角功能模块以及Webpack捆绑销售给我们我们需要的代码分离,但这是不够的,因为Webpack只允许我们创建包作为一个构建过程的一部分,我们希望能够产生一个单独的JS包,这是建立在不同的时间,从一个单独的代码在一个单独的构建系统,可以在运行时加载到应用程序和共享公共资源,如角。
为了解决这个问题,我们必须创建自己的Webpack加载器,称为share-loader。
share -loader允许我们指定希望在应用程序之间共享的模块列表,它将一个给定的模块捆绑到一个应用程序js捆绑包中,并提供一个其他捆绑包访问该模块的名称空间。
应用程序A webpack.config:
rules: [
{
test: /\.js?$/,
use: [{
loader: 'share-loader',
options: {
modules: [/@angular/, /@lodash/],
namespace: 'container-app'
}
}]
}
应用程序B webpack.json
const {Externals} = require('share-loader');…externals: [ Externals({ namespace: 'container-app', modules: [/@angular/, /@lodash/] }) ], output: { library: 'appB', libraryTarget: 'umd' },
在这个例子中,我们告诉Webpack将angular和lodash捆绑到应用程序A中,并在“容器-应用程序”命名空间下公开它。
在应用程序B中,我们定义angular和lodash不会绑定在一起,而是由命名空间“container-app”指向它们。
通过这种方式,我们可以跨应用程序共享一些模块,但是维护我们不希望共享的其他模块。
到目前为止,我们已经解决的几个关键的我们以前的文章中指定,我们现在有两个应用程序可以运行独立或在运行时加载远程虽然裹着js名称空间和css和html封装,他们之间也可以分享模块,封装模块不应该共享,现在让我们看看一些其他关键的我们所提到的。
DOM封装
为了解决css封装我们包装每个迷你应用程序与一个通用的角组件,该组件使用角css封装特性,我们有两个选择,我们可以使用模拟模式或本地模式根据我们需要的浏览器支持,不管怎样我们确保css不会泄漏。
@Component({
selector: 'ob-externals-wrapper',
template: require('./externals-wrapper.component.pug')(),
styleUrls: ['./externals-wrapper.component.less'],
encapsulation: ViewEncapsulation.Native
})
这个包装器组件还充当每个迷你应用程序和其他应用程序之间的通信层。所有的通信都是通过一个由每个包装器实例承载的事件总线实例来完成的,通过使用一个事件系统,我们有一种解耦的方式来通信数据的输入和输出,当一个小型应用程序从主应用程序中清除时,我们可以很容易地清除这种方式。
如果我们看看我们迄今为止的情况,我们可以看到,我们有一个解决方案是非常内联与web组件的概念,每个迷你应用程序是由一个独立的包装组件,封装所有js html和css,所有通信通过一个事件系统。
测试
由于每个应用程序也可以独立运行,所以我们可以在每个应用程序上独立运行测试套件,这意味着每个应用程序所有者都知道他的更改何时破坏了应用程序,并且每个团队主要关心他们自己的应用程序。
部署和服务
为了为每个应用程序提供自己的部署,我们为每个应用程序创建了一个节点服务,每当一个团队创建一个新的应用程序部署时,都会创建一个封装应用程序的js包,每个服务都会公开一个端点,该端点返回到包的路径。在运行时,当一个小型应用程序加载到容器应用程序中时,将调用端点并将js文件加载到应用程序并引导到主应用程序。这样,每个应用程序都可以单独构建部署。
例子:
没有什么比示例更好的了,如果您想尝试一下,可以到share-loader repo查看Readme中的示例和example-cli部分。
演示可以在回购本身,享受..
结束笔记:
感谢你的阅读!我希望本文能够帮助正在考虑这一举措的公司认识到,通过彻底改革代码库是有可能做到这一点的。
移动到微前端方法是朝着正确的方向移动,因为应用程序越大,速度越小。
本文展示了一个使用Angular作为框架的解决方案,类似的解决方案也可以使用其他框架来实现。