Related
I am trying to optimize a site performance by consolidating and compressing the CSS and JS files. My question is more about the (concrete) steps on how to achieve this, given a real situation I was facing (should be typical among other developers too, though).
My page references several CSS and JS files like the following:
<!--
It's easier to work on smaller files during development.
Hence, the multiple CSS and JS files.
-->
<link type="text/css" rel="stylesheet" href="/css/main.css" />
<link type="text/css" rel="stylesheet" href="/css/secondary-1.css" />
<link type="text/css" rel="stylesheet" href="/css/secondary-2.css" />
<script type="text/javascript" src="/scripts/js/main.js"></script>
<script type="text/javascript" src="/scripts/js/adapter/adapter.js"></script>
<script type="text/javascript" src="/scripts/js/adapter/title-adapter.js"></script>
For the production release, I'd like to combine the 3 CSS files into one and minify it using e.g. YUI Compressor. But then, I'd need to update all pages that needs these 3 files to reference to the newly-minified CSS. This seems error-prone (e.g. you're removing and adding some lines in many files). Any other less-risky approach? The same issue for the JS files.
Check out minify - it allows you combine multiple js, css files into one just by stacking them into a url, e.g.
<script src="/scripts/js/main.js,/scripts/js/adapter/adapter.js"></script>
We've used it for years and it does a great job and does it on the fly (no need to edit files).
I think the YUI Compressor is the best there is. It minifies JS and CSS and it even strips those console.log statements people use for low-level JavaScript debugging.
Check out how easy it is.
You can start it in an ant task and therefore include it into your post/pre-commit hooks if you use svn/git.
UPDATE: Nowadays I use grunt with the concat, minify & uglify contributions. You can use it with a watcher so it creates new minified files in the background whenever you change your source.
The uglify contrib not only strips console logs, but it also strips unused functions and properties.
See this tutorial for a brief insight.
UPDATE:
Nowadays people step back from grunt und its predecessor "gulp" and use npm as a build tool. Read up on it here.
UPDATE:
So now people use yarn to run npm. No wonder; yarns icon is a cat.
Most current frameworks use webpack to bundle the resources into packages, which then also takes care of minification.
I'd need to update all pages that needs these 3 files to reference to the newly-minified CSS.
Firstly I would say you should have common header. So it will needn't to change all headers at all time whenever necessary. It's good practice to have single header or 2-3. So as your page need you can change your header. So whenever you want to extend your web-app it will be less risky and tedious.
You havn't mentioned your development environments. You can see there are many compressing tools listed for different environments. And you are using good tool i.e.YUI Compressor.
Quick tip for windows users, if you only want to concat files:
Open a cmd at the desired place, and just pipe your files to a file using "type"
ex:
type .\scripts\*.js >> .\combined.js
If the order of your scripts is important, you have to explicitly type the files, in the desired order.
I use this in a bat file for my angular/bootstrap projects
del combos.js
type .\lib\jquery.min.js >> .\combos.js
type .\lib\bootstrap.min.js >> .\combos.js
type .\lib\Angular\angular.min.js >> .\combos.js
type .\lib\Angular\angular-route.min.js >> .\combos.js
type .\lib\Angular\angular-sanitize.min.js >> .\combos.js
type .\app.js >> .\combos.js
type .\services\*.js >> .\combos.js
type .\controllers\*.js >> .\combos.js
I ended up using CodeKit to concatenate my CSS and JS files. The feature that I find really useful is the ability to do the concatenation upon file save; because it monitors the respective CSS / JS assets. Once I got them properly combined e.g. to 1 CSS and 1 JS files, all other files simply can refer to these 2.
You can even ask CodeKit to do on-the-fly minification / compression.
Disclaimer: I am not affiliated in any way with CodeKit. I randomly found it on the web and it has served as a great tool in my development process. It also comes with good updates since I first used it more than a year ago.
It's 2015 year in the street and the easiest way imo is using gulp - http://gulpjs.com/.
Minifying code using gulp-uglify for js scripts and gulp-minify-css for css is very simple.
Gulp is definitely worth of trying
I dont see you mention a content management system (Wordpress, Joomla, Drupal, etc) but if you are using any popular CMS they all have plugins/modules available (free options as well) that will minify and cache your css and js.
Using a plugin gives you the ability to keep the uncompressed versions available for editing, then when changes are made the plugin automatically includes your changes and re compresses the file. Just make sure you choose a plugin that will let you exclude files (such as a custom js file) incase it breaks anything.
I have tried in the past to keep these files up manually and it always turns into a maintenance nightmare. Good luck, hope this helped.
For people who want something a little more lightweight and flexible, I created js.sh today to address this problem. It's a simple command line tool targeted toward JS files that could easily be modified to take care of CSS files too. Benefits:
Easily configurable for use by multiple developers on a project
Combines JS files in the order specified in script_order.txt
Compresses them with Google's Closure Compiler
Splits JS into <25kb chunks where possible since the iPhone won't cache anything greater than 25kb
Generates a small PHP file with <script> tags that you can include where appropriate
Usage: js.sh -u yourname
It could use some improvements, but it's better for my use case than all of the other solutions I've seen so far.
First step of optimization is file minimization. (I strongly recommend GULP for minimization and optimization. Its simple watch solution, installation and all files are compressed at once.Supports all CSS, JS,less, sass, etc...)
OR Old school solution:
1) In general, as a process of optimization, to optimize a site performance, try merging all CSS in one file and compress file by using Compass. That way your multiple requests to static CSS will be replaced with single one.
2) Problem of multiple JS you can resolve by using CDN (or Google Hosted Libraries) so requests go to other server not yours. That way server doesn't wait for previous request to complete before sending next.
3) If you have your own locally stored JavaScript minimize each file by using Brackets plugin "Compress JavaScript". It's basically one click to compress JavsScript.Brackets is free editor made for CSS and JS but can be used for PHP and other languages. There is plenty of plugins to choose made for both front-end and back-end developers. In general "one click" to do all these (so far multiple) commands. Btw, Brackets replaced my very expensive Dreamweaver ;)
3) Try using tools like Sass, Compass, less to minimize you CSS.
Note: Even without using SASS mixing or variables your CSS will be compressed (just simple use pure CSS and Compass "watch" command will compress it for you).
I hope this helps!
If you're doing any pre-processing on the files you serve, you probably want to set up a proper build system (eg a Makefile). That way, you have some source files with no duplication, and whenever you make a change, you run 'make' and it brings all the automatically-generated files up to date. If there is already a build system in place, learn how it works and use it. If not, you'll need to add one.
So first, figure out how to combine and minify your files from the command line (the YUICompressor documentation covers this). Designate a directory for automatically-generated stuff, separate from the stuff you work on but accessible to the web server, and output to there, eg gen/scripts/combined.js. Put the commands you used into a Makefile, and rerun 'make' every time you've made a change and want it to take effect. Then update the headers in other files to point to the combined and minified files.
In my symfony project I do something like this
{# layout.html.twig #}
{% block styles %}
{% if app.environment == 'prod' %}
<link href="{{ asset('bundles/appmain/min.css') }}" rel="stylesheet" type="text/css" />
{% else %}
<link href="{{ asset('bundles/appmain/hello.css') }}" rel="stylesheet" type="text/css" />
<link href="{{ asset('bundles/appmain/world.css') }}" rel="stylesheet" type="text/css" />
{% endif %}
{% endblock %}
{# some-view.html.twig #}
{% extends 'AppMainBundle::layout.html.twig' %}
{% block styles %}
{{ parent() }}
{% if app.environment != 'prod' %}
<link href="{{ asset('bundles/appsecond/styles.css') }}" rel="stylesheet" type="text/css" />
{% else %}
{% endblock %}
When the dev version is ready for production, I use this script to combine all css files and replace the contents of min.css.
But this solution works only if the same css files are included in all pages.
You can use the cssmin node module which is built from the famous YUI compressor
$ npm -g i cosmic # install
# Usage
var puts = require('util').puts,
fs = require('fs'),
cssmin = require('./cssmin');
var css = fs.readFileSync("/Any/Random/CSS/File.css", encoding='utf8');
var min = cssmin(css);
puts(min);
When loading a CSS/JS file from CDN or any external server, it is possible (even with low probability) to miss the file due to external failure. In this case, the html page will be corrupted in the lack of appropriate CSS and JS.
Is there a practical approach to load a local version upon CDN failure?
<link rel="stylesheet" href="http://cdn.com/style.css" type="text/css" />
IF (not loaded style.css){
<link rel="stylesheet" href="/css/style.css" type="text/css" media="screen" />
}
It would be easier to do this for JS, as we can test a JS function (provided in the JS file); then, loading the local file upon failure. For example, testing, if jQuery library is available.
However, I am curious to know if there is a practical method for this!
I would do it this way.
Create a class within your stylesheet ui-helper-hidden and then add a div as the first element on your page;
<body><div class="ui-helper-hidden"></div><!-- rest of html --></body>
After you have checked to make sure your CDN javascript file has been loaded, then use this bit of code note i am using jquery
<script>
// CSS Fallback
$(function () {
if ($('.ui-helper-hidden:first').is(':visible') === true) {
$('<link rel="stylesheet" type="text/css" href="/pathtocss/nameofstylesheet.css" />').appendTo('head');
}
});
</script>
This will check to see if the element which should be hidden is or not. If it isnt hidden, then you know your css file has not loaded from the CDN.
I use this method for jQuery and jQuery UI via a CDN
For Javascript, you can listen for the onload and onerror events when building a dynamic script. However, in those same pages, it shows otherwise for CSS.
The only reliable way to dynamically load CSS is to do it via AJAX. You could load the styles via dynamic link tags but without those events, you won't know if they have been loaded at all. You could poll for the styles, but it's hackish IMO.
Another way to do it is make the server read those CDN files. If they're good, print the urls for the links. But if those links are dead, make it print the local urls instead. This would be more reliable, and offloads your logic to the server. This assumes that you have access to the server.
Or better, use the local versions in the first place! With good caching, bandwidth won't be an issue
I am currently looking at changing from using css to LESS in my current project. A few things to mention before I get to the question are:
1) The project is purely clientside (Html/Js/Css) so there is no server side component for the website (although there is a web service it calls via CORS)
2) I load almost everything via resource loading frameworks, currently I am using yepnope
So given the above I need to be able to get the LESS styles to be processed clientside, but as I am using a resource loader and more css/less could be loaded after the initial page load has happened I was wondering if:
1) Does Less work with content loaders when using client side processing? As it says:
Client-side usage
Link your .less stylesheets with the rel set to “stylesheet/less”:
<link rel="stylesheet/less" type="text/css" href="styles.less">
Then download less.js from the top of the page, and include it in the <head> element of your page, like so:
<script src="less.js" type="text/javascript"></script>
Make sure you include your stylesheets before the script.
I think I may be able to tell yepnope how to handle less files and give them the required element attributes. If I can providing the less resources are brought in before the less javascript will it be ok?
2) Is there any manual way to tell it what to process in javascript?
This would cover the case where everything has been loaded for the current page, the user clicks a button which dynamically loads a new template which is displayed in the current page, this may require new less resources to be loaded, but the less.js file has already been included.
Hopefully the above gives you some context as to what I am trying to do and what the 2 questions are.
Yes you can.
Reading this post Load less.js rules dynamically and adjusting it a bit:
less.sheets.push(document.getElementById('new-style-1'));
// Add the new less file to the head of your document
var newLessStylesheet = $("<link />").attr("id", "new-style-1").attr("href", "/stylesheets/style.less").attr("type", 'text/less');
$("head").append(newLessStylesheet);
// Have less refresh the stylesheets
less.refresh(true);
You could also generate all the CSS in your development environment and put it in one file.
There are lots of options. The easiests way would be to use an application. You could use apps like http://incident57.com/less/ for Mac. You can even compile online: Search for something as "lessphp".
I am trying to optimize a site performance by consolidating and compressing the CSS and JS files. My question is more about the (concrete) steps on how to achieve this, given a real situation I was facing (should be typical among other developers too, though).
My page references several CSS and JS files like the following:
<!--
It's easier to work on smaller files during development.
Hence, the multiple CSS and JS files.
-->
<link type="text/css" rel="stylesheet" href="/css/main.css" />
<link type="text/css" rel="stylesheet" href="/css/secondary-1.css" />
<link type="text/css" rel="stylesheet" href="/css/secondary-2.css" />
<script type="text/javascript" src="/scripts/js/main.js"></script>
<script type="text/javascript" src="/scripts/js/adapter/adapter.js"></script>
<script type="text/javascript" src="/scripts/js/adapter/title-adapter.js"></script>
For the production release, I'd like to combine the 3 CSS files into one and minify it using e.g. YUI Compressor. But then, I'd need to update all pages that needs these 3 files to reference to the newly-minified CSS. This seems error-prone (e.g. you're removing and adding some lines in many files). Any other less-risky approach? The same issue for the JS files.
Check out minify - it allows you combine multiple js, css files into one just by stacking them into a url, e.g.
<script src="/scripts/js/main.js,/scripts/js/adapter/adapter.js"></script>
We've used it for years and it does a great job and does it on the fly (no need to edit files).
I think the YUI Compressor is the best there is. It minifies JS and CSS and it even strips those console.log statements people use for low-level JavaScript debugging.
Check out how easy it is.
You can start it in an ant task and therefore include it into your post/pre-commit hooks if you use svn/git.
UPDATE: Nowadays I use grunt with the concat, minify & uglify contributions. You can use it with a watcher so it creates new minified files in the background whenever you change your source.
The uglify contrib not only strips console logs, but it also strips unused functions and properties.
See this tutorial for a brief insight.
UPDATE:
Nowadays people step back from grunt und its predecessor "gulp" and use npm as a build tool. Read up on it here.
UPDATE:
So now people use yarn to run npm. No wonder; yarns icon is a cat.
Most current frameworks use webpack to bundle the resources into packages, which then also takes care of minification.
I'd need to update all pages that needs these 3 files to reference to the newly-minified CSS.
Firstly I would say you should have common header. So it will needn't to change all headers at all time whenever necessary. It's good practice to have single header or 2-3. So as your page need you can change your header. So whenever you want to extend your web-app it will be less risky and tedious.
You havn't mentioned your development environments. You can see there are many compressing tools listed for different environments. And you are using good tool i.e.YUI Compressor.
Quick tip for windows users, if you only want to concat files:
Open a cmd at the desired place, and just pipe your files to a file using "type"
ex:
type .\scripts\*.js >> .\combined.js
If the order of your scripts is important, you have to explicitly type the files, in the desired order.
I use this in a bat file for my angular/bootstrap projects
del combos.js
type .\lib\jquery.min.js >> .\combos.js
type .\lib\bootstrap.min.js >> .\combos.js
type .\lib\Angular\angular.min.js >> .\combos.js
type .\lib\Angular\angular-route.min.js >> .\combos.js
type .\lib\Angular\angular-sanitize.min.js >> .\combos.js
type .\app.js >> .\combos.js
type .\services\*.js >> .\combos.js
type .\controllers\*.js >> .\combos.js
I ended up using CodeKit to concatenate my CSS and JS files. The feature that I find really useful is the ability to do the concatenation upon file save; because it monitors the respective CSS / JS assets. Once I got them properly combined e.g. to 1 CSS and 1 JS files, all other files simply can refer to these 2.
You can even ask CodeKit to do on-the-fly minification / compression.
Disclaimer: I am not affiliated in any way with CodeKit. I randomly found it on the web and it has served as a great tool in my development process. It also comes with good updates since I first used it more than a year ago.
It's 2015 year in the street and the easiest way imo is using gulp - http://gulpjs.com/.
Minifying code using gulp-uglify for js scripts and gulp-minify-css for css is very simple.
Gulp is definitely worth of trying
I dont see you mention a content management system (Wordpress, Joomla, Drupal, etc) but if you are using any popular CMS they all have plugins/modules available (free options as well) that will minify and cache your css and js.
Using a plugin gives you the ability to keep the uncompressed versions available for editing, then when changes are made the plugin automatically includes your changes and re compresses the file. Just make sure you choose a plugin that will let you exclude files (such as a custom js file) incase it breaks anything.
I have tried in the past to keep these files up manually and it always turns into a maintenance nightmare. Good luck, hope this helped.
For people who want something a little more lightweight and flexible, I created js.sh today to address this problem. It's a simple command line tool targeted toward JS files that could easily be modified to take care of CSS files too. Benefits:
Easily configurable for use by multiple developers on a project
Combines JS files in the order specified in script_order.txt
Compresses them with Google's Closure Compiler
Splits JS into <25kb chunks where possible since the iPhone won't cache anything greater than 25kb
Generates a small PHP file with <script> tags that you can include where appropriate
Usage: js.sh -u yourname
It could use some improvements, but it's better for my use case than all of the other solutions I've seen so far.
First step of optimization is file minimization. (I strongly recommend GULP for minimization and optimization. Its simple watch solution, installation and all files are compressed at once.Supports all CSS, JS,less, sass, etc...)
OR Old school solution:
1) In general, as a process of optimization, to optimize a site performance, try merging all CSS in one file and compress file by using Compass. That way your multiple requests to static CSS will be replaced with single one.
2) Problem of multiple JS you can resolve by using CDN (or Google Hosted Libraries) so requests go to other server not yours. That way server doesn't wait for previous request to complete before sending next.
3) If you have your own locally stored JavaScript minimize each file by using Brackets plugin "Compress JavaScript". It's basically one click to compress JavsScript.Brackets is free editor made for CSS and JS but can be used for PHP and other languages. There is plenty of plugins to choose made for both front-end and back-end developers. In general "one click" to do all these (so far multiple) commands. Btw, Brackets replaced my very expensive Dreamweaver ;)
3) Try using tools like Sass, Compass, less to minimize you CSS.
Note: Even without using SASS mixing or variables your CSS will be compressed (just simple use pure CSS and Compass "watch" command will compress it for you).
I hope this helps!
If you're doing any pre-processing on the files you serve, you probably want to set up a proper build system (eg a Makefile). That way, you have some source files with no duplication, and whenever you make a change, you run 'make' and it brings all the automatically-generated files up to date. If there is already a build system in place, learn how it works and use it. If not, you'll need to add one.
So first, figure out how to combine and minify your files from the command line (the YUICompressor documentation covers this). Designate a directory for automatically-generated stuff, separate from the stuff you work on but accessible to the web server, and output to there, eg gen/scripts/combined.js. Put the commands you used into a Makefile, and rerun 'make' every time you've made a change and want it to take effect. Then update the headers in other files to point to the combined and minified files.
In my symfony project I do something like this
{# layout.html.twig #}
{% block styles %}
{% if app.environment == 'prod' %}
<link href="{{ asset('bundles/appmain/min.css') }}" rel="stylesheet" type="text/css" />
{% else %}
<link href="{{ asset('bundles/appmain/hello.css') }}" rel="stylesheet" type="text/css" />
<link href="{{ asset('bundles/appmain/world.css') }}" rel="stylesheet" type="text/css" />
{% endif %}
{% endblock %}
{# some-view.html.twig #}
{% extends 'AppMainBundle::layout.html.twig' %}
{% block styles %}
{{ parent() }}
{% if app.environment != 'prod' %}
<link href="{{ asset('bundles/appsecond/styles.css') }}" rel="stylesheet" type="text/css" />
{% else %}
{% endblock %}
When the dev version is ready for production, I use this script to combine all css files and replace the contents of min.css.
But this solution works only if the same css files are included in all pages.
You can use the cssmin node module which is built from the famous YUI compressor
$ npm -g i cosmic # install
# Usage
var puts = require('util').puts,
fs = require('fs'),
cssmin = require('./cssmin');
var css = fs.readFileSync("/Any/Random/CSS/File.css", encoding='utf8');
var min = cssmin(css);
puts(min);
Is there a solution out there where I can have JavaScript/jQuery autoload dependent files when needed? For example, consider this scenario:
I have an autoloader script listening for when a particular script needs to be loaded.
A jQuery dialog() plugin is called.
The autoloader is told to listen for when this plugin is called, and loads the jQuery UI.
If more dialogs are called in the future, the required script will not be loaded.
Is this too much effort for simply trying to limit bandwidth? Should I just include all of the core files in one superpackage and be done with it?
Thank you for your time.
Yes you should inclde all of the scripts in one file. Or at least most of them groupped like this: jquery.js, global.js (that's where frequently - on more than one, two pages - used scripts should be) and page_specyfic.js.
Imagine that a dialog() is called and the user has to wait for .js to download and plugins to initialise.
Savings in bandwith (if any) wouldn't be worth harming the users expirience.
There are many examples of on demand script loading out there. For example remy sharp has a code sample on his blog that you could either use as is or turn into a jQuery plugin. Unfortunately it may not work in all browsers.
There is also the jQuery Lazy Plugin Loader which loads jQuery plugins on demand rather than up-front. To use it you would need to set up lazy loading for each piece of jQuery UI you are using as follows (name will be the function name for each piece you use):
$.lazy([{
src: 'jquery-ui-1.8.14.custom.min.js',
name: 'dialog'
}]);
You can also use the techniques in this question about loading jQuery itself on demand. For example you can dynamically create a script tag at the time needed to load jQuery UI.
Finally since you are talking about jQuery UI consider getting it from Google's CDN, which is likely cached in the user's browser anyway.
You can try this new jquery plugin. Works like yeapnope.js but more make sense.
http://plugins.jquery.com/plugin-tags/autoloader
$(document).autoLoader(
{
test: $.ui,
loadScript: "https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.14/jquery- ui.min.js",
complete: function(){
console.log($.ui);
}
}
);
I wouldn't worry too much. The files are cached. Once one page in your site loads the jquery UI (or any other include file like CSS), the next time it's needed it will be in the user's browser cache, never to be loaded again for days/weeks
Sounds like you want a script loader.
You can't generally do synchronous loading of scripts across browsers, though, so script loaders are necessarily asynchronous. What you're asking for isn't exactly possible since the script needs to load, call a callback, and then continue. You wouldn't want to call some plugin and not know whether it is executing synchronously or not, that gets you into a world of problems.
I recommend you look at DeferJS, a script loader for jQuery:
https://github.com/BorisMoore/jsdefer
From your comments, part of your wish seems to be to keep your code organized. I would recommend RequireJs. It lets you break your code up into clearly separated modules with explicit dependencies. Then when you want to go to production, there's a build tool that will merge them all back together into the (request/bandwidth saving) 2-3 files you want to serve.
Yeah, I have also thought about implementing something like this. I am not sure if it would be worthwhile or not in the end but there are quite a few libraries to do this for you like ensure
you could try something like this but it would be a pain. basically you are checking the type of error caught and message if dialog (the function you are trying to call doesn't exist) load the function and try calling the method again. Like I said it would be a pain to do this everywhere unless some elegant solution was thought of.
function loadDialog() {
$('#myDialog').dialog({});
}
try {
loadDialog()
} catch(e) {
if (e && e.type && e.type=='not_defined' && e.message == 'dialog is not defined') {
//load jQuery plugins...
loadDialog();
}
}
This is a follow-up post for a comment above:
<link rel="stylesheet" href="../system/stylesheets/universal.css" />
<link rel="stylesheet" href="../system/stylesheets/jquery-ui.min.css" />
<link rel="stylesheet" href="../system/stylesheets/uploadify.css" />
<link rel="stylesheet" href="system/stylesheets/style.css" />
<script src="../system/javascripts/swfobject.js"></script>
<script src="../system/javascripts/jquery.min.js"></script>
<script src="../system/javascripts/jquery-ui.min.js"></script>
<script src="../system/javascripts/global.jquery.js"></script>
<script src="../system/javascripts/analog.jquery.js"></script>
<script src="../system/javascripts/funtip.jquery.js"></script>
<script src="../system/javascripts/uploadify.jquery.js"></script>
<script src="system/javascripts/install.jquery.js"></script>
<link rel="stylesheet" href="system/templates/stylesheets/style.css" />
<script>
$(document).ready(function() {
$(':text, :password, textarea').funtip();
});
</script>