0

=== TL;DR ===

简短版(相信我,我整天都在研究这个):

我有一大堆脚本,包括有角和无角的东西(1.2.x)。当我尝试使用 LAB.js 加载所有内容时,包括 AlwaysPreserveOrder 标志,Angular 过早触发。深入研究,Angular 似乎正在使用 $(document).ready(/* start angular */)。可以理解,脚本加载器会破坏正常的 document.ready 事件。

所以,我尝试使用 $.holdReady(true) 和 $.holdReady(false),但是,我得到了非常奇怪的行为:虽然 $.holdReady(true) 确实按预期增加了 $.readyWait,但它不会阻止 angular从初始化......最重要的是,如果我尝试为 $(document).ready() 设置我自己的侦听器,它永远不会触发,即使 angular 似乎使用相同的事件来触发......

...为了增加谜团,每一次在一个蓝月亮,如果我将它设置为在失败时继续执行 window.location.reload() 直到一切正常,它实际上会正常工作 - 很少。这使它看起来像是一种比赛条件,但鉴于我正在传递订单标志,我无法想象会发生什么比赛,据我所知,这条线索似乎也不适合其他一切...... .

请注意,如果我切换到文件的非 lab.js 版本,一切似乎都工作得很好,包括 $.readyWait 计数,所有(包括角度)等待 $.holdReady(false) 被调用,$(document)我自己开火的 .ready() 标志等。

======长版======

我正在尝试制作一个最小的可行示例,但这里提供了更复杂的现实世界工作代码,这里提供了一个损坏的版本——它们仍在清理以尝试使问题尽可能明显,而不消除任何潜在的混杂因素。

这有效:

<body ng-app="mapmycustomersApp" ng-strict-di>
      <script src="build/libraries/LAB.min.js"></script>
      <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.32/angular.min.js"></script>
      <script src="https://code.angularjs.org/1.2.32/angular-resource.min.js"></script>
      <script src="https://code.angularjs.org/1.2.32/angular-route.min.js"></script>

      <script>
         document.write('<script src="build/app.js"></script>')
         document.write('<script src="build/controllers/main.js"></script>')
      </script>
</body>

这不会:

<body ng-app="mapmycustomersApp" ng-strict-di>
      <script src="build/libraries/LAB.min.js"></script>
      <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.32/angular.min.js"></script>
      <script src="https://code.angularjs.org/1.2.32/angular-resource.min.js"></script>
      <script src="https://code.angularjs.org/1.2.32/angular-route.min.js"></script>

      <script>
         $LAB.setGlobalDefaults({ AlwaysPreserveOrder: true });
         $LAB
         .script("build/app.js")
         .script("build/controllers/main.js")
      </script>
</body>

这也不是:

<body ng-app="mapmycustomersApp" ng-strict-di>
      <script src="build/libraries/LAB.min.js"></script>

      <script>
         $LAB.setGlobalDefaults({ AlwaysPreserveOrder: true });
         $LAB
         .script("https://ajax.googleapis.com/ajax/libs/angularjs/1.2.32/angular.min.js")
         .script("https://code.angularjs.org/1.2.32/angular-resource.min.js")
         .script("https://code.angularjs.org/1.2.32/angular-route.min.js")
         .script("build/app.js")
         .script("build/controllers/main.js")
      </script>
</body>

第三个(上面的最后一个)是我真正想要的。

他们产生这个错误

模块“mapmycustomersApp”不可用!您要么拼错了模块名称,要么忘记加载它。如果注册模块,请确保将依赖项指定为第二个参数。

在写这个答案时,我取得了一些进展,但遇到了一个新的死胡同,我发誓感觉就像 jquery 或 angular 中的错误。

为什么?我可以追溯到最远的地方告诉我 angularInit() 被调用得太早了,早在应用程序注册之前......这在 angular 文件的底部被调用,在第 2232 行:

  jqLite(document).ready(function() {
    angularInit(document, bootstrap);
  });

jqLit​​e 设置为 jQuery(我通过检查 angular.element === $ 确认了这一点,这是真的,这在 Angular 代码的其他地方得到了证明)。所以这应该只是在 $(document).ready() 上触发。装载机的已知问题。这就是他们创建 $.holdReady() 的原因。

但由于某种原因,这对我不起作用。 我得到非常奇怪的输出。

这就是我的意思:如果我将一个 jquery 脚本标签 (2.2.4) 移动到头顶,然后$.holdReady(true)在它的正下方调用,我可以看到它$.readyWait是递增的。(我什至可以多次调用它,然后看着它上升到四次。)

然后我.wait(() => $.readyWait(false))在我的通话结束时加上a .script()...但是如果我在那之前 console.log ,我可以看到它已经从,比如说,已经下降了40这意味着 ready 已经被解雇了。$.readyWait()什么都没做。

有价值的线索:

很少,非工作代码工作......然后在刷新时再次失败,没有任何变化。这似乎表明了一种竞争条件,但我不明白那会在哪里,而且这与我见过的其他情况不符……然而,它发生了。在那些时候, $.readyWait 返回 1 而不是 0

我知道这很多,但我在这里感到完全困惑。世界上到底发生了什么?

哦,还有一个令我困惑的非常重要的线索:$(document).ready(_ => console.log("I loaded"))永远不会跑。$(window).load,否则相同,运行良好。不管我在哪里声明那个监听器,它永远不会为我触发,即使我将监听器设置在页面的顶部......但它显然会以某种方式触发,因为它是触发的angularInit!我的理解是,如果我调用它,它应该会触发,即使它已经触发了!

...只是为了表明我没有犯一些非常明显的错误,只要我切换到所有脚本标签,所有相同的代码都会按预期触发(document ready事件和holdReady()两者都完全按照您的预期进行)。

在代码中:

<!doctype html>
<html>
  <head>
    <script src="https://code.jquery.com/jquery-2.2.4.js"></script>
    <script>
      $(window).load(function() {
         console.log("this fires");
      });
      $(document).ready(function() {
        console.log("this doesn't");
        debugger;
      });
      console.log("holding ready", $.readyWait);
      $.holdReady(true);$.holdReady(true);$.holdReady(true); // needed because we use LAB.js, which messes with Document.ready, which Angular depends on
      console.log('rw, ', $.readyWait)
    </script>

(详情: app.js包括

var app = angular.module('mapmycustomersApp' ['ngRoute','ui.bootstrap','ngStorage','ngFileUpload'])
.config(['$routeProvider', '$locationProvider', '$compileProvider', function($routeProvider, $locationProvider, $compileProvider) { // ...

main.js包括

app.controller('MainCtrl', ['$scope', '$http', // ...

所有代码都可以正常工作,我只是想把所有东西都移到 LAB.js 上,却发现自己做不到。其他评论:仅仅为了好玩而切换到 Angular 1.6.4 没有效果(除了因为 $http().success 不是一个函数,所以他们必须在以后的版本中更改了那个 API) ,并且我们在版本 2 中使用最新版本的 jquery,切换到 3+ 会引发其他不兼容错误。

4

1 回答 1

0

我在确定问题时是正确的。在与 Kyle Simpson 的长时间讨论中,他指出$.ready在 Angular 1.2x 源代码中使用 是一个常见错误,基于不正确的假设,这使得异步加载 javascript 源代码不可行。

由于某种原因,使用$.holdReady()最终确实奏效了。我不知道为什么它没有,但我怀疑我不小心包含了 jquery 依赖项两次,覆盖了以前的 readyCount。

于 2017-04-16T22:46:42.000 回答