在Angular 2,Angular 4或Angular 5中实现插件体系结构/插件系统/可插入框架

我想在Angular 2Angular 4Angular 5应用程序中实现一个可插入的(插件)框架。

(我开发这个可插拔框架的具体用例是我需要开发一个微型的内容pipe理系统,由于这里没有详细说明的一些原因, Angular 2/4/5几乎完全适合大部分的需求系统。)

通过可插入框架(或插件体系结构),我特指一个系统,它允许第三方开发人员通过使用可插入组件来创build或扩展主应用程序的function,而无需直接访问或了解主应用程序的源代码或内部工作。

可插入框架的例子包括常见的内容pipe理系统,如WordPressDrupal

理想情况下(与Drupal一样)可以简单地将这些可插入的组件(或插件)放到一个文件夹中,让应用程序自动检测它们,并让它们神奇地“工作”。

我试图确定以下五个问题的答案。

  1. 实用性: Angular 2/4/5应用程序的插件框架是否实用? (到现在为止,我还没有find用Angular2/4/5创build真正可插入框架的实用方法。)
  2. 预期的挑战:在实现Angular 2/4/5应用程序的插件框架中可能遇到什么挑战?
  3. 实施策略:可以采用哪些特定的技术或策略来实现Angular 2应用程序的插件框架?
  4. 最佳实践:Angular 2/4/5应用程序实现插件系统的最佳实践是什么?
  5. 替代技术: 如果插件框架在Angular 2/4/5应用程序中不可行,那么相对等效的技术(如React )可能适用于现代Web应用程序

