In my ASP.NET page I have
<script src="Common2.js" type="text/javascript" ></script>
How can I force the clients to never cache it so that it is always loaded fresh.
You can add a random query variable to the end of the file path
<script src="Common2.js?randomvar=<SOME RANDOM GUID>" type="text/javascript" ></script>
That will ensure it never caches.
One method, as mentioned by Zoidberg, is to put a random number (or the current time, etc) at the end of the url. However, you probably don't want the clients to never cache it. Caches, used properly, are beautiful things.
Consider using something which only changes when the file does, eg: the file's last-modified time, the file's revision number, or a hash of the contents of the file, etc. This way, the clients will always get a fresh version of it, but only when it's actually fresh!
Take a look at the source of this site:
<script src="http://sstatic.net/so/js/question.js?v=5290">
Related
I've noticed on many sites the JavaScript file has it's regular name then ?v=something The something can be alphanumeric, or just random numbers.
For example:
<script src="./bundle.js?v=21knfa"></scrip>
I have a few questions about this:
The purpose:
What is the purpose of adding ?v=something on the file name. For example, does it allow the developers to have multiple versions of their code?
Server or Client
Is this done by the server or the client?
How would this be done
How would you add the version automatically to your JavaScript filename, does it involve a software like Docker to do this? Or is this done in another way.
Any help would be appreciated.
The Purpose
It is for client-side caching.
If you have a JS file script.js, the users browser will cache it. When you change script.js, the browser will still have the old cached version.
So, you add a ?v=... to it so when you make changes, the browser will load the new one.
Since it is in the HTML file, it is the server that sets the ?v=...
To do this yourself, simply add the ?v=...
<script src="javascript.js?v=1.0.1" type="text/javascipt"></script>
Example
Say you make a script for your site and name it "script.js".
In your code you put:
<script src="script.js?v=1" type="text/javascipt"></script>
Now when you change "script.js", you change your code as follows:
<script src="script.js?v=2" type="text/javascipt"></script>
Now your visitor's browsers will get the new "version" of your script file.
Also
You could use ?foo=... or really any other string.
I need to clear browser cache when I push an updated javascript file on server. A simple answer would be to use below technique of query string.
<script type="text/javascript" src="/js/myjsfile.js?{my file version}"></script>
It would work but
Do I need to do this on every single script tag of every single
page of my application?
Can I do this at main screen like login which loads at the beginning
and I assume that would clear the cached file with new one, would it
work?
"Do I need to do this on every single script tag of every single page of my application?"
Yes you do. The cache is based on the file's url, including those extra parameters.
Adding those parameters doesn't actually remove the file from the browser's cache, it more or less sees it as a new, different file.
This also answers point 2, since having to do it on all pages means you can't do it on only one page.
Instead of having an external .js file, we can inline Javascript directly in HTML, i.e.
Externalized version
<html>
<body>
<script type="text/javascript" src="/app.js"></script>
</body>
</html>
Inlined version
<html>
<body>
<script type="text/javascript">
// app.js inlined
</script>
</body>
</html>
However, it's not recommended:
https://developer.yahoo.com/performance/rules.html#external
Put javascript and css inline in a single minified html file to improve performance?
The main reason is caching and pre-compiling - in the externalized version, the browser can download, pre-compile and store the file once for multiple pages, while it cannot do the same for inlined version.
However, is it possible to do something along these lines:
Inlined keyed version
<html>
<body>
<script type="text/javascript" hash="abc">
// app.js inlined
</script>
</body>
</html>
That is, do this:
In the first invocation, send the whole script and somehow tell the browser that the script hash is abc
Later, when the browser loads that or other pages containing the same script, it will send this key as a cookie. The server will only render the contents of the script if the key has been received.
That is, if the browser already knows about the script, the server will render just this:
Inlined keyed version, subsequent fetches (of the same or other pages)
<html>
<body>
<script type="text/javascript" hash="abc">
</script>
</body>
</html>
where notably the script contents are empty.
This would allow for shorter script fetching with a natural fallback.
Is the above possible? If not, is some other alternative to the above possible?
I don't know of a way to do what you asked, so I'll provide an alternative that might still suit your needs.
If you're really after a low latency first page load, you could inline the script, and then after the page loads, load the script via url so that it's in the browser cache for future requests. Set a cookie once you've loaded the script by direct url, so that your server can determine whether to inline the script or provide the external script url.
first page load
<script>
// inlined my-script.js goes here.
</script>
<script>
$(function(){
// load it again, so it's in the browser cache.
// notice I'm not executing the script, just loading it.
$.ajax("my-script.js").then(function(){
// set a cookie marking this script as cached
});
});
</script>
second page load
<script src="my-script.js"></script>
Obviously, this has the drawback that it loads the script twice. It also adds additional complexity for you to take care of when you update your script with new code - you need to make sure you address the cookie being for a old version.
I wouldn't bother with all this unless you really feel the need to optimize the first page. It might be worth it in your case.
The Concept
Here's an interesting approach (after being bugged by notifications :P)
You could have the server render your script this way. Notice the weird type attribute. That's to prevent the script from executing. We'll get to that in a second.
<script type="text/cacheable" data-hash="9182n30912830192c83012983xm019283x">
//inline script
</script>
Then create a library that looks for these scripts with weird types, get the innerHTML of these scripts, and execute them in the global context as if they were normally executing (via eval or new Function). This makes them execute like normal scripts. Here's a demo:
<script type="text/cacheable" data-hash="9182n30912830192c83012983xm019283x">
alert(a);
</script>
<script type="text/cacheable" data-hash="9182n30912830192c83012983xm019283x">
alert(b);
</script>
<script>
// Let's say we have a global
var a = "foo";
var b = "bar"
// Getting the source
var scripts = Array.prototype.slice.call(
document.querySelectorAll('script[type="text/cacheable"]')
);
scripts.forEach(function(script){
// Grabbing
var source = script.innerHTML;
// Create a function (mind security on this one)
var fn = new Function(source);
// Execute in the global scope
fn.call(window);
});
</script>
However...
Since you have the script source (the innerHTML), you can cache them somewhere locally (like in localStorage) and use the hash as its identifier. Then you can store the same hash in the cookie, where future page-requests can tell the server "Hey, I have cached script with [hash]. Don't print the script on the page anymore". Then you'll get this in future requests:
<script type="text/cacheable" data-hash="9182n30912830192c83012983xm019283x"></script>
That covers up the first half. The second phase is when your library sees an empty script. The other thing your library should do is when it sees an empty script, it should look up for that script with that hash in your local storage, get the script's source and execute it like you just did in the first place.
The Catch
Now there's always a trade-off in everything, and I'll highlight what I can think of here:
Pros
You only need one request for everything. Initial pageload contains scripts, subsequent pages become lighter because of the missing code, which is already cached by then.
Instant cache busting. Assuming the hash and code are 1:1, then changing the content should change the hash.
Cons
This assumes that pages are dynamic and are never cached. That's because if you happen to create a new script, with new hash, but had the client cache the page, then it will still be using the old hashes thus old scripts.
Initial page load will be heavy due to inlined scripts. But this can be overcome by compressing the source using a minifier on the server. Overhead of minification can also be overcome by caching minified results on the server.
Security. You'll be using eval or new Function. This poses a big threat when unauthorized code manages to sneak in. In addition, the threat is persistent because of the caching.
Out of sync pages. What happens if you get an empty script, whose hash is not in the cache? Perhaps the user deleted local storage? You'll have to issue a request to the server for it. Since you want the source, you'll have to have AJAX.
Scripts are not "normal". Your script is best put at the end of the page so that all inline scripts will be parsed by then. This means your scripts execute late and never in the time they get parsed by the browser.
Storage limits. localStorage has a size limit of 5-10MB, depending on which browser we're talking about. Cookies are limited to 4KB generally.
Request size. Note that cookies are shipped up to the server on request and down to the browser on response. That additional load might be more of a hassle than it is for good.
Added server-side logic. Because you need to know what needs to be added, you need to program your server to do it. This makes the client-side implementation dependent on the server. Switching servers (say from PHP to Python) wouldn't be as easy, as you need to port over the implementation.
If your <script> is not introduced as type=text/javascript, it will simply not be executed.
So you could have many tags like theses:
<script type="text/hashedjavascript" hash="abc">...</script>
<script type="text/hashedjavascript" hash="efg">...</script>
Then when the DOM is loaded, pick one and evaluate it.
I made an example here: http://codepen.io/anon/pen/RNGQEM
But it smells, real bad. It's definitely better to fetch two different files.
Actually what you should do, is have a single file my-scripts.js that contains the code for each of your script, wrapped in a function
// file: my-scripts.js
function script_abc(){
// what script abc is supposed to do
}
function script_efg(){
// what script efg is supposed to do
}
Then execute whatever your cookie tells you to. This is how AMD builders concatenate multiples files in one.
Also look for an AMD library such as requirejs
Edit: I misunderstood your question, removed the irrelevant part.
In PHP there's a function called stream_wrapper_register. With that i can get the file contents of every PHP file that is about to be included. So that basically gives me control over the 'code' that will get parsed.
I was wondering if there's something like this in javascript too? So suppose i include my file:
<script type="text/javascript" src="js/myfile.js"></script>
My code in that file then sets up the stream wrapper (suppose this is available in JS too). Now i want to be able to get the file contents of every other javascript file that will be included:
<script type="text/javascript" src="js/somefile.js"></script>
<script type="text/javascript" src="js/someotherfile.js"></script>
But this ofcourse must happen before before the browser actually executes those files.
So is there a way to intercept that somehow?
$.ajax("/path/to/javascript.js").done(function(source) {
eval(transmogrifySourceCode(source));
});
I used the jQuery syntax because AJAX-style gets are much easier that way, and you'll have to provide your own transmogrifySourceCode function to edit the source before you load it.
I do wonder why you'd want to do, that, though. You should be in full control over your input source, so why not just excise the code you don't want on the server?
No, you can't. Alone for security reasons you won't be allowed to get every script's content.
For Opera, there is a special BeforeScript event which can be listened to from local user scripts.
So there is no (good) way to detect (dynamically added) <script> elements in a page and prevent them from loading and executing a script. Yet you could load the script files by ajax, respecting the same-origin-policy (!), and evaling their modified contents as #DavidEllis suggested.
Elsewise, you need to proxy all script inclusions over your server and modify them there.
I have a .NET web applications which uses a lot of javascript. The .aspx and the .js files go hand-in-hand together.
Problem: The .aspx files are always up-to-date on the client (not cached) but the .js files might well be cached. This is even a problem if the files are only cached for one session since users are spending many hours on my site and everytime I update a .aspx/.js pair users are running into a problem.
Now, I found a solution but I am not sure if there is perhaps a better solution or if my solution has a performance drawback.
This is my solution:
.js-References in .aspx:
<script type='text/javascript' src='../scripts/<%# GetScriptLastModified("MyScript.js") %>'></script>
So, the "GetScriptLastModified" will append a ?v= parameter like this:
protected string GetScriptLastModified(string FileName)
{
string File4Info = System.Threading.Thread.GetDomain().BaseDirectory + #"scripts\" + FileName;
System.IO.FileInfo fileInfo = new System.IO.FileInfo(File4Info);
return FileName + "?v=" + fileInfo.LastWriteTime.GetHashCode().ToString();
}
So, the rendered .js-Link would look like this to the client:
<script type='text/javascript' src='/scripts/GamesCharts.js?v=1377815076'></script>
The link will change every time, when I upload a new version and I can be sure that the user immediately gets a new script or image when I change it.
Safari refuses to cache URLs with query parameters. So instead of a query parameter you can use something like a versioned path and use mod_rewrite to remove it.
Something like:
<script type='text/javascript' src='/scripts/1377815076/GamesCharts.js'></script>
And in Apache config file (config for other servers left as homework):
RewriteEngine On
RewriteRule ^/scripts/[0-9]+/(.+)$ /scripts/$1
What we do in our environment is we manually increase the counter in the calling script :
<script type='text/javascript' src='/scripts/GamesCharts.js?v=1'></script>
Whenever there is any update to the js file , we just increment the counter :
<script type='text/javascript' src='/scripts/GamesCharts.js?v=2'></script>
Your method works automatically but it needs to retrieve the LastWriteTime every time the script being called. If its a high traffic site, it will increase the CPU processing.
Just a thought: You could probably just render the JS as an ASPX; I've done this for forceful reload of CSS before with a JSP. Not proud of that solution, but it's worked before.
<script type='text/javascript' src='/mycode/GamesCharts.js.aspx'></script>
Then just render the JS statically in the ASPX file.