How to clear browser cache when launching updated version of application? - angularjs

I am deploying my UI application which is made up of AngularJS, Grunt and other UI things.
Whenever i update my UI and redeploy it using apache-tomcat, Browser is always getting my resources from cache. (As the path of new resources like css, html are same)
User needs to do CTRL+F5 (Hard reload) to clear cache and use new resources.
How can i force browser to clear cache when i redeploy application?
If somehow i can use angular + grunt to do this task than also it is
fine.
I don't want to update the versioning of my files each time i redeploy the product. As my files internally downloading many html files as well.
So somehow i want to clear the browser cache only.
Thanks!

You can't update cache without filenames versioning. But you don't have to do it manually. If you use Grunt, just check out some npm packages. Like this one for example: https://www.npmjs.com/package/grunt-cache-breaker
On rebuild it will create new hashes for your assets and inject them into your html files. So when your users will download your html files (they are downloaded, they are not cached), these html files will contain assets like css and js with new names, so the browser will force download instead of taking these files from memory.

You can add version to your html files
app.factory('preventTemplateCache', function () {
return {
'request': function (config) {
if (config.url.indexOf('partials') !== -1) {
config.url = config.url + '?t=' + 101;
}
return config;
}
}
})
and also give version to .js files as
<script src='app.js?v=101'></script>
101 is version number

Related

React App in Azure Website caching issue, browser serves old version

I have a react app which works perfectly fine.
However we are pushing code after NPM BUILD and deploying the code manually via SFTP
We can see the JS and CSS files have different names.
However the browser keeps downloading css and js files form cache, even if I disable cache in the browser.
I tried deleting all files in FTP, and magically the website keeps working? so it looks all the files are retrieved from the browser cache even if nothing exists in the server
I tried stopping and starting the azure website, but didnt make any difference
I tried cleaning the browser, cache, history, etc, no difference.
I wonder if I need to setup something in web.config, or in Azure website settings to make this works
This seems to be not an issue with Azure app service (Web app), you need to remove the caching of the react app with the following steps
Step 1: adding the following to in index.html
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
Step 2: inserting the following to the js
import { unregister } from './registerServiceWorker';
and call
unregister()
Reference Answer
It depends on how your app is configured, but I would not recommend disabling cache on the JS & CSS. Instead, it's best to add some version-dependent information to the file name so every time the JS or CSS update the file name changes.
Last time I worked on a web project we had it such that our JS & CSS had a content hash at the end. Something along the lines of main.205199ab45963f6a62ec.js instead of just main.js. Also note that you don't even have to manage that hash yourself, as there are ways to get ASP.NET or webpack (etc...) to update the reference in the HTML/JSX for you.
Here's webpack's page about it: https://webpack.js.org/guides/caching/

Cache busting with CRA React

