The bright side of caching
Caching is great when it comes to fetching large resources frequently: libraries, images or other assets. The mechanism is quite simple to explain and understand: the resources have a unique URL and based on that the browser saves on the user’s device the downloaded resource for future reuse.
Browsers can also receive instructions, from servers, for how long to cache content. In the case of large libraries (like main.dart.js, the current output of Flutter for the web platform), we’d like to have that cached forever, if nothing changes.
For example, my main.dart.js is currently ~2Mb in size. Fetching something that large over the network is both slow (requires many client-server roundtrips which takes time and the initial loading time is something that makes or breaks an app — web or mobile) and expensive (first, consider data costs incurred to the user, then to yourself serving that content).
So, having this mechanism in place saves us time and money. That's great, isn't it?
There is a side, an ugly side
Once the resources are cached, users will see, and use, those versions until the cache is cleared. That is not something happening very often, especially if the server instructed the browser not to do so.
Usually, to overcome this, users have to get into their browser’s settings, navigate through some intricate menus, find the right place where there is an option to empty its cache, then reload the website to get the new version of the resources.
Well, good luck explaining how to empty a browser’s cache to your clients (if you’re lucky enough to be able to reach out to them)!
The steps they have to take depend on their device, their operating system, and their preferred browser (and its version). For example, the same browser on Windows has a different location for that on macOS, not to talk of mobile.
Recently, I stumbled upon the “ugly side” of Flutter for web, a side that more developers will soon face.
I’m building a web-only app using Flutter and I constantly tinker it. I have a bunch of testers that play around the app and they report on bugs, request features and provide general feedback.
I sometimes got reports on bugs that I already fixed and realized quickly that the testers used cached versions of the app.
After creating your project your index.html, that is located in the web folder, will look like this:
In order to force browsers to reload our app we’ll add, each time we want that, an unique parameter to the main.dart.js script src (I called it version, though it can be anything, even just a random number after ?). The new index.html will look like:
No, no, no ugly side
This will guarantee the reload of your app and will still cache it for future reloads, until you decide to make a new update. Then you just increment your version number.
Still, there’s a small downside to this approach: previous versions will remain cached in the user’s browser for a while. If its something you can live with, fine. If not, a robust solution that can avoid this downside exists, though for it, you’ll have to do some server-side coding.
You can find more about caching here.