Contents
Pre-requisites
- Gulp: An Introduction to Gulp.js
- LiveReload: A Quick Guide to Using LiveReload with Gulp
Introduction
The fever over task-runners like Grunt, Gulp and Broccoli has largely died in 2017 due to superior alternatives such as Webpack. However, if you’re like me, you’re still using Gulp simply because it works for you and you’re not working on big projects or in big teams to necessitate the change to Webpack.
Since initially learning about LiveReload in 2015, I have moved to Firefox (and for the most part, appreciate it). This means I do not have access to an official addon as I did on Chrome. Not only this, I neither want to install an additional extension for something I do not do 24×7 (hobby web developer), nor bloat my Firefox installation, especially if I can help it.
The problem
If you read the article on implementing LiveReload, you must have come across the following section:
Next, we need to download the Google Chrome extension LiveReload, go to the Chrome Store and download it here. Make sure you can view it in your tool bar and that the circle is filled in with black. This is important or else it won’t work.
From dmitriz’s gulp-automation project, which inspired me to understand his code and implement my own from scratch, you can see why he argues for a different approach:
Many of them are based on installing a Chrome browser extension. However, in addition to being limited to the Chrome browser, extensions are potentially vulnerable. Extensions can also be guilty to slow down your browser, by adding unnecessary and slow running scripts blocking your useful content. As extension runs on every page in your browser, it doesn’t seem to be a good fit for the purpose of LiveReload.
Fixing our problem
For the more relevant part, let’s start discussing what we are going to do to solve the problem.
Run a server on Node using Gulp
We are going to use connect
as a server.
npm install --save-dev connect
Open your gulpfile.js
, require connect
and write a new task called server
:
// Require the connect npm plugin
var connect = require("connect");
// Initialize connect
var connectApp = connect();
// connect server
gulp.task("server", function() {
return connectApp.listen(8080);
});
For now, the connect server cannot do anything. It knows to listen to port 8080 but does not know what the root of the website/app is. I’m serving a static website that outputs to build/
and so will configure it as such using the serve-static
middleware for connect
:
npm install --save-dev serve-static
Editing our gulpfile.js
to reflect the new changes:
// Require the connect npm plugin and any additional middleware
var connect = require("connect");
var connectServeStatic = require("serve-static");
// Initialize connect
var connectApp = connect();
// connect server
gulp.task("server", function() {
return connectApp.use(connectServeStatic("build/")).listen(8080);
});
Now our connect
server knows to server static files from build/
when requested over port 8080.
Set up base for LiveReload to use
We’ll add connect-livereload
to our project. All it literally does is inject a piece of code to your files while the connect
server is running. This means that your built files will not include this piece of code.
Since it is a middleware for our connect
server, we’ll use it so.
Install it as usual:
npm install --save-dev connect-livereload
Require the module in your gulpfile.js
and modify the server
task to make use of it.
// Require the connect npm plugin and any additional middleware
var connect = require("connect");
var connectServeStatic = require("serve-static");
var connectLiveReload = require("connect-livereload");
// Initialize connect
var connectApp = connect();
// connect server
gulp.task("server", ["html", "scss", "js"], function() {
return connectApp
.use(connectLiveReload({ port: 35729 }))
.use(connectServeStatic("build/"))
.listen(8080);
});
If you refresh your page now, you will see the following code injected before the close of your body tag:
<script
src="//localhost:35729/livereload.js?snipver=1"
async=""
defer=""
></script>
connect-livereload
does not provide the actual script, it merely injects the code to make it available for gulp-livereload
to use, as we’ll see in the next section.
Start listening for changes
Since you’re here, you must be accustomed to gulp.watch
.
Currently, this is what our watch
task looks like:
gulp.task("watch", ["server"], function() {
gulp.watch("source/**/*.html", ["html"]);
gulp.watch("source/assets/**/*.scss", ["scss"]),
gulp.watch("source/assets/**/*.js", ["js"]);
});
We’ll install gulp-livereload
and modify our gulpfile.js
accordingly:
npm install --save-dev gulp-livereload
var gulpLiveReload = require("gulp-livereload");
gulp.task("watch", ["server"], function() {
// start the LiveReload server
gulpLiveReload.listen();
gulp.watch("source/**/*.html", ["html"]);
gulp.watch("source/assets/**/*.scss", ["scss"]),
gulp.watch("source/assets/**/*.js", ["js"]);
});
Call LiveReload within a task re-run to request a page refresh
Finally, for the magic, we’ll add a new pipe to our html
, scss
, and js
tasks to LiveReload when these tasks are called to be run again by gulp.watch
. Here is the scss
task as a sample:
gulp.task("scss", function() {
return sass("source/assets/**/*.scss")
.pipe(gulp.dest("build/assets/"))
.pipe(gulpLiveReload());
});
One key thing to note is that our server
task only runs after html
, scss
and js
tasks have been run once; our watch
task also runs only after server
task is run. This means that the first attempt by gulp-livereload
to reload the page will be futile because the LiveReload server is not yet listening for changes. This approach prevents unnecessary reloads on the first run of html
, scss
and js
tasks.
Shortcomings
Each task requires an additional pipe with a call to gulp-livereload
for my implementation to work. dmitriz’s implementation works differently and does not necessitate such a behaviour.
If there are any more shortcomings in your opinion, please feel free to open an issue on the GitHub project, or submit a PR.
An alternate approach
Another approach is to use gulp.watch
and livereload.changed()
like so:
gulp.task("watch", ["server"], function() {
// start the LiveReload server
gulpLiveReload.listen();
var watch1 = gulp.watch("source/**/*.html", ["html"]);
watch1.on("changed", function(event) {
gulpLiveReload.changed(event.path);
});
// gulp.watch('source/assets/**/*.scss', ['scss']),
// gulp.watch('source/assets/**/*.js', ['js']);
});
This will, however, reload the page before the html
task finishes running. To counter this, you would have to install run-sequence
and configure the order of tasks. That’s yet another module we would have to add to our project. Although it would bring the entire reload logic within a single watch
task, it is extensive and unnecessary in my opinion. The run-sequence
project page says it is a temporary fix and such a fix should not be required with the release of Gulp 4.0. Fingers crossed.