Using requirejs, I have two files that depend on the same module.
Must I make sure that I always use the same module name in both dependencies?
For example, if I defined a path for require.config:
'myMod':'src/js/myModule'
And then one time use the name and one time use the actual path, like so:
//in a.js
define(function(require){
var mod = require('myMod');
});
//in b.js
define(function(require){
var mod = require('src/js/myModule');
});
Obviously, the best practice would be to use the module name everywhere, but I want to know why this causes an actual error.
Does requirejs store a map between the module name used and the actual loaded module?
As you yourself say:
Obviously, the best practice would be to use the module name everywhere
RequireJS enforces this practice, up to a point. The thing is that when you tell RequireJS in your paths that 'myMod':'src/js/myModule' you are effectively saying that you want to load src/js/myModule under the name myMod. In all likelihood, if you then refer to it by the full path this is an error in your part. If this were to happen in my code, I'd consider it to be an error. So I'd like RequireJS to fail when this happens.
There's also the issue of semantics. The basic case is that if two names passed to require differ, once relative paths have been resolved, then they refer to two different modules. Going by this, if you do require('myMod') and require('src/js/myModule') you are expecting two different modules. And if you do require('myMod') and require('myMod'), you get the same module. So with the paths you've set, what do you actually want? The same module or different modules?
RequireJS has ways to give you both. If you want both module names to give you the same module, then you remove your paths config and use a map config instead:
map: {
'*': {
myMod: 'src/js/myModule'
}
}
What this means is "in all modules (*), if a request is made for the module myMod, then load module src/js/myModule instead." The intent is crystal clear.
If you'd like instead both paths to give you different modules although they share the same source, you could do it using a RequireJS plugin that loads the same source for your modules but has RequireJS register this single source as different modules.
Related
Here there is a paths alias "jquery" in require.js config
Require.JS is triggering require callback for "jquery" but it doesn't trigger the require callback if absolute path is specified for jquery module.
<script type="text/javascript">
var require = {
baseUrl : "../../",
paths : {
'jquery' : '/js/jquery-1.8.3',
},
};
</script>
<script type='text/javascript' src='/js/require.js'></script>
<script type="text/javascript">
require(['jquery'],function(obj) {
console.info("jquery loaded","alias");
});
require(['jquery'],function(obj) {
console.info("jquery loaded","alias");
});
require(['/js/jquery-1.8.3.js'],function(obj) {
console.info("jquery loaded","absolute path");
});
</script>
I expected console.info from the last require statement to be executed, but it doesn't trigger. Looks like if you require a path alias and then require the absolute path,it's not triggering the callback.
Is this a bug in Require.JS or any alternate ways to get callback triggered with both path alias and absolute path?
Correct. Trying to load the same module under two different names does not work. There's good evidence this is by design.
RequireJS treats modules as singletons. (The scope of the "singelton-ness" is a RequireJS context. So the same module could be instantiated once for a context and a second time for a different context but it will never be instantiated twice for the same context.) When RequireJS loads a module, the module acquires a name. If the define call has a string as the first argument, the name is this string. Otherwise, if there was no map affecting the loading of the module, the name will be the string that appeared in require or define call that the listed the module as a dependency. So, using your configuration in the question, if you do require(['jquery'], ... or have a module foo that is defined as define(['jquery'], ... then the name that will be given to the module found at js/jquery-1.8.3 is jquery. Then there is the fact that inside a module, you get get the module name by doing something like this:
define(['module'], function (module) {
console.log("my module name: " + module.id);
});
Ok, so what happens if your module is required twice with two different module names? Remember that modules are singletons so RequireJS won't execute the define twice. Which id should the module get?
In practice, it is almost always the case that when the same module code is being loaded under two different names, it is an programming mistake rather than something the developers really wanted to do. I've never run into a codebase where RequireJS was used where it would have been valid to load jQuery both as jquery and using a path with the version number. The latter case would indeed be an error. So rather than use some sort of default behavior that will probably lead to surprises down the road, RequireJS breaks right away when you try to load the same module under two different names. This is the safe thing to do.
Now, if you really really must be able to load the same module with two names, then you can do it but you must be explicit about what you want: you can use map. This will establish an unambiguous relation between the module names you want to use. What a map does is basically saying "when code in module X requires module Y load module Z instead." So in your case:
map: {
'*':
'/js/jquery-1.8.3': 'jquery'
}
}
This says "when code in any module (*) requires /js/jquery-1.8.3 load jquery instead". In case you wonder, this won't result in a circular dependency. RequireJS will see /js/jquery-1.8.3 passed to require or define, will then inspect the map and convert it to jquery, will then find jquery in paths and convert it to /js/jquery-1.8.3 and then add the .js extension and fetch the module. After it has gone through paths it does not go back to map, because the result it gets from paths is a path, not a module name (and map only transforms module names).
Note that with the map above, there is only one module loaded, which is named jquery. If module.id were used in it, it would always have the value "jquery" and could not have any other value.
Important side note: you should not put the .js in the require call, otherwise it won't work: require(['/js/jquery-1.8.3'], ....
A named module makes sense for me:
define('myModule', ['dep1', 'dep2'], function (dep1, dep2) {
//Define the module value by returning a value.
return function () {};
});
When I want to use this module, I can use require to import it:
require('myModule', function(myModule){})
However, what I can't understand is the anonymous module like this (from requireJS examples):
define(['dep1', 'dep2'], function (dep1, dep2) {
//Define the module value by returning a value.
return function () {};
});
Is the code above used to define an anonymous module? If so, how can this module be used/imported/refered by other modules? Does anyone have ideas about this?
If you scroll down a bit on that page you linked, it says
Notice that the above module does not declare a name for itself. This is what makes the module very portable. It allows a developer to place the module in a different path to give it a different ID/name. The AMD loader will give the module an ID based on how it is referenced by other scripts.
So the module will in fact get a name, based on how you load the file that contains it.
I guess the idea is that you develop with "anonymous" modules (one per file), and then have a build tool that bundles them all up (giving them names in the process).
I am using RequireJS to modularize my code. The website I am using will have several distinct page categories. For each one of them I want to load general JS file with all the dependencies that exist across all page categories, jQuery being most obvious one. I have managed to do that already.
For the rest of the pages, I want to have separate JS files. Each page has its own module but I am thinking that it would make more sense if I download several pages modules all at once and this way when the user comes to other pages, he will have the JS file already cached and won't have to download.
For now I tried to concatenate these modules which did work rather well. However, I am not sure how to load the specific module from a file. So far I am doing this:
require(['./js/common'], function (common) {
require(['public']);
});
common is the dependencies file and public is my concatenated file with all the dependencies. Inside it has modules for each page but I don't know how to call a specific one. Any ideas?
Take a look at the bundles option for RequireJS' runtime configuration. In your case it would be something like:
bundles: {
public: ['mod1', 'mod2', ...]
}
I've used mod1, mod2 because I don't see the name of the actual modules in your question but what you'd want there are the names of the modules inside the public bundle and that you want to load individually. With this, RequireJS will know that when you want to load such module, it has to get them from public instead of searching for them indivdually.
Then you should be able to change your loading code to:
require(['./js/common'], function (common) {
require(['mod1']);
});
Again, mod1 is for illustration.
Take a look at require-lazy.
It allows you to bundle independent parts of the application into separate bundles. From the developer's point of view, instead of:
define(["module1", "module2"], function(module1, module2) {...});
you write:
define(["lazy!module1", "lazy!module2"], function(module1, module2) {...});
Modules 1 & 2 will not be loaded upfront; they and their dependencies will be loaded lazilly when you call:
module1.get().then(function(realModule1) {
// here you have access to the real module1
});
Check out the examples for a few use cases.
I have the baseUrl in a RequireJs based application set to /angular. Sometimes I'd like to set the path relative to the base, other times I'd like to set it relative to the current directory.
I'm very new to RequireJs and very confused.
This is what I have now:
require([
'require',
'angular',
'module/draft/draftDisplay'// module that I want relative to baseUrl
], function(requireLocal, angular) {
requireLocal('./autoSave'); // Modules that I want relative to current url
requireLocal('./module');
Which creates this error: Error: Module name "autoSave" has not been loaded yet for context: _
As I said before, I can't get a good handle on how RequireJs works. Some things I don't understand are:
1) When does RequireJs use the baseUrl and when does it use the current directory
2) What's the difference between a module ID and its path?
3) Does the way previous modules are specified in the require([...],.. array affect how subsequent ones are resolved (which seemed to be the case when I tinkered)
If you could include those things in your answer, that would be very helpful.
1) When does RequireJs use the baseUrl and when does it use the current directory
Always baseUrl, except when the module name is prefixed by ./ or ../, in which case it uses the directory of the module that requires the other.
2) What's the difference between a module ID and its path?
The path includes the baseUrl. E.g. could be something like scripts/vendor/angular/angular.js. The module id does not include the baseUrl or the suffix, e.g. for baseUrl: 'scripts', the module id above would be vendor/angular/angular.
3) Does the way previous modules are specified in the require([...],.. array affect how subsequent ones are resolved (which seemed to be the case when I tinkered)
No.
Your error is caused from using the synchronous version of require for modules that are not loaded. Use the asynchronous version instead; something like:
requireLocal(['./autoSave', './module'], function(autosave, module) {
// here autosave and module are loaded
});
// BEWARE: Here neither autosave nor module are loaded
But are you sure you want the inner require? From the information you provide, pulling the requirements in the outer require would probably suffice, i.e.
require(['require','angular','module/draft/draftDisplay','./autoSave','./module'],...)
Also, the import called 'module' has special meaning in require; it contains information about the current module; If you are literally using './module' it will be quite different than 'module' (the first looks for a module.js file in the same directory as this file, the second provides information about this module).
So lets say I have some small bit of library code that I develop and test in isolation. I use RequireJS during development and have a root level file that depends on 1 other file. So it's define looks something like...
// lib/main.js
define(['lib/dep1'] function(dep1) {
...
})
I run r.js on the code which results in dist/myLibrary.js, which looks something like this:
define('lib/dep1',[], function(){...})
define('lib/main',["lib/dep1"], function(dep1){...})
If I pull myLibrary.js straight into another project it won't work. Nothing is defining itself as the module for that file. But if I append an actual module definition, it works.
define('lib/dep1',[], function(){...})
define('lib/main',["lib/dep1"], function(dep1){...})
define(['lib/main'], function(lib) {
return lib;
})
And the ['lib/main'] seems to be scoped to the module, because if I have an actual lib/main in my app, it doesn't get used.
Questions:
Regarding the scoping, is that the normal behavior? The fact that lib/main is recognized as a module id from the same file rather than going to look for it someplace else. If I import 10 such libraries that all have a lib/main, they won't collide?
Is there a better way? I am at least initially unconcerned about supporting the non-AMD use case as this is all internal lib development and we all use RequireJS. So within a fully AMDed environment, is there another, better way to do this? Assuming there's no pitfall to this approach, it seems fairly simple and boilerplate to support.