MVC 5 & Angular 2 多个 "Siloed" 应用程序结构建议

MVC 5 & Angular 2 Multiple "Siloed" Apps Structure Advisement

我们有一个已经存在的大型 MVC 5 ASP.NET 4.5.1 Web 应用程序。由于它涵盖了如此多的领域,核心概念是每个页面都是它自己的应用程序。除了 JQuery、常规 Javascript 和 Handlebars 模板之外,所有现有页面均未使用任何内容。

Angular 2 看起来非常令人兴奋,但我正试图弄清楚它如何与我们的理念相得益彰。例如,下面是我们的 mvc 路由当前如何为我们单独的应用程序提供服务...

Area/Controller1/Action1(App1)

Area/Controller1/Action2(App2)

Area/Controller2/Action1(App3)

等等

然后我们有单独的 API 控制器来提供我们的 JSON 数据。从 Angular 2 的初始 readings/learnings 开始,我似乎无法理解我将如何提供单独的应用程序(因为我能找到的所有东西总是希望 index.html 作为家,这使得如果你真的在做一个 SPA,那就有意义了)。从本质上讲,我们正在尝试继续开发通过此现有结构提供的多个 SPA,而不必更改网站上较旧的 "legacy" 应用程序以使用 Angular,直到它们被单独重新访问,并弄清楚如何有效地进行路由。

路由: 所以我脑海中的一个例子是 Area/Controller/Action(App)/routeUrlstuff

我仍然希望能够让他们复制并粘贴扩展 link 并使用 angular 路由带回,而不是仅仅切断 URL 和在起点启动 angular 应用程序。

我真的没有任何代码可以展示,因为我正在尝试做一个当前项目作为概念证明,从现在开始使用 Angular 2 对于应用程序是可行的。

我不确定我是否得到了完整的图像,但会尝试通过告诉你我如何使用 Angular 进行多个 SPA 来帮助你。

在 _Layout.chtml 中,您创建以下内容

<body data-ng-app="appMain">
  <main>
     @RenderBody()
  </main>
</body>

在Home.chtml中设置了上面的布局,你写下面的

<script src="@Url.Content("~/Models/HomeModel.js")" type="text/javascript"/>
<script src="@Url.Content("~/ViewModels/HomeViewModel.js")" type="text/javascript"/>

<div data-ng-controller="HomeViewModel" ng-init="">
  home view html content
</div>

在 HomeController.cs 中创建 return 上述

的索引操作

对于路由和多模块,angular在这方面非常好。请参阅下面的另一个示例代码:

CreateAccount.chtml 上面的布局设置。

<script src="@Url.Content("~/Models/AccountCreateModel.js")" type="text/javascript"/>
<script src="@Url.Content("~/ViewModels/AccountCreateViewModel.js")" type="text/javascript"/>

<div data-ng-app="accountCreate" data-ng-controller="AccountCreateViewModel">
    html content
</div>

AccountCreateViewModel.js:

    var accountCreateModule = angular.module('accountCreate', ['common'])
        .config(function ($routeProvider, $locationProvider) {
            $routeProvider.when(rootPath + 'Area/Controller/Action(App)/routeUrlstuff', { templateUrl: rootPath + 'Templates/redirectPage.html', controller: 'AccountCreateViewModel' });
            $routeProvider.otherwise({ redirectTo: rootPath + 'Area/Controller/Action(App)' });
            $locationProvider.html5Mode(true);
        });

accountCreateModule.controller("AccountCreateViewModel", function ($scope, $window) {

    // module scope code here
});

每个 angular 模块都有自己的路由和业务逻辑,与其他模块完全不同。每个区域及其 mvc 操作(整页重新加载)和 Web api 方法(JSON))现在都是一个以非常简洁的方式编写的 SPA 筒仓。

