I worked on a project which were very close to your situation. The requirements were the following:
- On development side, each page can have one or several JavaScript files and one or several CSS files. All those files are local.
- In production, there must be one and one only JavaScript file and one and one only CSS file. Those two files are hosted on a distant server, optimized for static files and configured for client and server side caching.
- Each page has a link to JQuery hosted at Google CDN in both development and production environments.
- The deployment process must be automated.
- The deployment process must not modify files other than JavaScript and CSS files for compression.
- The website is written in ASP.NET.
- Every page of the website uses the same CSS and the same JavaScript.
- On development side, CSS and JavaScript files are always in a specific predefined directory.
Here what was done by my colleague who worked on this part of the project.
- All files except JQuery are minified and combined into two files:
g.css
and g.js
, g
for global
. This is simply done by walking though the specific directory and searching for those files. After minifying them, the two files are send to the static content server, if they were changed since the last deployment.
- The process is executed at every build, since we couldn't find how to execute it at deployment only. It doesn't matter, after all, since it takes a few milliseconds to walk through all files and check if they were modified since the last build.
- The website is deployed as is, except the original JavaScript and CSS files. We don't need them in production.
- The masterpage (in ASP.NET) contains a specific method which is adjusting the calls to the JavaScript and CSS files depending of the context.
This method is like this:
public string MakeCssCalls(IList<string> cssFiles)
{
// ...
}
In development environment, it returned the links to CSS files inside the cssFiles
argument. In production, it ignored this argument and just returned "<link rel="stylesheet" href="http://static.example.com/g.css" type="text/css" />"
. It made the difference between production and development by comparing the machine name to the list of names in Web.config
file: some machines were configured as being development machines; all machines outside this list were assumed production servers.
Performance-wise, it would be smarter to have a different Web.config
file for production, indicating that we are actually in production (and, for example, precising the exact link to the static server with those files; in our case it was hardcoded, and that sucks). We couldn't do it this way because one of the requirements of this project was to have a single Web.config
file for all environments, but if you can, do it.
Also when is the testing generally performed? I think it should be performed on the optimized files (just in case the compressors have some bug).
We tested the website on non-minified versions of files. We never had any bug coming from Google Closure (nor our home-made crappy CSS minifier).
Short answer: One monolithic file with a far-future expires header.
Long answer: It depends. If you're Gmail and have over 1 megabyte of javascript in your application, then you're going to want to split that up to reduce load and improve user experience. For the rest of us though, the best solution I've found is to load your main library (such as jQuery) from a third-party CDN (like Google). Then split up your one monolithic file into two: lib.js
and app.js
. Lib should contain any third-party libraries, and app your site-specific code. The general idea being that you're very rarely going to be updating libraries or adding new ones, so that file will rarely ever have to be cache invalidated and re-downloaded by your users.
Use a technique such as the one described on this site to move almost all on-page <script>
tags without a src
attribute to an external file:
http://paulirish.com/2009/markup-based-unobtrusive-comprehensive-dom-ready-execution/
So what you should be left with are <script>
tags with a src
, and the only inline ones remaining should be because you need to pass data from your backend code to javascript (and want to avoid an AJAX request on page load that will block rendering). Say the namespace of my project is Foo
, then that one inline tag will look like:
<script type="text/javascript">
Foo.Context = {{ js_context|safe }};
</script>
js_context
is a variable I passed to my template from my backend, and is already a JSON object. The squiggly brackets and |safe
are just from the front-end template engine I happen to be using (in this case, Django's built-in templates). Now my page-specific code, living in app.js
, can look at the contents of Foo.Context
after document.ready
fires for any variables it needs while avoiding an initial AJAX request.
As usual for your static files, you should be serving them with far-future expires headers:
Cache-Control: public, max-age=31536000
When you update your scripts, your build step should either rename the concatenated and minified file or add a GET query parameter to the end of the <script>
src
, like so:
app.12345.js
or app.js?v=12345
That way your users will request the new file, breaking the cache. As a caveat, please be aware that old versions of some reverse-proxies (such as Squid) don't play nicely with GET parameters on static files, so the latter of those two options wouldn't work. But I have never run into that issue yet, so your milage may vary.
When you do get to the Gmail level of script sizes, you can start looking at using a library like LABjs or RequireJS, to dynamically only load the pieces of app.js
that you need for particular pages or functionality.
Last but not least, I hope your <script>
tags are at the bottom of your page, right before the closing </body>
. This prevents the browser from showing a blank white page while your scripts are downloaded, parsed, and executed. The only exception to this rule are libraries that absolutely must be in the <head>
to function, such as Modernizr.
Best Answer
Take a look at this as a reference (Book of Speed): http://www.bookofspeed.com/chapter3.html
Essentially the best way is to combine all your javascript into one file called something like all.min.js that is also minimized.
Typically in HTML5 you would do something like:
As you can see, you DO NOT need the type attribute in HTML5, but you do in other versions of HTML and XHTML. The spec clarifies that if the content is other than "text/javascript" then you need to specify the type attribute, in HTML5.
Some things to remember:
Note:
If you are going to specify another type other than
text/javascript
you would use one of the following:The above list is from: http://dev.w3.org/html5/spec-author-view/the-script-element.html#scriptingLanguages
Remember that you would not use the language attribute, only the type attribute.