Błąd minifikacji Angular.module

82

Najgorszy czas na ustalenie, dlaczego minifikacja nie działa.

Wprowadziłem przez obiekt tablicy moich dostawców przed funkcją zgodnie z licznymi sugestiami w Internecie, a mimo to „Nieznany dostawca: aProvider <- a”

Regularny:

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);
    }])

Zminimalizowane:

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);
    }])

Każda sugestia byłaby bardzo zobowiązana!

BPWDevelopment
źródło
1
Czego używasz do zmniejszania kodu? uglifyJS? Zobacz także: github.com/btford/ngmin ;)
AndreM96
Użyłem ngmin, wszystko, co zrobiłem, to wyrównać kod w innym formacie białych znaków. Próbowałem użyć express-uglify jako oprogramowania pośredniego, ale nie działało, więc spróbowałem ręcznie użyć uglifera online. Tak czy inaczej, kod był taki sam.
BPWDevelopment
Czy też nie brakuje ]? (przed zamknięciem ))
AndreM96
Było, zapomniałem o nich w tym konkretnym fragmencie. Nie zmienia to faktu, że „nieznany dostawca” nadal się zdarza :(
BPWDevelopment,
2
Ok, cóż, jakiego minifier online użyłeś? Działa to dobrze z twoim kodem: marijnhaverbeke.nl/uglifyjs
AndreM96

Odpowiedzi:

139

Napotkałem ten problem już wcześniej z wtyczką Grunt.js Uglify .

Jedną z opcji jest maglowanie

uglify: {
  options: {
    mangle: false
  },

Uważam, że uruchamia funkcje regex na „podobnych ciągach znaków” i je minimalizuje.

Na przykład:

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

Stanie się:

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

Wyłącz to --- ta funkcja nie działa dobrze w Angular.

Edytować:

Mówiąc dokładniej, jak wyjaśnia @JoshDavidMiller:

Uglifikuj mangletylko zmienne podobne do zmiennych, co w rzeczywistości powoduje problem AngularJS. Czyli problem tkwi we wtrysku a nie definicji.

function MyCtrl($scope, myService)zostałby zniekształcony function MyCtrl(a, b), ale definicja usługi wewnątrz łańcucha nigdy nie powinna zostać zmieniona.

  • Uruchomienie ng-minprzed uruchomieniem uglifyrozwiązuje ten problem.
Dan Kanze
źródło
3
Zaktualizował swój kod. Jego problemem nie było to, że „$ locationProvider” zmieniło się w „b” czy coś w tym rodzaju. Po prostu nie zadziałało. Jednak +1 za tę odpowiedź :)
AndreM96
1
Dzięki trudno było znaleźć tę opcję.
powtórz
Natknąłem się dokładnie na to samo, używając kątowego bootstrap + yeoman. Używając generatora kątowego Yeoman , stworzył distkompilację, która miałaby wspomniany błąd zależności „Nieznany dostawca”. Ustawienie mangle: falserozwiązało problem. (uwaga: problem był tylko problem w gruntwybudowanym dista nie dewelopera przyjazne appkompilacji)
craigb
6
Czy mangle: true naprawdę manipuluje „jak struny”? Jestem prawie pewien, że zmienia się tylko jak zmienne , co w rzeczywistości powoduje problem z AngularJS. Czyli problem tkwi we wtrysku a nie definicji. function MyCtrl($scope, myService)zostałby zniekształcony function MyCtrl(a, b), ale definicja usługi wewnątrz łańcucha nigdy nie powinna zostać zmieniona. Bieganie ng-minprzed uruchomieniem uglifyrozwiązuje ten problem, prawda?
Josh David Miller
1
ng-minjest teraz przestarzały i ng-annotate
zastępuje
51

Problem

Od AngularJS: Złe części :

Angular ma wbudowany iniektor zależności, który przekaże odpowiednie obiekty do Twojej funkcji na podstawie nazw jej parametrów:

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

Tutaj nazwy parametrów $scopei $windowzostaną dopasowane do listy znanych nazw, a odpowiednie obiekty zostaną utworzone i przekazane do funkcji. Angular pobiera nazwy parametrów, wywołując toString()funkcję, a następnie analizując definicję funkcji.

Problem z tym polega oczywiście na tym, że przestaje działać w momencie zminimalizowania kodu . Ponieważ zależy Ci na wrażeniach użytkownika, zminimalizujesz swój kod, więc użycie tego mechanizmu DI zepsuje Twoją aplikację. W rzeczywistości typową metodologią programistyczną jest użycie niezminimalizowanego kodu w celu ułatwienia debugowania, a następnie zminimalizowanie kodu podczas wypychania do produkcji lub przemieszczania. W takim przypadku ten problem nie podniesie brzydkiej głowy, dopóki nie znajdziesz się w punkcie, w którym najbardziej boli.

(...)

Ponieważ ten mechanizm wstrzykiwania zależności w rzeczywistości nie działa w ogólnym przypadku, Angular zapewnia również mechanizm, który działa. Oczywiście zapewnia dwa. Możesz przekazać tablicę w następujący sposób:

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

Lub możesz ustawić $injectwłaściwość w swoim konstruktorze:

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

Rozwiązanie

Możesz użyć ng-annotatedo automatycznego dodawania adnotacji wymaganych do minifikacji:

ng-annotatedodaje i usuwa adnotacje iniekcji zależności AngularJS. Jest nieinwazyjny, więc w przeciwnym razie kod źródłowy pozostaje dokładnie taki sam. Żadnych utraconych komentarzy ani przesuniętych linii.

ng-annotatejest szybszy i stabilniejszy niż ngmin(który jest obecnie przestarzały) i ma wtyczki dla wielu narzędzi:


Począwszy od AngularJS 1.3 jest również nowy parametr o ngAppnazwie ngStrictDi:

jeśli ten atrybut jest obecny w elemencie aplikacji, wtryskiwacz zostanie utworzony w trybie „strict-di”. Oznacza to, że aplikacja nie będzie w stanie wywołać funkcji, które nie używają jawnych adnotacji funkcji (i dlatego nie nadają się do minifikacji), jak opisano w przewodniku Dependency Injection , a przydatne informacje dotyczące debugowania pomogą w wyśledzeniu źródła tych błędów.

Paolo Moretti
źródło
1
+1 Po prostu przejście na grunt-ng-annotate rozwiązało ten problem, a ngmin i tak jest teraz przestarzałe, więc to kolejny powód do zmiany.
Pier-Luc Gendreau
to była poprawka, której szukałem dni!
pedrommuller
Mam ten sam problem z budowaniem zminimalizowanego kodu za pomocą browserify, angular i gulp-minify. Usunąłem gulp minify i zastąpiłem go gulp-ng-annotate, kod jest nadal zminimalizowany, ale nadal nie działa.
Dimitri Kopriwa
@BigDong, jeśli używasz browserify, najlepszym sposobem jest prawdopodobnie włączenie ngStrictDi(coś w rodzaju <div ng-app="myApp" ng-strict-di />) i używanie go gulp-ng-annotatenawet w swoim środowisku programistycznym, aby łatwo wyśledzić te błędy minifikacji.
Paolo Moretti
@PaoloMoretti Próbowałem z ngStrictDi i gulp-ng-annotate, przeglądarka może łączyć, ale kod nie jest zminimalizowany, czy to nie powinno być zadanie ng-annotate?
Dimitri Kopriwa
22

Mam ten sam błąd. Jednak dla mnie problemem jest deklaracja kontrolera w dyrektywach. Zamiast tego powinieneś to zrobić.

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
źródło
1
Dziękuję @angelokh! Miałem dokładnie ten problem. I był przy użyciu controller: function ($scope) {}notacji.
jbasko
2
Jest to bardziej podobne do rozwiązania rzeczywistego problemu niż mangle: falsesugerowane w innych odpowiedziach, ponieważ nadal chcemy mieć możliwość modyfikowania nazw.
jbasko
9

Miałem podobny problem, używając grunt, ngmin i uglify.

Uruchomiłem proces w następującej kolejności: concat, ngmin, uglify

Ciągle otrzymywałem błąd $ wtryskiwacza z kątowego, dopóki nie dodałem w opcji uglify mangle: false - wtedy wszystko zostało naprawione.

Próbowałem też dodać wyjątki, aby uglifikować w ten sposób:

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

Ale bez skutku...

Oto mój gruntFile.js dla dalszych wyjaśnień:

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
źródło
AH DZIĘKUJĘ, DZIĘKUJĘ! to zaoszczędziło mi tyle czasu.
mylescc
5

Sugestia AndrewM96 ng-minjest słuszna.

Wyrównanie i biała przestrzeń mają znaczenie zarówno dla Uglify, jak i dla Angulara.

BPWDevelopment
źródło
10
Wydaje się, że ng-min po prostu przetwarza pliki kątowe, aby były przyjazne uglify. W naszym procesie budowania używamy obu ( ng-minwcześniej uglify) i nadal mieliśmy problem z uglified js.
craigb
4
Dlaczego jest to oznaczone jako odpowiedź? (Również AndrewM96 powinno być AndreM96)
Jay
chociaż w dokumentach ng-min brzmi obiecująco, nie rozwiązuje problemu
special0ne
@craigb ma ten sam problem. Może to kombinacja rzeczy. Używam również RequireJS. Zasadniczo wykonuję wszystkie czynności związane ze zmianą funkcji, które ng-min powinien wykonać samodzielnie i nadal uruchamiam ng-min, niż uruchamiam, wymagając kompilacji, a następnie uruchamiam Uglify z mangle true. Ten proces wydaje się działać przez większość czasu.
uciekł kot
3

Miałem podobny problem. I rozwiązałem to w następujący sposób. Musimy uruchomić moduł Gulp o nazwie gulp-ng-annotate, zanim uruchomimy uglify. Więc instalujemy ten moduł

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

Następnie wykonaj żądanie w Gulpfile.js

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

I w swoim zadaniu roboczym zrób coś takiego

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

To mnie rozwiązało.

[EDYCJA: poprawione literówki]

Paulo Borralho Martins
źródło
gulp-MG-annotate powinno być gulp-NG-annotate?
hally9k
Tak, przepraszam za pomyłkę. Gdzie czyta mg-annotatejest zawszeng-annotate
Paulo Borralho Martins
2

Jest to bardzo trudne do debugowania, ponieważ wiele usług ma takie same nazwy (głównie e lub a). To nie rozwiąże problemu, ale dostarczy Ci nazwę nierozwiązanej usługi, która pozwoli Ci wyśledzić, w zniekształconych wynikach, lokalizację w kodzie i ostatecznie umożliwi rozwiązanie problemu:

Przejdź do lib/scope.jsUglify2 ( node_modules/grunt-contrib-uglify/node_modules/uglify-js/lib/scope.js) i zastąp wiersz

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

z

this.mangled_name = this.name + "__debugging_" + counter++
Bas
źródło