How to force Firefox to bypass BFCache for Angular

2019-02-16 17:11发布

问题:

I'm working on an Angular.js page and making changes to an html 'partial'. Refreshing the page causes Firefox to correctly re-request the main html page from the server, but subsequent 'partial' templates that are rendered client-side are never re-requested and instead grabbed from the BFCache, so changes to those files aren't detected.

Screenshot from Firebug:

I can confirm via the development server (Django) that those partials are never requested.

I've tried every kind of refresh, including the Reload Plus extension.

回答1:

These are less than ideal, but two options appear to address the problem.

One is the Clear Cache addon, which can be configured to clear disk and memory cache and reload the current tab. I'm not sure if this clears the cache completely or just for the current tab's domain, but it's probably the entire cache.

The other is to disable the browser cache in firebug:

I'm not sure if this simply disables the cache for the current page, current domain, or everywhere.

It'd still be nice to have a 'Reload this page and everything referenced by this page' option.



回答2:

I would just set a flag in your templates to add a simple onunload function like this:

{% if CLEARBFCACHE %} <body onunload="myFunction()"> {% endif %}

Then set CLEARBFCACHE = 1 (in settings.py) when in development.

If you need to test for production set CLEARBFCACHE = 0 then deploy to your staging server or (if you don't have a separate server) I believe you can change the url from 127.0.0.1 to locahost to make Firefox think it's a different site.



回答3:

Another option would be to include the partial in the following manner:

angular
    .module("app")
    .directive("appPartial", function () {
        return {
            templateUrl: "partials/partial1.htm"+getTempStr(),
            restrict: "E",
            scope: {},
            controller: PartialController,
            controllerAs: "vm"
        }
    });

function getTempStr(){
    // dev
    return "?a=" + (new Date().getTime()).toString();
    // prod
    //return "";
}

This way each time the partial is loaded it will not be stored in the BFCache.

To utilize the BFCache in the production environment you could uncomment the 'return ""' line instead of the other return line.



回答4:

Another option is to have your development web server send a Cache-Control header for at least html files:

Cache-Control: no-cache, max-age=0, must-revalidate, no-store

Source: How to Defeat the Browser Back Button Cache

Then all the files served from the development server will never be cached, but files from e.g. CDNs will be cache, so you don't have to retrieve those on each refresh.



回答5:

Because you are making a single page application. So the correct solution should be:

(1) set physical page (ie, main page) to "Cache-Control: No-cache". The physical page should be small, because logical pages are all dynamically loaded by JavaScript, so loading physical page itself should be fast

(2) for "partial" page (ie logical page), change the folder name every time when you release a new build,

For example: when a new release is deployed, assume the physical page is index.html

Inside index.html, All JavaScript, css and angularJs Template file are all in a folder asset-{{TimeStamp}}. Because index.html has no cache, so browser will always get latest index.html. Because all JS , css and other template html files are in different folder from last release. so the browser will load all files from the new folder rather than from cache.

you can create a build process to do it automatically: search all js , css files in index.html and replace folder name asset/** with asset-{{TimeStamp}}/**, then copy all files to asset-{{TimeStamp}} from asset folder

Now you don't have headache cache issue but browser do leverage local cache to speed up your web page .

(3) for angularJs template file, because it is also loaded by JS, so we strongly suggest you use relative path ( related to currently running JS code) to get it. some sample JS code like this:

function _getCurrentJSPath() {
    var scripts = document.getElementsByTagName("script");
    var currentFile = scripts[scripts.length - 1].src;
    var currentPath = currentFile.substr(0, currentFile.lastIndexOf('/')) + "/";
    return currentPath;
}
...
 templateUrl: _getCurrentJSPath() + 'currencyField.html',