現在、クライアントサーバーでホストされているプロジェクトに取り組んでいます。新しい「モジュール」の場合、アプリケーション全体を再コンパイルする意図はありません。つまり、クライアントは、実行時にルーター/遅延ロードされたモジュールを更新したいとします。私はいくつかのことを試してみましたが、動作させることができません。まだ試してみたいことや見逃したことを知っている人がいるかどうか疑問に思っていました。
気づいたのは、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
私は同じ問題に直面していました。私の知る限り、これまでのところ:
Webpackはすべてのリソースをバンドルに入れ、すべてのSystem.import
を__webpack_require__
に置き換えます。したがって、SystemJsNgModuleLoaderを使用して実行時にモジュールを動的にロードする場合、ローダーはバンドル内のモジュールを検索します。モジュールがバンドルに存在しない場合、エラーが発生します。 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;
}
});
});
}
}
my-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
)にインポートする他のすべてのモジュールは、実行時にサーバーから要求されます。
https://github.com/kirjs/angular-dynamic-module-loading ソリューションとAngular 6のライブラリサポートを使用して、Githubで共有したアプリケーションを作成しました。会社のポリシーにより、オフラインにする必要がありました。サンプルプロジェクトソースに関する議論が終わったら、すぐにGithubで共有します!
更新:リポジトリが見つかりました。 https://github.com/lmeijdam/angular-umd-dynamic-example
angular 6ライブラリを使用して実行し、ロールアップがトリックを実行します。私はそれを試してみましたが、最後に再構築せずに、スタンドアロンのangular 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でプラグインを開発する方法を見つけることができます
Webpackを使用してメインアプリケーションをビルドおよび実行する場合、これはSystemJSを使用してUMDバンドルをロードできると考えています。 ng-packagrを使用するソリューションを使用して、動的プラグイン/アドオンモジュールのUMDバンドルを構築しました。このgithubは、説明されている手順を示しています。 https://github.com/nmarra/dynamic-module-loading
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 {
}
``
はい、ルーターでモジュールとして参照することで、モジュールを遅延ロードできます。以下に例を示します https://github.com/start-angular/SB-Admin-BS4-Angular-6