When I updated my site, run npm run build and upload the new files to the server I am still looking the old version of my site.
Without React, I can see the new version of my site with cache-busting. I do this:
Previous file
<link rel="stylesheet" href="/css/styles.css">
New file
<link rel="stylesheet" href="/css/styles.css?abcde">
How can I do something like this or to achieve cache busting with create react app?
There are many threads in the GitHub of create react app about this but no one has a proper/simple answer.
EDIT: create-react-app v2 now have the service worker disabled by default
This answer only apply for CRA v1
This is probably because of your web worker.
If you look into your index.js file you can see
registerServiceWorker();
Never wondered what it did? If we take a look at the file it got imported from we can see
// In production, we register a service worker to serve assets from local cache.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on the "N+1" visit to a page, since previously
// cached resources are updated in the background.
// To learn more about the benefits of this model, read {URL}
// This link also includes instructions on opting out of this behavior.
If you want to delete the web worker, don't just delete the line. Import unregister and call it in your file instead of the register.
import { unregister } from './registerServiceWorker';
and then call
unregister()
P.S. When you unregister, it will take at least one refresh to make it work
I had the same issue when I use create-react-app ( and deploy to heroku). It keeps showing the old version of my app 😡.
I found the problem seems to be on the browser side, it caches my old index.html with its outdated js bundle
You may want to add the following to your server side response header
"Cache-Control": "no-store, no-cache"
or if you are also using heroku create-react-app-buildpack, update the static.json file
"headers": {
"/**": {
"Cache-Control": "no-store, no-cache"
}
}
I think in this way you can still keep that poor service worker 😂, and the latest content will be shown on the N+1 load (second refresh)
Hope this helps...
As mentioned by some of the previous answers here, both the service worker and the (lack of) cache headers can conspire against you when it comes to seeing old versions of your React app.
The React docs state the following when it comes to caching:
Using Cache-Control: max-age=31536000 for your build/static
assets, and Cache-Control: no-cache for everything else is a safe
and effective starting point that ensures your user's browser will
always check for an updated index.html file, and will cache all of
the build/static files for one year. Note that you can use the one
year expiration on build/static safely because the file contents
hash is embedded into the filename.
As mentioned by #squarism, older versions of create-react-app defaulted to opt-out of service worker registration, while newer versions are opt-in. You can read more about that in the official docs. It's quite a straightforward process to match you configuration to the latest template if you started with an older version of create-react-app and you want to switch to the new behaviour.
Related questions:
How to avoid caching for create-react-app
ReactJS: How to prevent browser from caching static files?
how to clear browser cache in reactjs
If your problem is with resources statically referenced in index.html, such as .css files or additional .js files (e.g. configuration files), you can declare a React environment variable, assign a unique value to it and reference it in your index.html file.
In your build script (bash):
REACT_APP_CACHE_BUST={e.g. build number from a CI tool} npm run build
In your index.html:
<link rel="stylesheet" href="%PUBLIC_URL%/index.css?cachebust=%REACT_APP_CACHE_BUST%" />
The variable name has to start with REACT_APP_. More about environment variables in React: https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables.
It appears that they changed from opt-out to opt-in with regards to the service worker. Here's the commit that changed the README and it has examples similar to Kerry G's answer:
https://github.com/facebook/create-react-app/commit/1b2813144b3b0e731d8f404a8169e6fa5916dde4#diff-4e6ec56f74ee42069aac401a4fe448ad

react + webpack - pass POST data to build

Coming from a PHP background, I used to have an index.php which does two things:
serve the webpage if no parameters were set;
or serve JSON data when a specific POST parameter was included in the request.
Something like this:
// -- index.php
<?php
if ($_POST["some_parameter"]) {
...
echo json_encode(someArrayData);
exit(0);
}
?>
<html>
...
</html>
I have built the complete frontend application with npm, webpack, webpack-dev-server, and react. Having completed the first part, how can I effectively serve JSON data instead of HTML when a request includes a specific POST parameter?
I can see 2 ways of doing this:
Build the frontend as usual and everytime I build the bundle, modify index.html, inject my PHP code in it, and rename it to index.php. I then would have to run this folder via apache or nginx, so I'd be able to run the index.php script. This method is downright ugly and is probably the worst way to do it.
Run a separate PHP server which just serves data or redirects to the static webpack-generated build. All requests should then start from this server, and this server determines whether to serve data or redirect to the frontend. The problem comes to neatly passing the POST data received from the request to the static react app. As far as I know, the only way to do this would be to include a URL (GET) parameter to the redirect and manually parse it with javascript on the frontend. This is a dirty solution, in my opinion.
So, to summarize:
I need an efficient way to get POST data in a react/webpack/webpack-dev-server environment.
It should work with my hot-module-replacement dev setup.
I'm fine with switching to a node-based backend like express.
There shouldn't be any ajax involved in the static react app.
Any ideas? There has to be a way to do this properly.
UPDATE: I solved this by simply copying an index.php from my source directory to my build directory via the webpack config. I serve the build folder to a PHP server and keep a webpack --watch building my source.
I lose built-in features like auto-reload and css injection, but it's worth the convenience of not having to implement SSR for a very simple task (getting a single POST variable).
For anyone interested, I also added 2 npm scripts:
npm run start runs my original webpack-dev-server with hot-reload, serving static content including a static index.html file
npm run static runs the webpack --watch which copies the index.php file to the build directory
This lets me have hot-reloading when developing frontend, and allows POST data fetching when programming logic.
It's easy, convenient, and works on most web hosting providers.

AngularJS: Development and switching backends using Gulp