一般来说,使用Angular 2/4/5是非常理想的,因为:

  • 这是非常快的
  • 它具有相对较小的占地面积(在AOTtree shaking ) – 并且该占地面积继续缩小
  • 它消耗很less的带宽(在初始负载之后)
  • 它function强大
  • 它与许多最好的和最新的Web技术(如TypeScriptObservables搭配使用
  • 得到Google支持,未来很可能得到支持和加强

我非常希望在当前的项目中使用Angular 2/4/5 。 如果我能够使用Angular 2/4/5 ,我也将使用Angular-CLIAngular Universal (用于服务器端渲染)。

到目前为止,关于上述问题,我的想法如下。 请审阅并提供您的反馈和启示。

  • Angular 2/4/5应用程序使用包 – 但这不一定与允许应用程序中的插件相同。 其他系统(例如Drupal )中的插件可以通过将插件文件夹放入公用模块目录中进行添加,系统会自动“拾取”插件。 在Angular 2/4/5 ,一个包(可能是一个插件)通常通过npm安装,添加到package.json ,然后手动导入到应用程序中 – 就像在app.module 。 这比Drupal文件夹的Drupal方法复杂得多,让系统自动检测软件包。 安装插件越复杂,人们使用它们的可能性就越小。 如果有一个Angular 2/4/5自动检测和安装插件的方法会好得多。 我非常有兴趣find一种方法,允许非开发人员安装Angular 2/4/5应用程序并安装任何选定的插件,而无需了解应用程序的所有体系结构。

  • 通常,提供可插拔体系结构的好处之一就是对第三方开发者来说,扩展系统的function是非常容易的。 显然,这些开发人员不会熟悉所插入应用程序的所有复杂代码。 一旦开发插件,其他更less的技术用户可以简单地安装应用程序和任何选定的插件。 然而, Angular 2/4/5比较复杂,学习曲线很长。 为了进一步复杂化,大多数生产Angular 2/4/5应用程序也使用Angular-CLIAngular UniversalWebPack 。 有人正在实现一个插件,可能至less要有一些关于如何将所有这些组合在一起的基本知识,以及对TypeScript深入了解和对TypeScript的合理熟悉。 知识需求是如此极端,以至于没有第三方想要开发插件吗?

  • 大多数插件可能会有一些服务器端组件(例如用于存储/检索插件相关数据)以及一些客户端输出。 Angular 2/4/5明确地(强烈地)阻止开发人员在运行时注入自己的模板,因为这会带来严重的安全风险。 为了处理插件可以容纳的多种types的输出(例如,显示graphics),似乎允许用户创build以另一种forms注入到响应stream中的内容可能是必要的。 我想知道怎样才能适应这种需要,而不用象征性地切割Angular 2/4/5的安全机制。

  • 大多数生产Angular 2/4/5应用程序是使用Ahead of Time (汇编) Ahead of Time编译的。 (可能都应该是)我不确定如何将插件添加到(或与之集成)预编译的应用程序。 最好的scheme将涉及与主应用程序分开编译插件。 但是,我不确定如何使这项工作。 回退可能是使用任何包含的插件来重新编译整个应用程序,但是对于仅仅想要(在他自己的服务器上)安装应用程序的pipe理用户以及任何select的插件而言,这会使事情复杂一些。

  • Angular 2/4/5应用程序中,特别是预编译的应用程序,单个错误或冲突的代码可能会破坏整个应用程序。 Angular 2/4/5应用程序并不总是最容易debugging的。 应用不良行为的插件可能会导致非常不愉快的体验。 目前我还没有意识到一种优雅地处理不良行为插件的机制。

请分享您的想法和想法。 提前谢谢了。

更新:

2017年11月22日。 现在,Angular 5已经不在了,我正在重新评估哪些新function可能会更好地促进可插入框架的实现。

你正在寻找的是懒惰的模块加载。 这是一个例子: http : //plnkr.co/edit/FDaiDvklexT68BTaNqvE?p=preview

 import {Component} from '@angular/core'; import {Router} from '@angular/router'; @Component({ selector: 'my-app', template: ` <a [routerLink]="['/']">Home</a> | <a [routerLink]="['/app/home']">App Home</a> | <a [routerLink]="['/app/lazy']">App Lazy</a> <hr> <button (click)="addRoutes()">Add Routes</button> <hr> <router-outlet></router-outlet> ` }) export class App { loaded: boolean = false; constructor(private router: Router) {} addRoutes() { let routerConfig = this.router.config; if (!this.loaded) { routerConfig[1].children.push({ path: `lazy`, loadChildren: 'app/lazy.module#LazyModule' }); this.router.resetConfig(routerConfig); this.loaded = true; } } } 

最佳汤…

示例应用程序与一个工作的插件系统(感谢Gijs创buildgithub回购!) https://github.com/PacktPublishing/Mastering-Angular-2-Components/tree/master/angular-2-components-chapter-10 based在电子书掌握Angular 2组件

  • 插件体系结构来扩展核心应用程序组件
  • 文件插件系统(只需添加插件目录/文件,无需编辑任何核心configuration文件或需要重新编译您的应用程序!)
  • 加载和dynamic使用插件
  • build立一个基本的插件pipe理器来激活/停用dynamic插件

干杯,尼克拉斯

我做了一个加载和编译其他模块的bootstrap时间,但我还没有解决循环依赖的问题

  const moduleFile: any = require(`./${app}/${app}.module`), module = moduleFile[Object.keys(moduleFile)[0]]; route.children.push({ path: app, loadChildren: (): Promise<any> => module }); promises.push(this.compiler.compileModuleAndAllComponentsAsync(module)); 

然后在AppModule中添加这个:

 { provide: APP_INITIALIZER, useFactory: AppsLoaderFactory, deps: [AppsLoader], multi: true }, 

我正在寻找一个angular度为2/4的插件系统,以便为工作中的企业应用程序开发RAD环境。 经过一番研究,我决定实现一个收集数据库存储(但可能在文件系统中)的伪angular组件。

存储在数据库数据库中的组件基于ng-dynamic ,主要组件的实现类似于这个:

 declare var ctx: any; @Component({ selector: 'my-template', template: ` <div> <div *dynamicComponent="template; context: { ctx: ctx };"></div> </div> `, providers: [EmitterService], }) export class MyTemplateComponent implements OnMount, AfterViewInit, OnChanges { // name private _name: string; get name(): string { return this._name; } @Input() set name(name: string) { this._name = name; this.initTemplate(); } template: string; ctx: any = null; private initTemplate() { this.templateSvc.getTemplate(this.name).subscribe(res => { // Load external JS with ctx implementation let promise1 = injectScript(res.pathJs); // Load external CCS let promise2 = injectScript(res.pathCss); Promise.all([promise1, promise2]).then(() => { // assign external component code this.ctx = ctx; // // sets the template this.template = res.template; this.injectServices(); if (this.ctx && this.ctx.onInit) { this.ctx.onInit(); } }); }); } 

外部JavaScript代码类似于angular度组件:

 var ctx = { // injected _httpService: {}, _emitterService: null, // properies model: { "title": "hello world!", }, // events onInit() { console.log('onInit'); }, onDestroy() { console.log('onDestroy'); }, onChanges(changes) { console.log('changes', changes); }, customFunction1() { console.log('customFunction1'); }, childTemplateName: string = 'other-component'; }; 

而组件的模板就像angular模板:

 <a (click)="customFunction1()">{{ ctx.model.title }}</a> <input [(ngModel)]="ctx.model.title" type="text" /> 

也可以嵌套:

 <a (click)="customFunction1()">{{ ctx.model.title }}</a> <my-template [name]="childTemplateName"></my-template> 

尽pipe这并不完美,但是自定义组件的开发人员具有与angular2 / 4相似的框架。

这可以通过“手动”完成。 由于webpack对外部(plug-ins)模块一无所知,因此不能将它们包含在bundle中。 所以我所做的就是查看webpack生成的代码,并在main.bundle.js中find这些代码:

 var map = { "./dashboard/dashboard.module": ["../../../../../src/app/dashboard/dashboard.module.ts","dashboard.module"]}; 

让我们来看看那个数组包含的内容:

  1. “./dashboard/dashboard.module” – 这是我们要延迟加载的模块的路由URL,例如: {path:'dashboard',loadChildren:'./dashboard/dashboard.module#DashboardModule'}
  2. “../../../../../src/app/dashboard/dashboard.module.ts” – 这是入口点(构造器)从
  3. “dashboard.module” – 没有chunk.js的实际文件名(例如: dashboard.module.chunk.js

所以从理论上讲,如果你添加条目到map属性来configuration你的路由,并按照这个模式,你可以有一个插件系统。 现在的挑战是如何添加或删除该地图属性的条目。 显然,它不能从angular码完成,它应该做外部工具。

我尝试使用ABP,Angular和ASP.NET Core实现插件架构: https : //github.com/chanjunweimy/abp_plugin_with_ui

基本上,我使用不同的angular度应用程序开发了angular度插件,然后将其dynamic添加到一起。

更多信息我如何实现它:

我有2个angular度cli应用程序,1是主angularcli应用程序,另一个是插件angular度cli应用程序。 我们在Angular-cli插件架构方法中面临的问题是我们如何整合它们。

现在,我所做的是,我在这两个应用程序上运行ng-build,并将它们放入一个“wwwroot”文件夹,然后托pipe在ASP.NET核心2.0服务器中。 显示这个想法的更简单的存储库是Angular Multiple App: https : //github.com/chanjunweimy/angular-multiple-app

abp_plugin_with_ui是一个仓库,它开发一个包含后端和Angular cli的插件。 对于后端,我使用了aspnetboilerplate框架,前端是使用多angular度cli应用程序开发的。

为了让主应用程序与插件应用程序集成,我们必须在这两个应用程序上运行“ng-build”(注意,我们也必须改变为插件应用程序的href),然后移动插件的内置内容angular色cli应用程序,到主应用程序“wwwroot”文件夹。 完成所有这些之后,我们可以运行“dotnet run”来为ASP.NET Core 2.0 Web应用程序提供“ng build”生成的静态文件。

希望它有帮助。 任何意见,欢迎! ^^