您不必在 Angular 中为整个站点进行路由。如果您已经在 ASP.NET MVC 端进行路由,请保持这种方式。将其视为每个页面请求都是它自己的 angular 应用程序。这意味着每个页面在正文中都有它自己的 ng-app,它包括它自己的 angular.js 以及 app.js 是什么。 app.js 将初始化 angular 并设置您仅用于该页面的 controllers/components。每个页面都会这样做。如果您在每个应用程序中有多个视图,那么您可以使用 angular 路由仅针对该应用程序的那些视图。当一个人点击一个新应用程序 link 时,它使用 ASP.NET 服务器端路由来提供将初始化的新页面 angular,仅在客户端为该应用程序设置页面间路由,并设置该应用程序使用的控制器。

想法是配置 MVC 路由以将每个控制器的所有可能 url 映射到相同的相应视图,即:

routes.MapRoute("app1", "app1/{*catchall}", new {controller = "App1", action = "Index", id = UrlParameter.Optional});
routes.MapRoute("app2", "app2/{*catchall}", new {controller = "App2", action = "Index", id = UrlParameter.Optional});

每个 Index.cshtml 都有自己的脚本部分指向相应的 angular 应用程序。所以如果你粘贴 URL app2/angular/dashboard/page 它也会路由相应的控制器和视图 (App2/Index.cshtml),然后当 angular 路由器启动时它会打开 [=24] 的其余部分=] /angular/dashboard/page 页。

然后为每个应用调整基础 url 和 angular 路由,这取决于您的应用结构。

从解决方案架构师的角度来看,我建议在服务器端和客户端都进行更改。

为了配合,假设有 3 个应用程序 Area/Controller1/Action1(App1)

Area/Controller1/Action2(App2)

Area/Controller2/Action1(App3)

1。确定 SPA

main 应用程序开发 angular SPA。主应用程序的视图链接到 Controller1。这应该是您 angular SPA 中的一条路线。但是 Controller1/Action1 应该是 angular SPA 中不可用的路线。它需要使用 html href 链接直接从服务器请求。这实质上意味着页面重新加载。

2。服务器上的项目位置

完成不同 SPA 的开发后,使用压缩的 JS 和 index.html 生成生产版本。

将不同的SPA放在各自的文件夹下。比如/home/user/proj/Controller1/Action1就是一个文件夹,里面有独立的SPA

proj
-- Controller 1
---- Action 1
---- Action 2
-- Controller 2
---- Action 1

3。网络服务器配置

配置您的 web/app 服务器。例如在 nodejs 中。 (IIS 会有类似的东西)

# For Action SPA
location = /area/(.*)/(.*) {
  root /home/user/proj//;
}
# For main SPA
location = / {
  root /home/user/proj;
}

其中 $1 将包含目标控制器 $2 将包含目标 Action

将获取 /area/target Controller/target 中的 index.html Action,这些 Action 是它们自己的独立 SPA。

4。摘要

不同的团队可以使用这种方法将 Main、App1、App2 和 App3 作为独立的可交付成果来处理。

我最近在做一个有类似问题的项目。我们确实考虑过实施多个 SPA,但最终决定实施一个具有多个模块的 SPA。

我认为我们也可以为多个 SPA 扩展该解决方案。让我们看一个简单的用例:

您想创建 2 个 SPA

  1. UserApp 包含 2 个模块(AddUser 和 ManageUser)
  2. ProductApp 包含 2 个模块(AddProduct 和 ManageProduct)

您有以下 MVC 控制器操作要用于上述 SPA:

  1. myApp/user/add
  2. myApp/user/manage
  3. myApp/product/add
  4. myApp/product/manage

MVC 控制器操作 1 和 2 将与 SPA UserApp 一起使用,路由到 AddUser 和 ManageUser 模块。同样,控制器操作 3 和 4 将与 SPA ProductApp 一起使用,路由到 AddProduct 和 ManageProduct 模块。

概念上看起来像这样:

Multiple SPAs with multiple modules

Angular捆绑:

