Angular.module Minification Bug

82

Die verdammteste Zeit haben, um herauszufinden, warum die Minifizierung nicht funktioniert.

Ich habe über ein Array-Objekt meine Provider vor der Funktion nach zahlreichen Vorschlägen im Web injiziert und trotzdem "Unbekannter Provider: aProvider <- a".

Regulär:

var app = angular.module('bpwApp', ['ui.bootstrap', 'ui', 'myTabs'])
    .config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider){
    $routeProvider.
        when('/', {templateUrl: 'partials/home.jade', controller: HomeCtrl});

    $locationProvider.html5Mode(true);
    }])

Minimiert:

var app = angular.module('bpwApp', ['ui.bootstrap', 'ui', 'myTabs'])
    .config(['$routeProvider', '$locationProvider', function(a, b){
    a.
        when('/', {templateUrl: 'partials/home.jade', controller: HomeCtrl});

    b.html5Mode(true);
    }])

Jeder Vorschlag wäre sehr dankbar!

BPWDevelopment
quelle
1
Womit minimieren Sie Ihren Code? uglifyJS? Überprüfen Sie auch: github.com/btford/ngmin ;)
AndreM96
Ich habe ngmin verwendet. Der Code wurde lediglich in einem anderen Leerzeichenformat ausgerichtet. Ich habe versucht, Express-Uglify als Middleware zu verwenden, aber es hat nicht funktioniert, also habe ich versucht, manuell einen Online-Uglifier zu verwenden. In beiden Fällen war der Code derselbe.
BPWDevelopment
Fehlt auch kein ]? (vor dem Schließen ))
AndreM96
Ich habe sie in diesem speziellen Ausschnitt vergessen. Es ändert nichts an der Tatsache, dass "unbekannter Anbieter a" immer noch vorkommt :(
BPWDevelopment
2
Ok, welchen Online-Minifier haben Sie verwendet? Dies funktioniert gut mit Ihrem Code: Marihnhaverbeke.nl/uglifyjs
AndreM96

Antworten:

139

Ich bin zuvor mit dem Uglify- Plugin von Grunt.j auf dieses Problem gestoßen .

Eine der Optionen ist Mangle

uglify: {
  options: {
    mangle: false
  },

Was meiner Meinung nach Regex-Funktionen auf "Like-Strings" ausführt und sie minimiert.

Beispielsweise:

angular.module("imgur", ["imgur.global","imgur.album"]);

Würde werden:

angular.module("a", ["a.global","a.album"]);

Deaktivieren Sie es --- diese Funktion spielt mit Angular nicht gut.

Bearbeiten:

Genauer gesagt erklärt @JoshDavidMiller:

Verunstalten mangle nur wie Variablen, was tatsächlich das AngularJS-Problem verursacht. Das heißt, das Problem liegt in der Injektion und nicht in der Definition.

function MyCtrl($scope, myService)würde entstellt werden function MyCtrl(a, b), aber die Service-Definition innerhalb einer Zeichenfolge sollte niemals geändert werden.

  • Laufen ng-minvor dem Laufen uglifylöst dieses Problem.
Dan Kanze
quelle
3
Er hat seinen Code aktualisiert. Sein Problem war nicht, dass "$ locationProvider" zu "b" wurde oder so etwas. Es hat einfach nicht funktioniert. Allerdings +1 für diese Antwort :)
AndreM96
1
Dank suchte schwer, diese Option zu finden.
Reen
Ich bin genau auf dasselbe gestoßen, als ich eckigen Bootstrap + Yeoman verwendet habe. Unter Verwendung des Yeoman-Winkelgenerators wurde ein distBuild erstellt, der den erwähnten Abhängigkeitsfehler "Unbekannter Anbieter" aufweisen würde. Die Einstellung mangle: falselöste das Problem. (Hinweis: Das Problem war nur ein Problem in der erstellten grunt, distnicht der entwicklerfreundlichen appBuild)
Craigb
6
Mangle mangle: true wirklich "wie Saiten"? Ich bin mir ziemlich sicher, dass es nur wie Variablen verstümmelt , was tatsächlich das AngularJS-Problem verursacht. Das heißt, das Problem liegt in der Injektion und nicht in der Definition. function MyCtrl($scope, myService)würde entstellt werden function MyCtrl(a, b), aber die Service-Definition innerhalb einer Zeichenfolge sollte niemals geändert werden. Laufen ng-minvor dem Laufen uglifylöst dieses Problem, nein?
Josh David Miller
1
ng-minist jetzt zugunsten vonng-annotate
Atav32
51

Problem

Von AngularJS: Die schlechten Teile :

Angular verfügt über einen integrierten Abhängigkeitsinjektor, der anhand der Namen seiner Parameter geeignete Objekte an Ihre Funktion übergibt:

function MyController($scope, $window) {
    // ...
}

Hier werden die Namen der Parameter $scopeund $windowmit einer Liste bekannter Namen abgeglichen, und entsprechende Objekte werden instanziiert und an die Funktion übergeben. Angular ruft die Parameternamen ab, indem es toString()die Funktion aufruft und dann die Funktionsdefinition analysiert.

Das Problem dabei ist natürlich, dass es nicht mehr funktioniert, sobald Sie Ihren Code minimieren . Da Sie sich für die Benutzererfahrung interessieren, werden Sie Ihren Code minimieren. Wenn Sie diesen DI-Mechanismus verwenden, wird Ihre App beschädigt. Tatsächlich besteht eine übliche Entwicklungsmethode darin, nicht minimierten Code in der Entwicklung zu verwenden, um das Debuggen zu vereinfachen, und dann den Code zu minimieren, wenn auf Produktion oder Staging umgestellt wird. In diesem Fall wird dieses Problem seinen hässlichen Kopf erst wieder aufrichten, wenn Sie an dem Punkt angelangt sind, an dem es am meisten weh tut.

(...)

Da dieser Abhängigkeitsinjektionsmechanismus im allgemeinen Fall nicht funktioniert, bietet Angular auch einen Mechanismus, der dies tut. Um sicher zu sein, bietet es zwei. Sie können entweder ein Array wie folgt weitergeben:

module.controller('MyController', ['$scope', '$window', MyController]);

Oder Sie können die $injectEigenschaft für Ihren Konstruktor festlegen :

MyController.$inject = ['$scope', '$window'];

Lösung

Sie können ng-annotatezum automatischen Hinzufügen von Anmerkungen, die zum Minimieren erforderlich sind, Folgendes verwenden:

ng-annotateFügt AngularJS-Abhängigkeitsinjektionsanmerkungen hinzu und entfernt sie. Es ist nicht aufdringlich, so dass Ihr Quellcode ansonsten genau gleich bleibt. Keine verlorenen Kommentare oder verschobenen Zeilen.

ng-annotateist schneller und stabiler als ngmin(was jetzt veraltet ist) und es hat Plugins für viele Tools:


Ab AngularJS 1.3 gibt es auch einen neuen Parameter ngAppnamens ngStrictDi:

Wenn dieses Attribut im App-Element vorhanden ist, wird der Injektor im "strict-di" -Modus erstellt. Dies bedeutet, dass die Anwendung keine Funktionen aufruft, die keine explizite Funktionsanmerkung verwenden (und daher für die Minimierung ungeeignet sind), wie im Handbuch zur Abhängigkeitsinjektion beschrieben. Nützliche Debugging-Informationen helfen dabei, die Wurzel dieser Fehler aufzuspüren.

Paolo Moretti
quelle
1
+1 Durch einfaches Wechseln zu grunt-ng-annotate wurde dieses Problem behoben, und ngmin ist jetzt ohnehin veraltet. Dies ist ein weiterer Grund für einen Wechsel.
Pier-Luc Gendreau
Das war die Lösung, nach der ich tagelang gesucht habe!
jack.the.ripper
Ich habe das gleiche Problem beim Erstellen eines minimierten Codes mit browserify, angle und gulp-minify. Ich habe gulp minify entfernt und durch gulp-ng-annotate ersetzt. Der Code ist immer noch minimiert, funktioniert aber immer noch nicht.
Dimitri Kopriwa
@BigDong Wenn Sie browserify verwenden, ist es wahrscheinlich am besten, ngStrictDi(so etwas wie <div ng-app="myApp" ng-strict-di />) zu aktivieren und gulp-ng-annotatesogar in Ihrer Entwicklungsumgebung zu verwenden, damit Sie diese Minifizierungsfehler leicht aufspüren können.
Paolo Moretti
@PaoloMoretti Ich habe es mit ngStrictDi und gulp-ng-annotate versucht. Browserify kann gebündelt werden, aber der Code wird nicht minimiert. Soll es nicht ein ng-annotate-Job sein?
Dimitri Kopriwa
22

Ich habe den gleichen Fehler bekommen. Für mich ist das Problem jedoch die Controller-Deklaration der Direktiven. Sie sollten dies stattdessen tun.

myModule.directive('directiveName', function factory(injectables) {
    var directiveDefinitionObject = {
      templateUrl: 'directive.html',
      replace: false,
      restrict: 'A',
      controller: ["$scope", "$element", "$attrs", "$transclude", "otherInjectables",
        function($scope, $element, $attrs, $transclude, otherInjectables) { ... }]
    };
    return directiveDefinitionObject;
  });

https://github.com/angular/angular.js/pull/3125

Angelokh
quelle
1
Vielen Dank, dass Sie @angelokh! Ich hatte genau dieses Problem. Ich habe die controller: function ($scope) {}Notation verwendet.
Jbasko
2
Dies ist eher die Lösung des eigentlichen Problems als mangle: falsein anderen Antworten vorgeschlagen, da wir weiterhin in der Lage sein möchten, Namen zu entstellen.
Jbasko
9

Ich hatte ein ähnliches Problem mit grunt, ngmin und uglify.

Ich habe den Prozess in dieser Reihenfolge ausgeführt: concat, ngmin, uglify

Ich habe weiterhin den $ injoror-Fehler von Angular erhalten, bis ich in der uglify options mangle hinzugefügt habe: false - dann wurde alles behoben.

Ich habe auch versucht, die Ausnahmen hinzuzufügen, um so zu uglifizieren:

 options: {
  mangle: {
     except: ['jQuery', 'angular']
  }
}

Aber ohne Erfolg ...

Hier ist meine gruntFile.js zur weiteren Verdeutlichung:

module.exports = function(grunt) {
'use strict';
// Configuration goes here
grunt.initConfig({
    pkg: require('./package.json'),

    watch: {
        files: ['scripts/**/*.js', 'test/**/*spec.js', 'GruntFile.js'],
        tasks: ['test', 'ngmin']
    },

    jasmine : {
        // Your project's source files
        src : ['bower_components/angular/angular.js', 'bower_components/angular-mocks/angular-mocks.js', 'scripts/app.js', 'scripts/**/*.js' ],
        // Your Jasmine spec files

        options : {
            specs : 'test/**/*spec.js',
            helpers: 'test/lib/*.js'
        }
    },

    concat: {
      dist : {
          src: ['scripts/app.js', 'scripts/**/*.js'],
          dest: 'production/js/concat.js'
      }
    },

    ngmin: {
        angular: {
            src : ['production/js/concat.js'],
            dest : 'production/js/ngmin.js'
        }

    },

    uglify : {
        options: {
            report: 'min',
            mangle: false
        },
        my_target : {
            files : {
                'production/app/app.min.js' : ['production/js/ngmin.js']
            }
        }
    },

  docular : {
      groups: [],
      showDocularDocs: false,
      showAngularDocs: false
  }

});

// Load plugins here
grunt.loadNpmTasks('grunt-ngmin');
grunt.loadNpmTasks('grunt-docular');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jasmine');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-connect');

// Define your tasks here
grunt.registerTask('test', ['jasmine']);
grunt.registerTask('build', ['concat', 'ngmin', 'uglify']);
grunt.registerTask('default', ['test', 'build', 'watch']);

};

