Angular ng-upgrade 陷入与单例提供程序的无限循环

Angular ng-upgrade stuck in infinite loop with singleton provider

我们正在为一个拥有庞大、混乱的代码库(794k+ JS LoC,396k+ JSP LoC)的大客户执行 AngularJS-Angular 迁移,并且遇到了我们正在努力解决的问题。这是一个人为的问题,所以我会尝试解释一下上下文。

直到最近,他们还有几十种不同的手动复制 AngularJS 版本,都是 1.5.6 或更低版本。我们设法将它们放到一个由 NPM 管理的 AngularJS 1.8.2 副本中。然而,一些重大更改是如此之大,以至于我们无法承担在所有地方修复它们的费用——例如,$http 重大更改影响了该代码库中成千上万的地方。

因此,我们所做的是将 HTTP 服务(以及其他一些服务)从 1.5.6 反向移植到 1.8.2。我们使用 .provider(...) 函数将旧提供程序的副本传递到新版本的 AngularJS 中。我们只会在我们向后移植的服务中没有已知安全修复程序的情况下这样做。这解决了这个问题,但很久以后我们遇到了另一个问题:像 HTTP 提供者这样的一些提供者有状态。当多次使用提供者时,此状态可能会丢失 - AngularJS 使用 new 从我们的构造函数实例化提供者,此时 HTTP 提供者的任何先前状态都将被清除。在客户端的应用程序中,存在很多复杂的误导,因此同一提供商很可能在同一会话中被反向移植两次。因此,我们遇到了一个问题:提供者被构建了两次,在第二次构建时清除了在第二次构建之前可能已更改的 HTTP 提供者的状态。

所以,为了避免这种情况发生(我觉得我有点像在忏悔箱里......)我们添加了一个抽象层将其变成一个单例:

let innerHttpProviderInstance = null;
function $HttpProvider_V_1_5_6() {
  if(innerHttpProviderInstance == null) {
    innerHttpProviderInstance = new $HttpProvider_V_1_5_6_inner();
  }
  return innerHttpProviderInstance;
}

//The original HttpProvider from 1.5.6
function $HttpProvider_V_1_5_6_inner() { ... }

然后像这样使用(在很多地方):

const app = angular.module('app', mainAppDependencies).config(['$provide', '$controllerProvider','$locationProvider', function($provide, $controllerProvider,$locationProvider) {
    ...
}])
  .provider('$http', $HttpProvider_V_1_5_6)

现在,我们终于接近完成到 AngularJS 1.8.2 的升级,并且正在考虑使用 ng-upgrade 迁移到 Angular。我们有一个非常简洁的混合架构设置:Angular 应用程序 upgrade 是根 AngularJS 节点,而 downgrade 是 Angular 叶节点.我们希望首先升级一些 neaf 节点,然后一次升级这些节点的一个父节点,直到我们在 Angular 上拥有整个分支。这主要基于 Victor Savkin 的“升级 Angular 应用程序”。在引入上述单例更改之前,这似乎运作良好。现在,无论何时加载应用程序,它都会陷入无限循环,重新加载页面并将 !#%2F 添加到 URL 的开头。这似乎类似于以下 GitHub 问题,尽管它显然已修复:https://github.com/angular/angular/issues/5271

当我们从我们的提供者反向移植中删除单例时,它工作正常。当我们以任何方式重新引入它时(我们尝试了多种方法),它又会崩溃。我认为这与绑定和 ng-upgrade 尝试重新加载页面有关,因为它认为状态已经改变但我真的不清楚。所以,这就是我们发现自己身处的 gundarks 的巢穴。对我们的下一步有什么建议吗?

编辑: 我们偶然发现了 setuplocationsync,这似乎与这里发生的事情有关。如果我理解正确,它应该解决一个已知的错误,在该错误中 Angular/AngularJS 触发彼此的路由,导致它们循环。当我们在我们的设置中调用这个函数时,它 almost 解决了这个问题——页面最终会加载(而在它无限期重新加载之前),但它仍然会经历几十次重新加载迭代,并且而不是仅仅将 !#%2F 添加到 URL,它现在像这样重复完整的 URL(我们试图到达页面 /full-url/):/full-url/full-url/full-url/full-url/.../full-url?params=vals

我可能无法准确回答它为什么会循环,但以下是我在迁移应用程序时采取的可能有用的步骤。我们也没有升级任何 angularjs 服务/组件,只是降级了迁移的 angular 个。

订单如下: 常量>服务>对话框>组件>路由

创建接口和清理在整个迁移过程中确实从未停止过,但在后期阶段有接口帮助。如果还没有,我会建议先完成它。我还建议创建一个端点常量来存储你所有的 http 内容。

对于服务,我们就是这样做的。我们的大多数应用程序都有分散在整个应用程序中的 $http 连接,因此在将所有内容移至端点常量和创建单个 http 服务之间使调试变得更加容易。我建议从注入最少的小型服务开始,然后逐渐迁移所有内容。

将所有内容迁移到单个服务和常量后,如果循环问题仍然存在,您应该能够更容易地诊断它,或者完全移至 HTTPClient 并查看是否会停止循环。

如果循环是一个紧迫的问题,请从挑出 http 服务开始,然后处理端点常量

对话框和组件是比较乏味的工作,将另当别论。

我会在下面留下一些资源。

资源: 查看代码工艺,他们有很棒的课程 ng-migration 也是一个很好的统计和跟踪工具

经过大量 的调试,我最终解决了这个问题。如果您发现自己有路由循环或 hashprefix 问题,尽管设置了 $locationProvider.hashPrefix('')检查您是否没有在同一页面上有两个 AngularJS 应用程序.

我认为只有 AngularJS 应用程序和 Angular 应用程序,它们以某种方式争夺创建此递归循环的地址资源。事实证明,Angular (2+) 应用程序与它无关。页面上多了一个 AngularJS 应用程序,两个 AngularJS 应用程序正在争夺地址资源。该代码只有 bootstrap 一个应用程序,但是在某个地方有一个 well-hidden div 带有 ng-app 属性,这会导致 AngularJS 自动 bootstrap它在后台。

第二个 AngularJS 应用程序(通过 ng-app 自动初始化)没有 $locationProvider.hashPrefix(''),而第一个应用程序有。所以他们不同意地址应该是什么,并不断改变它。解决方案只是将 $locationProvider.hashPrefix('') 添加到第二个应用程序,这可以通过控制器完成,如下所示:

模板:

<div ng-app="myApp" ng-controller="myController">
...

控制器:

var app = angular.module('myApp', [])
  .config(['$locationProvider', function($locationProvider) {
    $locationProvider.hashPrefix('');
  }]);
app.controller('myController',function () {...})