Ошибка минификации Angular.module

82

Тратя время на попытки выяснить, почему минификация не работает.

Я внедрил через объект массива мои поставщики перед функцией в соответствии с многочисленными предложениями в Интернете, но все еще "Неизвестный поставщик: aProvider <- a"

Обычный:

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

Уменьшено:

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

Любое предложение будет очень признательно!

BPWРазработка
источник
1
Что вы используете для минимизации кода? uglifyJS? Также проверьте: github.com/btford/ngmin ;)
AndreM96
Я использовал ngmin, все, что он делал, это выстраивал код в другом формате пробелов. Я попытался использовать express-uglify в качестве промежуточного программного обеспечения, но это не сработало, поэтому я попытался вручную использовать онлайн-углификатор. В любом случае код остался прежним.
BPWDevelopment
Кроме того, нет ли пропавших без вести ]? (до закрытия ))
AndreM96
Были, я забыл их в этом конкретном фрагменте. Это не меняет того факта, что "неизвестный провайдер а" все еще случается :(
BPWDevelopment
2
Хорошо, а какой онлайн-минификатор вы использовали? Это отлично работает с вашим кодом: marijnhaverbeke.nl/uglifyjs
AndreM96,

Ответы:

139

Я сталкивался с этой проблемой раньше с Grunt.js Uglify .

Один из вариантов - выжимать

uglify: {
  options: {
    mangle: false
  },

Который, как я полагаю, запускает функции регулярного выражения «как строки» и минимизирует их.

Например:

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

Станет:

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

Отключите его - эта функция плохо работает с Angular.

Редактировать:

Чтобы быть более точным, как объясняет @JoshDavidMiller:

Uglify искажает mangleтолько переменные, что и является причиной проблемы AngularJS. То есть проблема в инъекции, а не в определении.

function MyCtrl($scope, myService)будет искажено function MyCtrl(a, b), но определение службы внутри строки никогда не должно измениться.

  • Бег ng-minперед бегом uglifyрешает эту проблему.
Дэн Канце
источник
3
Он обновил свой код. Его проблема заключалась не в том, что "$ locationProvider" превратился в "b" или что-то в этом роде. Это просто не сработало. Однако +1 за этот ответ :)
AndreM96
1
Спасибо, долго искал такой вариант.
reen
Я столкнулся с тем же самым при использовании angular bootstrap + yeoman. Используя генератор угловых углов yeoman, он создал distсборку, в которой будет указанная ошибка зависимости «Неизвестный поставщик». Установка mangle: falseрешила проблему. (примечание: речь шла только проблема в gruntпостроенном distне разработчик дружественной appсборки)
craigb
6
Есть ли на mangle: true самом деле калечить « как струны»? Я почти уверен, что он искажает только переменные , что и является причиной проблемы AngularJS. То есть проблема в инъекции, а не в определении. function MyCtrl($scope, myService)будет искажено function MyCtrl(a, b), но определение службы внутри строки никогда не должно изменяться. Бег ng-minперед бегом uglifyрешает эту проблему, не так ли?
Джош Дэвид Миллер
1
ng-minтеперь устарел в пользуng-annotate
Atav32
51

Проблема

Из AngularJS: Плохие части :

Angular имеет встроенный инжектор зависимостей, который передает соответствующие объекты вашей функции на основе имен ее параметров:

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

Здесь названия параметров $scope и $windowбудут сопоставлены со списком известных имен, и соответствующие объекты будут созданы и переданы функции. Angular получает имена параметров, вызывая toString()функцию, а затем анализируя определение функции.

Проблема, конечно же, в том, что он перестает работать, как только вы минимизируете свой код . Поскольку вы заботитесь о пользовательском опыте, вы будете минимизировать свой код, поэтому использование этого механизма DI сломает ваше приложение. Фактически, общая методология разработки заключается в использовании неуменьшенного кода при разработке для облегчения отладки, а затем для минимизации кода при продвижении в производственную или промежуточную среду. В этом случае проблема не поднимет свою уродливую голову, пока вы не окажетесь в точке, где она болит больше всего.

(...)

Поскольку этот механизм внедрения зависимостей на самом деле не работает в общем случае, Angular также предоставляет механизм, который работает. Безусловно, он обеспечивает два. Вы можете передать такой массив:

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

Или вы можете установить $injectсвойство в своем конструкторе:

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

Решение

Вы можете использовать ng-annotateдля автоматического добавления аннотаций, необходимых для минимизации:

ng-annotateдобавляет и удаляет аннотации внедрения зависимостей AngularJS. Это ненавязчиво, поэтому в остальном исходный код остается неизменным. Никаких потерянных комментариев или перемещенных строк.

ng-annotateработает быстрее и стабильнее, чем ngmin(который теперь устарел), и имеет плагины для многих инструментов:


Начиная с AngularJS 1.3 есть также новый параметр в ngAppназывается ngStrictDi:

если этот атрибут присутствует в элементе приложения, инжектор будет создан в режиме «strict-di». Это означает, что приложение не сможет вызывать функции, которые не используют явную аннотацию функций (и поэтому не подходят для минификации), как описано в руководстве по внедрению зависимостей , а полезная отладочная информация поможет отследить корень этих ошибок.

Паоло Моретти
источник
1
+1 Простое переключение на grunt-ng-annotate устранило эту проблему, и ngmin в любом случае устарел, так что это еще одна причина для перехода.
Пьер-Люк Жендро
это было исправление, которое я искал несколько дней!
pedrommuller
Я столкнулся с той же проблемой при создании миниатюрного кода с помощью browserify, angular и gulp-minify. Я удалил gulp minify и заменил его на gulp-ng-annotate, код все еще минимизирован, но все еще не работает.
Dimitri Kopriwa
@BigDong, если вы используете browserify, лучший способ - это, вероятно, включить ngStrictDi(что-то вроде <div ng-app="myApp" ng-strict-di />) и использовать gulp-ng-annotateдаже в своей среде разработки, чтобы вы легко отслеживали эти ошибки минификации.
Паоло Моретти,
@PaoloMoretti Я пробовал использовать ngStrictDi и gulp-ng-annotate, browserify может объединяться, но код не минифицирован, разве это не должно быть ng-annotate?
Dimitri Kopriwa,
22

У меня такая же ошибка. Однако для меня проблема заключается в объявлении контроллера директив. Вам следует сделать это вместо этого.

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

ангелох
источник
1
Спасибо @angelokh! У меня была именно такая проблема. Я использовал controller: function ($scope) {}обозначения.
jbasko
2
Это больше похоже на решение реальной проблемы, чем mangle: falseпредлагается в других ответах, потому что мы все еще хотим иметь возможность искажать имена.
jbasko
9

У меня была аналогичная проблема с использованием grunt, ngmin и uglify.

Я запустил процесс в следующем порядке: concat, ngmin, uglify

Я продолжал получать ошибку $ injector от angular, пока не добавил в uglify options mangle: false - тогда все было исправлено.

Я также попытался добавить исключения для uglify следующим образом:

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

Но без толку ...

Вот мой gruntFile.js для дальнейшего пояснения:

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

};

