Blackball Software

View Original

Live reloading javascript modules without refreshing the browser

Live reloading has been around for a decade or more and has made the lives of developers much easier and much more efficient.

However, while CSS updates are seamlessly refreshed into your page, I am yet to find a plug-in-play equivalent for javascript modules. This makes sense - CSS is stateless and furthermore sits completely outside of the DOM and Javascript “states”. Browsers are designed to inject CSS into a page at any point during the page lifecycle, so a “hot-reload” isn’t really an issue.


Note - some examples in this article are written in Typescript because, well, that’s how I write javascript these days. Remember however that Typescript is not executed by the browser - it is converted to regular ES5-compatible javascript before being sent down to the browser. The live-reloading is working against regular javascript, not Typescript.


Why live-reloading javascript is difficult

Javascript has many features which make hot-reloading much much more difficult. To name a few…

Javascript is stateful

If your module has any kind of property, then chances are that property is designed to change and hold its value through the course of the module’s lifetime.

Javascript is instantiated

Your browser will download javascript files, but these are not what you or your users are actually interacting with. More specifically, then are interacting with an instantiation of that file. Consider a module named PersonEditor.js:

See this content in the original post

It would be a simple matter to detect changes to this file and reload it to the browser, but it’s not the file that’s being used, instead it is an instantiation of it::

See this content in the original post

In this case, it is the editor object that you want to live-reload. This instantiation could be when the page loads, or it could be a dozen steps/pages into a Single Page Application. It is likely the result of a user interaction like a button click. There may be more than one current instantiation.

How on earth could the browser know any of this?

Fixing the instantiation problem

The answer to the above is that it is impossible for the browser to know how and where your modules are being used. It will always be vulnerable to the nuances of your particular coding style. So, to get around this, we flip it on it’s head - there is only one part of your code that knows where and when your modules are being used, and that is the modules themselves. Let’s write a base class that has a little more self-awareness than your average javascript module:

See this content in the original post

Listening for the reload

Just a quick note here - in our implementation we have a FileWatcher in C# which pings out a notification over websockets to give us real-time reloads. But worst case, there’s nothing wrong with just downloading the file again every second or so, even if it hasn’t changed. Yes, it hammers your server but you’re only on your development machine and is far less than a production site would endure from 1000’s of concurrent of visitors.

Fixing the stateful problem

Okay, this is the juicy bit. I’ll give you the source code first:

See this content in the original post

I’ve commented the pertinent parts in code, but the gist is:

For properties

If the property is new, just add it and the value to the current module. If the property already exists then do nothing - we want to maintain the state of the current module.

For functions

Just override them - there is no “state” in a function, so this is pretty easy.

Summary

I can’t imagine a “one size fits all” solution for javascript merging, but you can see that with just a few lines of code in the appropriate place within your code base then it is possible to achieve live-reloading for javascript. We have been using this for a few months now and it works very well. The code above deals with the nuances of how we at Blackball Software build our modules and I expect you may have to tweak it for edge cases within your own code, but the principle remains sound.

Have fun.