AngularJs 缩小过程

AngularJs minification process

我正在完成一个 Web 应用程序,一切都很顺利。

我使用 Grunt 将我所有的 .js 文件合并到一个唯一的文件中,这就是我在 index.html 文件中用来加载代码的文件。

问题出在我使用 .min 时。 grunt 使用 'grunt-contrib-uglify' 任务生成的文件版本。

当我重新加载页面时,出现以下错误:

angular.js:38Uncaught Error: [$injector:modulerr] http://errors.angularjs.org/1.5.5/$injector/modulerr?p0=myapp&p1=Error%…ogleapis.com%2Fajax%2Flibs%2Fangularjs%2F1.5.5%2Fangular.min.js%3A39%3A222)

我一直在 Google 上阅读相关内容,但没有成功。

我该如何解决这个问题?

编辑:

这是一个典型的文件控制器(都具有相同的结构):

(function() {
  var app = angular.module("post", []);
  var controllers = {};
  controllers.postCtrl = ['$scope', '$rootScope', 'myFactory', function($scope, $rootScope, myFactory) {
      $scope.loading  = {state:false};
      $scope.filters  = $scope.filters  || myFactory.authors;
      $scope.init = function() {
          var idx = myFactory.get_author_by_index(Number($('input[name="author"]').val()));
          $scope.filterSelected = $scope.filters[idx];
          angular.element(document).ready(function () {
              angular.forEach($('div.general_page_content').find('a'), function(value, index) {
                  $(value).attr('target', "_new");
              })
              angular.forEach($('div.general_page_content').find('iframe'), function(value, index) {
                  $(value).attr("width", "100%");
              })
              angular.forEach($('div.general_page_content').find('img'), function(value, index) {
                  $(value).attr("width", "100%").css('width', '100%');
              })
              myFactory.containerResize();
          });
      }
      $rootScope.$on('loading', function(evt, value) {
          $scope.loading.state = value;
      });
      $rootScope.$on('autocomplete:focus', function(ev) {
          $scope.search_focus = true;
      });
      $rootScope.$on('autocomplete:blur', function(ev) {
          $scope.search_focus = false;
      });
      $scope.showSocialShare = function(ev) {
          $scope.url  = decodeURIComponent($('input[name="url"]').val());
          $scope.text = decodeURIComponent($('input[name="text"]').val());
          $scope.img  = decodeURIComponent($('input[name="img"]').val());
          myFactory.showSocialShare($scope, ev);
      };
      $scope.favorite_post = function(ev, id, title) {
          myFactory.favorite_post($scope, ev, id, title);
      }

      $scope.fetchPostsChange = function(selected) {
          document.location = '/blog/?author='+selected.id;
      }
      $scope.search = function(text) {
          document.location = '/blog/?search='+encodeURIComponent(text);
      }
      $scope.go_to_favorites_post = function() {
          document.location = '/blog/archive/';
      }
      $scope.init();
  }];
  app.controller(controllers);
})();

更新:

我只取了两个.js 文件并处理它们以缩小它并检查是否出现相同的错误。奇怪的是,只考虑两个文件,出现了同样的错误,所以我粘贴了缩小的文件,以便您能够检测到问题所在。

