背景
seajs是一款優秀的模塊開發插件,但是當我們使用它來進行模塊化開發的時候,由於它的每個模塊的加載都會進行一次http請求,那么當模塊數量倍增的時候,會拖慢頁面的加載速度。
通常我們為了能加快頁面的加載速度,都會對js進行壓縮並把關聯的模塊打包為一個獨立的js文件,這樣可以大大減少js的文件大小並且減少http請求的次數,這樣可以提升到頁面的加載速度。
我們可以是用spm來對js文件進行打包、壓縮(使用spm-build),但是當我們編寫兼容多種環境的js的時候(既可以直接用script引用又可以使用seajs來引用),那么我們就沒辦法使用前面的方法來對js進行打包和壓縮了。
其次就是使用spm-build還要先了解package.json文件配置,還是有少許麻煩的,為了減輕上手的難度和簡化功能,這里我們將介紹另類的使用nodejs來對js腳本進行壓縮。
實現
http://tool.css-js.com/站點提供了多種腳本壓縮,通過使用YUI壓縮,我們發現它實際上是使用http的POST來實現代碼壓縮的,如圖:
接着我們使用ajax來進行測試,代碼如下:
$.post('http://tool.css-js.com/!java/?type=js&munge=true&preserveAllSemiColons=false&disableOptimizations=false', { code: 'var x = (function() { var foo = 1, bar = 2; return (foo + bar) }())' }, function(res){ console.log(res); });
結果跟截圖的效果是相同的,接下來使用nodejs來編寫一個用於讀取js文件並發起http然后把壓縮的拼接到一個獨立的js文件中去。
使用原生的nodejs的http來發起post的代碼如下:
var m_http = require('http'); var m_qs = require('querystring'); var options = { host: 'tool.css-js.com', port: 80, method: 'POST', path: '/!java/?type=js&munge=true&preserveAllSemiColons=false&disableOptimizations=false', headers: { 'Content-Type':'application/x-www-form-urlencoded' } }; exports.compress = function (str, callback){ var req = m_http.request(options, function(res) { res.setEncoding('utf-8'); var compressed = ''; res.on('data', function(chunk){ compressed += chunk; }); res.on('end', function(){ callback(compressed) }); }); req.write(m_qs.stringify({ code: str })); req.end(); };
有了壓縮的方法,接下來我們需要提供一個可以循環讀取文件夾內的所有js文件並壓縮合並到獨立的js文件的函數,代碼大致如下:
var m_fs = require('fs'); var m_path = require('path'); //其他代碼略 exports.combineDir = function (dir, output){ var self = this; m_fs.readdirSync(dir).forEach(function (filename) { if (filename.indexOf('.') === -1) { self.compressDir(m_path.join(dir, filename)); return; } var path = m_path.join(dir, filename); m_fs.readFile(path, 'utf8', function (err, content){ if (err) { return; } self.compress(content, function (compressed){ var id = filename.substr(0, filename.indexOf('.')); compressed = compressed.replace('this.define(', ['this.define(\'', id, '\','].join('')); m_fs.appendFileSync(output, compressed); }); }); }); };
以上代碼之所以要替換'this.define'字符串是因為我們編寫的兼容腳本中,會使用閉包將腳本包含在內部,並在內部對this.define進行判斷,當this.define存在的情況下,才會將編碼的模塊賦值給module.exports來完成模塊,示例代碼如下:
//a.js (function(){ var a = (function() { var foo = 1, bar = 2; return (foo + bar) }()) if (this.define) { this.define(function (require, exports, module) { 'require:nomunge,exports:nomunge,module:nomunge'; module.exports = a; }); } }).call(this); //b.js (function(){ var b = (function() { var foo = 1, bar = 2; return (foo + bar) }()) if (this.define) { this.define(function (require, exports, module) { 'require:nomunge,exports:nomunge,module:nomunge'; module.exports = a; }); } }).call(this);
使用編碼好的函數來進行合並后,結果如下:
(function(){var b=(function(){var c=1,a=2;return(c+a)}());if(this.define){this.define('a',function(require,exports,module){module.exports=b})}}).call(this); (function(){var c=(function(){var d=1,b=2;return(d+b)}());if(this.define){this.define('b',function(require,exports,module){module.exports=a})}}).call(this);
擴展
對於創建請求,我們可以使用nodegrass來完成我們的功能,它內部封裝了http和https的get、post的函數,簡化了調用,大家可以使用看看。
其次對於循環文件夾內的js文件,當存在多層嵌套的時候,由於沒有將父級文件夾添加到seajs定義的id內,因此調用合並后的js文件的時候,會出現錯誤,這里就不詳細解說如何解決了,這個問題就留給大家去解決啦。
對於以上代碼由於使用了異步方法,造成了多層文件嵌套,如果大家覺得嵌套太多導致代碼難看、難以閱讀,可以使用wind.js或者eventproxy.js或者其他的異步js來解決多層嵌套的問題。
結尾
這次的文章就到這里了,內容中有任何問題不吝賜教,謝謝各位!
另外感謝http://tool.css-js.com/站點提供的功能.