Je continue de voir la recommandation de préparer les fichiers JS pour la production à concat puis à uglify.
Par exemple ici , dans l'une des tâches de grognement de Yeoman.
Par défaut, le flux est: concat -> uglifyjs.
Étant donné qu'UglifyJS peut effectuer à la fois la concaténation et la minification, pourquoi auriez-vous besoin des deux en même temps?
Merci.
Exécution d'un test de base pour voir s'il existe une différence de performances entre l'exécution de concat
, puis de uglify
par rapport à simplement uglify
.
package.json
{
"name": "grunt-concat-vs-uglify",
"version": "0.0.1",
"description": "A basic test to see if we can ditch concat and use only uglify for JS files.",
"devDependencies": {
"grunt": "^0.4.5",
"grunt-contrib-concat": "^0.5.0",
"grunt-contrib-uglify": "^0.6.0",
"load-grunt-tasks": "^1.0.0",
"time-grunt": "^1.0.0"
}
}
Gruntfile.js
module.exports = function (grunt) {
// Display the elapsed execution time of grunt tasks
require('time-grunt')(grunt);
// Load all grunt-* packages from package.json
require('load-grunt-tasks')(grunt);
grunt.initConfig({
paths: {
src: {
js: 'src/**/*.js'
},
dest: {
js: 'dist/main.js',
jsMin: 'dist/main.min.js'
}
},
concat: {
js: {
options: {
separator: ';'
},
src: '<%= paths.src.js %>',
dest: '<%= paths.dest.js %>'
}
},
uglify: {
options: {
compress: true,
mangle: true,
sourceMap: true
},
target: {
src: '<%= paths.src.js %>',
dest: '<%= paths.dest.jsMin %>'
}
}
});
grunt.registerTask('default', 'concat vs. uglify', function (concat) {
// grunt default:true
if (concat) {
// Update the uglify dest to be the result of concat
var dest = grunt.config('concat.js.dest');
grunt.config('uglify.target.src', dest);
grunt.task.run('concat');
}
// grunt default
grunt.task.run('uglify');
});
};
Dans src
, j'ai mis un tas de fichiers JS, y compris la source non compressée de jQuery, copiée plusieurs fois, répartie dans des sous-dossiers. Beaucoup plus que ce qu'un site/application normal possède habituellement.
Il s'avère que le temps nécessaire pour concaténer et compresser tous ces fichiers est essentiellement le même dans les deux scénarios.
Sauf lorsque vous utilisez également l'option sourceMap: true
Sur concat
(voir ci-dessous).
Sur mon ordinateur:
grunt default : 6.2s (just uglify)
grunt default:true : 6s (concat and uglify)
Il est à noter que le main.min.js
Résultant est le même dans les deux cas.
De plus, uglify
se charge automatiquement d'utiliser le séparateur approprié lors de la combinaison des fichiers.
Le seul cas où cela importe est lors de l'ajout de sourceMap: true
Au concat
options
.
Cela crée un fichier main.js.map
À côté de main.js
, Et se traduit par:
grunt default : 6.2s (just uglify)
grunt default:true : 13s (concat and uglify)
Mais si le site de production charge uniquement la version min
, cette option est inutile.
J'ai trouvé un gros inconvénient en utilisant concat
avant uglify
.
Lorsqu'une erreur se produit dans l'un des fichiers JS, le sourcemap
sera lié au fichier main.js
Concaténé et non au fichier d'origine. Alors que lorsque uglify
fait tout le travail, il sera un lien vers le fichier d'origine.
Mise à jour:
Nous pouvons ajouter 2 options supplémentaires à uglify
qui lieront le uglify
sourcemap à concat
sourcemap, gérant ainsi le "désavantage" que j'ai mentionné ci-dessus.
uglify: {
options: {
compress: true,
mangle: true,
sourceMap: true,
sourceMapIncludeSources: true,
sourceMapIn: '<%= paths.dest.js %>.map',
},
target: {
src: '<%= paths.src.js %>',
dest: '<%= paths.dest.jsMin %>'
}
}
Mais cela semble hautement inutile.
Je pense qu'il est prudent de conclure que nous pouvons abandonner concat
pour les fichiers JS si nous utilisons uglify
, et l'utiliser à d'autres fins, si nécessaire.
Dans l'exemple que vous mentionnez, que je cite ci-dessous, les fichiers sont d'abord concaténés avec concat
puis uglifiés/minifiés par uglify
:
{
concat: {
'.tmp/concat/js/app.js': [
'app/js/app.js',
'app/js/controllers/thing-controller.js',
'app/js/models/thing-model.js',
'app/js/views/thing-view.js'
]
},
uglifyjs: {
'dist/js/app.js': ['.tmp/concat/js/app.js']
}
}
La même chose pourrait être obtenue avec:
{
uglifyjs: {
'dist/js/app.js': [
'app/js/app.js',
'app/js/controllers/thing-controller.js',
'app/js/models/thing-model.js',
'app/js/views/thing-view.js'
]
}
}
En règle générale, la tâche clean
s'exécute ensuite après les tâches qui écrivent dans un dossier temporaire (dans cet exemple concat
) et supprime le contenu de ce dossier. Certaines personnes aiment aussi exécuter clean
avant des tâches comme compass
, pour supprimer des choses comme des sprites d'image nommés de façon aléatoire (qui sont nouvellement générés à chaque exécution de la tâche). Cela permettrait aux roues de tourner même pour les plus paranoïaques.
Tout cela est une question de préférence et de workflow, comme c'est le cas avec quand exécuter jshint
. Certaines personnes aiment l'exécuter avant la compilation, d'autres préfèrent l'exécuter sur des fichiers compilés.
Les projets complexes avec une quantité incroyable de fichiers JavaScript
- ou avec un nombre de plus en plus important de pairs et de contributeurs, pourraient choisir de concaténer des fichiers en dehors de uglify
juste pour que les choses soient plus lisibles et maintenables. Je pense que c'était le raisonnement derrière le choix de Yeoman
flux de transformation.
uglify
peut être notoirement lent en fonction de la configuration du projet, donc il peut y avoir un petit gain à le concaténer avec concat
d'abord - mais cela devrait être confirmé.
concat
prend également en charge les séparateurs, ce que uglify
n'a pas pour autant que README.md
les fichiers sont concernés.
concat: {
options: {
separator: ';',
}
}