Make JavaScript run faster)

Source: Internet
Author: User
Tags file url php template perl script
ArticleDirectory
    • Hao freshman year
    • Decomposition path
    • Compression
    • Cache is a good thing
    • Cache is really a good thing
    • Sometimes poor human resources
    • Only Dongfeng
    • Yan guan6 Road

From: http://blog.htmlor.com/2006/08/03/serving_javascript_fast_chinese/

By Cal Henderson

The next generation of Web applications makes JavaScript and CSS usable. We will tell you how to make these applications fast and agile.

It has established an application known as "Web 2.0" and also implemented rich content (rich
Content) and interaction, we expect CSS and JavaScript to play a more important role. To make the application clean, we need to improve the rendering page files, optimize their size and
Form to ensure the best user experience-in practice, this means a combination: make the content as small as possible, download as fast as possible, at the same time, avoid unnecessary re-acquisition of unchanged resources.

The situation is a bit complicated because of the CSS and JS files. Compared with imagesSource codeIt is likely to be changed frequently. Once changed, the client needs to download it again to make the local cache invalid (saved in its
The same is true for the version in the cache ). In this article, we will focus on how to make the user experience the fastest: including the download of the initial page, the download of the subsequent page, and the gradual development of the application and content changes.
Download resources.

I always believe in this: developers should make things as simple as possible. Therefore, we prefer the methods that allow the system to automatically handle optimization problems. With only a little effort, we can build a rare environment: it makes development simple, has excellent terminal performance, and does not change the existing working method.

Hao freshman year

The old idea is that to optimize performance, you can merge multiple CSS and JS files into a very small number of large files. It is better to combine a 50 K file with ten 5k JS files. AlthoughCodeTotal Bytes
The number does not change, but the overhead caused by multiple HTTP requests is avoided. Each request is created and eliminated on both the client and server, resulting in overhead of the request and response header, and Server
More processes and thread resource consumption at the end (there may be CPU time required for compressing the content ).

(In addition to HTTP requests) concurrency is also important. By default, when persistent connections is used, ie and Firefox only download two resources at the same time in the same domain name (recommended in section 8.1.4 of HTTP 1.1 specifications) (htmlor Note: You can change this default configuration by modifying the Registry and other methods ). This means that image resources cannot be downloaded while we are waiting to download two JS files. That is to say, the user cannot see the image on the page during this period of time.

(Although merging files can solve the above two problems), this method has two disadvantages. First, package all resources together and force the user to download all resources at a time. If
Yes) by converting a large part of content into multiple files, the download overhead is distributed to multiple pages, and the speed pressure on the session is reduced (or some overhead is completely avoided, it depends on the selected path ). If
Page downloads are faster and the initial Page downloads are slow. We will find that more users are not waiting to open the next page.

Second, if a single file system is used in an environment with frequent file changes, the client needs to re-download all CSS and JS files each time the file is changed. If our application has a K merged JS large file, any minor changes will force the client to digest the K again.

Decomposition path

(It seems inappropriate to merge large files .) The alternative solution is a compromise: the CSS and JS resources are scattered into multiple sub-files, and the number of files is kept as small as possible by function. This solution is also available
In the Development era, codes are distributed into logical blocks (
Chunks) can improve efficiency, and files can be merged to improve performance during download. However, you only need to add something to the build system (the tool set for converting development code into product code is prepared for deployment ).
There's no problem.

For applications with different development and product environments, some simple technologies can be used to better manage the code. In the development environment, to make the code clear, the code can be divided into multiple logical parts (logical components ). You can create a simple function in smarty (a PHP template language) to manage JavaScript downloads:

 
Smarty:
{Insert_js files = "foo. JS, bar. JS, Baz. js "}

PHP:
Function smarty_insert_js ($ ARGs ){
Foreach (explode (',', $ ARGs ['files']) as $ file ){
Echo "<SCRIPT type = \" text/JavaScript \ "Source = \"/JavaScript/$ file \ "> </SCRIPT> \ n ";
}
}

