Optimizing Jekyll
For a more up-to-date and comprehensive explanation this subject has been revisited here
Upon launching my new redesigned website, I’ve been spending the some time optimizing the site performance and load speeds using Google PageSpeed Insights. Therefore, I thought I would take a second to share with you my discoveries and also recommend a few helpful resources for improving the performance of your own Jekyll website. Using these methods, I have managed to receive the following PageSpeed rating. Though this is still a work-in-progress, and I will be adding updates as I explore this topic further.
In an effort not to repeat the information in the articles listed below, I'll only share findings that weren't explicitly discussed in any of the following resources.
Optimize your own images
As with most websites, when I first launched the new design, the biggest hindrance to my websites loading speed was the size of the images on my page. Though, after observing the disappointing quality of the “optimized” images provided by PageSpeed Insights, I instead elected to use GIMP to manually decrease and compress my images to preserve the image quality until PageSpeeds requirements were satisfied. I would highly recommend doing the same, particularly for images that are featured in various places throughout your website.
Resources:
- Scoring 100 on Google’s PageSpeed Insights with Jekyll
- How to get your Jekyll + GitHub pages website to pass Google’s PageSpeed Tests
- Optimized Jekyll site with Grunt
The Results
I’ve provided my resulting Gulpfile below, though it’s also available via gist.
var gulp = require('gulp'),
shell = require('gulp-shell'),
minifyHTML = require('gulp-minify-html'),
minifyCSS = require('gulp-minify-css'),
sass = require('gulp-sass'),
importCss = require('gulp-import-css'),
autoprefixer = require('gulp-autoprefixer'),
rename = require('gulp-rename'),
imagemin = require('gulp-imagemin'),
pngquant = require('imagemin-pngquant'),
jpegtran = require('imagemin-jpegtran'),
gifsicle = require('imagemin-gifsicle'),
optipng = require('imagemin-optipng'),
replace = require('gulp-replace'),
glob = require('glob'),
fs = require('fs'),
concat = require('gulp-concat-util'),
uncss = require('gulp-uncss'),
critical = require('critical');
download = require('gulp-download');
// Jekyll
gulp.task('jekyll', function() {
return gulp.src('index.html', { read: false })
.pipe(shell(['jekyll build']));
});
// HTML
gulp.task('optimize-html', function() {
return gulp.src('_site/**/*.html')
.pipe(minifyHTML({
quotes: true
}))
.pipe(replace(/<link href=\"\/css\/style.scss\"[^>]*>/, function(s) {
var style = fs.readFileSync('_site/css/style.scss', 'utf8');
return '<style>\n' + style + '\n</style>';
}))
.pipe(gulp.dest('_site/'));
});
// Javascript
gulp.task('javascript', ['jekyll'], function() {
return gulp.src('js/main.js', { read: false })
.pipe(shell(['jspm install']))
.pipe(shell(['jspm bundle-sfx js/main _site/js/min.js --minify --no-mangle']));
});
// CSS
gulp.task('optimize-css', function() {
return gulp.src('_site/css/style.scss')
.pipe(autoprefixer())
.pipe(uncss({
html: ['_site/**/*.html'],
ignore: []
}))
.pipe(minifyCss({keepBreaks: false}))
.pipe(gulp.dest('_site/css/'));
});
// Eliminate render-blocking CSS
gulp.task('include-css', function() {
return gulp.src('_site/**/*.html')
.pipe(replace(/<link href=\"\/css\/style.scss\"[^>]*>/, function(s) {
var style = fs.readFileSync('_site/css/style.scss', 'utf8');
return '<style>\n' + style + '\n</style>';
}))
.pipe(gulp.dest('_site/'));
});
// Eliminate render-blocking CSS in above-the-fold content
gulp.task('styles:critical', function() {
return gulp.src('src/sass/critical.css')
.pipe(minifyCSS())
.pipe(concat.header('<style>'))
.pipe(concat.footer('</style>'))
.pipe(rename({
basename: 'criticalCSS',
extname: '.html'
}))
.pipe(gulp.dest('_includes/'));
});
// Optimize Images
gulp.task('optimize-images', function () {
return gulp.src(['_site/**/*.jpg', '_site/**/*.jpeg', '_site/**/*.gif', '_site/**/*.png'])
.pipe(imagemin({
progressive: false,
svgoPlugins: [{removeViewBox: false}],
use: [pngquant(), jpegtran(), gifsicle()]
}))
.pipe(gulp.dest('_site/'));
});
// Purge cache
gulp.task('purge-cache', function() {
var options = {
token: config.cloudflareToken,
email: config.cloudflareEmail,
domain: config.cloudflareDomain
};
cloudflare(options);
});
// Remove unused CSS
gulp.task('uncss', function() {
return gulp.src([
'css/style.scss'
])
.pipe(uncss({
html: [
// your site pages here
'http://127.0.0.1:4000/',
]
}))
.pipe(gulp.dest('css/uncss/'));
});
// Google Analytics
gulp.task('fetch-analytics', function() {
return download('https://www.google-analytics.com/analytics.js')
.pipe(gulp.dest('assets/'));
});
// Run (Default)
gulp.task('default', ['javascript', 'optimize-css', 'include-css', 'optimize-html', 'optimize-images', 'styles:critical', 'fetch-analytics']);
// Run: Build
gulp.task('build', ['javascript', 'optimize-css', 'include-css', 'optimize-html', 'optimize-images', 'styles:critical', 'fetch-analytics']);
// Run: Clean
gulp.task('clean', ['uncss']);
Updates
Leveraging browser caching
Since sharing my original post, I’ve since found a way to resolve the persistent “Leverage browser caching” issue, which was ironically referencing Google’s own analytics tools.
// Google Analytics
gulp.task('fetch-analytics', function() {
return download('https://www.google-analytics.com/analytics.js')
.pipe(gulp.dest('assets/')); // or whatever folder you want it in
});
This way, a new version of the analytics.js
file is downloaded on each deployment, rather than utilizing Google’s remote file. Therefore, you should also instead include your local analytics.js
file in your html.