Hot Reloading Everywhere

A presentation by Christophe Porteneuve at Confoo Montréal 2018
https://joind.in/talk/1cd00

whoami


const christophe = {
  family: { wife: 'Élodie', son: 'Maxence', [Symbol.guess]: '*Shh*' },
  city: 'Paris, FR',
  company: 'Delicious Insights',
  trainings: ['Web Apps Modernes', 'Node.js', 'Git Total',
    'ES Total', 'Webpack'],
  firstTimeInMontreal: true,
  lastTimeInMontreal: Symbol.noWay,
  webSince: 1995,
  claimsToFame: [
    'Prototype.js',
    'Ruby On Rails',
    'Prototype and Script.aculo.us',
    'Paris Web',
    'NodeSchool Paris',
  ],
}
          

What IS Hot Reloading

exactly?

What are we trying to achieve?

We want to avoid having to reload the full runtime
(web page, Node server, Electron host…) unless absolutely necessary.

We want to see the impact of our code changes instantly, the moment we save.

We want a fast workflow with instant feedback.

In a perfect world, we’d have this.

Hot Swapping

A well-known dev principle that means you can hot-replace running code in a runtime.

Possible in a number of runtimes, sometimes inherent to a language (Lisp, Erlang, Smalltalk, Elm, Elixir… VB6 *cough*).

VS has Edit and Continue for C# and VB.NET, too.


For CSS and images, browsers have been doing it forever, it’s a simple DOM update.

For JavaScript, this is currently exclusive to v8 (Chrome·ium, Safari, Node, Electron…)

Hot (Module) Reloading

For CSS and images, same thing as hot swapping.

For JS, entirely different: we re-run the script in the current host context (running web page, Electron host…). Not very useful in Node.

This comes with a number of gotchas we’ll highlight later.

Hot Swapping

You rev that v8!

Hot swapping Node

Usually makes more sense than HMR.

Unfortunately not supported by all IDEs / editors:

Chrome DevTools is great

WebStorm 2017+ is doing alright (well, sort of)

VS Code / VS don’t hot-swap Node 😢

Hot swapping Node