Output:
<SCRIPT type = "text/JavaScript" Source = "/JavaScript/Foo. js"> </SCRIPT>
<SCRIPT type = "text/JavaScript" Source = "/JavaScript/bar. js"> </SCRIPT>
<SCRIPT type = "text/JavaScript" Source = "/JavaScript/Baz. js"> </SCRIPT>

(Htmlor Note: WordPress will replace "src" with an unknown character, so only "Source" is written here. Please replace it with your code, the same below)

That's simple. Then we run the build process command to merge the identified files. In this example, foo. js and bar. js are merged because they are almost always downloaded together. We can let the application configuration remember this and modify the template function to use it. (The Code is as follows :)

Smarty:
{Insert_js files = "foo. JS, bar. JS, Baz. js "}

PHP:
# Source file ing. After merging files in the build process, use this figure to find the JS source file.

$ Globals ['config'] ['js _ source_map '] = array (
'Foo. js' => 'foobar. js ',
'Bar. js' => 'foobar. js ',
'Baz. js' => 'baz. js ',
);

Function smarty_insert_js ($ ARGs ){
If ($ globals ['config'] ['is _ dev_site ']) {
$ Files = explode (',', $ ARGs ['files']);
} Else {
$ Files = array ();
Foreach (explode (',', $ ARGs ['files']) as $ file ){
$ Files [$ globals ['config'] ['js _ source_map '] [$ file] ++;
}
$ Files = array_keys ($ files );
}

Foreach ($ files as $ file ){
Echo "<SCRIPT type = \" text/JavaScript \ "Source = \"/JavaScript/$ file \ "> </SCRIPT> \ n ";
}
}

Output:
<SCRIPT type = "text/JavaScript" Source = "/JavaScript/foobar. js"> </SCRIPT>
<SCRIPT type = "text/JavaScript" Source = "/JavaScript/Baz. js"> </SCRIPT>

The source code in the template does not need to be changed to adapt to the development and product stages respectively. It helps us to keep the files scattered during development and merge the files when releasing them into products. If you want to go further, you can merge them.
Cheng (merge
Process) is written in PHP, and then executed using the same (merged file) configuration. In this way, there is only one configuration file to avoid synchronization problems. For perfection, we can also analyze
The probability that CSS and JS files appear on the page at the same time determines which files are most reasonable to merge (almost always the files that appear at the same time are the first choice for merging ).

For CSS, You can first create a master-slave relationship model, which is very useful. A primary style table controls all style sheets of an application. Multiple child style sheets control different application regions. In this way, most pages only need to download two CSS files, and one of them (the main style table) will be cached at the first request of the page.

For applications that do not have many CSS and JS resources, this method may be slower than a single large file in the first request. However, if the number of files is small, you will find it faster, because each page
The data volume is smaller. The download cost is distributed across different application regions, so the number of concurrent downloads is kept at a minimum, and the average download data volume on the page is also small.

Compression

When talking about resource compression, most people will immediately think of mod_gzip (but be careful that mod_gzip is actually a devil, at least a nightmare ). The principle is simple: when a browser requests a resource, it sends a header indicating that it can accept the content encoding. Like this:

 
Accept-encoding: gzip, deflate

When the server encounters such a header request, it uses gzip or deflate to compress the content to the client and decompress it. This process reduces the data transmission volume and consumes customers.
CPU time of the user and server. It is also unsatisfactory. However, mod_gzip works like this: first create a temporary file on the disk, then send it to the client, and finally Delete this
File. In high-capacity systems, due to disk Io problems, the limit will soon be reached. To avoid this situation, you can use mod_deflate (only supported by Apache 2 ). It adopts a more reasonable method: compress in the memory. For Apache 1 users, a RAM disk can be created to allow mod_gzip to write temporary files on it. Although there is no pure memory mode, it is not slower than writing files to the disk.

Even so, there is still a way to completely avoid the compression overhead, that is, pre-compress the relevant static resources. during download, mod_gzip provides the appropriate compression version. If you add compression
The build process is transparent. There are usually few files to be compressed (images do not need to be compressed, because they cannot reduce more sizes), only CSS and JS files (and other uncompressed static content ).

The configuration option tells mod_gzip where to find the pre-compressed file.

 
Mod_gzip_can_negotiateyes
Mod_gzip_static_suffix.gz
Addencodinggzip.gz

The new version of mod_gzip (starting from 1.3.26.1a) can automatically pre-compress files after an additional configuration option is added. However, before that, you must ensure that Apache has the correct permissions to create and overwrite compressed files.

 
Mod_gzip_update_staticyes

Unfortunately, things are not that simple. Some Netscape
Versions 4 (especially 4.06-4.08) think they can interpret the Compressed Content (they send a header), but they cannot be decompressed correctly. In most other versions
Netscape 4 also has various problems when downloading compressed content. So we need to test the proxy type on the server side (if it is Netscape
4, You Need To) Get Them uncompressed versions. This is simple. IE (Version 4-6) has some more interesting problems: When downloading compressed JavaScript, sometimes IE does not properly decompress the file,
Or decompress the file to half of the interrupt, and then display the half file on the client. If your application depends heavily on JavaScript (htmlor Note: for example, Ajax application ),
Avoid sending compressed files to IE. In some cases, some older 5.x versions of IE can correctly receive compressed JavaScript, but they will ignore the etag of this file.
Header, do not cache it. (Thincat friendly reminder: Although compression is incompatible with Some browsers, the number of browsers that cannot support compression is now very small, I
It is considered that the compression caused by the browser is not normal. Can these outdated browsers be installed in popular Windows or UNIX environments)

Since there are so many problems with gzip compression, we may wish to focus on the other side: do not change the file format compression. Many of these JavaScript compression scripts are currently available, most of them
Use a regular expression-driven statement set to reduce the size of the source code. They do a few things: Remove comments, compress spaces, and shorten private variable names and remove negligible syntaxes.

Unfortunately, most scripts do not work as well, either with a relatively low compression rate, or in some cases, code is messy (or both ). Due to incomplete understanding of the parsing tree
It is difficult to distinguish a comment from a reference string that looks like a comment. Because of the mixed use of closed structures, it is not easy to use regular expressions to find which variables are private, so some techniques to shorten the variable name will disrupt
Some closed code.

Fortunately, there is a compressed file to avoid these problems: the dojo compressed file (the ready-made version is here ).
It uses rhino (Mozilla's JavaScript Engine, implemented in Java) to create a parsing tree and submit it to the file. It can reduce the size of the Code, only use
Very Low Cost: because it is only compressed once during build. Since compression is implemented in the build process, it is clear. (Since there is no problem with compression,) We can do whatever we want in the source code
Add spaces and comments without worrying about the impact on the product code.

Compared with JavaScript, CSS file compression is simpler. Because the CSS syntax does not contain too many referenced strings (usually URL paths and fonts), we can use regular expressions.
Use a bold expression to remove spaces (htmlor Note: This sentence is the best, haha ). If there is a reference string, we can always combine a string of spaces (because the URL path and
Search for multiple spaces and tabs in the body name ). In this case, a simple Perl script is enough:

 
#! /Usr/bin/perl

My $ DATA = '';
Open F, $ argv [0] or die "can't open source file: $! ";
$ Data. =$ _ while <F>;
Close F;

$ DATA = ~ S! \/\*(.*?) \*\/!! G; # Remove comments
$ DATA = ~ S! \ S +! ! G; # compress Spaces
$ DATA = ~ S! \}!} \ N! G; # add a line break after ending braces
$ DATA = ~ S! \ N $ !!; # Delete the last line feed
$ DATA = ~ S! \{! {! G; # Remove spaces after braces
$ DATA = ~ S !; \}!}! G; # Remove spaces before ending braces

