[英]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.