📜 ⬆️ ⬇️

Java library for the efficient transfer of CSS and JavaScript

This article describes a method of transferring JavaScript and CSS using the resource linking method, followed by minimizing and compressing it, using the small Java library Combinatorius, which allows you to speed up and simplify the transfer of content.

Demo: combinatorius.dkiriusin.com
GitHub: github.com/deniskiriusin/combinatorius

Main features of the library


  1. Connecting JavaScript and CSS into one JavaScript and CSS resource, respectively, to reduce the number of HTTP requests.
  2. Local caching of generated data to improve response time.
  3. Valid Expires and Cache-Control HTTP headers to help the browser with conditional requests.
  4. ETag support to determine the correspondence between the browser cache with the data on the server.
  5. Compression with gzip to reduce the size of the HTTP response.
  6. Support YUI Compressor.
  7. Support for versions of transmitted resources (fingerprinting & static resources versioning).
  8. Support for CSS themes via URL parameters or cookies.
  9. Simple configuration.

Brief analysis


The speed of loading a web page depends on many factors about some of which Web developers need to know.
')
We will not discuss the speed provided by the Internet provider, DNS settings or the geographical location of the resource, but focus on the HTTP protocol and its methods that we can use to speed up the transfer of CSS and JavaScript content.

So, the main factors affecting page loading speed are:

- The size of the transmitted content
- Number of HTTP requests

Accordingly, our goal is to reduce the size of the transmitted content and the number of requests to the server to a minimum.

Problem 1: The size of the transmitted content


Consider what happens during page loading. Modern Internet resources with a rich user interface send dozens and sometimes hundreds of HTTP requests to download content. Many of them are in CSS and JavaScript. The total weight of the transmitted CSS and JavaScript is usually several hundred kilobytes or more. You can reduce the amount of content transferred by minimizing and compressing it.

Minimization is a process whose task is to reduce the script while maintaining its functionality. This is achieved by removing comments from the script, spaces, as well as reducing the names of variables.

The library fully supports YUI Compressor to minimize CSS / JavaScript and gzip to compress data, which can reduce the overall weight of the transmitted CSS and JavaScript at times. By default, the library does not minimize the resources whose names contain the suffix ".min.".

prop.YUI.OmitFilesFromMinificationRegEx = .*\\.min\\.(js|css)$ 

The regular expression can be changed in combinatorius.properties .

Since data compression is a resource-intensive process, the final data is cached both on the server side and on the client side, and is provided directly from the cache on subsequent requests. In the case of changes in CSS and JavaScript, data is minimized, compressed and cached again.

Problem 2: Number of HTTP requests


We need the client to send only two requests, regardless of the number of CSS and JavaScript resources on the page. One per CSS and JavaScript, respectively. Let's see what the benefits of this approach.

As stated in RFC 2616, the HTTP request must conform to the format:

 Request = Request-Line *(( general-header | request-header | entity-header ) CRLF) CRLF [ message-body ] 

In real life, it would look something like this:

 GET /Protocols/rfc2616/rfc2616-sec5.html HTTP/1.1 Host: www.w3.org Connection: keep-alive Pragma: no-cache Cache-Control: no-cache Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.89 Safari/537.36 HTTPS: 1 Referer: https://www.google.ie/ Accept-Encoding: gzip, deflate, sdch Accept-Language: en-US,en;q=0.8 Cookie: _ga=GA1.2.587820689.1448903370; JSESSIONID=00002Fn37WPDiDzeIspqmDaEY1J:-1; web_vid=1140991966240108 

Based on the same RFC 2616, the HTTP response format is as follows:

 Response = Status-Line *(( general-header | response-header | entity-header ) CRLF) CRLF [ message-body ] 

Chrome DevTools will show us something like:

 HTTP/1.1 200 OK Date: Tue, 12 Apr 2016 15:56:01 GMT Last-Modified: Thu, 18 Feb 2016 10:16:05 GMT ETag: "19982-52c08a77e8340" Accept-Ranges: bytes Content-Length: 104834 Keep-Alive: timeout=10, max=100 Connection: Keep-Alive Content-Type: text/css X-Pad: avoid browser bug 

The size of HTTP headers can vary from 200 bytes to 2KB or more, taking into account Cookies.

It's funny, but most of all you have to pay for the little things. For example, creating a page with ten static resources, each one byte in size, the browser will have to send and receive in return several kilobytes of HTTP headers alone to download 10 bytes of payload.

But the main problem is not even that, but the fact that the requests are slow. Modern browsers are multi-threaded and try their best, but one way or another almost every HTTP request needs to determine the DNS, create a connection to the server, then SSL handshakes, in the case of HTTPS ... And only after that can we get an HTTP response from the server. All this takes time, and the more requests the longer it takes to load the page.

Fortunately, HTTP headers can be extremely useful, and the library skillfully arranges them to speed up the loading of CSS and JavaScript as much as possible.

Cache-Control (HTTP / 1.1)


Cache-Control header directives determine who can cache the HTTP response, under what conditions, and for how long. It is best to send the request at all, and save a copy of the answer in the browser cache and take it from there but not communicate with the server. This eliminates the need to pay for data transmission over the network.
So the max-age directive defines the maximum time in seconds during which the received response can be reused from the browser cache. The library caches data for one year by default.

 Cache-Control: public, s-maxage=31536000, max-age=31536000 

You can change the configuration in combinatorius.properties .

Expires (HTTP / 1.0)


This header is essentially an analogue of Cache-Control, extruded it into HTTP / 1.1. Expires also determines how long data can be cached on the client side. The library sets Expires one year ahead by default.

 Expires: Thu, 15 Apr 2017 22:00:00 GMT 