Sten Muchow
quelle
AH DANKE DANKE! das hat mir so viel zeit gespart.
Mylescc
5

AndrewM96 Vorschlag von ng-minist richtig.

Die Ausrichtung und der Leerraum sind sowohl für Uglify als auch für Angular von Bedeutung.

BPWDevelopment
quelle
10
ng-min scheint nur die eckigen Dateien so zu verarbeiten, dass sie für sie geeignet sind uglify. In unserem Build-Prozess verwenden wir beide ( ng-minvorher uglify) und hatten immer noch ein Problem mit dem hässlichen js.
Craigb
4
Warum ist dies als Antwort markiert? (Auch AndrewM96 sollte AndreM96 sein)
Jay
obwohl in docs ng-min vielversprechend klingt, löst es das Problem nicht
special0ne
@craigb mit dem gleichen Problem. Vielleicht ist es eine Kombination von Dingen. Ich benutze auch RequireJS. Ich mache im Grunde alle funktionsverändernden Dinge, die ng-min selbst tun sollte, und führe immer noch ng-min aus, als run erfordert build und dann führe uglify mit mangle true aus. Dieser Prozess scheint meistens zu funktionieren.
entkommen Katze
3

Ich hatte ein ähnliches Problem. Und löste es folgendermaßen. Wir müssen ein Gulp-Modul namens gulp-ng-annotate ausführen, bevor wir uglify ausführen. Also installieren wir dieses Modul

