如何模块化AngularJS应用程序/插件

我有一些关于从Grails(REST-API,AngularJS,MongoDB,Tomcat,Spock,几个插件的一部分)到Node.js + Angular.js的迁移(软件)架构问题。 我可能不得不解释一下Grails项目的结构,所以我们来看看:

有一个主要的Grails应用程序(在几个其他应用程序旁边),它build立在几个插件上。 每个插件都可以自行执行 – 这意味着它有自己的用户界面,单独的模板,服务,控制器,路由,testing等。它也托pipe在不同的存储库。 这是由Grails插件机制完成的。 其优点是testing工作量less,编译时间less,模块化,单一职责等。

但是,编译和testing的时间太昂贵了。 另外我不喜欢API提供模板/视图的一部分的事实。 我希望后端API“只是作为后端API”,而前端“只是作为前端”。 因此,每个AngularJS应用程序/插件将提供自己的视图,路线,服务等,但他们也可能依赖于其他插件。

所以我想达到如下:

  • 一个主要的AngularJS应用程序,其中包括几个插件(一个插件可以像报告生成器,留言簿或任何其他应用程序的一个独立的部分,无论是与特定的路线,或只是一小部分的页面)。
  • 每个插件必须是一个独立的AngularJS应用程序(可能在开发过程中通过grunt或其他)执行。 所以UI开发人员不需要启动整个后端应用程序,进一步说,我们可以只用JavaScript来运行functiontesting
  • 只通过REST进行通信,前端必须从API中检索所有的数据
  • 每个插件必须自行testing
  • 插件可能需要其他插件才能工作
  • 主要的index.html(和app.js?)可能由Nginx服务器提供,该服务器与后端(API)的其余部分分离,

尽pipe我头脑中有一个特定的图像,但我正在为如何设置这个架构而苦苦挣扎。

在Grails中,插件机制以某种方式合并插件依赖的设置(如URL映射,依赖关系等)到它们被包含/注入的主应用程序 – 这也是我想要用AngularJS实现的。 所以:

  • AngularJS有一些相同的机制吗?
  • 我该如何将每个插件的路由提供/合并到主应用程序中?
  • 我怎样才能声明应用程序和插件的依赖?
  • 什么工具可能是有用的构build过程?
  • 如何build立插件资源(css / less文件,视图,服务等)的懒惰恢复?
  • 阻止应用程序在启动时提供插件的所有资源(我猜路线在启动时是必需的)

由于这不仅仅是一个如何做这个或那个问题,我原谅自己,如果我失去了重要的部分,或者如果一些部分不够清楚。 只要问我,我会深入回答每个问题。

**这个答案是不完整的**

在深入研究之前,我想确保自己明白你的意思。

下面是一个Loader模块的快速实现,用于pipe理延迟加载(插件,供应商资产,pipe理员等)。

这有帮助吗?

angular.module('Bizcoin.loader') .service('Loader', Loader); function Loader($injector, $ocLazyLoad, User) { var Loader = { load: load, get: get, plugins: {}, flags: {} }; init(); return Loader; function init() { load('vendors'); if (userIsAdmin) load('admin'); } function load(plugin) { Loader.plugins[plugin] = Loader[plugin] || $ocLazyLoad.load('path/to/'+plugin+'.js'); return Loader.plugins[plugin].then(setFlag); function setFlag() { return Loader.flags[plugin] = true; } } function get(plugin) { return load(plugin).then(function() { return $injector.get(plugin); }); } } 

我工作的是一个由20+个独立的区域(或模块)组成的大型.Net / AngularJS应用程序,以及在所有区域内通用和重用的一些核心function。

让我详细谈谈我如何为我的具体情况做这件事,并且可能会提出一些想法。 我使用.Net的事实在这里是无关紧要的,因为这可以通过任何框架来实现。

每个区域都作为一个独立的应用程序,仅依赖于核心function,始终存在。 每个区域都有自己的ASP.Net MVC路线。 每个区域向核心应用程序注册它想要提供的菜单链接。 当客户去应用程序仪表板时,只有应用程序的核心部分。 当用户点击菜单中的链接时,它将导航到其中一个区域提供的内容,并且只加载核心加上该区域的资源。

让我们看看这是如何完成的。

在应用程序的主页面,我加载如下脚本:

 <script type="text/javascript"> // a JS object with all the necessary app data from the server. // eg: menu data, etc window.appContext = @Html.Action("ApplicationContext", "Portal")); </script> @Scripts.Render("~/bundles/angular-main") @RenderSection("AngularAreas", required: false) 

我使用点.Net捆绑和部分。

应用程序的主要(核心)AngularJS部分由angular度configuration,国际化服务,全局通知服务,可重用的UI组件等组成。这是加载的@Scripts.Render("~/bundles/angular-main")

当用户导航到该区域时,每个区域将填写@RenderSection("AngularAreas", required: false)部分。

让我们看看一些AngularJS代码。 这里是主要app.ts的一部分。

 // If user is visiting an Area, the NgModules array will be augmented. // with the modules the Area wants to provide (to be bootstrapped) export var LoadNgModules = [ NgModules.Config, NgModules.Core ]; angular.module(NgModules.Bootstraper, LoadNgModules); angular.element(document).ready(function () { angular.bootstrap(document, [NgModules.Bootstraper]); }); 

现在让我们看一个示例区域。

以下是Area将如何提供其资产,以@RenderSection("AngularAreas", required: false)输出@RenderSection("AngularAreas", required: false)

 @section AngularAreas { @Scripts.Render("~/bundles/areas/staff-management") } 

它是一个简单的包,包含该区域的所有脚本。 现在,让我们看看这个区域的AngularJS代码的重要部分。

 var StaffManagementNgModule = 'areas.staff-management'; // Push our self into the modules to be bootstrapped LoadNgModules.push(StaffManagementNgModule ); // Define the module angular .module(StaffManagementNgModule , ['ngRoute', NgModules.Core]) .config([ '$routeProvider', '$locationProvider', ($routeProvider: ng.route.IRouteProvider, $locationProvider) => { $routeProvider .when(staff', { template: '<staff></staff>' }) .when('staff/details/:id', { template: '<staff-details></staff-details>' }); } ]);; 

就是这样,从这里开始,Area是一个正常的Angular应用程序。

总而言之,我们加载了主要的(核心)AngularJSfunction,并提供了一个区域可以用自己的模块填充的LoadNgModules数组。

我们将Area脚本和“我们自己”加载到LoadNgModules数组中。

最后运行angular.bootstrap。

为了完整起见,下面是C#的一个片段,显示了一个区域如何向主应用程序指示它是可用的

 public class ItemManagementModuleRegistration : IModuleRegistration { public void Register(ModuleContext moduleContext) { string thisAreaName = "Staff"; moduleContext.RegisterMenu(menuContext => { var ItemsMenu = menuContext.Items(thisAreaName); // add urls and stuff... }); // register more stuff with the moduleContext } } 

使用reflection可以很容易地find哪些区域“安装”。

这些是设置的主要运动部分。 每个区域都可以有自己的API和testing。 这是相当灵活的。