项目中seajs模块化的使用开发的时候来的确省事很多,但seajs的机制导致的请求过多不适合线上直接部署,玉伯那儿有一个spm部署的工具,但后来得知当前这个spm项目可用性还不是很高。所以准备自行搞一套发布的机制。
seajs有两种define的形式:
define(function(require, exports, module){});
和
define("friend-middleware.js",["lib/jquery.js","lib/underscore.js"],function(){});
下面一种是上面一种的简略写法。多了模块定义,和依赖模块数组两个参数,这两个参数是可选的,但其实机制是有些区别的,第一种在内部闭包内require模块的时候直接向指定路径的去getSrcipt,这一种方式得保证一个文件一个模块,而下面一种书写方式可以多个模块放在同一个文件中,在模块定义后,另一个模块同一个文件调用改模块只要在依赖列表中增加需要模块的定义就可以了。这样就可以使用这个特性对模块进行依赖压缩了,另外要压缩的话要对css进行查找并另外压缩一个css文件。
为了方便发布,用了nodejs进行编写,趁机学习下node;js压缩用了node版本的uglifyJs,这个压缩工具跟google压缩有的一拼,jQuery的压缩也是用了它。css的压缩还是用了yc的css压缩工具node版。
(function(){ var fs = require('fs'), jsp = require('./lib/uglify-js').parser, pro = require('./lib/uglify-js').uglify, cssmin = require('./lib/node-css-compressor').cssmin, _ = require('./lib/underscore.js'); var compressCode = '', compressCss = '', dependCssList = [], jsPath = 'D:/Workspace/BB/deploy/htdocs/js/' //模块下css依赖绝对path补齐 pubilcJsFile = [ //公共文件列表 ], jsFileArray = [ //待压缩文件列表 ]; var log = function(arg){ console.log(arg); }; var replaceForDepend = function(filename, code){ var dependenceArray = []; var modulename = filename.replace(/^.*\/js\//i,''); code.replace(/require\('(.+)'\)/ig, function(){ var match = arguments[1]; if(/\.css$/i.test(match)){ dependCssList.push(jsPath + match); return; } if(!(/\.js$/i.test(match))){ match += '.js'; } dependenceArray.push('"' + match + '"'); }); code = code.replace(/define\(function\(/, 'define(\'' + modulename + '\', [' + dependenceArray.join(',') + '], function('); code = code.replace("/require\('(.+\.css)'\)/i", '', code); return code; }; var compress = function(str ,type){ if (type === 'javascript') { var orig_code = str; var ast = jsp.parse(orig_code); ast = pro.ast_lift_variables(ast); ast = pro.ast_mangle(ast); ast = pro.ast_squeeze(ast); var finalCode = pro.gen_code(ast); return finalCode; }else if(type === 'css'){ return cssmin(str); }else{ return str; } }; var writeFinalCode = function(file, str){ fs.writeFile(file, str, 'utf8', function (err) { if (err) throw err; console.log('It\'s saved to ' + file + '!'); }); }; var _init = function(argv0,argv1){ var minJsFile = argv0 || 'babylon.js'; var minCssFile = argv1 || 'babylon.css'; var i, pdata, data, rdata; log('Javascript analyze start!'); for(i = 0; i < pubilcJsFile.length ; ++i){ pdata = fs.readFileSync(pubilcJsFile[i], 'utf8'); compressCode += pdata; log(pubilcJsFile[i] + ' complete!'); } for(i = 0; i < jsFileArray.length; ++i){ data = fs.readFileSync(jsFileArray[i], 'utf8'); rdata = replaceForDepend(jsFileArray[i], data); compressCode += rdata; log(jsFileArray[i] + ' complete!') } log('Javascript compress start!'); compressCode = compress(compressCode, 'javascript'); log('CSS analyze start!'); dependCssList = _.uniq(dependCssList) for (i = 0; i < dependCssList.length; ++i) { cdata = fs.readFileSync(dependCssList[i], 'utf8'); compressCss += cdata; log(dependCssList[i] + ' complete!'); } log('Css compress start!'); compressCss = compress(compressCss, 'css'); writeFinalCode(minJsFile, compressCode); writeFinalCode(minCssFile, compressCss); } return _init; })()(process.argv[2],process.argv[3]);
核心就是code = code.replace(/define\(function\(/, 'define(\'' + modulename + '\', [' + dependenceArray.join(',') + '], function(');语句一个替换。另外将模块内引用的css文件提取出来单独压缩到一个css文件里,并将模块代码中引入css的语句去掉。
但如果文件比较多,可能jsList要放很多文件,会比较麻烦。
下面是第二个版本,是自动搜索js文件的压缩。
var configure = { targetPath : 'D:/Workspace/BB/deploy/htdocs/js', publicJs : ['sea.js','jquery.js','underscore.js','underscore.string.js'], exceptJs : [ //这些文件或文件夹不会被载入压缩 //dir-path 'seajs', //js-file 'core.js', ], additionalJs : [ //附加js文件需完整路径 ] }; (function(config){ var fs = require('fs'), path = require('path'), jsp = require('./lib/uglify-js').parser, pro = require('./lib/uglify-js').uglify, cssmin = require('./lib/node-css-compressor').cssmin, _ = require('./lib/underscore.js'); var compressCode = '', compressCss = '', dependCssList = [], jsPath = 'D:/Workspace/BB/deploy/htdocs/js/', pubilcJsFile = [], jsFileArray = []; var log = function(arg){ console.log(arg); }; var replaceForDepend = function(filename, code){ var dependenceArray = []; var modulename = filename.replace(/^.*\/js\//i,''); code.replace(/require\('(.+)'\)/ig, function(){ var match = arguments[1]; if(/\.css$/i.test(match)){ dependCssList.push(jsPath + match); return; } if(!(/\.js$/i.test(match))){ match += '.js'; } dependenceArray.push('"' + match + '"'); }); code = code.replace(/define\(function\(/, 'define(\'' + modulename + '\', [' + dependenceArray.join(',') + '], function('); code = code.replace("/require\('(.+\.css)'\)/i", '', code); return code; }; var compress = function(str ,type){ if (type === 'javascript') { var orig_code = str; var ast = jsp.parse(orig_code , false); ast = pro.ast_lift_variables(ast); ast = pro.ast_mangle(ast); ast = pro.ast_squeeze(ast); var finalCode = pro.gen_code(ast, {quote_keys : true, ascii_only : true}); return finalCode; }else if(type === 'css'){ return cssmin(str); }else{ return str; } }; var analyzePath = function(pathname){ var pathList = fs.readdirSync(pathname); for (var index in pathList){ var filename = pathList[index]; if ( !_.include(config.exceptJs,filename) && !(/\.svn/.test(filename)) && !(/\.css/.test(filename)) && !(/\.txt/.test(filename)) && !(/\.html/.test(filename)) ){ var pt = path.join(pathname , filename); if(((path.existsSync(pt)) && (fs.lstatSync(pt).isDirectory()))){ analyzePath(pt); } if(((path.existsSync(pt)) && (fs.lstatSync(pt).isFile()))){ if(_.include(config.publicJs,filename)){ pubilcJsFile.push(pt); }else{ jsFileArray.push(pt); } } } } }; var writeFinalCode = function(file, str){ fs.writeFile(file, str, 'utf8', function (err) { if (err) throw err; console.log('It\'s saved to ' + file +'!'); }); }; var _init = function(argv0,argv1){ var minJsFile = argv0 || 'babylon.js'; var minCssFile = argv1 || 'babylon.css'; var i, pdata, data, rdata; analyzePath(config.targetPath); log('Javascript compress start!'); for(i = 0; i < pubilcJsFile.length ; ++i){ pdata = fs.readFileSync(pubilcJsFile[i], 'utf8'); compressCode += pdata + '\n\n\n'; log(pubilcJsFile[i] + ' complete!'); } jsFileArray = _.union(config.additionalJs, jsFileArray); for(i = 0; i < jsFileArray.length; ++i){ data = fs.readFileSync(jsFileArray[i], 'utf8'); rdata = replaceForDepend(jsFileArray[i], data); compressCode += rdata; log(jsFileArray[i] + ' complete!'); } compressCode = compress(compressCode, 'javascript'); log('CSS compress start!'); dependCssList = _.uniq(dependCssList) for (i = 0; i < dependCssList.length; ++i) { cdata = fs.readFileSync(dependCssList[i], 'utf8'); compressCss += cdata + '\n'; log(dependCssList[i] + ' complete!'); } compressCss = compress(compressCss, 'css'); writeFinalCode(minJsFile, compressCode); writeFinalCode(minCssFile, compressCss); } return _init; })(configure)(process.argv[2],process.argv[3]);
但上面这个版本对于uglify有时候会报错,测试了下还是第一个版本比较稳定点。用法是直接在cmd中输入:
node combo [js文件] [css文件]
不足的还请大伙拍砖~~:)