Lesson Monday

Next, let's learn how to use gulp to streamline our build process and add a development server complete with live reloading.

So far, we are taking our development files, creating optimized versions of them wherever we can, and then placing them in a build folder. The idea of deploying is to move all of these files onto a server.

Development Servers

To this end, we're going to start using a local server for development, and we want our build pipeline to update the correct files on the server and reload the browser if necessary when the files have been changed during development.

BrowserSync

We're going to use a package called BrowserSync to implement our development server with live reloading. First, let's download it with NPM as always:

$ npm install browser-sync --save-dev

Next, let's remember to require it in our gulpfile.

gulpfile.js
var browserSync = require('browser-sync').create();

Gulp Tasks

We have created a variable, browserSync, and set it equal to the create function, which is the part of the browser-sync package we will use to create our server. Now, make a task to start that server.

gulpfile.js
[...] 

gulp.task('serve', function() {
  browserSync.init({
    server: {
      baseDir: "./",
      index: "index.html"
    }
  });
});

[...] 

We are calling browserSync.init() and passing in some options telling browserSync to launch the local server from the directory that we are currently in (baseDir: "./",) and we are telling it that the entry point, the place where we want to start our app, is our index.html file. Now we can run gulp serve from the top level of our project directory to launch our server and run the app.

Live Reloading with BrowserSync and Gulp

Now we're going to learn how to automatically replace the files on the server and reload the browser when our JavaScript changes. To do this, we will use a new gulp method called watch. When we call gulp.watch() we pass in 2 arguments. The first is an array of file names that we want gulp to keep an eye on. The second argument is an array of tasks to run whenever any of the aforementioned files change. We'll add a call to gulp.watch inside of our serve task so that the files are being watched automatically as soon as we start the server.

gulpfile.js
[...] 

gulp.task('serve', function() {
  browserSync.init({
    server: {
      baseDir: "./",
      index: "index.html"
    }
  });

  gulp.watch(['js/*.js'], ['jsBuild']);
});

[...] 

This says to watch all of the files inside of our development js folder and whenever one of the files changes, run the task jsBuild. Let's make that task next:

gulpfile.js
[...] 

gulp.task('jsBuild', ['jsBrowserify', 'jshint'], function(){
  browserSync.reload();
});

[...] 

This task lists an array of dependency tasks that need to be run whenever any of the js files change. We want to run the linter and we want to run jsBrowserify and its dependencies. The linter can be run at the same time as we concatenate and browserify our files since they are independent from each other. Then once those are complete, we use the task function to call browserSync.reload() and reload the browser.

Incidentally, we could have called minifyScripts here instead of jsBrowserify, but since we are working on a task for the local development server here, we can assume a development environment and ignore the production build tasks.

Let's add a watcher for our Bower dependencies next. This line will also go at the bottom of our serve task.

gulpfile.js
[...] 

gulp.task('serve', function() {
  browserSync.init({
    server: {
      baseDir: "./",
      index: "index.html"
    }
  });

  gulp.watch(['js/*.js'], ['jsBuild']);
  gulp.watch(['bower.json'], ['bowerBuild']);

});

[...] 

Now, we are watching the Bower manifest file for changes so that whenever we install or uninstall a frontend dependency our vendor files will be rebuilt and the browser reloaded with the bowerBuild task. Let's write that task next.

gulpfile.js

[...] 

gulp.task('bowerBuild', ['bower'], function(){
  browserSync.reload();
});

[...] 

We can keep adding as many watchers and build tasks as we need using this format. Just remember that any time you change a gulp task, you must restart the server.

Note: As you structure your gulpfile, it's very important to be aware of task dependencies. By default, gulp runs all tasks simultaneously. So you must use dependency arrays to specify if a task must be completed before another one. For example, in our jsBuild task, we know that jsBrowserify and jshint will both be completed before the browser reloads because they are listed in the array of dependencies. But there is no guarantee that jshint will be completed before or after jsBrowserify. If we needed the linter to finish before jsBrowserify was started, it would need to be listed as a dependency of jsBrowserify.

Terminology


  • Deploying: Moving a project's files to a web server for use.

  • BrowserSync: An npm package used to implement development servers with live reloading capabilities.

Examples


Example Gulp task to launch a BrowserSync development server with live reloading:

gulpfile.js
gulp.task('serve', function() {
  browserSync.init({
    server: {
      baseDir: "./",
      index: "index.html"
    }
  });
});

Tips


  • By default, gulp runs all tasks simultaneously. So you must use dependency arrays to specify if a task must be completed before another one.