Caching issue js files using requirejs - javascript

I keep having issues with the cached js files, which causes the user to not have the latest js and the browser to give js errors on files it can't find that should be included with requirejs.
My requirejs (version 2.3.6) setup is as follows:
<script src="{% static 'main/js/require.js' %}"></script>
<script>
function requireConfig(urlArgs) {
requirejs.config({
baseUrl: '/static/main/js/',
urlArgs: urlArgs,
waitSeconds: 15,
packages: [{
name: 'moment',
location: '//cdnjs.cloudflare.com/ajax/libs/moment.js/2.23.0',
main: 'moment'
}],
paths: {
// vendors
'jquery': '//cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min',
'jquery_ui': '//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min' ,
'text_contrast': './vendors/text_contrast',
},
shim: {
'jquery_ui': {
deps: ['jquery']
},
'text_contrast': {
exports: 'hex',
},
}
});
}
</script>
<script type="text/javascript">
requireConfig("bust=v18");
require(['platform/init'], function (m) {
m.init({
debug: {{ debug|yesno:"true,false" }},
});
});
</script>
I include this before the closing body tag. Whenever the user refreshes the page after clearing his cache, all (new) js files with the bust argument are loaded correctly. The problemen is, I cant force my users to clear its cache everytime I deploy some new js.
Update
If I add +(new Date()).getTime() to the requireConfig function, the files now get the time argument, which seems to work. But on some urls the browser still looks for a js file at the wrong path. For example when I go to an url it loads:
/static/main/js/text_contrast.js?bust=v181560938449189
which gives a 404.
After clearing my cache and a reload the file is found at:
/static/main/js/vendors/text_contrast.js?bust=v181560939213670
This seems to occur on urls I've already loaded in the past with that wrong path pointing to the text_contrast.js file

You can add time stamp to the urlArgs in the requirejs.config object so that url's will not be cached.
One way you can do it is by changing your code from
requireConfig("bust=v18");
to
requireConfig("bust=v18"+(new Date()).getTime());

Related

RequireJS Optimizer - still attempting to load files from old dir - w/ Django

