在 Angular 中,使用 WebPack 5 模块联合,我们如何公开远程使用的 Web Worker?
In Angular, using WebPack 5 Module Federations, how can we expose Web Workers used by a remote?
在我的配置中,我目前正在使用一个 shell 应用程序和一个遥控器,它们都是用 angular 12.2.0 编写的,我遇到了一个我不确定如何解决的问题。
SHELL webpack.config.js
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
const mf = require("@angular-architects/module-federation/webpack");
const path = require("path");
const share = mf.share;
const sharedMappings = new mf.SharedMappings();
sharedMappings.register(
path.join(__dirname, '../../tsconfig.json'),
['auth-lib']
);
module.exports = {
output: {
uniqueName: "portal",
publicPath: "auto"
},
optimization: {
runtimeChunk: false
},
resolve: {
alias: {
...sharedMappings.getAliases(),
}
},
plugins: [
new ModuleFederationPlugin({
// For hosts (please adjust)
remotes: {
"myremote": "myremote@http://localhost:4250/remoteEntry.js"
},
shared: share({
"@angular/core": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
"@angular/common": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
"@angular/common/http": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
"@angular/router": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
...sharedMappings.getDescriptors()
})
}),
sharedMappings.getPlugin()
],
};
远程 webpack.config.js
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
const mf = require("@angular-architects/module-federation/webpack");
const path = require("path");
const share = mf.share;
const sharedMappings = new mf.SharedMappings();
sharedMappings.register(
path.join(__dirname, 'tsconfig.json'),
[/* mapped paths to share */]
);
module.exports = {
output: {
uniqueName: "interviews",
publicPath: "auto"
},
optimization: {
runtimeChunk: false
},
resolve: {
alias: {
...sharedMappings.getAliases(),
}
},
plugins: [
new ModuleFederationPlugin({
// For remotes (please adjust)
name: "myremote",
filename: "remoteEntry.js",
exposes: {
'./Module': './src/app/myremote/myremote.module.ts'
},
shared: share({
"@angular/core": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
"@angular/common": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
"@angular/common/http": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
"@angular/router": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
...sharedMappings.getDescriptors()
})
}),
sharedMappings.getPlugin()
],
};
当我像这样在 shell 应用程序中使用对我的远程模块的引用时,一切正常:
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './components/home/home.component';
const routes: Routes = [{
path: '',
component: HomeComponent,
pathMatch: 'full'
}, {
path: 'myremote',
loadChildren: () => import('myremote/Module').then(m => m.MyRemoteModule)
}];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
当远程模块的组件在其代码中实例化一个新的 Web Worker 时出现问题,如下所示:
const myWorker = new Worker(
new URL('../path/to/workers/worker.ts',
import.meta.url
),
{ type: 'module', name: 'my-worker' }
);
// since I'm also using comlink, I also do that, but that's not really relevant here
this.worker = this.createWrapper<WorkerService>(myWorker);
上面的代码,在 Webpack 5 中,已经在我的应用程序中生成了一个名为 my-worker.js 的单独包,它既不是组件的一部分,也不是模块的包的一部分。
因此,将模块暴露给 Module Federation,不会将工作人员的代码也暴露到 remoteEntry.js 中。
然后尝试使用 shell 应用程序导航到加载我的遥控器的路线,将出错并通知:
ERROR Error: Uncaught (in promise): SecurityError: Failed to construct 'Worker': Script at 'http://localhost:4250/my-worker.js' cannot be accessed from origin 'http://localhost:5000'.
Error: Failed to construct 'Worker': Script at 'http://localhost:4250/my-worker.js' cannot be accessed from origin 'http://localhost:5000'.
所以问题是,如果我想要一个遥控器并公开其模块以在 shell 应用程序中使用,并且其中一个模块使用 Web Worker,我如何确保为了让 shell 应用程序能够实例化它,Webpack 创建的单独的 worker bundle 也被公开了?
感谢 alexander-akait,答案就在这里
https://github.com/webpack/webpack/discussions/14380
鉴于错误是安全错误
ERROR Error: Uncaught (in promise): SecurityError
此问题与需要在本地服务器中设置 CORS 有关。
我找到了解决方法。
第 1 步。在单独的文件中创建自定义工作器class
export class CorsWorker {
private readonly worker: Worker;
constructor(url: string | URL) {
const objectURL = URL.createObjectURL(
new Blob([`importScripts(${JSON.stringify(url.toString())});`], {
type: 'application/javascript'
})
);
this.worker = new Worker(objectURL);
URL.revokeObjectURL(objectURL);
}
getWorker() {
return this.worker;
}
}
第 2 步 - 不要使用 Webpack Worker,而是使用 CorsWorker 导入您的工作文件。
import { CorsWorker as Worker } from './cors-worker';
// aliasing CorsWorker to Worker makes it statically analyzable
const corsWorker = new Worker(new URL('../worker', import.meta.url));
const worker = corsWorker.getWorker();
这个例子对我有用。我们可以通过关注此 GitHub 讨论主题获得更多信息
https://github.com/webpack/webpack/discussions/14648
在我的配置中,我目前正在使用一个 shell 应用程序和一个遥控器,它们都是用 angular 12.2.0 编写的,我遇到了一个我不确定如何解决的问题。
SHELL webpack.config.js
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
const mf = require("@angular-architects/module-federation/webpack");
const path = require("path");
const share = mf.share;
const sharedMappings = new mf.SharedMappings();
sharedMappings.register(
path.join(__dirname, '../../tsconfig.json'),
['auth-lib']
);
module.exports = {
output: {
uniqueName: "portal",
publicPath: "auto"
},
optimization: {
runtimeChunk: false
},
resolve: {
alias: {
...sharedMappings.getAliases(),
}
},
plugins: [
new ModuleFederationPlugin({
// For hosts (please adjust)
remotes: {
"myremote": "myremote@http://localhost:4250/remoteEntry.js"
},
shared: share({
"@angular/core": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
"@angular/common": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
"@angular/common/http": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
"@angular/router": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
...sharedMappings.getDescriptors()
})
}),
sharedMappings.getPlugin()
],
};
远程 webpack.config.js
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
const mf = require("@angular-architects/module-federation/webpack");
const path = require("path");
const share = mf.share;
const sharedMappings = new mf.SharedMappings();
sharedMappings.register(
path.join(__dirname, 'tsconfig.json'),
[/* mapped paths to share */]
);
module.exports = {
output: {
uniqueName: "interviews",
publicPath: "auto"
},
optimization: {
runtimeChunk: false
},
resolve: {
alias: {
...sharedMappings.getAliases(),
}
},
plugins: [
new ModuleFederationPlugin({
// For remotes (please adjust)
name: "myremote",
filename: "remoteEntry.js",
exposes: {
'./Module': './src/app/myremote/myremote.module.ts'
},
shared: share({
"@angular/core": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
"@angular/common": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
"@angular/common/http": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
"@angular/router": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
...sharedMappings.getDescriptors()
})
}),
sharedMappings.getPlugin()
],
};
当我像这样在 shell 应用程序中使用对我的远程模块的引用时,一切正常:
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './components/home/home.component';
const routes: Routes = [{
path: '',
component: HomeComponent,
pathMatch: 'full'
}, {
path: 'myremote',
loadChildren: () => import('myremote/Module').then(m => m.MyRemoteModule)
}];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
当远程模块的组件在其代码中实例化一个新的 Web Worker 时出现问题,如下所示:
const myWorker = new Worker(
new URL('../path/to/workers/worker.ts',
import.meta.url
),
{ type: 'module', name: 'my-worker' }
);
// since I'm also using comlink, I also do that, but that's not really relevant here
this.worker = this.createWrapper<WorkerService>(myWorker);
上面的代码,在 Webpack 5 中,已经在我的应用程序中生成了一个名为 my-worker.js 的单独包,它既不是组件的一部分,也不是模块的包的一部分。 因此,将模块暴露给 Module Federation,不会将工作人员的代码也暴露到 remoteEntry.js 中。 然后尝试使用 shell 应用程序导航到加载我的遥控器的路线,将出错并通知:
ERROR Error: Uncaught (in promise): SecurityError: Failed to construct 'Worker': Script at 'http://localhost:4250/my-worker.js' cannot be accessed from origin 'http://localhost:5000'. Error: Failed to construct 'Worker': Script at 'http://localhost:4250/my-worker.js' cannot be accessed from origin 'http://localhost:5000'.
所以问题是,如果我想要一个遥控器并公开其模块以在 shell 应用程序中使用,并且其中一个模块使用 Web Worker,我如何确保为了让 shell 应用程序能够实例化它,Webpack 创建的单独的 worker bundle 也被公开了?
感谢 alexander-akait,答案就在这里
https://github.com/webpack/webpack/discussions/14380
鉴于错误是安全错误
ERROR Error: Uncaught (in promise): SecurityError
此问题与需要在本地服务器中设置 CORS 有关。
我找到了解决方法。
第 1 步。在单独的文件中创建自定义工作器class
export class CorsWorker {
private readonly worker: Worker;
constructor(url: string | URL) {
const objectURL = URL.createObjectURL(
new Blob([`importScripts(${JSON.stringify(url.toString())});`], {
type: 'application/javascript'
})
);
this.worker = new Worker(objectURL);
URL.revokeObjectURL(objectURL);
}
getWorker() {
return this.worker;
}
}
第 2 步 - 不要使用 Webpack Worker,而是使用 CorsWorker 导入您的工作文件。
import { CorsWorker as Worker } from './cors-worker';
// aliasing CorsWorker to Worker makes it statically analyzable
const corsWorker = new Worker(new URL('../worker', import.meta.url));
const worker = corsWorker.getWorker();
这个例子对我有用。我们可以通过关注此 GitHub 讨论主题获得更多信息 https://github.com/webpack/webpack/discussions/14648