Print $ data;

Then, you can pass a single CSS file to the script for compression. The command is as follows:

Perl compress. pl site.source.css> site.compress.css

After completing these simple text optimization work, we can reduce the amount of data transferred by up to 50% (this amount depends on your code format and may be more ). This provides a faster user experience. But what we really want to do is to avoid user requests as much as possible-unless necessary. Now the HTTP cache knowledge comes in handy.

Cache is a good thing

When a user agent (such as a browser) requests a resource from the server, it caches the server's response after the first request to avoid repeated identical requests. The cache duration depends on two factors
Principal: the proxy configuration and the Cache control header of the server. All browsers have different configuration options and processing methods, but most of them cache at least one resource until the session ends (unless explicitly reported
Knowledge ).

To prevent the browser from caching pages with frequent changes, you may have sent headers without caching dynamic content. In PHP, the following two commands can be performed:

 
<? PHP
Header ("cache-control: Private ");
Header ("cache-control: No-Cache", false );
?>

It sounds too simple? Indeed -- Some proxies (browsers) will ignore these headers in some environments. Make sure that documents are not cached in the browser:

<? PHP
# Make it "invalid" in the past"
Header ("expires: Mon, 26 Jul 1997 05:00:00 GMT ");

# Always changed
Header ("last-modified:". gmdate ("D, D m y h: I: s"). "GMT ");