I need to switch my application server backend during development. Currently, the server is hard-coded in a module as a string.
I was looking around for a good way to switch the server using gulp.
I am unsure how to achieve something like gulp watch localhost or gulp watch devserver, where the last argument denotes the backend server?
You might have a configuration file for every different config, for example dev.constant.js and local.constant.js.
Each of these files contains an angular constant wich holds your current config, including your backend's url:
angular
.module('myFancyModule')
.constant('config', {
backendUrl: 'https://my.backend.com/api/'
});
Using yargs and gulp-if, you can check if a flag (e.g. --dev) is set and add the corresponding config file to the stream using gulp-add-src.
Another, more safe way is, to copy the particular config file to a file called config.constant.js once.
var argv = require('yargs').argv;
var fs = require('fs-extra');
gulp.task('watch', function() {
if(argv.dev) {
fs.copySync('./config/dev.constant.js', './config.constant.js');
}
//Your watch task here
}
Doing it this way, you are able to change your configuration very quick and easy. You could use this for builds and deploys as well, if you're doing them via gulp.

AngularJS, Cordova/IonicFramework: load index.hml from server

Background:
I'm loading my entire AngularJS Cordova/Ionic web-app from the server. This is amazing. I can change the app without going through Apple.
Questions:
Q1) How can I use js-files.zip, loaded from server, in my index.html file?
Q2) How can I effectively load index.html startup logic from my server?
Problems:
On some older devices, loading time is too high if I load all my .js files from my server, so I want to be able to configure that logic (in index.html) also from the server.
The only thing I'm not loading from the server is the content of index.html
So, how can I essentially load index.html from a server?
If i try to do that
1) Pulling, say, indexfromserver.html using ajax and doing html rewrite of index.html with document.write(res), then there are AngularJS problems:
E.g., module missing errors (*1 below), because the following isn't in index.html until after the ajax response rewrites index.html:
<body ng-app="myapp" ng-controller="MainCtrl">
1.1) I can include the necessary modules in the initial local index.html, but then if I rewrite index.html, I'll get these errors/warnings about classList null in ionic (ask me for details), deviceready not fired, and angularjs loading more than once
2) I can redirect index.html to, say, indexfromserver.html, but then all my $http responses are rejected promises.
Regarding 2) I've been told I should be able to add a controller for indexfromserver.html or specify $urlRouterProvider.otherwise('/app/indexfromserver');
This hasn't fixed the $http requests from being rejected.
I don't understand exactly how index.html is involved in making $http work correctly, as it doesn't have a controller and isn't the 'otherwise' route provided. I only see mention of index.html in config.xml so far.
Request:
Can someone please post a snippet of a typical example how an AngularJS Cordova/Ionic app could effectively use index.html logic that's loaded from a server?
Otherwise, can someone show/explain if/how I can use js-files.zip from server, and uncompress and use in index.html?
Details/Notes:
(*1)
Uncaught Error: [$injector:modulerr] Failed to instantiate module myapp due to:
Error: [$injector:nomod] Module 'myapp' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
http://errors.angularjs.org/1.2.17/$injector/nomod?p0=myapp
It was pointed out that you can use JavaScript in certain situations to unzip an archive, but that is not very common. The web does not work like that, Ionic runs inside of a browser so you should use the same processes as you would to optimize a website.
You would build the app instead like any website. Take the following steps to create a more optimized app for loading quickly. If you aren't familiar with build tools, take a look at http://yeoman.io/, and this specific generator for Ionic https://github.com/diegonetto/generator-ionic.
Here is a very basic list of some steps you can take, though the generator provides a few more options and other things could be added as well.
Concat and minimize your app's JavaScript into single file.
Concat and minimize your app's CSS into a single file.
Compress your angular templates with a tool like this https://www.npmjs.org/package/grunt-angular-templates into a single JS file.
Deploy static assets to server.
Link to above assets in index.html.
Ultimately the goal is to optimize the assets so you don't have to load a lot of files, and each file is as compressed as possible.
If you want to go the zip file route, and you assume your users aren't always connected to the internet, probably the best way is to:
Check if the version is new via a server call, and if so, download the zip file, extract it (maybe via stuk.github.io/jszip/), and use a Cordova interface to write the new JS code to the phone's memory, and run the code by adding some script tags in your loader.

Resources