!function(){angular.module("myapp",["ngMaterial","ngMessages","ngStorage","toaster","ngMdIcons","lvl.services","smart-table","angularGrid","ngFileUpload","angular-timeline","header","dashboard","sidebar","autocomplete","timeline","sidebarCollection","myappFactory","objectCtrl","homeCtrl","Collections","Collection","posts","post","model","postArchive","720kb.socialshare","services","footer"]).config(function(a,b,c){a.theme("default").primaryPalette("lime").accentPalette("grey").warnPalette("red"),a.theme("darkTheme").primaryPalette("lime").accentPalette("grey").warnPalette("red").dark(),b.enabled(!1),c.get("user")})}(),function(){var a=angular.module("autocomplete",[]),b={},c={};b.autocompleteCtrl=["$http","$scope","$mdBottomSheet","$sessionStorage","myappFactory",function(a,b,c,d,e){b.init=function(){b.session=d,angular.isUndefined(b.session.advance_search)&&(b.session.advance_search={select_all:!0,show_cost:!0,show_free:!0,items:[{name:"Thingiverse",selected:!0},{name:"Youmagine",selected:!0},{name:"MyMinifactory",selected:!0},{name:"Cults 3D",selected:!0},{name:"Pinshape",selected:!0},{name:"Turbosquid",selected:!0},{name:"Shapeways",selected:!0},{name:"GrabCAD",selected:!0},{name:"CGTrader",selected:!0},{name:"Threeding",selected:!0}]})},b.querySearch=function(c){var d=c.trim();return d&&d.length>2?(b.isFetching=!0,a.get(e._myapp_link+"/api/index.php/myapp/autocomplete/"+encodeURIComponent(d)).then(function(a){return a.data})):void 0},b.collectionSearch=function(c){var d=c.trim();return d&&d.length>2?(b.isFetching=!0,a.get(e._myapp_link+"/api/index.php/myapp/collection_search/"+encodeURIComponent(d)).then(function(a){return a.data})):void 0},b.search=function(a){var c="";b.session.advance_search.show_cost&&!b.session.advance_search.show_free?c+=" free:0 ":!b.session.advance_search.show_cost&&b.session.advance_search.show_free&&(c+=" free:1 "),angular.forEach(b.session.advance_search.items,function(a,d){(b.session.advance_search.select_all||a.selected)&&(c+=" plataforma:"+a.name)}),window.location="/?search="+encodeURIComponent(a)+"&params="+Base64.encode(c)},b.go_to_collection=function(a){window.location="/collections/"+encodeURIComponent(a)},b.showAdvancedSearch=function(){c.show({templateUrl:"/advanced_search_sheet.html",controller:"ListBottomSheetCtrl"})},b.init()}],b.ListBottomSheetCtrl=["$scope","$mdBottomSheet","$sessionStorage","myappFactory",function(a,b,c,d){a.session=c,a.toggle_all_sites=function(){a.session.advance_search.select_all=!a.session.advance_search.select_all,a.session.advance_search.select_all&&angular.forEach(a.session.advance_search.items,function(a,b){a.selected=!0})},a.toggle_advance_search=function(b){if(a.session.advance_search.select_all)a.session.advance_search.items[b].selected=!0,d.showMessage({msg:"Uncheck 'All repositories' first!"});else if(a.session.advance_search.items[b].selected=!a.session.advance_search.items[b].selected,!a.session.advance_search.items[b].selected){var c=0;angular.forEach(a.session.advance_search.items,function(a,b){a.selected&&++c}),c||(a.session.advance_search.items[b].selected=!0,d.showMessage({msg:"There must be at least 1 respository selected!"}))}},a.free_cost_checked=function(b){var c="cost"==b?!a.session.advance_search.show_cost:!a.session.advance_search.show_free;c?"cost"==b?a.session.advance_search.show_cost=!0:a.session.advance_search.show_free=!0:"cost"==b?a.session.advance_search.show_free?a.session.advance_search.show_cost=!1:d.showMessage({msg:"Free and Price cannot be unchecked!"}):"free"==b&&(a.session.advance_search.show_cost?a.session.advance_search.show_free=!1:d.showMessage({msg:"Free and Price cannot be unchecked!"}))}}],c.myEnter=function(){return function(a,b,c){b.bind("keydown keypress",function(b){13===b.which&&(a.$apply(function(){a.search(a.searchText)}),b.preventDefault())})}},c.onBlur=["$rootScope","$mdUtil","$timeout",function(a,b,c){return{require:"^mdAutocomplete",link:function(d,e,f,g){c(function(){var c=(e.find("input"),e[0],g.blur),h=g.focus;g.blur=function(){c.call(g),b.nextTick(function(){a.$broadcast("autocomplete:blur"),d.$eval(f.mdBlur,{$mdAutocomplete:g})})},g.focus=function(){h.call(g),b.nextTick(function(){a.$broadcast("autocomplete:focus"),d.$eval(f.mdFocus,{$mdAutocomplete:g})})}})}}}],a.controller(b).directive(c)}();

如果缩小,每个模块的注入都必须命名。所以之前你可能有过:

app.factory('myFactory', function($route) {
});

现在你也必须 name/declare 它们,否则 angular 不知道要注入什么:

app.factory('myFactory', ['$route', function($route) {
}]);

为什么?

Minification 将所有内容都变成小变量。所以我们的工厂变成:

app.factory('myFactory', function(a) {
      a.<functioncall>
});

Angular 不知道 "a" 是什么,因此必须告知:

app.factory('myFactory', ['$route', function(a) {
      a.<functioncall>
}]);

所以它现在知道 and a= $route 等等。

正如您在 Google 上肯定发现的那样,该错误取决于 Variable Mangling https://github.com/gruntjs/grunt-contrib-uglify#mangle

所以,当你 mangle $rootScope 变成 a 并且,当然,angular 依赖注入 无法解决它:

angular
  .module('test', [])
  .run(function($injector) {
    console.log(
      "$rootScope exists?", 
      $injector.has('$rootScope')
    );
    
    try {
      // mangle $rootscope => a
      $injector.get('a');
    } catch(e) { 
      console.log('a exists?', e.message); 
    }
    
  })
;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.js"></script>
<section ng-app="test"></section>

有很多方法可以解决这个问题:

  1. 禁用变量重整(不是一个好方法,但却是一个解决方案)。
  2. 始终使用Angular DI Annotation

    一个。数组表示法:someModule.run(["$rootScope", function(a) {}]);

    b。 $注入 属性: runFn.$inject = ['$rootScope']; function runFn(a) {}; someModule.run(runFn);

  3. 使用 ngAnnotate 在构建时进行注释。我建议你这个选项,因为你不需要关心注释...

Important thing: Always run your code in angular strict di mode, that gives you the opportunity of control each annotation issue.