Стен Мучоу
источник
А СПАСИБО СПАСИБО! это сэкономило мне столько времени.
mylescc
5

Предложение AndrewM96 ng-minверное.

Выравнивание и пустое пространство имеют значение как для Uglify, так и для Angular.

BPWРазработка
источник
10
ng-min, похоже, просто обрабатывает файлы angular, поэтому они удобны uglify. В нашем процессе сборки мы использовали оба ( ng-minраньше uglify), и у нас все еще была проблема с uglified js.
Craigb
4
Почему это отмечено как ответ? (Кроме того, AndrewM96 должен быть AndreM96)
Джей
хотя в документах ng-min звучит многообещающе, это не решает проблему
special0ne
@craigb имеет ту же проблему. Может быть, это комбинация вещей. Я также использую RequireJS. Я в основном делаю все то, что ng-min должен делать сам, и по-прежнему запускаю ng-min, затем запускаю require build и затем запускаю uglify с mangle true. Кажется, что этот процесс работает в большинстве случаев.
escapedcat
3

У меня была аналогичная проблема. И решил это следующим образом. Нам нужно запустить модуль Gulp под названием gulp-ng-annotate, прежде чем запускать uglify. Итак, мы устанавливаем этот модуль

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

Затем выполните требование в Gulpfile.js

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

И в вашей задаче usemin сделайте что-то вроде этого

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

Это решило проблему для меня.

[РЕДАКТИРОВАТЬ: исправленные опечатки]

Пауло Борральо Мартинс
источник
gulp-MG-annotate должен быть gulp-NG-annotate?
hally9k 03
Да, извините за ошибку. Где читает mg-annotate, всегда естьng-annotate
Пауло Борральо Мартинс 05
2

Это очень сложно отладить, потому что многие службы названы одинаково (в основном e или a). Это не решит ошибку, но предоставит вам имя неразрешенной службы. которая позволит вам отследить в некорректном выводе местоположение в коде и, наконец, позволит вам решить проблему:

Зайдите в lib/scope.jsUglify2 ( node_modules/grunt-contrib-uglify/node_modules/uglify-js/lib/scope.js) и замените строку

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

с участием

this.mangled_name = this.name + "__debugging_" + counter++
Bas
источник