# HTTP and 1.1
Header ("cache-control: No-store, no-cache, must-revalidate ");
Header ("cache-control: Post-check = 0, pre-check = 0", false );

# HTTP and 1.0
Header ("Pragma: No-Cache ");
?>

In this way, the content we do not want to cache is enough. However, the browser should be encouraged to cache content that will not be changed during each request. "If-modified-
Since "request header can do this. If the client sends an "if-modified-since" header in the request, Apache (or other services
Will respond with status code 304 (not changed), telling the browser that the cache is up to date. This mechanism can avoid repeatedly sending files to the browser, but it still causes the elimination of an HTTP request.
Consumption. Well, think about it.

Similar to the IF-modified-since mechanism, entity tag (Entity
Tags ). In Apache, each response to a static file sends an "etag" header, which contains a file modification time, file size, and inode.
Checksum ). Before downloading an object, the browser sends a head request to check the object's etag. Etag and if-modified-since
The same problem occurs: the client still needs to execute an HTTP request to verify whether the local cache is valid.

In addition, if you use multiple servers to provide content, be careful to use if-modified-since and etags. In the environment of two Server Load balancer instances
A resource can be obtained from server a this time, and from server B next time (htmlor Note: LVS load balancing system is a typical example ). This is good, and load balancing is also adopted.
Cause. However, if the two servers generate different etag or file modification date for the same file, the browser will be at a loss (each time it will be downloaded again ). By default, etag is composed of Files
While the inode numbers of files between multiple servers are different. You can use Apache configuration options to turn it off:

 
Fileetag mtime size

With this option, Apache only uses the file modification date and file size to determine etag. Unfortunately, this leads to another problem (the same can affect if-modified-
Since ). Since etag depends on the modification time, You Have To synchronize time. When you can upload files to multiple servers, the upload time difference is usually one to two seconds. In this way, the two servers generate
Etag is still different. Of course, we can also change the configuration so that the generation of etag depends only on the file size, but this means that if the file content changes but the size does not change, the etag will not change. This is not the case
Line.

Cache is really a good thing

It seems that we are starting to solve the problem from the wrong direction. (Now the problem is) these possible cache policies cause one thing to happen repeatedly, that is: the client queries the server to check whether the local cache is the most
New. If the server notifies the client when changing the file, the client will not know that its cache is up to date (until the next notification is received )? Unfortunately, tiangong is not doing well-(fact) is that the client sends a message to the server
Request.

In fact, it is not clear. Before obtaining JS or CSS files, the client sends a request to the server with the <SCRIPT> or <link> flag.
Which page to load these files. At this time, you can use the server response to notify the client of changes to these files. It is a bit vague. The details are as follows: if you change the content of CSS and JS files,
You can also change their file names to tell the client that all URLs are permanently cached-because each URL is unique.

If we can determine that a resource will never change, we can issue some domineering cache headers (htmlor Note: This sentence is also very imposing ). In PHP, just two lines:

 
<? PHP
Header ("expires:". gmdate ("D, D m y h: I: s", time () + 315360000). "GMT ");
Header ("cache-control: Max-age = 315360000 ");
?>

