可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Having the darnedest time trying to figure out why minification is not working.
I have injected via an array object my providers prior the function per numerous suggestions across the web and yet still "Unknown provider: aProvider <- a"
Regular:
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);
}])
Minified:
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);
}])
Any suggestion would be much obliged!
回答1:
I ran into this problem before with Grunt.js Uglify plugin.
One of the options are mangle
uglify: {
options: {
mangle: false
},
Which I believe runs regex functions on "like strings" and minifys them.
For example:
angular.module("imgur", ["imgur.global","imgur.album"]);
Would become:
angular.module("a", ["a.global","a.album"]);
Disable it --- this feature doesn't play nice with Angular.
Edit:
To be more precise as @JoshDavidMiller explains:
Uglify mangle
only mangles like variables, which is what actually causes the AngularJS problem. That is, the problem is in injection and not definition.
function MyCtrl($scope, myService)
would get mangled to function MyCtrl(a, b)
, but the service definition inside of a string should never get altered.
- Running
ng-min
before running uglify
solves this problem.
回答2:
Problem
From AngularJS: The Bad Parts:
Angular has a built in dependency injector that will pass appropriate
objects to your function based on the names of its parameters:
function MyController($scope, $window) {
// ...
}
Here, the names of the parameters $scope
and $window
will be
matched against a list of known names, and corresponding objects get
instantiated and passed to the function. Angular gets the parameter
names by calling toString()
on the function, and then parsing the
function definition.
The problem with this, of course, is that it stops working the
moment you minify your code. Since you care about user experience
you will be minifying your code, thus using this DI mechanism will
break your app. In fact, a common development methodology is to use
unminified code in development to ease debugging, and then to minify
the code when pushing to production or staging. In that case, this
problem won’t rear its ugly head until you’re at the point where it
hurts the most.
(...)
Since this dependency injection mechanism doesn’t actually work in the
general case, Angular also provides a mechanism that does. To be sure,
it provides two. You can either pass along an array like so:
module.controller('MyController', ['$scope', '$window', MyController]);
Or you can set the $inject
property on your constructor:
MyController.$inject = ['$scope', '$window'];
Solution
You can use ng-annotate
for auto adding annotations required for minifying:
ng-annotate
adds and removes AngularJS dependency injection
annotations. It is non-intrusive so your source code stays exactly the
same otherwise. No lost comments or moved lines.
ng-annotate
is faster and stabler than ngmin
(which is now deprecated) and it has plugins for many tools:
grunt-ng-annotate
gulp-ng-annotate
browserify-annotate
Starting from AngularJS 1.3 there's also a new param in ngApp
called ngStrictDi
:
if this attribute is present on the app element, the injector will be
created in "strict-di" mode. This means that the application will fail
to invoke functions which do not use explicit function annotation (and
are thus unsuitable for minification), as described in the Dependency
Injection guide, and useful debugging info will assist in tracking
down the root of these bugs.
回答3:
I got same error. However, for me, the problem is directives' controller declaration. You should do this instead.
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
回答4:
I had a similar issue using grunt, ngmin and uglify.
I ran the process in this order: concat, ngmin, uglify
I was continuing to get the $injector error from angular until I added in the uglify options mangle: false - then everything was fixed.
I also tried to add the exceptions to uglify like this:
options: {
mangle: {
except: ['jQuery', 'angular']
}
}
But to no avail...
Here is my gruntFile.js for further clarification:
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']);
};
回答5:
AndrewM96 suggestion of ng-min
is right.
The alignment and white space matters to Uglify as well as Angular.
回答6:
I had a similar problem. And solved it the following way. We need to run a Gulp module called gulp-ng-annotate before we run uglify.
So we install that module
npm install gulp-ng-annotate --save-dev
Then do the require in Gulpfile.js
ngannotate = require(‘gulp-ng-annotate’)
And in your usemin task do something like this
js: [ngannotate(), uglify(),rev()]
That solved it for me.
[EDIT: Fixed typos]
回答7:
Uglify has an option to disable mangling on specific files:
options: {
mangle: {
except: ['jQuery', 'angular']
}
}
https://github.com/gruntjs/grunt-contrib-uglify#reserved-identifiers
回答8:
This is very difficult to debug because a lot of services are named the same (mostly e or a). This will not solve the error, but will provide you with the name of the unresolved service which enables you to track down, in the uglified output, the location in the code and finally enables you to solve the issue:
Go into lib/scope.js
of Uglify2 (node_modules/grunt-contrib-uglify/node_modules/uglify-js/lib/scope.js
) and replace the line
this.mangled_name = this.scope.next_mangled(options);
with
this.mangled_name = this.name + "__debugging_" + counter++