You can change the configuration in combinatorius.properties .

ETag (HTTP / 1.1)


Provides cache validation and allows the client to send a conditional request. This allows the cache to be more efficient, since the web server does not need to send a full response if the content has not changed. The library uses ETag in the same way as fingerprints. For example, there is no need to change the names of CSS and JavaScript resources, after making changes to them, with long caching. The library automatically recognizes changes made to CSS and JavaScript. Data is automatically minimized if needed, compressed, cached and delivered to the client with all the necessary headers.

Usage guide


The library is available from the central repository.

 <dependency> <groupId>com.dkiriusin</groupId> <artifactId>combinatorius</artifactId> <version>1.0.60</version> </dependency> <dependency> <groupId>com.yahoo.platform.yui</groupId> <artifactId>yuicompressor</artifactId> <version>2.4.8</version> </dependency> 

Register the servlet in web.xml .
 <servlet> <servlet-name>Combinatorius</servlet-name> <servlet-class>com.dkiriusin.combinatorius.CombinatoriusServlet</servlet-class> <load-on-startup>0</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Combinatorius</servlet-name> <url-pattern>/combo/*</url-pattern> </servlet-mapping> 

, from now on, all requests to /combo/* will be processed by the library.

All you need to do next is to create a file combinatorius.properties and place it in the Classpath.
On the example of Tomcat, you can achieve this by changing the common.loader in catalina.properties and adding the path to combinatorius.properties . In my case (Ubuntu 12.04 LTS):

 view /etc/tomcat7/catalina.properties 

Before:

 common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar,/var/lib/tomcat7/common/classes,/var/lib/tomcat7/common/*.jar 

After:

 common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar,/var/lib/tomcat7/common/classes,/var/lib/tomcat7/common/*.jar,${catalina.base}/combinatorius-conf 

Accordingly, create a directory:

 mkdir /var/lib/tomcat7/combinatorius-conf 

And copy the combinatorius.properties. into it combinatorius.properties.

combinatorius.properties
 #---------------------# # required properties # #---------------------# # root CSS directory prop.css.dir = /var/lib/tomcat7/webapps/my_project/css # cached CSS directory prop.css.cache.dir = /var/lib/tomcat7/webapps/my_project/css_cache # root JS directory prop.js.dir = /var/lib/tomcat7/webapps/my_project/js # cached JS directory prop.js.cache.dir = /var/lib/tomcat7/webapps/my_project/js_cache #---------------------# # optional properties # #---------------------# # themes root directory prop.themes.dir = /var/lib/tomcat7/webapps/my_project/themes # Cache-Control: s-maxage directive (31536000 by default) prop.s-maxage = 31536000 # Cache-Control: max-age directive (31536000 by default) prop.max-age = 31536000 # Enables gzip compression (true by default) prop.isCompressionEnabled = true # Enables YUI compressor (true by default) prop.isYUICompressorEnabled = true # Insert line breaks in output after the specified column number (-1 by default) prop.YUI.CSSCompressor.linebreakpos = -1 # Splits long lines after a specific column (100 by default) prop.YUI.JavaScriptCompressor.linebreak = 100 # Minify only, do not obfuscate (false by default) prop.YUI.JavaScriptCompressor.nomunge = false # verbose output (false by default) prop.YUI.JavaScriptCompressor.verbose = false # Preserve unnecessary semicolons (such as right before a '}') (false by default) prop.YUI.JavaScriptCompressor.preserveAllSemiColons = true # Disable all the built-in micro optimizations (true by default) prop.YUI.JavaScriptCompressor.disableOptimisations = true # Define files to be omitted of minification ('.*\.min\.(js|css)$' by default) prop.YUI.OmitFilesFromMinificationRegEx = .*\.min\.(js|css)$ 

The library works with CSS and JavaScript resources in the prop.css.dir and prop.js.dir directories, as well as their sub-directories. CSS and JavaScript files are recursively read in alphabetical order, minimized, compressed and sent to the client. Minimized data is cached on the server side in the prop.css.cache.dir and prop.js.cache.dir .

Resources matching the regular expression prop.YUI.OmitFilesFromMinificationRegEx not minimized.

CSS themes


Also provides support for CSS themes. The CSS theme is the prop.themes.dir sub-directory with one or more CSS files. For example prop.themes.dir/green/theme.css . The theme name must match the sub-directory name and can be transferred to the library as a theme parameter URL or as the value of combinatorius.theme in Cookies.

Connect additional resources


It is possible to connect additional resources that are not included in prop.css.dir and prop.js.dir . Such a need may arise if the script is rarely used (on one or two pages in a project) and should not be included in the “assembly” by default. You can transfer additional resources using the resources parameter URL.

 /combinatorius/combo/&type=js&resources=extra_js/extra1.js,extra_js/extra2.js&theme=blue 

JSP Tag


For simplicity and reliability, I recommend using the JSP tag to generate a URL. One tag per CSS and JavaScript, respectively. The required attributes are type and path .

 <%@ taglib uri="https://github.com/deniskiriusin/combinatorius" prefix="cb" %> <cb:combo type="css" path="/combo"> <jsp:attribute name="theme">blue</jsp:attribute> <jsp:attribute name="csv_resources">extra_css/extra1.css,extra_css/extra2.css</jsp:attribute> </cb:combo> <cb:combo type="js" path="${path}"></cb:combo> 

Using the JSP tag has one important advantage. The tag automatically signs resources by adding a version at the end of the URL to solve problems related to cache emptying with aggressive cache busting.

 /combinatorius/combo/&type=js&v=1465737376000 


Links to read:

developer.yahoo.com/performance/rules.html
developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/?hl=en

Source: https://habr.com/ru/post/303840/


All Articles