Require.js to load same script on multiple pages - javascript

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.

Related

Caching issue js files using requirejs

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());

Importing JS files outside Webpack

So, i have a script that creates a global object
(function() {
window.GlobalThing = {}
})();
This script will also fill this object attributes
if (!GlobalThing .Resource) { GlobalThing .Resource = require('./Resource'); }
Then, some other scripts that are not bundled with webpack need to use this GlobalThing. I`ve tryed to make it global using ProvidePlugin for example:
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery",
'GlobalThing': path.join(__dirname,"Path/GlobalThing.js")
})
],
I've tryed adding an Alias as well
resolve: {
alias: {
'GlobalThing': path.join(__dirname,"Path/GlobalThing.js")
}
}
I have tryed doing something like this in the end of my GlobalThing.js file as well:
(function(){
window.GlobalThing = GlobalThing;
})();
Then i had to import to run the script, so i got a random js file inside the webpack to test it:
import GlobalThing from "GlobalThing ";
Still, it seems some scripts cannot seethe GlobalThing. If i bundle everything up with webpack, it works, but i didnt want to as our app is very old and have some very old stuff. Is there a way i can expose GlobalThing to those older scripts ?
ProvidePlugin takes a module name (i.e. string) and internally uses require to load it. So that wont help you in loading your own script.
That said, following should work:
Keep the GlobalThing script out of webpack bundling
Make sure the script itself is copied to the output folder
Make sure the index.html (or whatever html page you are using) has the scripts in the order GlobalThingScript.js followed by webpack bundle.js
To give you more insight:
When webpack bundles the scripts other than GlobalThingScript.js, it doesnt even know there is something called GlobalThing. It just bundles the other files.
That would mean it is upto you to make sure the GlobalThingScript.js is also made it to the final output folder. Also, the html source should use <script> tags to include the GlobalThingScript.js before webpack bundle.js.
In case problem persists, please do edit the OP to include the html source.
Hope that helps.

How to properly configure requireJS

Hi I'm trying to make starting template for SPA project mainly using:
RequireJS, KnockoutJS, TypeScript, etc.
I'm having hard time figuring out how to configure paths and folder structure for RequireJS to work properly...
here is my folder structure:
Scripts
app
components
main.js
lib
knockout.js
jquery.js
here is my RequireJS config file:
var config = {
waitSeconds: 15,
paths: {
app: '../app',
'knockout': '/lib/knockout-3.4.2.',
sammy: '/lib/sammy-0.7.5.',
jquery: '../scripts/lib/jquery-1.10.2.'
}
};
This is my attempt for main.js:
define(['jquery', 'PageOne', 'PageTwo'], function ($, pageOne, pageTwo) {
$(document).ready(function () {
var app = Sammy('#main', function () {
this.get('#/pageOne', function () {
pageOne.activate();
});
this.get('#/pageTwo', function () {
pageTwo.activate();
});
});
app.run();
});
});
Here is my Index.cshtml script tag:
<script src="~/Scripts/lib/require.js" data-main="scripts/app/components/main"></script>
I saw in different project that config is called in header so this is in html header:
<script src="~/Scripts/app/config/require.config.js"></script>
My problem is that in main.js it looks for jquery under path defined in data-main (scripts/app/components/), but my jquery is in scripts/lib folder.
I'm trying to figure out by reading online the whole day but it's too much time for me I need someone to give me some hints how is this supposed to work?
Seriously having hard time figuring this out and RequireJS website just isn't helping me atm.
Note: I am beginner in JavaScript based projects, first SPA attempt,
never used RequireJS...
Your configuration file does not do anything. I'm assuming from your description that the script element that loads it is located before the script element that loads RequireJS. That's one valid way to configure RequireJS, but if you want RequireJS to pick up the configuration, you need to set the global variable require before you load RequireJS, and RequireJS will use the value of require as its configuration. Right now you are setting config, which is ignored by RequireJS. So:
var require = {
waitSeconds: 15,
// etc...
And once the configuration is in effect, you should be able to reduce your data-main to data-main="components/main".
I see some of your paths in the paths configuration end with a dot. That's most likely a mistake on your part, or you have some very strange file names.

Requirejs uses wrong path for asset in library

In a website I'm creating RequireJS is used as module loader. There is a "3d photo viewer" on this website and I'm using the Photo Sphere Viewer lib for these. One of the dependencies for this library is D.js. This is required in the library code as such:
... define(["three","D.js","uevent","doT"],b) ...
In my RequireJS configuration I defined a baseUrl and paths for the libs required by Photo Sphere Viewer libraries:
requirejs.config({
baseUrl: '/scripts',
paths: {
'vendor': 'lib/vendor',
'three': 'lib/vendor/three',
'uevent': 'lib/vendor/uEvent',
'doT': 'lib/vendor/dOT',
'D.js': 'lib/vendor/D'
}
});
photo-sphere-viewer is required by the website after a user requests to get a 3d photo. Code works a little like this:
requirejs(['main', 'deps', 'for', 'app'], function(a, b, c, d){
var showSphere = function(){
// loading logic and such
require('vendor/photos-sphere-viewer', function(PSV){
// do your 3d photo thing
});
};
var button = document.getElementById('button');
button.addEventListener('click', showSphere);
});
D.js is located at /scripts/lib/vendor/D.js. However when I test this, I get a 404 for /D.js. As if it is completely ignoring the baseUrl and path, but these aren't ignored for the other libraries, these get loaded normally.
I've added console.log(require.urlTo('D.js')) to the top of the Photo Sphere Viewer js file and somehow this logs the correct path: "/scripts/lib/vendor/D.js". However, one line further when D.js is actually required it seems to have 'changed its mind'. I've working on this for some time without result and I'm kinda considering just putting D.js in the website root, but of course that's not my preferred solution.
I think that the .js is throwing things off.
Change the config
requirejs.config({
baseUrl: '/scripts',
paths: {
'vendor': 'lib/vendor',
'three': 'lib/vendor/three',
'uevent': 'lib/vendor/uEvent',
'doT': 'lib/vendor/dOT',
'D_js': 'lib/vendor/D'
}
});
And call the define this way.
define(["three","D_js","uevent","doT"],b) ...
I suspect your problem is because of asynchronous loading of requireJs dependencies.
Since requireJs loads the dependencies asynchronously, the D.js file might not have been loaded yet when you are trying to use it.
If thats the problem, solution is to load the dependencies in a separate file before your require.js
<script type="text/javascript" src="/js/config.js"></script>
<script type="text/javascript" src="/js/require.js"></script>
See
Require JS is ignoring my config
and http://requirejs.org/docs/api.html#config

Webpack: Loading external JSON without bundling

I'm building my first one-page site using redux + deku and I need to internationalize it. I want to have a json file with all the texts, something like this:
# http://mysite.me/assets/i18n.json
{
"en": {
"greeting": "Hello"
},
"es": {
"greeting": "Hola"
}
}
Ideally I can require it in my boot file:
const T = require('/assets/i18n.json')
setTranslation(T)
But it mustn't be bundled in the same file, it should remain an external dependencies and it should load at runtime, so that I can edit it without needing to recompile the entire app!
Is it possible with webpack? Is my only option to include it directly in the HTML?
<script type="text/javascript" src="i18n.js"></script>
<script type="text/javascript" src="app.js"></script>
For now this last solution is ok, but I was thinking on splitting the translations per main components, therefore the ability to include it directly through js would be nice.
Thanks to anyone who will help :)
One option would either to not require the file but instead fetch (or some other request method) and then exclude JSON from webpack.
The other way would be to split the code using the common chunks plugin for webpack. Your webpack config would look something like this:
entry: {
app: './index.jsx',
i18n: [
'en',
'fr',
'...others'
]
},
output: {
path: '/path/to/dist/',
filename: '[name].js'
}
See the official docs for more information

Categories

Resources