【问题标题】:Angular "state already defined" error after running concat/uglify运行 concat/uglify 后出现角​​度“已定义状态”错误
【发布时间】:2015-06-17 10:08:47
【问题描述】:

我正在开发一个 AngularJS 应用程序。为了在生产中发布代码,我使用了这个 Grunt 配置/任务:

  grunt.registerTask( 'compile', [
    'sass:compile', 'copy:compile_assets', 'ngAnnotate', 'concat:compile_js', 'uglify', 'index:compile'
  ]);

调试真的很困难,对于已经遇到此类问题的人来说,这是一个问题,可以指出一些方向。

我的主模块包括这些子模块:

angular
  .module('controlcenter', [
    'ui.router',
    'ui.bootstrap',
    'templates-app',
    'templates-common',
    'authentication',
    'api',
    'reports',
    'interceptors',
    'controlcenter.websites',
    'controlcenter.users',
    'controlcenter.campaigns',
    'controlcenter.reports',
    'controlcenter.login'
  ])
  .run(run);

我得到的错误如下:

Uncaught Error: [$injector:modulerr] Failed to instantiate module controlcenter due to:
Error: [$injector:modulerr] Failed to instantiate module controlcenter.websites due to:
Error: State 'websites'' is already defined

如果我删除网站模块,我会收到相同的错误 controlcenter.users。

我正在使用ui-router 处理应用内的路由。

在我的构建过程(用于集成测试)之后,一切正常:

  grunt.registerTask( 'build', [
    'clean', 'html2js', 'jshint', 'sass:build',
    'concat:build_css', 'copy:build_app_assets', 'copy:build_vendor_assets',
    'copy:build_appjs', 'copy:build_vendorjs', 'copy:build_vendorcss', 'index:build', 'karmaconfig',
    'karma:continuous'
  ]);

所以也许ngAnnotateconcat/uglify 在这里做奇怪的事情?


更新 1: 它与我的模块配置有关。代码如下:

angular
  .module('controlcenter.websites',
    [
      'ui.router'
    ]
  )
  .config(config);

config.$inject = ['$stateProvider'];

function config($stateProvider) {
  $stateProvider.state( 'websites', {
    url: '/websites',
    views: {
      "main": {
        controller: 'WebsitesController',
        templateUrl: 'websites/websites.tpl.html'
      }
    }
  });
}
  1. 当我将状态名称更改为 website_2 时,出现错误 'websites_2 已定义'。
  2. 当我完全删除模块时,下一个模块在配置文件中有同样的问题。那么结构错了吗?

更新 2

这个问题似乎与 concat 相关。

它将每个 JS 文件一个接一个地添加到一个更大的文件中。我所有的模块都在最后。最后一个模块总是存在“已定义状态”的问题。所以这不仅仅是模块相互附加的顺序,还有别的东西......


更新 3: 我把我的代码(我已经排除了每个控制器代码和函数,只是脚手架)放在gist 中。这是我编译后的结果,没有丑化。

【问题讨论】:

  • 所有这些模块( controlcenter.websites , controlcenter.users ,...)是否具有相同名称的状态或每个模块中的状态名称不同?
  • 不同的名字。状态始终是网站、用户等。奇怪的是,即使我将网站重命名为 website_2,我也会收到一个错误,“websites_2”已经定义。
  • 如果你使用whatever.$inject = ...,那么你不需要使用ngAnnotate。注释会将.config(config) 转换为.config(['$stateProvider', config])

标签: angularjs


【解决方案1】:

问题:

您有多个包含 config 函数的文件来配置您的模块,如下所示:

angular
    .module('controlcenter.websites', [])
    .config(config);

function config() {
    // ...
}

问题在于,在您连接所有文件后,您最终会得到一个包含多个 config 声明的大文件。由于 JavaScript 的变量提升,所有声明都被移到顶部,并且只有最后一个被评估,这个是:

function config($stateProvider) {
  $stateProvider.state( 'websites', {
    url: '/websites',
    views: {
      "main": {
        controller: 'WebsitesController',
        templateUrl: 'websites/overview/websites.tpl.html'
      }
    },
    data : {requiresLogin : true }
  });
}

