[英]How to compress JS and css in java at build time using maven-minify-plugin with YUI compressor
[英]How to minify/compress thousands of JS files - including some large ones - at the same time or sequentially without crashing the console?
通过我正在重构的演示 ,我有一个包含196 MB的src
文件夹。 大约142 MB由两个二进制文件组成。
其余2137个文件中约有2000个(大约46 MB)由JavaScript文件组成,其中大部分属于两个大型框架的官方和完整发行版。 最大的JavaScript文件大约是23MB。 这是unminified代码原文为C ++编译和-与emscripten -到ASM 。
我想编写一个Node.js脚本,它将我的所有文件从src
路径复制到dist
路径,并缩小它遇到的每个JS或CSS文件。 不幸的是,涉及的JS文件的数量和/或大小似乎打破了我的脚本。
让我们来看看我采取的步骤......
我开始编写一个小的构建脚本,将我的src
文件夹中的所有数据复制到我的dist
文件夹中。 我很惊讶地发现这个过程在几秒钟内完成。
以下是我的脚本代码。 请注意,您需要使用Node 8来运行该代码。
const util = require('util');
const fs = require('fs');
const path = require('path');
const mkdir = util.promisify(require('mkdirp'));
const rmdir = util.promisify(require('rimraf'));
const ncp = util.promisify(require('ncp').ncp);
const readdir = util.promisify(fs.readdir);
const readFile = util.promisify(fs.readFile);
const writeFile = util.promisify(fs.writeFile);
const stat = util.promisify(fs.stat);
const moveFrom = path.join(__dirname,"../scr");
const moveTo = path.join(__dirname,"../dist");
var copyFile = function(source, target) {
return new Promise(function(resolve,reject){
const rd = fs.createReadStream(source);
rd.on('error', function(error){
reject(error);
});
const wr = fs.createWriteStream(target);
wr.on('error', function(error){
reject(error);
});
wr.on('close', function(){
resolve();
});
rd.pipe(wr);
});
};
var copy = function(source, target) {
stat(source)
.then(function(stat){
if(stat.isFile()) {
console.log("Copying file %s", source);
switch (path.extname(target)) {
default:
return copyFile(source, target);
}
} else if( stat.isDirectory() ) {
return build(source, target);
}
}).catch(function(error){
console.error(error);
});
};
var build = function(source, target) {
readdir(source)
.then(function(list) {
return rmdir(target).then(function(){
return list;
});
})
.then(function(list) {
return mkdir(target).then(function(){
return list;
});
}).then(function(list) {
list.forEach(function(item, index) {
copy(path.join(source, item), path.join(target, item));
});
}).catch(function(error){
console.error(error);
})
};
build(moveFrom, moveTo);
每当我遇到它们时,我都会缩小我的CSS文件,我添加了CSS缩小功能。
为此,我对我的代码进行了以下修改。
首先,我添加了这个功能:
var uglifyCSS = function(source, target) {
readFile(source, "utf8")
.then(function(content){
return writeFile(target, require('ycssmin').cssmin(content), "utf8");
}).catch(function(error){
console.error(error);
});
}
然后,我修改了我的复制功能,如下所示:
var copy = function(source, target) {
stat(source)
.then(function(stat){
if(stat.isFile()) {
console.log("Copying file %s", source);
switch (path.extname(target)) {
case ".css":
return uglifyCSS(source, target);
default:
return copyFile(source, target);
}
} else if( stat.isDirectory() ) {
return build(source, target);
}
}).catch(function(error){
console.error(error);
});
};
到现在为止还挺好。 在这个阶段,一切仍然顺利进行。
然后,我做了同样的事情来缩小我的JS。
再次,我添加了一个新功能:
var uglifyJS = function(source, target) {
readFile(source, "utf8")
.then(function(content){
return writeFile(target, require('uglify-js').minify(content).code, "utf8");
}).catch(function(error){
console.error(error);
});
}
然后,我再次修改了我的复制功能:
var copy = function(source, target) {
stat(source)
.then(function(stat){
if(stat.isFile()) {
console.log("Copying file %s", source);
switch (path.extname(target)) {
case ".css":
return uglifyCSS(source, target);
case ".js":
return uglifyJS(source, target);
default:
return copyFile(source, target);
}
} else if( stat.isDirectory() ) {
return build(source, target);
}
}).catch(function(error){
console.error(error);
});
};
这里出了问题。 随着进程不断遇到越来越多的JS文件,它会一直在减速,直到进程似乎完全停止。
似乎有太多并行进程启动并继续消耗越来越多的内存,直到不再留下内存并且进程只是默默地死掉。 我尝试了除UglifyJS之外的其他缩小器,我遇到了所有这些问题。 所以问题似乎并不是特定于UglifyJS。
有任何想法如何解决这个问题?
这是完整的代码:
const util = require('util');
const fs = require('fs');
const path = require('path');
const mkdir = util.promisify(require('mkdirp'));
const rmdir = util.promisify(require('rimraf'));
const ncp = util.promisify(require('ncp').ncp);
const readdir = util.promisify(fs.readdir);
const readFile = util.promisify(fs.readFile);
const writeFile = util.promisify(fs.writeFile);
const stat = util.promisify(fs.stat);
const moveFrom = path.join(__dirname,"../scr");
const moveTo = path.join(__dirname,"../dist");
var copyFile = function(source, target) {
return new Promise(function(resolve,reject){
const rd = fs.createReadStream(source);
rd.on('error', function(error){
reject(error);
});
const wr = fs.createWriteStream(target);
wr.on('error', function(error){
reject(error);
});
wr.on('close', function(){
resolve();
});
rd.pipe(wr);
});
};
var uglifyCSS = function(source, target) {
readFile(source, "utf8")
.then(function(content){
return writeFile(target, require('ycssmin').cssmin(content), "utf8");
}).catch(function(error){
console.error(error);
});
}
var uglifyJS = function(source, target) {
readFile(source, "utf8")
.then(function(content){
return writeFile(target, require('uglify-js').minify(content).code, "utf8");
}).catch(function(error){
console.error(error);
});
}
var copy = function(source, target) {
stat(source)
.then(function(stat){
if(stat.isFile()) {
console.log("Copying file %s", source);
switch (path.extname(target)) {
case ".css":
return uglifyCSS(source, target);
case ".js":
return uglifyJS(source, target);
default:
return copyFile(source, target);
}
} else if( stat.isDirectory() ) {
return build(source, target);
}
}).catch(function(error){
console.error(error);
});
};
var build = function(source, target) {
readdir(source)
.then(function(list) {
return rmdir(target).then(function(){
return list;
});
})
.then(function(list) {
return mkdir(target).then(function(){
return list;
});
}).then(function(list) {
list.forEach(function(item, index) {
copy(path.join(source, item), path.join(target, item));
});
}).catch(function(error){
console.error(error);
})
};
build(moveFrom, moveTo);
简单的解决方案:你的整个问题是你没有限制你的parellization:
list.forEach(function(item, index) {
copy(path.join(source, item), path.join(target, item));
});
您同步调度异步操作。 这意味着他们会立即返回而无需您等待。 您需要使操作顺序或设置绑定到正在运行的操作。 这将列出一系列功能:
const copyOperations = list.map((item) => {
return copy(path.join(source, item), path.join(target, item));
});
然后让它们按顺序运行 :
const initialValue = Promise.resolve();
copyOperations.reduce((accumulatedPromise, nextFn) => {
return accumulatedPromise.then(nextFn);
}, initialValue);
现在,如果你想等待所有这些都完成,你需要返回一个promise,所以你的代码的副本部分将如下所示:
.then(function(list) {
const copyOperations = list.map((item) => {
return copy(path.join(source, item), path.join(target, item));
});
const allOperations = copyOperations.reduce((accumulatedPromise, nextFn) => {
return accumulatedPromise.then(nextFn);
}, Promise.resolve());
return allOperations;
})
这当然只是一次复制一个文件,如果您需要同时执行更多操作,则需要更高级的机制。 尝试这种承诺池机制 ,你可以设置一个阈值,如require('os').cpus().length;
使用ES6发生器的有界并行化示例
用这个替换上面then
函数的主体
const PromisePool = require('es6-promise-pool')
const maxProcesses = require('os').cpus().length;
const copyOperations = list.map((item) => {
return copy(path.join(source, item), path.join(target, item));
});
const promiseGenerator = function *(){
copyOperations.forEach( operation => yield operation );
}
var pool = new PromisePool(promiseGenerator(), maxProcesses)
return pool.start()
.then(function () {
console.log('Complete')
});
Oligofren的建议似乎没有帮助。 但是,删除23 MB的JS文件确实解决了这个问题。 所以看起来问题不是大量的文件(我怀疑),而是一个对于NodeJ来说太大的文件。 我想玩NodeJs的内存设置(例如node --stack-size
)可以解决这个问题。
无论如何,虽然我仍然需要一个解决方案来让一切工作而不删除23 MB文件,我想从现在要处理的文件中删除这一个文件。 它几乎只是我正在努力的概念验证。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.