Nando @ Aria Media

( learning Clojure ColdFusion Lucee Javascript jQuery Angular CSS Linux Apache HTML5 & etc )

Hot Browser Reloading With Browsersync

| Comments

I’ve fallen down the rabbit hole of front end development in the last few weeks, and in that time Flexbox CSS, Bootstrap v4, Foundation, Sass / SCSS, and Gulp have all whizzed by as I’ve taught myself portions of each, and today I landed at the bottom, Browsersync.

In a nutshell, Browsersync will live reload one or more browsers for you whenever your project’s files change. It will also live reload external devices pointed at the same web application. This allows you to simultaneously and efficiently test a responsive design in multiple browsers, phones and tablets - without needing to touch or reload any of them. It also synchronizes form entries and clicks across browsers and devices. For example, with my phone and tablet connected to Browsersync, I log into the app I’m developing on the tablet, and my keystrokes and touch gestures are replicated on the phone, and I’m logged in on both. Then I start navigating in the application in Chrome on my desktop, and the phone, tablet and other browsers follow as “slaves”. Interacting with any interface causes the rest of them to follow. What an incredible tool! In the few minutes I’ve been experimenting with it, I was able to easily and quickly fix a layout issue on the tablet and phone for the login screen.

Browsersync works by wrapping your application or website in its own Node server process, on another port. You can start Browsersync on the command line, or via a Grunt or Gulp task. During startup, you specify the local url of your app, the files you want it to watch - a number of other options are possible - and then a browser window opens, on port 3000 by default, and browsersync is activated for that browser. To add others, you simply use the same url. To add external devices, on localhost:3001 there is an admin panel for Browsersync that will list the external url to use. Navigate to that external url on your phone or tablet or another computer, for me it currently http://192.168.1.103:3000/, and Browsersync is set up on these devices as well. Once you have it configured, it is really simple to use.

A quick overview on installation and configuration: Basic instructions are on the homepage https://www.browsersync.io/.

  1. Install or upgrade Node.js
  2. Install Browsersync using npm
1
npm install -g browser-sync

The -g flag means global so it is available from anywhere.

  1. Start Browsersync

There are 2 start modes, –server and –proxy. server is for static files, proxy is for applications running on a server. Here’s a simplified example starting Browsersync from the command line:

1
browser-sync start --proxy "myproject.dev" --files "css/*.css"

… assuming you have your local host file set up to point your app at myproject.dev. Or you can also start Browsersync using a localhost:port url, like this:

1
browser-sync start --proxy "localhost:8500/myproject" --files "css/*.css"

There are a few real world ways to do this, but what I’ve done is to create a gulpfile.js in the root of my project so I can configure Browsersync conveniently and start it with a simple gulp command after cd’ing in to my project directory. That way I don’t have to remember much. Here’s my first working gulpfile.js for a real development scenario. I have ACF (Adobe ColdFusion) installed on port 8501 on my dev laptop, and gulp.watch() is configured for an FW/1 app. I’m not sure if I actually want to watch the controllers and services directories, perhaps the views directory and CSS directory is enough. But for now I will leave it like this and see how it goes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var gulp = require('gulp');
var browserSync = require('browser-sync').create();
var reload = browserSync.reload;

gulp.task('browser-sync', function() {
  browserSync.init({
      proxy: "localhost:8501/myproject"
  });

  gulp.watch([
      'controllers/*.cfc', 'services/*.cfc', 'views/**/*.cfm', 'dist/*.css'
  ]).on("change", function() {
      console.log("Watch hit");
      browserSync.reload();
  });
});

gulp.task('default', ['browser-sync']);

Note carefully the syntax of gulp.watch() for multiple directories / files. It’s an array of strings separated by commas. Note also that the root of the relative paths specified is where the gulpfile.js is located.

But wait, wouldn’t it be better if I could use Browsersync to develop against both ACF and Lucee? Here’s a solution that I have working that creates named instances of Browsersync so they don’t step on each other:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
var gulp = require('gulp');
var bsacf = require('browser-sync').create('browserSyncACF');
var bslucee = require('browser-sync').create('browserSyncLucee');

gulp.task('browser-sync', function() {
  bsacf.init({
      proxy: "localhost:8501/myproject",
      browser: "google chrome",
      reloadOnRestart: true
  });

  bslucee.init({
      proxy: "localhost:8888/myproject",
      browser: "vivaldi",
      port: 3010,
      reloadOnRestart: true,
      ui: {
          port: 3011
      }
  });

  gulp.watch([
      'controllers/*.cfc', 'services/*.cfc', 'views/**/*.cfm', 'dist/*.css'
  ]).on("change", function() {
      console.log("Watch hit");
      bsacf.reload();
      bslucee.reload();
  });
});

gulp.task('default', ['browser-sync']);

I specify separate browsers for each so that ACF and Lucee sessions don’t step on one another, and separate ports for the Lucee instance of Browsersync so they can coexist. The advantage to doing it this way is I can start both instances with a single gulp command on the command line. Unfortunately the ACF and Lucee instances of Browsersync will not mirror or ghost each other, so I will still have to click and enter test data on forms separately while developing, but this is still much better than needing to reload both browsers manually all the time.

Update:

I’ve noticed one other feature of Browsersync that I wanted to see if I could get working - injecting modified CSS into the browser to avoid reloading the DOM. I’ve now rearranged my gulpfile.js to be the following, adding a second gulp.watch() call to inject the minified bootstrap css directly into the browser, and removing the dist directory from the first gulp.watch() call so the browsers are not reloaded.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
var gulp = require('gulp');
var bsacf = require('browser-sync').create('browserSyncACF');
var bslucee = require('browser-sync').create('browserSyncLucee');

gulp.task('browser-sync', function() {
  bsacf.init({
      proxy: "localhost:8501/easycal",
      browser: "google chrome",
      reloadOnRestart: true
  });

  bslucee.init({
      proxy: "localhost:8888/easycal",
      browser: "vivaldi",
      port: 3010,
      reloadOnRestart: true,
      ui: {
          port: 3011
      }
  });

  gulp.watch([
      'controllers/*.cfc', 'services/*.cfc', 'views/**/*.cfm',
      'layouts/**/*.cfm', 'dist/*.css', 'easycal-app.css'
  ]).on("change", function() {
      console.log("Watch hit");
      bsacf.reload();
      bslucee.reload();
  });

  gulp.watch([
      'dist/toolkit.min.css'
  ]).on("change", function() {
      console.log("CSS hit");
      return gulp.src(['dist/toolkit.min.css'])
          .pipe(bsacf.stream())
          .pipe(bslucee.stream());
  });
});

gulp.task('default', ['browser-sync']);

I like how this is shaping up now. :-)

Comments or suggestions welcome!

Comments