Requie.js and Backbone.js here will not say, can go to see the official documents, are very detailed!
But using the require.js default with the compression mode is not very convenient, so this paper mainly use r.js compression, to achieve local non-compression, production environment compression
R.js is run on node, and Uglifyjs is used by default. Uglifyjs really good , then why is the default way is not very convenient?
R.js individual compression of a file is also very useful, but in the actual project, always can not a pressure bar! So R.js provides a way to compress multiple files
, you can use a configuration file called Bulid.js to configure the module, which compresses multiple modules.
However, there are several issues:
1. To maintain a profile, the more modules, the more difficult to maintain. Of course, you can also write a script to automatically generate the configuration file, the personal feeling is not good, because the second question.
2. After compression, the entire folder will be compressed after the full copy, so you will have to submit two JS folder into your code base. and the compressed folder in the existence of code redundancy, because all the code will be compressed according to layer dependencies in a portal file, loading is simply loading the portal file, but the other files are also compressed, copied into the new folder.
3. Compression is all compressed every time, the efficiency is very low! Partial compression may also be possible, but I have not found a suitable method.
4. The local use of uncompressed, compressed after submission, there is no guarantee that 100% compression is correct (in case the dependency in the configuration error), so it needs to be submitted to the test environment to discover.
The problem is over, there is a welcome reply that can be solved. Let's talk about my implementation. Window development Environment &&node Environment &&apache need to open rewrite and ETag.
The first is about the principle:
A total of two steps, 1 is the merger; 2 is compression;
1. Use the Apche. htaccess file to redirect the requested portal file (main.js) to a PHP script (local environment). In this script to determine whether it is necessary to merge (this is the merge, not the compression), if necessary, the use of r.js in another directory into a main.dev.js, and then based on the dependent file modification time to generate the ETag token, set the ETag, and output the content. If you do not need to merge (judged by the ETag), return 304 directly to read the previous cache: This is a merged but uncompressed JS file that is loaded locally for easy debugging.
2. This main.dev.js does not need to be submitted to your code base, just keep it locally on the line. This is another PHP script that needs to be called through a batch. The role of the script is to main.dev.js the same directory compression out of a main.js, here also need to root main.js whether there and Main.js and Main.dev.js file modification time to do a comparison, to determine whether compression. Compressed Main.js Even if it is submitted to the code base (from Main.dev.js to main.js equivalent to a single file compression, there is no dependency, so the probability of error is very small.). A good solution to the problem raised above).
The following is the implementation of the specific way:
1. The directory structure is this:
The simple explanation
On the left is the directory structure of JS, Lib is the core framework, such as jquery, etc., common is put into their own write common modules, such as pop mask layer. The app is the code that is applied, main is the portal file, product is placed merged and compressed, and the file name is the same as in main. The other is the backbone directory structure, may also be some not comprehensive, here first not consider.
To the right is the directory of compressed scripts, r.js that Require.js provides compression JS script, Compile.bat call the Compressed script batch file, combine.php Merge code php script, conplile.php is the compression code of the script, Notmodified.php is to determine whether the need to merge, the principle is to use the dependent file modification time to generate the ETag token,combine_css.php is a combination of CSS script, after the JS compression.
2. Understand the structure and basic principles of the directory, and the rest is to paste the code.
Introduction of HTML:
<script data-main= "<?= $jsServer? >js/app/product/spc" src= "<?= $jsServer? >js/lib/ require-2.1.14.min.js?v=<?= $ver?> "></script>
The entrance to the program is JS/APP/PRODUCT/SPC, which is merged and compressed by Js/app/mian/spc.js, which is used in the production environment.
In the local environment, we have to rely on. htaccess this file.
. htaccess
# REDIRECT Js/app/product/spc.js to combine.php This script, and replace product with main,#, which is js/app/mian/spc.js the true portal file path as a parameter pass Rewritecond %{request_filename} ^ (. *) product (. *\.js) $rewriteCond%1main%2-frewriterule ^ (. *) product (. *\.js) $ autocompile/ combine.php?f=$1main$2 [b,l] #css的合并, as above, except that the script that corresponds to processing is different Rewritecond%{request_filename} ^ (. *) product (. *\.css) $rewriteCond%1main%2-frewriterule ^ (. *) product (. *\.css) $ autocompile/combine_css.php?f=$1main$2 [L] #如果xxx. (js| | CSS) has a corresponding xxx.dev. (js| | CSS) is redirected to the. Dev.js or. dev.css# This is for the merged and already compressed JS or CSS to use the # such as Lib/jquery.js, you can download the corresponding debug version locally, modify the file name is Jquery.dev.js, In this way the local can also debug jquery rewritecond%{request_filename} ^ (. *) \. (js|css) $rewriteCond%1.dev.%2-frewriterule ^ (. *) \. (JS|CSS) $ $1.dev.$2 [L]
Here's the key combine.php.
Include ' notmodified.php ';d efine (' BASE ', DirName (__file__)); $file _main = base. ' /.. /'. $_get[' F ']; $file _config = BASE. ' /.. /js/config.js '; Require.js Public Profile $comment_reg_exp = '/(\/\* ([\s\s]*?) \*\/| ([^:]|^) \/\/(. *) $)/M '; Matches the comment's regular $js_require_reg_exp = '/[^. \s*require\s*\ (\s*[\ "\"] ([^\ ' \ "\s]+) [\" \ ']\s*\]/'; Extract dependent file of regular $exclude_reg_exp = '/\s*exclude\s*:\s* (\[.*?\])/s '; Extract dependent files but do not need to merge compressed regular $alias_reg_exp = '/\s*paths\s*:\s* (\{.*?\})/s '; Extract the regular $data_dep_file with the alias file = Array ($file _main, $file _config); An array of dependent files, including itself and the config file $data_ex_dep_file = array (); An array of dependent files that do not need to be compressed $data_alias_file = '; An aliased file string if (file_exists ($file _config)) {$text _config = file_get_contents ($file _config); $text _config = preg_replace ($comment _reg_exp, ", $text _config); Remove the comment preg_match_all ($exclude _reg_exp, $text _config, $matches); if (Isset ($matches [1][0])) {//Take out a dependent file configuration that does not need to be compressed $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])) {//Remove the True file configuration with aliases $data _alias_file = Str_replace ("\ n", "", Str_replace ("", "", Str_replace ( "'", ' "', $matches [1][0])); }}function Get_true_path ($alias) {//Remove the real filenames with aliases 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) {//Remove Dependent file foreach ($matches [1] as $v) {$v = Trim ( $V); $v _true = Get_true_path ($v); $v _path = BASE. ' /.. /js/'. $v _true. (STRRCHR ($v, '. ') = = '. js '? ': '. js '); The full path of the dependent file 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); Recursive fetch dependent File}}}}}get_dep_files ($file _main); $ext = STRRCHR ($file _main, '. '); $file _name = basename ($file _main, $ext); $file _source = ' app/main/'. $file _name; $file _output = BASE. ' /.. /js/app/product/'. $file _name. Dev.js '; if (file_exists ($file _output) && notmodified ($data _dep_file)) {//Determines if compression is required and, if not modified, returns 304 to read cache exit;} EXEC (' node R.js-o mainconfigfile= '. BASE. ' /.. /js/config.js baseurl= '. BASE. ' /.. /js/name= '. $file _source. ' out= '. $file _output. ' Optimize=none '); Using node compression, generate the dev file, OptimiZe=none is a merge-only meaning header (' content-type:application/javascript '); Echo file_get_contents ($file _output); exit;
Here's notmodified.php.
function notmodified ($files = Array ()) {$s = ' ', if (is_string ($files)) {$files = array ($files); } if ($files) {foreach ($files as $f) {$s. = Filemtime ($f);//splicing depends on the file modification time used to generate the ETag token }} $etag = sprintf ('%08x ', CRC32 ($s)); Header ("ETag: \" $etag \ "");//Output ETag if (isset ($_server[' Http_if_none_match ')) && strpos ($_server[' http_if_ None_match '], $etag)) {header (' http/1.1 304 not Modified ');//If not modified, return 304 to read cache return true;} return false;}
Below is a look at the entry file Main/spc.js
Require ([javascriptserver+ "js/config.js"], function () {//Loads the public configuration file, and then begins defining the module. Javascriptserver is a global variable, place JS domain name, if it is the domain, then do not write require ([' app/main/spc ');}); Define (function (require) { "use strict"; var app = require (' App/views/app '); new app ();});
Here's a look at the common configuration file Config.js This has done most of the work, and the final production of the compression work
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 ' }, usestrict:true, exclude: [' jquery ', ' Underscore ', ' backbone '],//do not need to merge files, use R.js to merge or compress, read this configuration file: node R.js-o mainconfigfile= '. BASE. ' /.. /js/config.js ... Shim: {/* currently backbone and underscore are supported amd! If the version is not supported, the following configuration is required. backbone: { deps: [' jquery ', ' underscore '], exports: ' Backbone ' }, underscore: { Exports: ' _ ' } */ }});
It's easy to see the batch Compile.bat first.
@echo offphp compile.php ". /js/app/product "PHP compile.php". /css/product "Pause
It's easy to see compile.php again.
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 ') {//press 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); }}else if (substr ($f, -8) = = '. Dev.css ') {//Pressure 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);}
Finally, in fact, this routine has two can tolerate small bugs, usually need to pay attention to.
1. If the local time is modified (forward), then compression may cause compression to fail.
Workaround: After the time is correct, delete the Produt/xxx.js, re-update from the repository, and then compress. In fact, note that the compression of the local time should not be modified.
2. If ctrl+f5 or empty the cache, even if the file is not modified, it will be merged again, because the ETag is cleared!
Solution: This problem in fact no tube, if it is not modified by the re-compression, do not submit on it, submitted also not much relationship!
At present can only endure, if there is a good way, welcome guidance.