Grunt:跨多个目录的项目缩小 js,以便每个目录的内容扁平化为单个缩小文件
Grunt: minify js across project of multiple dirs, so that the contents of each dir flattens to a single minified file
我有一个项目,javascript 个文件结构如下:
我的目标:我想 minify/uglify 这些 javascript 文件 在构建输出中,例如有 1 个缩小每个目录的文件 。因此,例如,如果我将 js 存储在 bld/js 中的构建输出中,那么在 bld/js 中,我最终会得到文件:dir1.min.js(连接和缩小 [= 的结果56=]、fileB.js 和 /src/js/dir1/ 中的所有其他 js 文件)、dir2.min.js(连接和缩小 fileG.js 和 fileN.js 的结果,以及所有/src/dir2/) 中的其他 js 文件,依此类推。
现在 - 我正在使用 grunt 框架生成构建,并且必须在 grunt 文件中执行此操作。我是 grunt、node.js 和 javascript 的新手。 我想解释一下我解决这个问题的思路,想知道是否有更熟悉这些框架的人可以告诉我我的 approach/thinking 是否有问题。
据我了解,在 grunt 中做事的主要方式是使用插件。所以我的第一个想法是使用一个插件来完成这个任务。有一个名为'npm-contrib-uglify'的插件,专门用于缩小。 https://github.com/gruntjs/grunt-contrib-uglify 问题是 - 我的理解是,要执行我希望在此处执行的操作(每个目录 1 min.js 个文件),我需要为每个目录创建特定任务。所以初始化看起来像这样:
grunt.initConfig({
uglify: {
dir1: {
files: {
'dest/js/dir1.min.js': ['src/js/dir1/**.js']
}
},
dir2: {
files: {
'dest/js/dir2.min.js': ['src/js/dir2/**.js']
}
},
...
dirN: {
files: {
'dest/js/dirN.min.js': ['src/js/dirN/**.js']
}
},
}
});
如果我只有一个或两个目录,并且项目格式将保持相对不变 - 我想我可以接受。但是这个项目中有很多目录,更重要的是,项目结构会发生变化;我希望能够将目录添加到我的项目中,而不必每次都通过添加新目标来更新 grunt 文件。我希望有一种方法来参数化任务 - 意思是,有一个通用目标说 'for some dir, minify all the .js files in that dir to some .min.js file' 然后我可以为每个目录以编程方式调用目标 - 但这似乎不可能。
所以我决定避开插件,只依赖node.js。有一个名为“npm-uglify”的 npm 模块。 https://www.npmjs.com/package/uglify-js。我想我可以制作自己的自定义 grunt 任务,它循环遍历源目录,并在每个嵌套目录上调用 uglify-js。这是一个示例(请注意,我在几个地方使用了伪代码,特别是为了获取目录列表,因为我无法访问机器,也没有提交给内存的语法。但这应该给出这样的想法我的意思是:)
var fs = require('fs');
var uglifyJs = require('uglify-js');
...
grunt.registerTask('minifyMyJs', function(myJsSrcDir) { // myJsSrcDir is an abs. path to the src dir
for (dir in myJsSrcDir): // go through all the dirs.. realize I should use fs.readdir, but just use pseudocode here to give the idea
var minifiedOutputFileName = fs.basename(dir); // to get dir1 from /src/js/dir1
var minified = uglifyJs.Minify(["**.js"]);
fs.writeFile(minifiedOutputFileNam, result.code, function(err)) {
if(err) {
... error checking...
}
}
}
}
现在 - 我不是要你为我解决这个问题,而且我知道上面的代码不会编译。相反,我的问题是——我只是在以完全错误的方式思考这个问题吗? grunt 的全部意义在于避免在 node.js 中编写这样的代码(所以我这样做违背了目的吗?) 你真的可以参数化grunt 插件的目标,例如 grunt-contrib-uglify?我 运行 遇到许多插件的相同问题,我有一些任务需要在许多不同的 dirs/files 中一遍又一遍地重复,但以完全相同的方式,所以我想能够配置一个通用目标,我可以为不同的值以编程方式调用(而不是为特定目录或文件名硬编码大量目标,这些目标可能经常更改)。但这似乎不可能,所以我继续在 node.js 中实施。 我处理 grunt 的方式是否存在根本性缺陷?如果是这样 - 你能提供一些不同的观点吗?
想法
am I just thinking about this in the completely wrong way?
不,我不认为你以错误的方式思考这个问题。质疑 why/how 对于任何发展 [period] 总是一件好事。这就是让我们长期成为更好的开发人员的原因。
无论您选择将哪个 solution/tools 用于您的构建过程,无论它是一个专门的任务 运行ner 例如 grunt or gulp, or a higher level solution such as nodejs perhaps combined with npm-scripts,您仍然需要编写一些代码。当然,上述所有方法的共同点是您必须使用 JavaScript.
进行编码
Is the whole point of grunt to avoid coding things like this in node.js (and so I'm defeating the purpose by doing this?
我不能说哪个 solution/tool 比另一个 更好用(那太自以为是了) 我个人有开发构建过程的经验使用提到的所有这些工具。通常,尽管我经常发现使用专用任务 运行ner (g运行t 或 gulp) 比使用更自定义的方法(例如 npm-scripts
结合 nodejs
.
通常,我也会为 g运行t(例如 grunt-contrib-uglify
)找到一个非常棒的插件,然后在进行一些修补后发现它满足了我 90% 的要求,但剩下的 10% 却惨遭失败。然而,当 utilizing/integrating 任何开源 package/module 时,情况往往如此......可以这么说,你仍然需要填补空白!
G运行ts 鲜为人知的宝石
在放弃 gruntjs
并进入 nodejs
仅开发模式之前,(再次重申我对任何方法都没有偏见),我建议您查看一些 g运行ts 功能,例如:
在使用 g运行t 时,我发现利用上面列出的功能可以帮助我解决剩余的 10%我之前提到的要求。特别是自定义任务功能已多次证明非常有用。
解决方案
值得注意的是 grunt-contrib-uglify is a multi-task plugin. I.e. It allows you to configure multiple Targets 正如您在问题的 uglify
配置中所展示的那样。您包含了三个目标,名称为 dir1
、dir2
和 dirN
.
由于 grunt-contrib-uglify
是 多任务 并牢记我之前列出的 g运行t 功能......一种方法使用 g运行t 满足您的要求是:
- 使用自定义任务动态配置和运行 uglify 任务。
- 利用
grunt.file.expand
获取每个.js文件的父文件夹路径。
- filter paths 数组到唯一的目录路径。
- 为
uglify
任务的每个目标动态创建 files
配置。
- 使用
grunt.config
配置具有多个目标的 uglify
任务,然后使用 grunt-task.run
配置 运行 任务。
Grunfile.js
module.exports = function (grunt) {
'use strict';
grunt.initConfig({
uglify: {
// <-- Intentionally blank, will be dynamically generated.
}
});
/**
* 1. Helper custom Task to dynamically configure and run the uglify task.
*/
grunt.registerTask('myUglify', 'Configures uglify task', function () {
var srcDirGlob = 'src/js/**/*.js';
var destDir = 'dist/js/';
var config = {};
// 2. Get the paths to the parent diretory of each .js file.
var paths = grunt.file.expand({ filter: 'isFile' }, srcDirGlob)
.map(function (_path) {
return _path.substring(0, _path.lastIndexOf("/"));
});
// 3. Filter paths Array to only unique directory paths.
paths.filter(function(_path, pos){
return paths.indexOf(_path) === pos;
})
// 4. Dynamically create the `files` configuration for each Target.
.forEach(function(_path, index) {
var dirName = _path.substring(_path.lastIndexOf('/') + 1);
var destPath = destDir + dirName + '.min.js';
var srcGlob = _path + '/*.js';
config[index] = {
files: {}
}
config[index].files[destPath] = srcGlob;
});
// NOTE: The dynamically created `config` object is now something like this:
//
// config: {
// 0: {
// files: {
// 'dist/js/dir1.min.js': 'src/js/dir1/**.js'
// }
// },
// 1: {
// files: {
// 'dist/js/dir2.min.js': 'src/js/dir2/**.js'
// }
// },
// ...
// }
// 5. Configure the uglify task with multiple Targets
// (i.e. the `config` object) and then run the Task.
grunt.config('uglify', config);
grunt.task.run(['uglify']);
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.registerTask('default', ["myUglify"]);
};
结果
运行 $ grunt
通过 CLI 使用上面的 Gruntfile.js
和 src
目录,如下所示:
.
└── src
└── js
├── dir1
│ ├── fileA.js
│ └── fileB.js
├── dir2
│ ├── fileG.js
│ └── fileN.js
└── dir3
├── fileQ.js
└── fileR.js`
.. 将连接 uglify
.js
个文件,生成目录结构如下:
.
└── dist
└── js
├── dir1.min.js
├── dir2.min.js
└── dir3.min.js`
注意:源 .js
文件的父文件夹名称用于生成的 concatenated/uglified .js
文件的名称。
总结
Gruntfile.js
(上文)中使用的 pattern/approach 可以根据需要针对许多可用插件进行修改。
- 只要插件本身支持Multi-Tasks然后考虑使用g运行t Custom Task来填充在空白处。
希望对您有所帮助。
我有一个项目,javascript 个文件结构如下:
我的目标:我想 minify/uglify 这些 javascript 文件 在构建输出中,例如有 1 个缩小每个目录的文件 。因此,例如,如果我将 js 存储在 bld/js 中的构建输出中,那么在 bld/js 中,我最终会得到文件:dir1.min.js(连接和缩小 [= 的结果56=]、fileB.js 和 /src/js/dir1/ 中的所有其他 js 文件)、dir2.min.js(连接和缩小 fileG.js 和 fileN.js 的结果,以及所有/src/dir2/) 中的其他 js 文件,依此类推。
现在 - 我正在使用 grunt 框架生成构建,并且必须在 grunt 文件中执行此操作。我是 grunt、node.js 和 javascript 的新手。 我想解释一下我解决这个问题的思路,想知道是否有更熟悉这些框架的人可以告诉我我的 approach/thinking 是否有问题。
据我了解,在 grunt 中做事的主要方式是使用插件。所以我的第一个想法是使用一个插件来完成这个任务。有一个名为'npm-contrib-uglify'的插件,专门用于缩小。 https://github.com/gruntjs/grunt-contrib-uglify 问题是 - 我的理解是,要执行我希望在此处执行的操作(每个目录 1 min.js 个文件),我需要为每个目录创建特定任务。所以初始化看起来像这样:
grunt.initConfig({
uglify: {
dir1: {
files: {
'dest/js/dir1.min.js': ['src/js/dir1/**.js']
}
},
dir2: {
files: {
'dest/js/dir2.min.js': ['src/js/dir2/**.js']
}
},
...
dirN: {
files: {
'dest/js/dirN.min.js': ['src/js/dirN/**.js']
}
},
}
});
如果我只有一个或两个目录,并且项目格式将保持相对不变 - 我想我可以接受。但是这个项目中有很多目录,更重要的是,项目结构会发生变化;我希望能够将目录添加到我的项目中,而不必每次都通过添加新目标来更新 grunt 文件。我希望有一种方法来参数化任务 - 意思是,有一个通用目标说 'for some dir, minify all the .js files in that dir to some .min.js file' 然后我可以为每个目录以编程方式调用目标 - 但这似乎不可能。
所以我决定避开插件,只依赖node.js。有一个名为“npm-uglify”的 npm 模块。 https://www.npmjs.com/package/uglify-js。我想我可以制作自己的自定义 grunt 任务,它循环遍历源目录,并在每个嵌套目录上调用 uglify-js。这是一个示例(请注意,我在几个地方使用了伪代码,特别是为了获取目录列表,因为我无法访问机器,也没有提交给内存的语法。但这应该给出这样的想法我的意思是:)
var fs = require('fs');
var uglifyJs = require('uglify-js');
...
grunt.registerTask('minifyMyJs', function(myJsSrcDir) { // myJsSrcDir is an abs. path to the src dir
for (dir in myJsSrcDir): // go through all the dirs.. realize I should use fs.readdir, but just use pseudocode here to give the idea
var minifiedOutputFileName = fs.basename(dir); // to get dir1 from /src/js/dir1
var minified = uglifyJs.Minify(["**.js"]);
fs.writeFile(minifiedOutputFileNam, result.code, function(err)) {
if(err) {
... error checking...
}
}
}
}
现在 - 我不是要你为我解决这个问题,而且我知道上面的代码不会编译。相反,我的问题是——我只是在以完全错误的方式思考这个问题吗? grunt 的全部意义在于避免在 node.js 中编写这样的代码(所以我这样做违背了目的吗?) 你真的可以参数化grunt 插件的目标,例如 grunt-contrib-uglify?我 运行 遇到许多插件的相同问题,我有一些任务需要在许多不同的 dirs/files 中一遍又一遍地重复,但以完全相同的方式,所以我想能够配置一个通用目标,我可以为不同的值以编程方式调用(而不是为特定目录或文件名硬编码大量目标,这些目标可能经常更改)。但这似乎不可能,所以我继续在 node.js 中实施。 我处理 grunt 的方式是否存在根本性缺陷?如果是这样 - 你能提供一些不同的观点吗?
想法
am I just thinking about this in the completely wrong way?
不,我不认为你以错误的方式思考这个问题。质疑 why/how 对于任何发展 [period] 总是一件好事。这就是让我们长期成为更好的开发人员的原因。
无论您选择将哪个 solution/tools 用于您的构建过程,无论它是一个专门的任务 运行ner 例如 grunt or gulp, or a higher level solution such as nodejs perhaps combined with npm-scripts,您仍然需要编写一些代码。当然,上述所有方法的共同点是您必须使用 JavaScript.
进行编码Is the whole point of grunt to avoid coding things like this in node.js (and so I'm defeating the purpose by doing this?
我不能说哪个 solution/tool 比另一个 更好用(那太自以为是了) 我个人有开发构建过程的经验使用提到的所有这些工具。通常,尽管我经常发现使用专用任务 运行ner (g运行t 或 gulp) 比使用更自定义的方法(例如 npm-scripts
结合 nodejs
.
通常,我也会为 g运行t(例如 grunt-contrib-uglify
)找到一个非常棒的插件,然后在进行一些修补后发现它满足了我 90% 的要求,但剩下的 10% 却惨遭失败。然而,当 utilizing/integrating 任何开源 package/module 时,情况往往如此......可以这么说,你仍然需要填补空白!
G运行ts 鲜为人知的宝石
在放弃 gruntjs
并进入 nodejs
仅开发模式之前,(再次重申我对任何方法都没有偏见),我建议您查看一些 g运行ts 功能,例如:
在使用 g运行t 时,我发现利用上面列出的功能可以帮助我解决剩余的 10%我之前提到的要求。特别是自定义任务功能已多次证明非常有用。
解决方案
值得注意的是 grunt-contrib-uglify is a multi-task plugin. I.e. It allows you to configure multiple Targets 正如您在问题的 uglify
配置中所展示的那样。您包含了三个目标,名称为 dir1
、dir2
和 dirN
.
由于 grunt-contrib-uglify
是 多任务 并牢记我之前列出的 g运行t 功能......一种方法使用 g运行t 满足您的要求是:
- 使用自定义任务动态配置和运行 uglify 任务。
- 利用
grunt.file.expand
获取每个.js文件的父文件夹路径。 - filter paths 数组到唯一的目录路径。
- 为
uglify
任务的每个目标动态创建files
配置。 - 使用
grunt.config
配置具有多个目标的uglify
任务,然后使用grunt-task.run
配置 运行 任务。
Grunfile.js
module.exports = function (grunt) {
'use strict';
grunt.initConfig({
uglify: {
// <-- Intentionally blank, will be dynamically generated.
}
});
/**
* 1. Helper custom Task to dynamically configure and run the uglify task.
*/
grunt.registerTask('myUglify', 'Configures uglify task', function () {
var srcDirGlob = 'src/js/**/*.js';
var destDir = 'dist/js/';
var config = {};
// 2. Get the paths to the parent diretory of each .js file.
var paths = grunt.file.expand({ filter: 'isFile' }, srcDirGlob)
.map(function (_path) {
return _path.substring(0, _path.lastIndexOf("/"));
});
// 3. Filter paths Array to only unique directory paths.
paths.filter(function(_path, pos){
return paths.indexOf(_path) === pos;
})
// 4. Dynamically create the `files` configuration for each Target.
.forEach(function(_path, index) {
var dirName = _path.substring(_path.lastIndexOf('/') + 1);
var destPath = destDir + dirName + '.min.js';
var srcGlob = _path + '/*.js';
config[index] = {
files: {}
}
config[index].files[destPath] = srcGlob;
});
// NOTE: The dynamically created `config` object is now something like this:
//
// config: {
// 0: {
// files: {
// 'dist/js/dir1.min.js': 'src/js/dir1/**.js'
// }
// },
// 1: {
// files: {
// 'dist/js/dir2.min.js': 'src/js/dir2/**.js'
// }
// },
// ...
// }
// 5. Configure the uglify task with multiple Targets
// (i.e. the `config` object) and then run the Task.
grunt.config('uglify', config);
grunt.task.run(['uglify']);
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.registerTask('default', ["myUglify"]);
};
结果
运行 $ grunt
通过 CLI 使用上面的 Gruntfile.js
和 src
目录,如下所示:
.
└── src
└── js
├── dir1
│ ├── fileA.js
│ └── fileB.js
├── dir2
│ ├── fileG.js
│ └── fileN.js
└── dir3
├── fileQ.js
└── fileR.js`
.. 将连接 uglify
.js
个文件,生成目录结构如下:
.
└── dist
└── js
├── dir1.min.js
├── dir2.min.js
└── dir3.min.js`
注意:源 .js
文件的父文件夹名称用于生成的 concatenated/uglified .js
文件的名称。
总结
Gruntfile.js
(上文)中使用的 pattern/approach 可以根据需要针对许多可用插件进行修改。- 只要插件本身支持Multi-Tasks然后考虑使用g运行t Custom Task来填充在空白处。
希望对您有所帮助。