使用 Webpack 5 模块联合服务样式和资产
Serving styles and assets with Webpack 5 module federation
我已经在我的 Angular 11 应用程序中成功实现了相对较新的 webpack 5 模块联合系统,因此它可以从另一个构建中按需远程加载模块。
我没有发现的一件事是如何处理样式表和图像等资产。例如,联合模块中有一个菜单元素需要自己的样式:
- 将它们放在组件的样式表中会使块膨胀,编译器会对此抱怨,而且它们在显示菜单之前不会加载
- 如果样式在联合模块的全局样式表上,则根本不会加载它们,因为我请求的是子模块而不是主模块(我想)
- 样式是federated module特有的,所以不能放在loader应用中
我想可以编译样式并将其放入联合模块的构建资产中,但是无论是否使用联合,这都会破坏链接。
我还在试验这个,但我觉得问一下会很好。有人遇到过这个问题吗?
好吧,我要 post 我想出的东西,它不是很漂亮,但它似乎适用于 CSS 资产。
首先,我在远程模块的构建中将它们分开:
angular.json:
"styles": [
"projects/xxx-admin/src/styles/styles.scss",
"projects/xxx-admin/src/styles/admin.scss",
{
"input": "projects/xxx-admin/src/styles/admin.scss",
"bundleName": "admin_module_styles",
"inject": false
}
],
这会生成一个 CSS,其中包含一个清晰的非自动生成的名称。然后我们在路由中从应用程序加载联邦模块:
{
path: "admin",
component: AdminPanelComponent,
canActivate: [XxxAdminGuard],
loadChildren: () => {
const baseUrl = getAdminFrontendBaseUrl();
return loadAdminStyles().then(
() => loadRemoteModule({
remoteName: "xxx_admin",
remoteEntry: `${baseUrl}/remoteEntry.js`,
exposedModule: "AdminModule",
}).then((m) => m.AdminModule));
},
},
...
export function loadAdminStyles(): Promise<void> {
return new Promise((resolve => {
const baseUrl = getAdminFrontendBaseUrl();
const el = document.getElementById("admin-module-styles");
// Load one instance, do it like this to handle errors and retrying
if (el) {
el.remove();
}
const headEl = document.getElementsByTagName("head")[0];
const styleLinkEl = document.createElement("link");
styleLinkEl.rel = "stylesheet";
styleLinkEl.id = "admin-module-styles";
styleLinkEl.href = `${baseUrl}/admin_module_styles.css`;
headEl.appendChild(styleLinkEl);
resolve();
}));
}
它不是最理想的,但我想不出更好的东西。
我认为实现这一点的最优雅方法是将微前端的全局 styles.scss 导入它的入口模块 component.scss 并在入口模块 component.ts 中设置 encapsulation: ViewEncapsulation.None
为了打破它的样式封装,这反过来又会导致该样式在全球范围内应用。
我已经在我的 Angular 11 应用程序中成功实现了相对较新的 webpack 5 模块联合系统,因此它可以从另一个构建中按需远程加载模块。
我没有发现的一件事是如何处理样式表和图像等资产。例如,联合模块中有一个菜单元素需要自己的样式:
- 将它们放在组件的样式表中会使块膨胀,编译器会对此抱怨,而且它们在显示菜单之前不会加载
- 如果样式在联合模块的全局样式表上,则根本不会加载它们,因为我请求的是子模块而不是主模块(我想)
- 样式是federated module特有的,所以不能放在loader应用中
我想可以编译样式并将其放入联合模块的构建资产中,但是无论是否使用联合,这都会破坏链接。
我还在试验这个,但我觉得问一下会很好。有人遇到过这个问题吗?
好吧,我要 post 我想出的东西,它不是很漂亮,但它似乎适用于 CSS 资产。
首先,我在远程模块的构建中将它们分开: angular.json:
"styles": [
"projects/xxx-admin/src/styles/styles.scss",
"projects/xxx-admin/src/styles/admin.scss",
{
"input": "projects/xxx-admin/src/styles/admin.scss",
"bundleName": "admin_module_styles",
"inject": false
}
],
这会生成一个 CSS,其中包含一个清晰的非自动生成的名称。然后我们在路由中从应用程序加载联邦模块:
{
path: "admin",
component: AdminPanelComponent,
canActivate: [XxxAdminGuard],
loadChildren: () => {
const baseUrl = getAdminFrontendBaseUrl();
return loadAdminStyles().then(
() => loadRemoteModule({
remoteName: "xxx_admin",
remoteEntry: `${baseUrl}/remoteEntry.js`,
exposedModule: "AdminModule",
}).then((m) => m.AdminModule));
},
},
...
export function loadAdminStyles(): Promise<void> {
return new Promise((resolve => {
const baseUrl = getAdminFrontendBaseUrl();
const el = document.getElementById("admin-module-styles");
// Load one instance, do it like this to handle errors and retrying
if (el) {
el.remove();
}
const headEl = document.getElementsByTagName("head")[0];
const styleLinkEl = document.createElement("link");
styleLinkEl.rel = "stylesheet";
styleLinkEl.id = "admin-module-styles";
styleLinkEl.href = `${baseUrl}/admin_module_styles.css`;
headEl.appendChild(styleLinkEl);
resolve();
}));
}
它不是最理想的,但我想不出更好的东西。
我认为实现这一点的最优雅方法是将微前端的全局 styles.scss 导入它的入口模块 component.scss 并在入口模块 component.ts 中设置 encapsulation: ViewEncapsulation.None
为了打破它的样式封装,这反过来又会导致该样式在全球范围内应用。