【问题标题】:How to modularize AngularJS applications / plugins如何模块化 AngularJS 应用程序/插件
【发布时间】:2014-02-15 20:08:29
【问题描述】:

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

有一个主要的 Grails 应用程序(除了几个其他应用程序之外),它建立在几个插件之上。这些插件中的每一个都能够自行执行——这意味着它有自己的 UI、单独的模板、服务、控制器、路由、测试等。它还托管在不同的存储库中。 这是由 Grails 插件机制完成的。好处是更少的测试工作、更少的编译时间、模块化、单一职责等等。

但是,编译和测试的时间仍然太昂贵。我也不喜欢 API 提供部分模板/视图的事实。我希望后端 API“只是作为后端 API”,而前端“只是作为前端”。 所以每个 AngularJS 应用程序/插件都会提供自己的视图、路由、服务等。但它们也可能依赖于其他插件。

所以我想要达到的效果如下:

  • 一个主要的 AngularJS 应用程序,其中包括多个插件(一个插件可以是报告生成器、留言簿或其他任何东西,它指的是应用程序的单个独立部分,或者具有特定路由,或者只是一小部分页)。
  • 每个插件都必须是一个独立的 AngularJS 应用程序(可能在开发过程中通过 grunt 等执行)。这样 UI 开发人员就不需要启动整个后端应用程序,而且我们可以只使用 JavaScript 运行功能测试
  • 仅通过 REST 进行通信,前端必须从 API 检索所有数据
  • 每个插件都必须是可单独测试的
  • 插件可能需要其他插件才能工作
  • 主要的 index.html(和 app.js?)可能由 Nginx 服务器提供,与后端 (API) 的其余部分分离

虽然我脑子里有一个具体的画面,但我正在为如何设置这个架构而苦苦挣扎。

在 Grails 中,插件机制以某种方式将插件相关设置(如 URL 映射、依赖项等)合并到包含/注入它们的主应用程序中——这也是我希望使用 AngularJS 实现的目标。所以:

  • AngularJS 是否有某种相同的机制?
  • 如何将每个插件的路由提供/合并到主应用程序中?
  • 如何声明应用程序和插件依赖项?
  • 哪些工具可能对构建过程有用?
  • 如何建立插件资源(css/less 文件、视图、服务等)的惰性检索?
  • 阻止应用程序在启动时提供插件的所有资源(我猜是启动时需要路由)

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

【问题讨论】:

    标签: node.js angularjs grails single-page-application modularization


    【解决方案1】:

    **此答案不完整**

    在深入研究之前,我想确保我理解你。

    这是一个加载器模块的快速实现,用于管理延迟加载(插件、供应商资产、管理内容等)。

    这有帮助吗?

    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);
        });
      }
    
    }
    

    【讨论】:

      【解决方案2】:

      我在一个大型 .Net/AngularJS 应用程序上工作,该应用程序由 20 多个独立的区域(或模块)和一些在所有区域中通用和重用的核心功能组成。

      让我详细介绍一下我如何针对我的特定案例执行此操作,它可能会提供一些想法。我使用 .Net 的事实在这里无关紧要,因为这可以通过任何框架实现。

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

      让我们看看这是怎么做到的。

      在应用程序的主页中,我像这样加载脚本:

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

      我使用 dot .Net 包和部分。

      应用程序的主要(核心)AngularJS部分由角度配置、国际化服务、全局通知服务、可重用的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]);
      });
      

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

      这是一个区域如何提供其资产,以@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 应用程序。

      总而言之,我们加载主要(核心)AngularJS 功能并提供 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
          }
      }
      

      使用反射可以轻松找到“已安装”的区域。

      这些是设置的主要移动部分。 每个区域都可以有自己的 API 和测试。它非常灵活。

      【讨论】:

      • 谢谢,这很有帮助。还有一个问题:如果你有不同的项目,你如何构建一个包含所有角度逻辑的 JavaScript 文件?例如,我有一个核心 Angular 应用程序项目和一个模块项目。您如何将这两个项目修补在一起,例如使用 Grunt 和 NPM?这里最好的方法是什么?
      • 我不构建单个文件。我建立n+1。一个用于应用程序的核心,一个用于 n 个区域中的每一个。然后我使用 AngularAreas 部分来加载区域 js 文件。 Gulp 任务可以用于此。
      • +1 这是一个很好的答案。您能否在 GitHub 中制作一个样板(如果可能的话,甚至写一个博客)?我相信它会帮助很多开发人员,尤其是那些将 AngularJS 与强类型 ASP.Net 结合使用的开发人员。
      • 是的,我可以理解样板的用处。我可以创建它并在接下来的几天内更新此答案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-09-07
      • 2011-11-28
      • 2011-02-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多