因此,每次您.config(config) 一个模块时,您都在告诉 Angular 使用该特定配置函数配置您的模块,这意味着它会执行多次并尝试多次定义状态 websites

解决方案:

用闭包包装每个 JavaScript 文件代码。这样您就可以避免多次声明一个变量/函数:

(function (angular) {

    'use strict';

    angular
      .module('controlcenter.website.details', ['ui.router'])
      .config(config);

    config.$inject = ['$stateProvider'];

    function config($stateProvider) {
      $stateProvider
        .state( 'websiteDetails', {
          url: '/websiteDetails/:id',
          views: {
            "main": {
              controller: 'WebsiteDetailsController',
              templateUrl: 'websites/details/website.details.tpl.html'
            }
          },
          data : {requiresLogin : true }
        })

        .state( 'websiteDetails.categories', {
          url: '/categories',
          views: {
            "detailsContent": {
              templateUrl: 'websites/details/website.details.categories.tpl.html'
            }
          },
          data : {requiresLogin : true }
        })
        ;
    }
})(window.angular);

编辑

  • 我强烈建议您将文件包装到闭包中。但是,如果您仍然不想这样做,您可以根据各自的模块命名您的函数。这样controlcenter.website.details 的配置函数将变为controlcenterWebsiteDetailsConfig。另一种选择是在构建阶段使用grunt-wrap 包装您的代码。

  • window.angular 和闭包: 这是我在丑化代码时喜欢使用的一种技术。通过将您的代码包装到一个闭包中并给它一个名为angular 的参数,其实际值为window.angular,您实际上是在创建一个可以被丑化的变量。例如这段代码:

    (function (angular) {
        // You could also declare a variable, instead of a closure parameter:
        // var angular = window.angular;
    
        angular.module('app', ['controllers']);
        angular.module('controllers', []);
        // ...
    })(window.angular);
    

    这很容易被丑化(注意每个对angular 的引用都被a 替换):

    !function(a){a.module("app",["controllers"]),a.module("controllers",[])}(window.angular);
    

    另一方面,解包后的代码 sn-p 如下:

    angular.module('app', ['controllers']);
    angular.module('controllers', []);
    

    会变成:

    angular.module("app",["controllers"]),angular.module("controllers",[]);
    

    有关闭包的更多信息,请查看this postthis post

【讨论】:

  • 它并不是不能检测它们,它的全部目的是消除写.$inject['$providerName', function($providerName) {...}]的需要。 OP 不应将 ngAnnotate.$inject 混合
  • @Phil 我知道,但是当涉及到使用数组表示法(['dep', function (dep) { }]) 的声明时,ngAnnotate 足够聪明,可以理解已经声明了依赖项,所以没有什么可做的。
  • 感谢您的帮助!但即使我从编译任务中删除 ngAnnotate,我也会遇到同样的错误。
  • @gruberb 你能发布你编译的(ngAnnotated & uglified)脚本吗?
  • @gruberb 谢谢。你也有编译版吗?否则,您也可以发布您的 gulpfile 吗?
【解决方案2】:

如果您在连接文件中检查它,您是否将状态定义了两次?可能是您要复制文件两次吗?检查您从中获取文件的临时文件夹(也在 grunt 配置中,您正在复制的内容以及正在删除的内容......)。

【讨论】:

    【解决方案3】:

    所以我遇到了同样的问题,但设置如下:

    • yeoman angular-fullstack(使用打字稿)
    • 网络风暴

    使用 angular-fullstack 配置,闭包已经实现(正如 Danilo Valente 建议的那样),所以我费了很大劲才发现在 Webstorm 中,我启用了 typescript 编译器,它编译了我所有的 *.ts 文件到 *.js。但由于 Webstorm 非常“聪明”,它不会在工作树中显示这些编译文件。 Grunt 当然会连接所有文件,无论它是否是 JS 的打字稿。这就是为什么 - 最后 - 我的所有状态都被定义了两次。

    所以明显的修复:禁用 webstorm 的 typescript 编译器并删除所有生成的 *.js 文件并且它可以工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-17
      • 1970-01-01
      • 2020-06-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多