我会将打字稿转译和捆绑留给 Angular CLI。如果您的项目变得复杂并且您觉得 angular cli 无法满足您的打包需求,您可以随时弹出 webpack 配置。

Yakov Fain 中有一篇非常好的博客,您可以查看配置 cli 以捆绑多个 SPA。基本上,您将配置 angular cli 以将您的 SPA 输出到不同的 dist 文件夹。在我们的例子中,假设这些将是:

  1. UserApp SPA 的 userAppDist
  2. ProductApp SPA 的 productAppDist

MVC 布局:

要在不同的页面上加载不同的 SPA,您必须为每个 SPA 创建不同的布局或子布局。

假设:_userLayout.cshtml 用于 UserApp

在 _userLayout.cshtml 中,您必须从 userAppDist 文件夹中加载脚本

像这样:

<main class="layout">
    <base href="/">
</main>

@*Now load scripts from userAppDist*@

同样,您必须为来自 productAppDist 的其他 SPA 加载脚本实施布局。假设 _productLayout.cshtml

路线:

为简单起见,您可以将服务器路由与 angular SPA 模块路由匹配。否则,您将必须在 angular App 上实施 HashLocationStrategy。 假设您选择简单选项,您会看到以下视图:

  1. myApp/user/add --> AddUser.cshtml
  2. myApp/user/manage --> ManageUser.cshtml
  3. myApp/product/add --> AddProduct.cshtml
  4. myApp/product/manage --> ManageProduct.cshtml

AddUser.chtml 和 ManageUser.cshtml 将使用 _userLayout 并且看起来像:

<user-app></user-app>

这是针对 UserApp SPA

AddProduct.cshtml 和 ManageProduct.cshtml 将使用 _productLayout 并且看起来像:

<product-app></product-app>

这是针对 ProductApp SPA

这些应用程序的 MainAppComponent 模板将有

<router-outlet></router-outlet>

这将根据来自服务器的路由解析为 angular 模块路由。现在你必须匹配你 angular Apps

中的路由

UserAppRoutingModule 示例:

const routes: Routes = [
  { path: 'myApp/user/add', loadChildren: 'userApp/modules/add-user/add-user.module#AddUserModule' },
  { path: 'myApp/user/manage', loadChildren: 'userApp/modules/manage-user/manage-user.module#ManageUserModule' }
];

同样,您必须为产品 App SPA 中的产品页面定义匹配路由。

希望这对您有所帮助。

更新:避免多个路由文件的配置

对于上述用例,将有以下控制器:

具有指向 user/index.cshtml(使用 _userLayout)的索引操作的 UserController,具有以下代码:

 <user-app></user-app>

具有指向 product/index.cshtml(使用 _productLayout)的索引操作的 ProductController,具有以下代码:

<product-app></product-app>

您还需要修改 routeConfig.cs 以包括以下内容:

routes.MapRoute("user", "user/{*catchall}", new { controller = "User", action = "Index", id = UrlParameter.Optional });
routes.MapRoute("product", "product/{*catchall}", new { controller = "Product", action = "Index", id = UrlParameter.Optional });

以上更改将强制执行以下行为:

  1. 路由到 myapp/user --> 将转到 user/index.cshtml
  2. 此外,myapp/user/blah/blah 之后的任何扩展路由仍将解析为 user/index.cstml

User/index.chtml 启动 Angular 用户 SPA,然后 angular 路由将启动。您将从产品路由和 product/index.cshtml 观察到类似的行为嗯。

angular SPA 用户的最终路由配置:

const routes: Routes = [
    { path: 'user', redirectTo: 'user/add' },
    { path: 'user/add', loadChildren: 'userApp/modules/add-user/add-user.module#AddUserModule' },
    { path: 'user/manage', loadChildren: 'userApp/modules/manage-user/manage-user.module#ManageUserModule' }
  ];

第一个路由是用户应用程序的默认路由。这映射到 MVC 路由 myapp/user。其余路由不需要匹配 MVC 路由。您也必须对产品 SPA 进行类似的配置。