We told the browser that the content will expire in 10 years (about 315,360,000 seconds in 10 years, or more) and the browser will keep it for 10 years. Of course, it is very likely that PHP does not need to output CSS and JS files (So header cannot be issued). This situation will be explained later.

Sometimes poor human resources

It is dangerous to manually change the file name when the file content is changed. If you change the file name, but the template does not point to it? If you have changed some templates, but haven't changed them? If you have changed the Template but have not changed the file
Name? What's worse, if you forget to change the name of the file or forget to change its reference? The best result is that the user can see the old but not the new content. The worst result is that the website cannot find files.
Running. It sounds like this (modifying the URL when modifying the file content) is a bad idea.

Fortunately, computers do this kind of thing-when a change happens, it needs to be done exactly, repeat and repeat (htmlor Note: Tomato eggs waiting ~) Boring work-always very good.

This process (modifying the File URL) is not so painful, because we do not need to change the file name at all. The resource URL and the file location on the disk do not need to be consistent. Using the mod_rewrite module of Apache, you can establish simple rules to redirect a specified URL to a specified file.

 
Rewriteengine on
Rewriterule ^/(. * \.) V [0-9.] + \. (CSS | JS | GIF | PNG | JPG) $/$1 $2 [l]

This rule matches any URL with a specified extension that also contains "version" Information (version nugget). It redirects these URLs to a path without version information. As follows:

 
URL path
/Images/foo.v2.gif->/images/foo.gif
/CSS/main.v1.27.css->/CSS/main.css
/JavaScript/md5.v6. js->/JavaScript/md5.js

With this rule, you can change the URL without changing the file path (because the version number has changed ). Because the URL is changed, the browser considers it as another resource (which will be downloaded again ). If you want to further develop, you can combine the previously mentioned script grouping function to generate a <SCRIPT> tag list with a version number as needed.

Here, you may ask me why a query string (Query
String) (for example,/CSS/main.css? V = 4 )? According to the HTTP cache specification, the user agent never caches URLs containing query strings. Although
Firefox ignores this, but opera and Safari do not -- to ensure that all browsers cache your resources, do not use query strings in URLs.

Now, you can change the URL without moving the file. It is better if you can make the URL update automatically. In a small product environment (if there is a large product environment, it is the development environment), you can easily implement this using the template function. Smarty is used here, and other template engines are also used.

Smarty:
<Link xhref = "{version xsrc = '/CSS/group.css'}" rel = "stylesheet" type = "text/CSS"/>

PHP:
Function smarty_version ($ ARGs ){
$ Stat = Stat ($ globals ['config'] ['site _ root']. $ ARGs ['src']);
$ Version = $ stat ['mtime'];

Echo preg_replace ('! \. ([A-Z] + ?) $! ', ". V $ version. \ $1", $ ARGs ['src']);
}

Output:
<Link xhref = "/CSS/group.v1234567890.css" mce_href = "/CSS/group.v1234567890.css" rel = "stylesheet" type = "text/CSS"/>

For each linked resource file, we get its path on the disk and check its mtime (the last modification date and time of the file ), then insert the time into the URL as the version number.
This solution is good for low-traffic sites (with little overhead for their stat operations) or development environments, but not for high-capacity environments-because each stat operation requires disk reading
(Resulting in increased server load ).

The solution is quite simple. In a large system, each resource has a version number, that is, the revision number of Version Control (you should have used version control, right ?). When we create a site for deployment, we can easily find the revision number of each file and write it in a static configuration file.

<? PHP
$ Globals ['config'] ['Resource _ version'] = array (
'/Images/foo.gif' => '2. 1 ',
'/CSS/main.css' => '1. 27 ',
'/JavaScript/md5.js' => '6. 123 ',
);
?>