npm install gulp-ng-annotate --save-dev

Führen Sie dann die Anforderungen in Gulpfile.js aus

ngannotate = require(‘gulp-ng-annotate’)

Und in Ihrer Usemin-Aufgabe machen Sie so etwas

js: [ngannotate(), uglify(),rev()] 

Das hat es für mich gelöst.

[EDIT: Tippfehler behoben]

Paulo Borralho Martins
quelle
gulp-MG-annotate sollte gulp-NG-annotate sein?
Hally9k
Ja, entschuldige den Fehler. Wo liest mg-annotateist immerng-annotate
Paulo Borralho Martins
2

Dies ist sehr schwierig zu debuggen, da viele Dienste den gleichen Namen haben (meistens e oder a). Dadurch wird der Fehler nicht behoben, sondern Sie erhalten den Namen des nicht aufgelösten Dienstes , mit dem Sie in der hässlichen Ausgabe den Speicherort im Code ermitteln und schließlich das Problem beheben können:

Gehen Sie in lib/scope.jsUglify2 ( node_modules/grunt-contrib-uglify/node_modules/uglify-js/lib/scope.js) und ersetzen Sie die Zeile

this.mangled_name = this.scope.next_mangled(options);

mit

this.mangled_name = this.name + "__debugging_" + counter++
Bas
quelle