require.js+backbone 使用r.js 在本地與生產環境 一鍵壓縮的實現方式

require.js+backbone 使用r.js 在本地與生產環境 一鍵壓縮的實現方式

時間: 2017-07-03 17:18:11      閱讀:210      評論:0      收藏:0      [點我收藏+]

標籤:fonts   mil   bin   mat   文件配置   文件夾   odi   框架   return   javascript

requie.js 和backbone.js 這裏就不說了,可以去看官方文檔,都很是具體!php

但是使用require.js 默認帶的壓縮方式感受不是很是方便,因此本文主要講 利用r.js壓縮,來實現本地不壓縮,生產環境壓縮
css

r.js 是執行在node上的,默認使用UglifyJS。UglifyJS真的很是好用,那爲何說默認的方式 不是很是方便呢?html

r.js 單獨壓縮一個文件也很是好用的,但在實際項目中。總不能一個一個壓吧!所以r.js提供了一種多文件的壓縮方式java

,使用一個叫bulid.js 的配置文件來配置模塊,這樣可以壓縮多個模塊。node


但是。問題有幾個:jquery

1.要維護一個配置文件,模塊越多,越很差維護。固然也可寫個本身主動生成配置文件的腳本,我的感受也很差用,因爲第二個問題。git

2.壓縮後,會生成整個目錄壓縮後的完整副本,這樣你就要提交兩個js的目錄到你的代碼庫裏了。而且壓縮後的目錄裏存在代碼的冗餘。因爲所有的代碼都會依據層層依賴關係被壓縮的一個入口文件裏,載入是僅僅需載入入口文件便可了,但其它的文件也被壓縮了。被拷貝到了新的目錄內。github

3.壓縮時每次都全部壓縮,效率很是低!apache

可能也能實現部分壓縮。只是我沒找到合適的方法。

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 -f
RewriteRule ^(.*)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 off
php 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

相關文章
相關標籤/搜索