When releasing a product, you can modify the template function to use the version number.

 
<? PHP
Function smarty_version ($ ARGs ){
If ($ globals ['config'] ['is _ dev_site ']) {
$ Stat = Stat ($ globals ['config'] ['site _ root']. $ ARGs ['src']);
$ Version = $ stat ['mtime'];
} Else {
$ Version = $ globals ['config'] ['Resource _ version'] [$ ARGs ['src'];
}

Echo preg_replace ('! \. ([A-Z] + ?) $! ', ". V $ version. \ $1", $ ARGs ['src']);
}
?>

In this way, you do not need to change the file name or remember which files have been changed. When a new version of the file is released, its URL will be automatically updated.-Interesting? We can get it done.

Only Dongfeng

I mentioned earlier that cache headers that send very-long-period messages to static files cannot send cache headers easily without PHP output. Obviously, there are two solutions: PhP output or Apache.

PHP is out of the box. All we need to do is change the rewrite rules, point the static file to the PHP script, and use PHP to send the header before outputting the file content.

Apache:
Rewriterule ^/(. * \.) V [0-9.] + \. (CSS | JS | GIF | PNG | JPG) $/redir. php? Path = $1 $2 [l]

PHP:
Header ("expires:". gmdate ("D, D m y h: I: s", time () + 315360000). "GMT ");
Header ("cache-control: Max-age = 315360000 ");

# Ignore the path ".."
If (preg_match ('! \.\.! ', $ _ Get [path]) {go_404 ();}

# Ensure that the path starts with a specified directory
If (! Preg_match ('! ^ (JavaScript | CSS | images )! ', $ _ Get [path]) {go_404 ();}

# Does the file not exist?
If (! File_exists ($ _ Get [path]) {go_404 ();}

# Issuing a File Header
$ Ext = array_pop (explode ('.', $ _ Get [path]);
Switch ($ ext ){
Case 'css ':
Header ("Content-Type: text/CSS ");
Break;
Case 'js ':
Header ("Content-Type: text/JavaScript ");
Break;
Case 'gif ':
Header ("Content-Type: image/GIF ");
Break;
Case 'jpg ':
Header ("Content-Type: image/JPEG ");
Break;
Case 'png ':
Header ("Content-Type: image/PNG ");
Break;
Default:
Header ("Content-Type: text/plain ");
}

# Output file content
Echo implode ('', file ($ _ Get [path]);

Function go_404 (){
Header ("HTTP/1.0 404 file not found ");
Exit;
}

This solution is effective but not outstanding. (Because) PHP requires more memory and execution time than Apache. In addition, we must be careful to prevent the possible cause of passing forged values by the path parameter.
Exploits. To avoid these problems, use Apache to directly send headers. The rewrite rule statement allows you to set environment variables when the rule matches.
(Environment
Variable). After the given environment variable is set, the header command can add the header. In combination with the following two statements, we set the rewrite rule and header
Bound together:

 
Rewriteengine on
Rewriterule ^ /(. *\.) V [0-9.] + \. (CSS | JS | GIF | PNG | JPG) $/$1 $2 [L, E = versioned_file: 1]

Header Add "expires" "mon, 28 Jul 2014 23:30:00 GMT" Env = versioned_file
Header Add "cache-control" "Max-age = 315360000" Env = versioned_file

Considering the Apache execution sequence, you should add the rewrite rules to the master configuration file (httpd. conf) instead of the Directory configuration file (. htaccess. No
Then, before setting environment variables, the header will be executed first (that makes no sense ). As for the header line, it can be placed in either of the two files. There is no difference.

Yan guan6 Road

(Htmlor Note: Thanks to tchaikov for telling me the meaning of "skinning rabbits", but I don't want to turn it over too formal. It should not be too outrageous .)

By combining the above technologies, we can build a flexible development environment and a fast and high-performance product environment. Of course, there is still some distance from the ultimate goal "Speed. There are many deeper Technologies
Technology (for example, separating static servo content and increasing concurrency with multiple domain names) deserves our attention, including the methods discussed with US (creating Apache filters, modifying resource URLs, and adding version information) shitu Tong
Other approaches. You can leave a comment to tell us the effective technologies and methods you are using.

(end)

Related Article

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.