using off-the-shelf Chrome

  1. Open Chrome and open its Dev Tools (F12 / Cmd+Alt+J)
  2. Start your Node using --inspect (or kill -USR1 its process).
  3. You should see a Node icon pop up next to the Select / Device top-left icons: click it (not seeing it? this is a temporary bug: browse chrome://inspect instead)
  4. Go to the Sources tab, edit a file, “save” it: hot-swapped 🎉
  5. (optional) You can map the filesystem for on-disk persistence of your changes.

Demo

Hot swapping Node

using WebStorm

  1. Open your project’s directory in WebStorm
  2. Open Preferences, go to Build, Execution, Deployment > Debugger > Live Edit
  3. Enable Auto in (ms), leave it to 300 or set it (no lower than, say, 100)
  4. Create a launch configuration for your main file
  5. Launch the configuration in Debug mode
  6. Change your file as you see fit: it’s hot-swapped!

Major caveat: currently breaks on hard file save 🤔


Demo

Hot swapping on the client side

Code that runs in a browser

There are a variety of tools we can use, not all of which handle JS well

Hot swapping on the client side

Using DevTools

CSS / Images: all browsers let us play

JS: because v8 only so far, only Chrome·ium / Electron (not Safari, Firefox, Edge)

Workspaces let us persist, again.


Demo

What about LiveReload?

Oh, friend.

LiveReload is ancient

It requires a dedicated server

It has very different models depending on OS

It doesn’t hot-swap anyway

What about BrowserSync?

It’s pretty cool

Its core goal is not hot reloading

It doesn’t hot-swap anyway


Still, it’s a neat tool. You could blend it in your dev infra to do its thing: sync user interaction across browsers.

What about fb-flo?

Whoa, you’ve been around the block, haven’t you? 😎

Small Chrome ext + (embeddable) server. Was the first option for hot-swapping build-step JS code. We could edit our files anywhere, restoring Editor Freedom™.

I demonstrated it 3+ years ago, and it was cool.

DevTools filesystem mapping now detects file changes, hot-swaps automatically: we don’t need fb-flo anymore. Not maintained anyway.

HMR

Hot Module Reloading

The usual deal

What most people refer to as hot-reloading these days.

This basically means Webpack*

Other tools either rely on Webpack for this (Grunt, Gulp)
or have a kludgy implementation (Browserify)**.

Relies on a client/server system: not bound to any editor.

Per-module, hence super fine-grained.

* All demos that follow use Webpack 4
** browserify-hmr only supports a single bundle, making it quite irrelevant, and is unmaintained.

A basic HMR demo

Serve with either webpack-dev-server in hot mode,
or a custom server that embeds webpack-dev-middleware and webpack-hot-middleware.

Detect on the client side and enable:


if (module.hot) {
  module.hot.accept() // This is called a “self accept”
}
              

Keep an eye on the console for WDS/HMR notices.


Demo


A word about the parts of the dependency graph being hot-reloaded…

Explore HMR in Webpack docs: concept, guide, API.

What about client state then?

HMR re-runs your modules, so… we lose it?

Like, in-memory state from active instances? (React/Vue/Angular/etc. components, etc.)

Well, by default, this would be scratched. Also, lifecycle mayhem may ensue.

So you would need a framework-specific layer on top.

Luckily, some such layers exist!

HMR with React

using React Hot Loader 3…

It was a bit involved:


"plugins": ["react-hot-loader/babel"]
                

entry: {
  main: [
    'react-hot-loader/patch',
    // …
  ]
}
                

import { AppContainer } from 'react-hot-loader'

function renderApp(Component) {
  ReactDOM.render(
    <AppContainer><Component/></AppContainer>,
    document.getElementById('root')
  )
}

renderApp()
if (module.hot) {
  module.hot.accept('./App', () => {
    renderApp(require('./App').default)
  })
}
                

HMR with React

…using React Hot Loader 4 (RHL4)

Much simpler for 99% use cases:


"plugins": ["react-hot-loader/babel"]
            

import { hot } from 'react-hot-loader'

// (App component here, be it SFC or class-based)

export default hot(module)(App)
            

Demo

(Not baked in Create React App, but you can easily rewire without ejecting.)

HMR with Vue

built in vue-loader

Anything based on vue-loader has auto Vue-specific HMR 🎉

However, due to Vue’s current handling of <script> tags and component lifecycle hooks, this still loses state on script change (template / style changes are fine).

This seems like something that could be improved with good heuristics. Not slated yet though.


Demo

HMR with Angular?

I wish.

Angular (and yes, we mean latest Angular, not ol’1.x) doesn’t lend itself well to per-module HMR.

Everybody either doesn’t do HMR or respawns the entire app tree in the page, which seems only marginally better than full-page reload IMHO.

See this article. It’s the most-readable version of this dominant solution that I could find.

If someone has a kick-ass solution available, come talk to me later! 🙏🏻

HMR with Ember?

Nowhere to be found 😢

I looked really hard. I asked the team. No dice.

Here’s a Canadian (Vancouver) Tomster as consolation:

The Vancouver Tomster

TL;DR

  1. Use Chrome’s Node-specific DevTools to hot-swap in Node
  2. Use Chrome’s DevTools to hot-swap in the client, if you do need hot-swap
  3. Use Webpack and its HMR feature to:
    • hot-reload your vanilla modules with full control
    • hot-reload whilst preserving client state (through React Hot Loader or Vue Loader), when available

Thank you!


Christophe Porteneuve

@porteneuve

Slides are up at bit.ly/confoo-hotreload
Code samples are on the GitHub repo

https://joind.in/talk/1cd00