How to force the browser to reload cached CSS/JS f

2019-08-23 04:20发布

I have noticed that some browsers (in particular, Firefox and Opera) are very zealous in using cached copies of .css and .js files, even between browser sessions. This leads to a problem when you update one of these files but the user's browser keeps on using the cached copy.

The question is: what is the most elegant way of forcing the user's browser to reload the file when it has changed?

Ideally, the solution would not force the browser to reload the file on every visit to the page. I will post my own solution as an answer, but I am curious if anyone has a better solution and I'll let your votes decide.

Update :

After allowing discussion here for a while, I have found John Millikin and da5id's suggestion to be useful. It turns out there is a term for this: auto-versioning.

I have posted a new answer below which is a combination of my original solution and John's suggestion.

Another idea that was suggested by SCdF would be to append a bogus query string to the file. (Some Python code to automatically use the timestamp as a bogus query string was submitted by pi.). However, there is some discussion as to whether or not the browser would cache a file with a query string. (Remember, we want the browser to cache the file and use it on future visits. We only want it to fetch the file again when it has changed.)

Since it is not clear what happens with a bogus query string, I am not accepting that answer.

30条回答
叼着烟拽天下
2楼-- · 2019-08-23 04:41

Here is a pure JavaScript solution

(function(){

    // Match this timestamp with the release of your code
    var lastVersioning = Date.UTC(2014, 11, 20, 2, 15, 10);

    var lastCacheDateTime = localStorage.getItem('lastCacheDatetime');

    if(lastCacheDateTime){
        if(lastVersioning > lastCacheDateTime){
            var reload = true;
        }
    }

    localStorage.setItem('lastCacheDatetime', Date.now());

    if(reload){
        location.reload(true);
    }

})();

The above will look for the last time the user visited your site. If the last visit was before you released new code, it uses location.reload(true) to force page refresh from server.

I usually have this as the very first script within the <head> so it's evaluated before any other content loads. If a reload needs to occurs, it's hardly noticeable to the user.

I am using local storage to store the last visit timestamp on the browser, but you can add cookies to the mix if you're looking to support older versions of IE.

查看更多
看我几分像从前
3楼-- · 2019-08-23 04:42

Dont use foo.css?version=1! Browsers aren't supposed to cache URLs with GET variables. According to http://www.thinkvitamin.com/features/webapps/serving-javascript-fast, though IE and Firefox ignore this, Opera and Safari don't! Instead, use foo.v1234.css, and use rewrite rules to strip out the version number.

查看更多
狗以群分
4楼-- · 2019-08-23 04:42

For a Java Servlet environment, you can look at the Jawr library. The features page explains how it handles caching:

Jawr will try its best to force your clients to cache the resources. If a browser asks if a file changed, a 304 (not modified) header is sent back with no content. On the other hand, with Jawr you will be 100% sure that new versions of your bundles are downloaded by all clients. Every URL to your resources will include an automatically generated, content-based prefix that changes automatically whenever a resurce is updated. Once you deploy a new version, the URL to the bundle will change as well so it will be impossible that a client uses an older, cached version.

The library also does js/css minification, but you can turn that off if you don't want it.

查看更多
ゆ 、 Hurt°
5楼-- · 2019-08-23 04:46

I recently solved this using Python. Here the code (should be easy to adopt to other languages):

def import_tag(pattern, name, **kw):
    if name[0] == "/":
        name = name[1:]
    # Additional HTML attributes
    attrs = ' '.join(['%s="%s"' % item for item in kw.items()])
    try:
        # Get the files modification time
        mtime = os.stat(os.path.join('/documentroot', name)).st_mtime
        include = "%s?%d" % (name, mtime)
        # this is the same as sprintf(pattern, attrs, include) in other
        # languages
        return pattern % (attrs, include)
    except:
        # In case of error return the include without the added query
        # parameter.
        return pattern % (attrs, name)

def script(name, **kw):
    return import_tag("""<script type="text/javascript" """ +\
        """ %s src="/%s"></script>""", name, **kw)

def stylesheet(name, **kw):
    return import_tag('<link rel="stylesheet" type="text/css" ' +\
        """%s href="/%s">', name, **kw) 

This code basically appends the files time-stamp as a query parameter to the URL. The call of the following function

script("/main.css")

will result in

<link rel="stylesheet" type="text/css"  href="/main.css?1221842734">

The advantage of course is that you do never have to change your html again, touching the CSS file will automatically trigger a cache invalidation. Works very good and the overhead is not noticeable.

查看更多
闹够了就滚
6楼-- · 2019-08-23 04:47

For ASP.NET 4.5 and greater you can use script bundling.

The request http://localhost/MvcBM_time/bundles/AllMyScripts?v=r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81 is for the bundle AllMyScripts and contains a query string pair v=r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81. The query string v has a value token that is a unique identifier used for caching. As long as the bundle doesn't change, the ASP.NET application will request the AllMyScripts bundle using this token. If any file in the bundle changes, the ASP.NET optimization framework will generate a new token, guaranteeing that browser requests for the bundle will get the latest bundle.

There are other benefits to bundling including increased performance on first time page loads with minification.

查看更多
【Aperson】
7楼-- · 2019-08-23 04:48

For my development, I find that chrome has a great solution.

https://developer.chrome.com/devtools/docs/tips-and-tricks#hard-reload

With developer tools open, simply long click the refresh button and let go once you hover over "Empty Cache and Hard Reload".

This is my best friend, and is a super light weight way to get what you want!

查看更多
登录 后发表回答