目前,我正在处理一个托管在客户端服务器上的项目。对于新的'模块' 无意重新编译整个应用程序。也就是说,客户端希望在运行时更新路由器/延迟加载的模块。我尝试了几件事,但我无法让它发挥作用。我想知道你们中是否有人知道我还能尝试什么,或者我错过了什么。
我注意到的一件事是,在构建应用程序时,我尝试使用angular cli的大部分资源都被webpack捆绑到单独的块中。这似乎是合乎逻辑的,因为它使用了webpack代码拆分。但是如果在编译时模块还不知道怎么办(但是编译后的模块存储在服务器的某个地方)?捆绑不起作用,因为它无法找到要导入的模块。并且使用SystemJS将在系统上找到时加载UMD模块,但也会通过webpack捆绑在一个单独的块中。
我已尝试过的一些资源;
我已经尝试并实施了一些代码,但目前还没有工作;
使用普通的module.ts文件扩展路由器
this.router.config.push({
path: "external",
loadChildren: () =>
System.import("./module/external.module").then(
module => module["ExternalModule"],
() => {
throw { loadChunkError: true };
}
)
});
UMD包的正常SystemJS导入
System.import("./external/bundles/external.umd.js").then(modules => {
console.log(modules);
this.compiler.compileModuleAndAllComponentsAsync(modules['External']).then(compiled => {
const m = compiled.ngModuleFactory.create(this.injector);
const factory = compiled.componentFactories[0];
const cmp = factory.create(this.injector, [], null, m);
});
});
导入外部模块,不使用webpack(afaik)
const url = 'https://gist.githubusercontent.com/dianadujing/a7bbbf191349182e1d459286dba0282f/raw/c23281f8c5fabb10ab9d144489316919e4233d11/app.module.ts';
const importer = (url:any) => Observable.fromPromise(System.import(url));
console.log('importer:', importer);
importer(url)
.subscribe((modules) => {
console.log('modules:', modules, modules['AppModule']);
this.cfr = this.compiler.compileModuleAndAllComponentsSync(modules['AppModule']);
console.log(this.cfr,',', this.cfr.componentFactories[0]);
this.external.createComponent(this.cfr.componentFactories[0], 0);
});
使用SystemJsNgModuleLoader
this.loader.load('app/lazy/lazy.module#LazyModule').then((moduleFactory: NgModuleFactory<any>) => {
console.log(moduleFactory);
const entryComponent = (<any>moduleFactory.moduleType).entry;
const moduleRef = moduleFactory.create(this.injector);
const compFactory = moduleRef.componentFactoryResolver.resolveComponentFactory(entryComponent);
});
尝试加载使用汇总的模块
this.http.get(`./myplugin/${metadataFileName}`)
.map(res => res.json())
.map((metadata: PluginMetadata) => {
// create the element to load in the module and factories
const script = document.createElement('script');
script.src = `./myplugin/${factoryFileName}`;
script.onload = () => {
//rollup builds the bundle so it's attached to the window object when loaded in
const moduleFactory: NgModuleFactory<any> = window[metadata.name][metadata.moduleName + factorySuffix];
const moduleRef = moduleFactory.create(this.injector);
//use the entry point token to grab the component type that we should be rendering
const compType = moduleRef.injector.get(pluginEntryPointToken);
const compFactory = moduleRef.componentFactoryResolver.resolveComponentFactory(compType);
// Works perfectly in debug, but when building for production it returns an error 'cannot find name Component of undefined'
// Not getting it to work with the router module.
}
document.head.appendChild(script);
}).subscribe();
SystemJsNgModuleLoader示例仅在模块已经提供为“懒惰”时才有效。应用程序的RouterModule中的路由(当使用webpack构建时将其转换为块)
我在StackOverflow上找到了很多关于这个主题的讨论,如果事先知道的话,提供的解决方案似乎非常适合动态加载模块/组件。但没有一个适合我们的项目用例。请让我知道我仍然可以尝试或深入研究。
谢谢!
编辑:我发现了; https://github.com/kirjs/angular-dynamic-module-loading并试一试。更新:我创建了一个存储库,其中包含使用SystemJS动态加载模块的示例(以及使用Angular 6); https://github.com/lmeijdam/angular-umd-dynamic-example
答案 0 :(得分:10)
我遇到了同样的问题。据我所知,直到现在:
Webpack将所有资源放在一个包中,并用System.import
替换所有__webpack_require__
。因此,如果要使用SystemJsNgModuleLoader在运行时动态加载模块,则加载程序将在bundle中搜索模块。如果捆绑包中不存在该模块,则会出现错误。 Webpack不会向服务器询问该模块。这对我们来说是一个问题,因为我们想要在构建/编译时加载一个我们不知道的模块。
我们需要的是在运行时为我们加载模块的加载器(懒惰和动态)。在我的示例中,我使用的是SystemJS和Angular 6 / CLI。
<强> app.component.ts 强>
import { Compiler, Component, Injector, ViewChild, ViewContainerRef } from '@angular/core';
import * as AngularCommon from '@angular/common';
import * as AngularCore from '@angular/core';
declare var SystemJS;
@Component({
selector: 'app-root',
template: '<button (click)="load()">Load</button><ng-container #vc></ng-container>'
})
export class AppComponent {
@ViewChild('vc', {read: ViewContainerRef}) vc;
constructor(private compiler: Compiler,
private injector: Injector) {
}
load() {
// register the modules that we already loaded so that no HTTP request is made
// in my case, the modules are already available in my bundle (bundled by webpack)
SystemJS.set('@angular/core', SystemJS.newModule(AngularCore));
SystemJS.set('@angular/common', SystemJS.newModule(AngularCommon));
// now, import the new module
SystemJS.import('my-dynamic.component.js').then((module) => {
this.compiler.compileModuleAndAllComponentsAsync(module.default)
.then((compiled) => {
let moduleRef = compiled.ngModuleFactory.create(this.injector);
let factory = compiled.componentFactories[0];
if (factory) {
let component = this.vc.createComponent(factory);
let instance = component.instance;
}
});
});
}
}
我-dynamic.component.ts 强>
import { NgModule, Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Other } from './other';
@Component({
selector: 'my-dynamic-component',
template: '<h1>Dynamic component</h1><button (click)="LoadMore()">LoadMore</button>'
})
export class MyDynamicComponent {
LoadMore() {
let other = new Other();
other.hello();
}
}
@NgModule({
declarations: [MyDynamicComponent],
imports: [CommonModule],
})
export default class MyDynamicModule {}
<强> other.component.ts 强>
export class Other {
hello() {
console.log("hello");
}
}
如您所见,我们可以告诉SystemJS我们的捆绑包中已存在哪些模块。所以我们不需要再次加载它们(SystemJS.set
)。我们在my-dynamic-component
中导入的所有其他模块(在此示例中为other
)将在运行时从服务器请求。
答案 1 :(得分:4)
我使用了https://github.com/kirjs/angular-dynamic-module-loading解决方案和Angular 6的库支持来创建我在Github上共享的应用程序。由于公司政策,它需要脱机。一旦有关示例项目源的讨论结束,我将在Github上分享它!
更新:可以找到回购; https://github.com/lmeijdam/angular-umd-dynamic-example
答案 2 :(得分:2)
我相信如果使用webpack构建和运行主应用程序,使用SystemJS加载UMD包是可能的。我使用了一个使用ng-packagr来构建动态插件/插件模块的UMD包的解决方案。这个github演示了描述的过程: https://github.com/nmarra/dynamic-module-loading
答案 3 :(得分:2)
使用angular 6库执行此操作,然后汇总即可完成操作。我刚刚进行了试验,可以与主应用程序共享独立的角度AOT模块,而无需最后重建。
angularCompilerOptions.skipTemplateCodegen
设置为false,然后在构建库后将获得模块工厂。rollup dist/plugin/esm2015/lib/plugin.module.ngfactory.js --file src/assets/plugin.module.umd.js --format umd --name plugin
在https://github.com/iwnow/angular-plugin-example中,您可以找到如何使用独立的构建和AOT开发插件
答案 4 :(得分:0)
是的,您可以通过将模块引用为路由器中的模块来延迟加载模块。以下是https://github.com/start-angular/SB-Admin-BS4-Angular-6
的示例答案 5 :(得分:0)
我已经在Angular 6中进行了测试,以下解决方案适用于从外部包或内部模块动态加载模块。
1。如果要从库项目或包中动态加载模块,请执行以下操作:
我有一个图书馆项目“ admin”(或者您可以使用一个软件包)和一个应用程序项目“ app”。 在我的“ admin”库项目中,我有AdminModule和AdminRoutingModule。 在我的“应用”项目中:
a。在tsconfig.app.json中进行更改:
"compilerOptions": {
"module": "esNext",
},
b。在app-routing.module.ts中:
const routes: Routes = [
{
path: 'admin',
loadChildren: async () => {
const a = await import('admin')
return a['AdminModule'];
}
},
{
path: '',
redirectTo: '',
pathMatch: 'full'
}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {
}
2。如果要从同一项目加载模块。
有4种不同的选项:
a。在app-routing.module.ts中:
const routes: Routes = [
{
path: 'example',
/* Options 1: Use component */
// component: ExampleComponent, // Load router from component
/* Options 2: Use Angular default lazy load syntax */
loadChildren: './example/example.module#ExampleModule', // lazy load router from module
/* Options 3: Use Module */
// loadChildren: () => ExampleModule, // load router from module
/* Options 4: Use esNext, you need to change tsconfig.app.json */
/*
loadChildren: async () => {
const a = await import('./example/example.module')
return a['ExampleModule'];
}
*/
},
{
path: '',
redirectTo: '',
pathMatch: 'full'
}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {
}
``
答案 6 :(得分:0)
不起作用
System.import('http://lab.azaas.com:52048/my-component-library.umd.js').then(module => {
console.log(module);
});
工作
System.import('./../../assets/umds/my-component-library.umd.js').then(module => {
console.log(module);
});