Require.js+backbone implementation of one-click compression using R.js in local and production environments

Source: Internet
Author: User
Tags local time php script

Requie.js and Backbone.js here will not say, can go to see the official documents, are very specific!

However, the use of require.js default with the compression mode is not very convenient, so this article is mainly about the use of r.js compression, to achieve local non-compression, production environment compression

R.js is executed on node, using UGLIFYJS by default. Uglifyjs really very useful, then why is the default way is not very convenient?

R.js compressing a file individually is also very useful, but in the actual project. Can not be a one-pressure bar! So R.js provides a way to compress multiple files

, a configuration file called Bulid.js is used to configure the module so that multiple modules can be compressed.


But. There are several issues:

1. To maintain a profile, the more modules, the more difficult to maintain. Of course, can also write a self-generated configuration file script, personal feeling is not good, because of the second problem.

2. After compression, a full copy of the entire directory compression will be generated, so you will have to submit two JS directories to your code base. And the code redundancy exists in the compressed directory. Since all of the code will be loaded into a portal file that is compressed based on the layer dependencies, loading is just a matter of loading the portal file, but the other files are also compressed. is copied to the new directory.

3. Compression every time all compression, the efficiency is very low!

Partial compression can also be achieved. I just didn't find the right way.

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 talent discovery.

The problem is over, there is a welcome reply that can be solved. Here's how I do it.

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 JS file (/js/dist/home.js) to a PHP script (local environment) and pass the/js/src/main/home.js as a parameter. In this script, it is necessary to infer whether there is a need to merge (this is simply merging, not compressing). Use R.js to merge into a home.dev.js under the/js/dist folder. The token of the ETag is then generated based on the time it depends on the file change. Sets the etag and outputs the content.

Assume that there is no need to merge (inferred from the ETag). Then directly return 304, to read the previous cache: This is a merged but uncompressed JS file that is loaded locally for easy debugging.


2. This home.dev.js does not need to be submitted by your code base, just keep it locally. There is also a PHP script that needs to be added. Called by a batch.

The role of the script is to home.dev.js again the same folder to compress a home.js, here also need to root home.js whether the existence and home. js and Homedev.js file change time to do a comparison, To infer whether or not to compress. Compressed homeJS even to be submitted to the code base (from homedev.js to home. JS is the equivalent of a single file compression. There is no dependency, so the probability of error is very small and very small. Very good to overcome the above-mentioned questions).


Below is the detailed implementation method:

1. The folder structure is this:


The simple explanation

On the left is the JS folder structure diagram. LIB is the core framework, such as jquery, plugin is put into the plug-in, common is put into their own written public module.

SRC is the source of the application. Main is the entry file.

The dist is placed merged and compressed, and the file name is the same as in main. The other is the backbone folder structure, there may be some not comprehensive, here first not considered.

The folder to the right is the compressed script. R.js is the require.js provided by the compression JS script, Compile.bat call the Compressed script batch file, combine_js.php Merge code php script, conplile.php is the compression code of the script, notmodified.php is to infer whether a merger is required. The principle is to use the dependent file change 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 folder, and the rest is the paste code.

Introduction of HTML:

<script data-main= "/js/dist/home" src= "/js/lib/require-2.1.14.min.js" ></script>
The entrance to the program is Js/dist/home, which is merged and compressed by Js/scr/mian/home.js, which is used in the production environment.

In the local environment, we have to rely on. htaccess this file.

. htaccess

# REDIRECT Js/dist/home.js to combine_js.php This script, and replace Dist with main,#, js/src/main/home.js this true portal file path as a reference pass Rewritecond%{ Request_filename} ^ (. *) dist (. *\.js) $rewriteCond%1src/main%2-frewriterule ^ (. *) dist (. *\.js) $ build/combine_js.php?

f=$1src/main$2 [b,l] #css的合并. And the same as above. Just the corresponding script is processed differently Rewritecond%{request_filename} ^ (. *) dist (. *\.css) $rewriteCond%1config%2-frewriterule ^ (. *) dist (. *\. CSS) $ build/combine_css.php?f=$1config$2 [L] #假设xxx. (js| | CSS) has a corresponding xxx.dev. (js| | CSS) is redirected to. dev.js or. dev.css# This is for the merged and already compressed JS or CSS to use the # analogy lib/jquery.js, you can download the corresponding debug version locally, change the file name is called 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_js.php.