Need some help with the requireJS optimizer.
My setup is as follows:
Django
JS inside the Django static folder
Folder structure
- dist
- copy of /static/ after optimizer (dir)
- django_app01
- django_app02
- django_app ...
- static
- bower_components
- js
What I'm doing:
Running r.js on the static folder.
r.js optmizes and moves the files to the "dist" folder as expected
Change the "static_url" settings in Django to get the static files from the "dist" folder.
Load the page and get a bunch of 404's as RequireJS is still trying to get modules from /static/...
Here's an example of the url's it's trying to fetch:
localhost/static/...
Rather than
localhost/dist/...
Any ideas why I'm getting all of these 404's after I run the optimizer. I expected the r.js optimizer to start to look for all the files in /dist.
build.js:
mainConfigFile : "static/js/require/common.js",
baseUrl: "static",
dir: "dist",
removeCombined: false,
findNestedDependencies: false,
modules: [
{
name: "js/require/common"
},
...
]
common.js:
requirejs.config({
baseUrl: '/static/',
paths: {
'jquery': 'bower_components/jquery/dist/jquery.min',
...
},
'shim': {
'blah': {
'deps': [...],
'exports': 'blah'
},
...
}
})
html:
<head>
<script src="//cdn.jsdelivr.net/requirejs/2.1.14/require.min.js"></script>
<script src="{% static 'js/require/common.js' %}"></script>
</head>
<script>
require(['{% static "js/interviews.js" %}']);
</script>
Judging by what you do show in your question, the runtime configuration you use when you do not optimize your files is the same as the one you use after optimization. That is, you have only one common.js file that sets your runtime for both cases. This file sets a baseUrl of /static/, so that's where RequireJS looks for the files. The optimization process does not override this setting behind the scenes.
For a project of mine, what I did was to put the sources subject to optimization into a directory that is just for the source (static-src). Django does not know anything about this directory. Then either one of two things happen when I run make to build my project:
I make a non-optimized build which copies all the files from static-src to a directory named build/static-build. (There are other subdirectories for other purposes under build.
I make an optimized build which runs r.js. r.js puts its output in build/static-build.
In either case, everything ends up in the same location. STATICFILES_DIRS is set in my settings to grab files from this directory when collectstatic is run. My server is set to serve the /static/ (the value of STATIC_URL) files from the location where collectstatic collected the static files.
This is just an example. You could use something else than make. You could certainly use different directory names. You could perhaps have a build process that is a bit simpler. (I created it when I was very new to Django and never bothered changing it.)
Okay, figured out how to do this without using a watcher, or make, or any other folder copy script madness.
requireJS allows you to set default variables before you load your require.js lib at the top of your HTML.
Changes I made from my previous configuration are:
- No longer define baseUrl in your module definitions (requirejs.config)
- Define baseUrl as a default depending on the environment.
build.js:
mainConfigFile : "static/js/require/common.js",
baseUrl: "static",
dir: "dist",
removeCombined: false,
findNestedDependencies: false,
modules: [
{
name: "js/require/common"
},
...
]
common.js:
requirejs.config({
// baseUrl: '/static/', DO NOT DEFINE baseUrl here anymore
paths: {
'jquery': 'bower_components/jquery/dist/jquery.min',
...
},
'shim': {
'blah': {
'deps': [...],
'exports': 'blah'
},
...
}
})
html:
<head>
<!-- Define the baseUrl depending on environment -->
<script>
var require = {
baseUrl: {% if debug %}'/static/'{% else %}'/dist/'{% endif %}
}
</script>
<script src="//cdn.jsdelivr.net/requirejs/2.1.14/require.min.js"></script>
<script src="{% static 'js/require/common.js' %}"></script>
</head>
<script>
require(['{% static "js/interviews.js" %}']);
</script>

Using the requirejs shimConfig to load jQuery plugin

This code uses the requirejs.shimConfig to load the jQuery.mCustomScrollbar plugin:
requirejs.config({
baseUrl:'scripts',
paths:{
async:'lib/plugins/async',
domReady:'lib/plugins/domReady',
jquery:"http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min",
"jquery.mCustomScrollbar":"lib/plugins/jquery.mCustomScrollbar.concat.min"
},
shim:{
"jquery.mCustomScrollbar":{
deps:['jquery'],
exports:'jQuery.fn.mCustomScrollbar'
}
}
});
However the Chrome console shows that requirejs tries to load the file from the baseUrl:
GET http://localhost:8180/GoogleMapErpProject/scripts/jQuery.mCustomScrollbar.js 404 (Not Found) require.js:34
Uncaught Error: Script error for: jQuery.mCustomScrollbar
http://requirejs.org/docs/errors.html#scripterror
EDIT:
I have found an unsatisfactory solution to the issue:
requirejs.config({
baseUrl:'scripts',
paths:{
async:'lib/plugins/async',
domReady:'lib/plugins/domReady',
jquery:"http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min",
plugins:"lib/plugins"
},
shim:{
"jquery":{
exports:"jQuery"
},
"plugins/jquery.mCustomScrollbar.concat.min":{
deps:['jquery'],
exports:'jQuery.fn.mCustomScrollbar'
}
}
});
Why would it not work when I specified a path in the paths and used that path in the shimConfig?
from require.js documentation,
baseUrl: the root path to use for all module lookups. So in the above
example, "my/module"'s script tag will have a
src="/another/path/my/module.js". baseUrl is not used when loading
plain .js files (indicated by a dependency string starting with a
slash, has a protocol, or ends in .js), those strings are used as-is,
so a.js and b.js will be loaded from the same directory as the HTML
page that contains the above snippet.
Hence paths should/can be like this:
paths:{
async:'lib/plugins/async',
domReady:'lib/plugins/domReady',
jquery:"http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min",
"jquery.mCustomScrollbar":"/lib/plugins/jquery.mCustomScrollbar.concat.min"
},
or
paths:{
...
"jquery.mCustomScrollbar":"./lib/plugins/jquery.mCustomScrollbar.concat.min"
},
or
paths:{
...
"jquery.mCustomScrollbar":"lib/plugins/jquery.mCustomScrollbar.concat.min.js"
},
if you dont want it to get loaded from baseUrl.

Require.js to load same script on multiple pages

I'm just learning about Require.js. Lets say I have the following pages:
pageA.html
pageB.html
For page A, I need to load the following scripts:
taco.js
hamburger.js
For page B, I need to load the following scripts:
taco.js
salad.js
superman.js
In order to do this using basic require.js loading, I think I would do this:
<!-- pageA.html contains:
<script data-main="/scripts/pageAScripts" src="/scripts/require.js"></script>
which is:
// pageAScripts.js
require(["/scripts/taco", "/scripts/hamburger"]);
and also
<!-- pageB.html contains:
<script data-main="/scripts/pageBScripts" src="/scripts/require.js"></script>
which is:
// pageBScripts.js
require(["/scripts/taco", "/scripts/salad", "/scripts/superman"]);
So now I have a main entry point for each page. But what I'm worried about is using taco.js in multiple places. Because when I use require.js's Optimizing solution in my build script, it creates pageAScripts.js (containing taco.js, hamburger.js) and pageBScripts.js (containing taco.js, salad.js and superman.js).
So now the code for taco.js is loaded twice for the user through the "optimization" process. In my case, taco.js is a pretty large file and having the user end up downloaded the data twice is undesirable.
What am I missing about the workflow here? I'm still pretty new to require.js so I'm sure there is something.
You can use the example of the multipage project. Adapting the RequireJS build config there to yours:
{
baseUrl: '.',
dir: '../build', // This is where the output will go.
modules: [
{
name: 'scripts/taco'
},
{
name: 'scripts/pageAscripts',
exclude: ['scripts/taco']
},
{
name: 'scripts/pageBscripts',
exclude: ['scripts/taco']
}
]
}
This is a minimal adaptation of what the multipage project example provides. At the end of the build you'll have 3 bundles: build/scripts/pageAscripts that contains code only for page A, build/scripts/pageBscripts that contains code only for page B, and build/scripts/taco that contains only the taco module and all its dependencies.
You can load taco.js as separate file and in build file tell requirejs to not to include taco.js as part of build file.
In you require config specify the path for taco.js -
requirejs.config({
paths: {
'taco': 'path/to/taco'
}
});
And require taco.js in your pageAScript.js and pageBScript.js as follows -
require(['taco'], function (taco) {
});
And in your build file tell requirejs not to include taco as part of build file using empty:
({
baseUrl: ".",
name: "page(A/B)Script",
out: "page(A/B)Script.js",
paths: {
taco: "empty:"
}
})
You can find more about this here.
And now you can load the taco.js on pageA.html and pageB.html using script tag.
<script type="text/javascript" src="path/to/taco/js"></script>
Here taaco.js will be loaded for first page and for second page it will be loaded from browser cache.

How to exclude urlArgs from build using r.js

I use r.js optimizer to combine js files based on build profile as it's suggested in documentation. Here's my build-config.js:
({
baseUrl: ".",
paths: {
jquery: '//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min',
},
name: "main",
out: "main-built.2013-07-30.js"
})
As you can see it's based upon main.js file, here's a code of it:
requirejs.config({
baseUrl: 'scripts',
urlArgs: "bust=" + (new Date()).getTime(),
paths: {
jquery: [
'//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min',
'lib/jquery-1.9.1.min',
],
},
});
require([
'layout',
'cue',
], function() {
});
If I preserve urlArgs: "bust=" + (new Date()).getTime() in main.js all external files (here jquery which is loaded from CDN) look like .../jquery.js?bust=1377412213
So it's PITA to comment out this line every time I make a build. I've read through all the documentation and googled for a solution but everything in vain. Maybe I do it wrong?
Way late to the party on this, but here the solution I used: append the urlArgs param to the config with a subsequent call.
HTML:
<script src="js/libs/require.js"></script>
<script src="js/config.js"></script>
<script>require(['main-app']);</script>
Config File:
requirejs.config({
paths: {...},
shim: {...}
}); 
// Apply the cache bust only for the browser.
if (window) {
requirejs.config({
urlArgs: REQUIRE_NOCACHE ? "bust="+(new Date()).getTime() : null
});
}
The optimizer only takes the first requirejs.config declaration and it ignores the subsequent code. The second requirejs.config declaration extends rather than overrides the first, so urlArgs is still successfully applied to modules in the browser. Hope that helps.
The following solution would work in your case, where you're renaming the main.js file in the r.js build:
urlArgs: require.specified('main') ? "bust="+(new Date()).getTime() : null
The above snippet will check for the module named 'main', which will match in development, but not in production, where the module is named 'main-built.2013-07-30'.
I've tested in development and production builds and it works! :-)
On the require.specified() function:
With requirejs is it possible to check if a module is defined without attempting to load it?
As of version 2.2.0, urlArgs now accepts functions.
It sends the moduleId and the url, so you can segment depending on the path if it should have extra args or not. See https://requirejs.org/docs/api.html#config-urlArgs

Require JS is ignoring my config

I'm having pretty simple directory structure for scripts:
/js/ <-- located in site root
libs/
jquery-1.10.1.min.js
knockout-2.2.1.js
knockout.mapping.js
models/
model-one.js
model-two.js
...
require.js
config.js
Since the site engine uses clean URLs I'm using absolute paths in <script>:
<script type="text/javascript" data-main="/js/config.js" src="/js/require.js"></script>
RequireJS config:
requirejs.config({
baseUrl: "/js/libs",
paths: {
"jquery": "jquery-1.10.1.min",
"knockout": "knockout-2.2.1",
"komapping": "knockout.mapping"
}
});
Somewhere in HTML:
require(["jquery", "knockout", "komapping"], function($, ko, mapping){
// ...
});
So the problem is that RequireJS completely ignores baseUrl and paths defined in config file. I get 404 error for every module required in the code below. I see in browser console that RequireJS tries to load these modules from /js/ without any path translations:
404: http://localhost/js/jquery.js
404: http://localhost/js/knockout.js
404: http://localhost/js/komapping.js
However after the page is loaded and the errors are shown I type in console and...
> require.toUrl("jquery")
"/js/libs/jquery-1.10.1.min"
Why so? How to solve this problem?
It's my first experience using RequireJS, so I'm feeling like I've skipped something very simple and obvious. Help, please.
Update
Just discovered this question: Require.js ignoring baseUrl
It's definitely my case. I see in my Network panel that config.js is not completely loaded before require(...) fires own dependency loading.
But I don't want to place my require(...) in config because it is very specific for the page that calls it. I've never noticed such problem with asynchronicity in any example seen before. How do authors of these examples keep them working?
Solved.
The problem was that config file defined in data-main attribute is loaded asynchronously just like other dependencies. So my config.js accidentally was never completely loaded and executed before require call.
The solution is described in official RequireJS API: http://requirejs.org/docs/api.html#config
... Also, you can define the config object as the global variable require before require.js is loaded, and have the values applied automatically.
So I've just changed my config.js to define global require hash with proper configuration:
var require = {
baseUrl: "/js/libs",
paths: {
"jquery": "jquery-1.10.1.min",
"knockout": "knockout-2.2.1",
"komapping": "knockout.mapping"
}
};
and included it just BEFORE require.js:
<script type="text/javascript" src="/js/config.js"></script>
<script type="text/javascript" src="/js/require.js"></script>
This approach allows to control script execution order, so config.js will always be loaded before next require calls.
All works perfectly now.
Fixed the issue.
My config was being loaded asynchronously, and therefore the config paths weren't set before my require statement was being called.
As per the RequireJS docs Link here, I added a script call to my config before the require.js call. And removed the data-main attribute.
var require = {
baseUrl: '/js',
paths: {
'jquery': 'vendor/jquery/jquery-2.0.3.min',
'picker': 'vendor/pickadate/picker.min',
'pickadate': 'vendor/pickadate/picker.date.min'
},
shim: {
'jquery': {
exports: '$'
},
'picker': ['jquery'],
'pickadate': {
deps: ['jquery', 'picker'],
exports: 'DatePicker'
}
}
}
All is now working

Categories

Resources