標籤:close 方法 就是 blog div array 部分 query 冗餘
require.js+backbone 使用r.js 在本地與生產環境 一鍵壓縮的實現方式時間:2017-07-03 17:18:11 閱讀:210 評論:0 收藏:0 [點我收藏+]
標籤:fonts mil bin mat 檔案配置 檔案夾 odi 架構 return
requie.js 和backbone.js 這裡就不說了,能夠去看官方文檔,都非常具體!
可是使用require.js 預設帶的壓縮方式感覺不是非常方便,所以本文主要講 利用r.js壓縮,來實現本地不壓縮,生產環境壓縮
r.js 是執行在node上的,預設使用UglifyJS。UglifyJS真的非常好用,那為什麼說預設的方式 不是非常方便呢?
r.js 單獨壓縮一個檔案也非常好用的,但在實際項目中。總不能一個一個壓吧!因此r.js提供了一種多檔案的壓縮方式
,使用一個叫bulid.js 的設定檔來配置模組,這樣能夠壓縮多個模組。
可是。問題有幾個:
1.要維護一個設定檔,模組越多,越不好維護。當然也可寫個自己主動組建組態檔案的指令碼,個人感覺也不好用,由於第二個問題。
2.壓縮後,會產生整個目錄壓縮後的完整副本,這樣你就要提交兩個js的目錄到你的程式碼程式庫裡了。並且壓縮後的目錄裡存在代碼的冗餘。由於全部的代碼都會依據層層依賴關係被壓縮的一個入口檔案裡,載入是僅僅需載入入口檔案即可了,但其它的檔案也被壓縮了。被拷貝到了新的目錄內。
3.壓縮時每次都所有壓縮,效率非常低!
可能也能實現部分壓縮。只是我沒找到合適的方法。
4.本地使用未壓縮的,壓縮後提交,不能保證100%的壓縮正確(配置裡的依賴萬一出錯了),這樣須要提交到測試環境才幹發現。
問題說完了,有能解決的歡迎回複。以下說說我的實現方式。
window開發環境&&node環境&&apache須要開啟rewrite和eTag。
首先大概說下原理:
總共分兩步,1是合并;2是壓縮;
1.利用apche的.htaccess檔案將請求的js檔案(/js/dist/home.js) 重新導向到一個php指令碼裡(本地環境裡),並將/js/src/main/home.js作為參數傳入。在這個指令碼裡來推斷是否須要合并(這裡說的僅僅是合并,不是壓縮),假設須要。利用r.js在/js/dist檔案夾下合并成一個home.dev.js 。然後依據所依賴的檔案改動時間來產生eTag的token。設定eTag,並將內容輸出。
假設不須要合并(通過eTag來推斷)。則直接返回304,去讀之前的緩衝。.這樣本地載入的即為合并但未壓縮的js檔案,便於調試.
2.這個home.dev.js 並不須要提交的你的程式碼程式庫,僅僅保留在本地即可。這是還須要還有一個php指令碼。通過一個批處理來調用。
指令碼的作用是把home.dev.js再同樣的檔案夾壓縮出一個home.js,這裡也須要根居home.js是否存在和home.js 與home.dev.js 的檔案改動時間做比較,來推斷是否壓縮。壓縮好的home.js 即使要提交到程式碼程式庫裡的(從home.dev.js 到home.js 相當於是單個檔案壓縮。沒有依賴關係,這樣出錯的機率就非常小非常小了.非常好的攻克了上述提出的問題)。
以下來說下詳細的實現方式:
1.檔案夾結構是這種:
簡單解釋下
左邊是js的檔案夾結構圖。lib是放核心架構的,如jquery等;plugin是放外掛程式的;common是放入自己寫的公用模組。
src是應用的源碼。main是入口檔案。
dist是放置合并過和壓縮過的,檔案名稱和main裡的同樣。其它的就是backbone的檔案夾結構了,可能還有些不全面,這裡先不考慮。
右邊是壓縮指令碼的檔案夾。r.js 即require.js 提供的壓縮js指令碼,compile.bat 調用壓縮指令碼的批次檔,combine_js.php 合并代碼的PHP指令碼,conplile.php 是壓縮代碼的指令碼,notmodified.php 是推斷是否須要合并。原理是利用所依賴的檔案改動時間產生Etag的token,combine_css.php 是合并css的指令碼,講完js壓縮後再說。
2.瞭解了檔案夾的結構和基本原理,剩下的就是貼代碼了。
html的的引入:
<script data-main="/js/dist/home" src="/js/lib/require-2.1.14.min.js"></script>
程式的入口是js/dist/home,這個檔案是由js/scr/mian/home.js經過合并和壓縮的到的,即在生產環境使用的。
在本地環境的話,就要靠.htaccess這個檔案了
.htaccess
# 將js/dist/home.js 重新導向到combine_js.php這個指令碼裡,並將dist替換為main,# 即js/src/main/home.js 這個真正的入口檔案路徑作為參數傳過去rewriteCond %{REQUEST_FILENAME} ^(.*)dist(.*\.js)$rewriteCond %1src/main%2 -fRewriteRule ^(.*)dist(.*\.js)$ build/combine_js.php?
f=$1src/main$2 [B,L] #css的合并。和上面的一樣。僅僅只是相應處理的指令碼不同 rewriteCond %{REQUEST_FILENAME} ^(.*)dist(.*\.css)$ rewriteCond %1config%2 -f RewriteRule ^(.*)dist(.*\.css)$ build/combine_css.php?f=$1config$2 [L] #假設xxx.(js||css)有相應的xxx.dev.(js||css) 則將重新導向到.dev.js或.dev.css #這個是給已經合并和已經壓縮好的js或css 來用的 #比方lib/jquery.js,你在本地能夠下載相應的debug版,改動檔案名稱為jquery.dev.js ,這樣本地也能夠調試jquery了 rewriteCond %{REQUEST_FILENAME} ^(.*)\.(js|css)$ rewriteCond %1.dev.%2 -f RewriteRule ^(.*)\.(js|css)$ $1.dev.$2 [L]
以下就是關鍵combine_js.php了
<?
php include ‘notmodified.php‘; define(‘BASE‘, dirname(__FILE__)); $file_main = BASE.‘/../‘.$_GET[‘f‘]; $file_config = BASE.‘/../js/config.js‘; $comment_reg_exp = ‘/(\/\*([\s\S]*?
)\*\/|([^:]|^)\/\/(.*)$)/m‘; $js_require_reg_exp = ‘/[^.]\s*require\s*\(\s*[\"\‘]([^\‘\"\s]+)[\"\‘]\s*\)/‘; $exclude_reg_exp = ‘/\s*exclude\s*:\s*(\[.*?\])/s‘; $alias_reg_exp = ‘/\s*paths\s*:\s*(\{.*?
\})/s‘; $data_dep_file = array($file_main, $file_config); //所依賴檔案的數組,包含自身和config檔案 $data_ex_dep_file = array(); //不需壓縮的依賴檔案數組 $data_alias_file = ‘‘; //有別名的的檔案字串 if(file_exists($file_config)){ $text_config = file_get_contents($file_config); $text_config = preg_replace($comment_reg_exp, ‘‘, $text_config); //去掉凝視 preg_match_all($exclude_reg_exp, $text_config, $matches); if(isset($matches[1][0])){ //取出不需壓縮的依賴檔案配置 $data_ex_dep_file = json_decode(str_replace("\n",‘‘,str_replace(" ", "", str_replace("‘", ‘"‘, $matches[1][0]))), true); } preg_match_all($alias_reg_exp, $text_config, $matches); if(isset($matches[1][0])){ //取出有別名的真本文件配置 $data_alias_file = str_replace("\n",‘‘,str_replace(" ", "", str_replace("‘", ‘"‘, $matches[1][0]))); } } function get_true_path($alias){ //取出有別名的的真本文件名稱 global $data_alias_file; $alias_escape = str_replace(‘.‘,‘\.‘, str_replace(‘/‘,‘\/‘,$alias)); $regExp =‘/‘.$alias_escape.‘:"(.*?)"/‘; preg_match_all($regExp, $data_alias_file, $matches); if(isset($matches[1][0])){ return $matches[1][0]; } return $alias; } function get_dep_files($file_name){ global $js_require_reg_exp, $data_dep_file, $data_ex_dep_file, $comment_reg_exp; if(file_exists($file_name)){ $text_main = file_get_contents($file_name); $text_main = preg_replace($comment_reg_exp, ‘‘, $text_main); preg_match_all($js_require_reg_exp, $text_main, $matches); if(isset($matches[1]) && is_array($matches[1]) && count($matches[1]) > 0){ //取出依賴檔案 foreach($matches[1] as $v){ $v = trim($v); $v_true = get_true_path($v); $v_path = BASE.‘/../js/‘.$v_true.(strrchr($v, ‘.‘) == ‘.js‘ ? ‘‘ : ‘.js‘); //所依賴的檔案的完整路徑 if(!in_array($v, $data_ex_dep_file) && !in_array($v_path, $data_dep_file)){ $data_dep_file[] = $v_path; get_dep_files($v_path); //遞迴取出依賴檔案 } } } } } get_dep_files($file_main); $ext = strrchr($file_main, ‘.‘); $file_name = basename($file_main, $ext); $file_source = ‘src/main/‘.$file_name; $file_output = BASE.‘/../js/dist/‘.$file_name.‘.dev.js‘; if(file_exists($file_output) && notmodified($data_dep_file)){ //依據所依賴檔案改動時間產生eTag,來推斷是否須要壓縮 exit; } $output = array(); $error = array(); exec(‘node r.js -o mainConfigFile=‘.BASE.‘/../js/config.js baseUrl=‘.BASE.‘/../js/ name=‘.$file_source.‘ out=‘.$file_output.‘ optimize=none‘, $output); //使用node 壓縮,產生dev檔案,儲存輸出結果。 foreach($output as $v){ if(preg_match(‘/Error/‘, $v)){ $error[]=json_encode($v); //儲存錯誤資訊 } } if(empty($error)){ header(‘Content-Type: application/javascript‘); echo file_get_contents($file_output); exit; } foreach($error as $e){ echo "console.error($e);"; //輸出錯誤資訊 } exit;
以下是notmodified.php
function notmodified($files = array()){$s = ‘‘;if(is_string($files)){$files = array($files); }if($files){foreach($files as $f){$s .= filemtime($f); //拼接所依賴檔案改動時間,用來產生eTag的token }}$etag = sprintf(‘%08x‘, crc32($s));header("ETag: \"$etag\"");//輸出eTag if(isset($_SERVER[‘HTTP_IF_NONE_MATCH‘]) && strpos($_SERVER[‘HTTP_IF_NONE_MATCH‘], $etag)){header(‘HTTP/1.1 304 Not Modified‘);// 假設沒有改動過,則返回304。去讀緩衝return true;}return false;}
以下看下入口檔案main/spc.js
require(["config.js"], function(config){<span style="font-family: Arial, Helvetica, sans-serif;">// 載入公用的設定檔後,再開始定義模組。
</span> require([‘src/main/home‘]); }); define(function(require){ "use strict"; var home = require("src/controllers/home"); new home({name:‘homeController‘}).init(); });
以下看公用設定檔config.js到此已經完畢大部分工作了,還剩下最後上生產的壓縮工作
/* * 預設的config. */requirejs.config({ baseUrl: typeof(javascriptServer) === ‘undefined‘ ? ‘‘ : javascriptServer + ‘js/‘, paths: { jquery: ‘lib/jquery-1.11.1.min‘, underscore: ‘lib/underscore-1.6.min‘, backbone: ‘lib/backbone-1.1.2.min‘, cookie: ‘plugin/jquery.cookie‘ }, useStrict: true, exclude: [‘jquery‘, ‘underscore‘, ‘backbone‘, ‘cookie‘], //不須要合并的檔案,使用r.js 進行合并或壓縮時。讀此設定檔:node r.js -o mainConfigFile=‘.BASE.‘/../js/config.js ...... shim: { /*眼下backbone和underscore都已支援amd!假設是不支援的版本號碼,則須要以下的配置。 backbone: { deps: [‘jquery‘, ‘underscore‘], exports: ‘Backbone‘ }, underscore: { exports: ‘_‘ } */ }});
先看批處理 compile.bat,非常easy
@echo offphp compile.php "../js/dist"php compile.php "../css/dist"pause
再看compile.php,也非常easy
define(‘BASE‘, dirname(__FILE__));if($argc == 1){compile(BASE);}else{compile(BASE.‘/‘.$argv[1]);}function compile($dir){$h = opendir($dir);while(($f = readdir($h)) !== false){if($f == ‘.‘ || $f == ‘..‘){continue; }$f = $dir.‘/‘.$f;if(is_dir($f)){compile($f);}else if(substr($f, -7) == ‘.dev.js‘){ //js $ext = strrchr($f, ‘.‘);$out = substr($f, 0, -7).‘.js‘;if(!file_exists($out) || filemtime($f) > filemtime($out)){ system(‘node r.js -o mainConfigFile=‘.BASE.‘/../js/config.js baseUrl=‘.BASE.‘/../js/ name=app/product/‘.basename($f, $ext).‘ out=‘.$out); system(‘jsl -process ../js/app/product/‘.basename($f, $ext).‘.js‘); //jslint 檢查文法 }}else if(substr($f, -8) == ‘.dev.css‘){ //css$out = substr($f, 0, -8).‘.css‘;if(!file_exists($out) || filemtime($f) > filemtime($out)){system(‘node r.js -o cssIn=‘.$f.‘ out=‘.$out.‘ optimizeCss=standard‘); }}}closedir($h);}
最後要說下。事實上這個套路有兩個能夠容忍小bug。平時須要注意下。
1.假設(往前)改動了本地時間後。再進行壓縮,可能會導致壓縮失敗。
解決的方法:把時間調正確後。刪除produt/xxx.js ,又一次從版本號碼庫裡更新,再進行壓縮。
事實上注意下壓縮時不要改動本地時間就能夠了。
2.假設ctrl+F5 或清空緩衝的話,即使檔案沒有改動,也會又一次合并。由於eTag 被清除了。
解決的方法:這個問題事實上不用管,假設是沒改動的被又一次壓縮了,不提交就能夠了。提交了也沒太大關係!
眼下僅僅能先忍著,假設有好的方法,歡迎指導。
等有時間。會把整套的原始碼整理在github上的。
require.js+backbone 使用r.js 在本地與生產環境 一鍵壓縮的實現方式
標籤:fonts mil bin mat 檔案配置 檔案夾 odi 架構 return
原文地址:http://www.cnblogs.com/lytwajue/p/7111887.html
require.js+backbone 使用r.js 在本地與生產環境 一鍵壓縮的實現方式