<?

Phpinclude ' notmodified.php ';d efine (' 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); An array of  //dependent files, containing both itself and the config file $data_ex_dep_file = array ();                      //non-compressed dependent file array $data_alias_file = ";                            //aliased file string if (File_exis TS ($file _config)) {    $text _config = file_get_contents ($file _config);    $text _config = Preg_ Replace ($comment _reg_exp, ", $text _config);  //Remove gaze     Preg_match_all ($exclude _reg_exp, $text _config, $matches);    if (Isset ($matches [1 ][0]) { //Remove a dependent file configuration that does not require compression         $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 true file name 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])) {  & nbsp     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);      & nbsp Preg_match_all ($js _require_reg_exp, $text _main, $matches);        if (Isset ($matches [1]) && Is_array ($matches [1]) && count ($matches [1]) > 0) { //take out dependent files           &NBS P foreach ($matches [1] as $v) {                $v = Trim ($v);      &NBS P         $v _true = Get_true_path ($v);                $v _path = BASE. ' /.. /js/'. $v _true. (STRRCHR ($v, '. ') = = '. js '? ': '. js '); Full path of the file  //depends on                 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 removal of dependent files                }           }  &nbsp ;    }   }}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)) { //generates an etag based on the dependent file churn time to infer if compression is required     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); &NBSP;//uses node compression to generate the dev file and save the output. foreach ($output as $v) {    if (Preg_match ('/error/', $v)) {        $error []=json_encode ($v) ; Save error messages    }}if (Empty ($error)) {    header (' Content-type:application/javascript ');    Echo file_get_contents ($file _output);    exit;} foreach ($error as $e) {   echo "Console.error ($e);//output error message}exit;



Here are the 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 change time, the token used to generate the ETag                }} $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 no change is made, 304 is returned. Go to read cache return true;} return false;}


Below is a look at the entry file Main/spc.js

Require (["config.js"], Function (config) {<span style= "font-family:arial, Helvetica, Sans-serif;" After the >//loads the public configuration file, it starts to define the module.

</span> require ([' src/main/home ');});    Define (function (require) {"Use strict";    var home = require ("Src/controllers/home"); New Home ({name: ' HomeController '}). init ();});


See the Public Profile Config.js Most of the work has been done, and the final production of the compression work

/* * default Config. */requirejs.config ({    baseurl:typeof (javascriptserver) = = = ' undefined '? ': Javascriptserver  + ' js/',    paths: {        jquery: ' Lib/jquery-1.11.1.min ', &nbs P       Underscore: ' Lib/underscore-1.6.min ',        backbone: ' Lib/backbone-1.1.2.min ',        cookies: ' Plugin/jquery.cookie '    },    usestrict:true,    EX Clude: [' jquery ', ' underscore ', ' backbone ', ' cookie '],//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: {       /* Now backbone and underscore are supported amd! If the version number is not supported, the following configuration is required.         backbone: {            deps: [' jquery ', ' underscore '],  &NB Sp         exports: ' Backbone '        },        underscore: {&nbs P       &NBsp   Exports: ' _ '        }        */      }});


See batch Compile.bat First, very easy

@echo offphp compile.php ". /js/dist "PHP compile.php". /css/dist "Pause

See compile.php again, very 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 Check Syntax              }}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);}


Finally, I want to say. In fact, there are two of these routines that tolerate small bugs. Usually need to pay attention to.

1. Assume (forward) that the local time has been changed. Compression may cause compression to fail.
Workaround: Turn the time right. Delete produt/xxx.js, update it again from the version number library, and then compress.

In fact, be careful not to change the local time when compressing.

2. Assuming ctrl+f5 or emptying the cache, even if the file is not changed, it will be merged again. Because the etag was cleared.
The solution: The problem is in fact no tube, assuming that it is not modified by the compression again, do not commit can be. It's not too much of a relationship to submit!

Now just endure, assuming there is a good way to welcome the guidance.

Wait, there's time. The entire set of source code will be compiled on GitHub.

Require.js+backbone implementation of one-click compression using R.js in local and production environments

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.