if I have 2 .js files (A.js, B.js) and each of them has their own dependencies.
A depends on: C.js, D.js, E.js
B depends on: C.js, F.js, G.js
A and B have C as common dependency. If both A and B are loaded with Require.js, will the C.js be loaded twice?
C.js will only be loaded once, as long as it's loaded using the same name in A.js amd B.js.
Another useful thing you can do is as follows:
require.config({
"paths": {
"C": "/somewhere/js/C-v1.0.0.js"
}
});
If you then require A and B, in A and B you can:
require(["C"], function(C) {
}
This way, if the location or version of C changes, you only have to change it in one place.
Related
I am trying to integrate requirejs framework to my app.
Is possible to create a virtual module (which doesn't exists as a physically file), where i could group all the jquery-validation plugins together?
For example, i need to load 4 dependencies everytime i want to use jquery-validate.
Instead of requesting them, each time, i create a jquery-val "virtual module", which should request all the dependencies automatically.
However, trying to load "jquery-val" actually tries to load the file from disk (which i don't have).
What should be the best practice in solving this issue?
// config
requirejs.config({
baseUrl: '/Content',
paths: {
'jquery': 'frameworks/jquery-3.1.1.min',
"jquery-validate": "frameworks/jquery.validate.min",
"jquery-validate-unobtrusive": "frameworks/jquery.validate.unobtrusive.min",
"jquery-unobtrusive-ajax": "frameworks/jquery.unobtrusive-ajax.min"
},
shim: {
"jquery-val": ["jquery", "jquery-validate", "jquery-validate-unobtrusive", "jquery-unobtrusive-ajax"]
}
});
// Solution 1: working, but ugly
define(["jquery", "jquery-validate-unobtrusive", "jquery-unobtrusive-ajax"], function ($) {
// My Module
});
// Solution 2: not working
define(["jquery-val"], function () {
// My Module
});
// Solution 3: create jquery-val.js file, which loads the dependencies automatically
// jquery-val.js
(function (global) {
define(["jquery", "jquery-validate-unobtrusive", "jquery-unobtrusive-ajax"], function ($) {
});
}(this));
take some time and read:
http://requirejs.org/docs/api.html#modulenotes
One module per file.: Only one module should be defined per JavaScript file, given the nature of the module name-to-file-path lookup algorithm. You shoud only use the optimization tool to group multiple modules into optimized files.
Optimization Tool
To answer your question:
It is good practice to define one module per file, so you don't need to define explicit a name for the module AND do the need for inserting it somewhere to be available before the other modules are loaded.
So you could require just the file: require("../services/myGroupModule") and this file would hold your module and requireJS would take care of the loading dependencies (and later the optimizations for concatenating into one file!). Here the module name is the file name.
You could nevertheless do the following and loading it as a file module or like you tried to define it beforehand and give the module a name:
//Explicitly defines the "foo/title" module:
define("myGroupModule",
["dependency1", "dependency2"],
function(dependency1, dependency2) {
return function myGroupModule {
return {
doSomething: function () { console.log("hey"); }
}
}
}
);
Maybe you should also give a look at some new module loaders:
WebPack 2: https://webpack.js.org/
SystemJS: https://github.com/systemjs/systemjs
Script A
;(function(g){
define('hotness', [], function() {
function f() {;
console.log('gotem');
}
return f;
});
define('otherModule', [], function() {});
})(this);
Script B
define([
'ScriptA',
], function() {
var hotness = require('hotness')
});
Error: Module name "hotness" has not been loaded yet for context: _
What is the recommended way to require in a definition from ScriptA in ScriptB?
I've also tried:
Script B Alt
define([
'require',
'ScriptA',
'hotness'
], function(require) {
var hotness = require('hotness')
});
Which gives me
Error loading resource ... /hotness.js: No such file or directory
Error: Script error for: hotness
EDIT:
It's important to note that ScriptA stands as is and will not be modified. The question is how can I get at the named module in ScriptA from ScriptB.
EDIT2:
I have no control over the HTML or any other aspect of the page. I must do everything within ScriptB.
EDIT3:
I have an example of something that works but it looks like a horrible antipattern work-around so I didn't even want to mention it:
define(['require'], function(require) {
// why use window? IDK.
// this is just verbatim what is "working" for someone else
window.require(['scriptA'], function(sA) {
//sA never actually used
window.require([
'otherModule'
], function(oM) {
var hotness = require('hotness'),
You should use a bundles configuration. Given what you've described works, you should use:
bundles: {
'scriptA': ['hotness', 'otherModule']
}
This essentially tells RequireJS "when you want to find hotness or otherModule then load scriptA because they are defined there". I use this every now and then to load modules from bundles that have been generated with r.js.
Other than this Joseph the Dreamer is correct that you should not in the same module mix the AMD and CommonJS methods of getting dependencies.
Your ScriptA uses named modules. They must be loaded after RequireJS but prior to any of the dependents. RequireJS does not recommend named modules unless you know what you're doing (like load a library like a regular script, and register it as a module at the same time).
Error: Module name "hotness" has not been loaded yet for context: _
This is a generic RequireJS error, when the module is loaded from the server, but somehow RequireJS can't provide it to the dependent. Usually happens when the module has syntax errors or when there's a circular dependency, among others.
ScriptB also has problems. It's trying to use both AMD style (array dependencies) and CommonJS style (explicit require). Just use one of the two syntaxes. Note that in the CommonJS format, it needs the module to have a first argument named require to trigger CommonJS format of writing.
define(['hotness'], function(hotness) {
// use hotness
});
// or
define(function(require){
var hotness = require('hotness');
// use hotness
});
Error loading resource ... /hotness.js: No such file or directory
Be sure to set a base url. It can be implicit (based on the data-main location) or explicit (using require.config). Module names are usually paths + filenames relative to the base url.
It is advised that you use one file per module, containing a module definition that has no name (the file name becomes the name of the module) and resides somewhere under the set baseUrl.
I know in RequireJS, we can use define() to define a module with dependencies.
The things I got confused is such chunk code in jQuery.
// ready.js
define([
"../core",
"../core/init",
"../deferred"
], function( jQuery ) {
// ...
})
Actually I can not figure out the dependencies with "../core" and "../core/init",
as RequireJS says,
RequireJS also assumes by default that all dependencies are scripts, so it does not expect to see a trailing ".js" suffix on module IDs.
But there isn't a file called ../core.js, just a ../core directory.
So is ../core dependency needless?
You're right, you don't have to specify folders as dependencies, only scripts that you want to load.
// ready.js
define([
"../core/init",
"../deferred"
], function(init, deferred) {
// ...
});
And to say more, RequireJS does not even allow you to mark the entire folder as a dependency.
I'm using requirejs for a project and I have 2 modules:
a.js: is a non-AMD module that I cannot touch its code
b.js: is a AMD module that I have written with define() function. It requires a.js to work.
app.js: is the actual application code that uses both a.js and b.js.
app.js looks like this:
//source code for app.js
require(['a.js', 'b.js'],
function( a, b ) {
a.x = 2;//this will fail because 'a' is not defined
});
Now the question is: what is the simplest way to require() both modules in app.js? I can't do it like:
//source code for app.js
require(['b.js', 'a.js'],
function( b ) {
a.x = 2;//it works because module 'a' defines a global variable named 'a'
b.x = 2;//this will fail because module 'b' is loaded before 'a' so it doesn't work
});
Since, as you say, a.js exports a global variable called a, you can configure RequireJS to expose it in an AMD manner using the shim config option. Any module requiring a.js won't even know it's not a proper module. In your case the config would be something like this:
requirejs.config({
shim: {
'a.js': {
exports: 'a' // a.js defines 'window.a'
}
}
});
This is standard fare for AMD loaders. Most of the time, a shim will not be needed. Your source for app.js is correct but you didn't show your source for b.js. Since you have control over b.js, it should be written as an AMD module with a dependency on a.js
// source code for b.js
// You have to use define, not require, and the dependency on a.js must be here rather than via a nested or internal require
define(['a.js'], function(a){
// your code for b.js
});
This will ensure that a.js is loaded before b.js when app.js is ready to execute.
Is it possible to "require" an entire folder using requireJS.
For example, I have a behaviors folder with a ton of behavior js files. I'd really like to be able to simply use require(['behaviors/*'], function() {...}); to load everything in that folder rather than having to keep that list up to date. Once compressed and optimized I'd have all those files lump together, but for development it's easier to work with them individually.
javascript in browser has no filesystem access and so it can't scan a directory for files. If you are building your app in a scripting language like php or ruby you could write a script that scans the directory and adds the file names to the require() call.
I don't know if I can recommend this approach anymore. I think the more explicit way to do this is by manually "requiring"/"exporting" the functionality you need. The exception I think is if you have a "namespace" of files that you want exported see below "Babel and ES6 Module Import Declarations (export-namespace-from) or see below "Babel and ES6 Module Import Declarations.
These solutions also assume that you have a meaningful file structure - where file names become part of that "require" * definition.
However, if you still need to do this there are a few existing tools and methods that might provide the behavior that you're looking for.
Possible Solutions
Babel and ES6 Module Import Declarations (plugin-export-namespace-from)
Have a setup that is ES6 compliant.
You need to update your .babelrc file to include babel-plugin-proposal-export-namespace-from.
Use export namespace plugin by writing syntax like the following:
common/index.js
export * from './common/a'; // export const a = false;
export * from './common/b'; // export const b = true;
main.js
import { a, b } from './common';
console.log(a); // false
console.log(b); // true
Babel and ES6 Module Import Declarations (plugin-wildcard)
Have a setup that is ES6 compliant.
You need to update your .babelrc file to include babel-plugin-wildcard.
Use wildcard namespace plugin by writing syntax like the following:
main.js
import { a, b } from './common/*'; // imports './common/a.js' and './common/b.js'
console.log(a); // false
console.log(b); // true
RequireJS (Now Outdated)
Download and install require-wild npm install require-wild
Configure the declaration as follows
grunt.initConfig({
requireWild: {
app: {
// Input files to look for wildcards (require|define)
src: ["./**/*.js"],
// Output file contains generated namespace modules
dest: "./namespaces.js",
// Load your require config file used to find baseUrl - optional
options: { requireConfigFile: "./main.js" }
}
}
});
grunt.loadNpmTasks("require-wild");
grunt.registerTask('default', ['requireWild']);
Then run the grunt task. Your file will be generated. Modify your setup to load namespaces.js
require(['namespaces'], function () { ... });
This now allows modules under src to use dependencies glob pattern matching.
require(['behaviors/**/*'], function (behaviors) { }
I know this is old, but I'd like to share my solution:
For this solution you need JQuery
1) Create a bash script that will list all the js files in
"MyDirectory/", and save it to "directoryContents.txt":
#!/bin/bash
#Find all the files in that directory...
for file in $( find MyDirectory/ -type f -name "*.js" )
do
fileClean=${file%.js} #Must remove .js from the end!
echo -n "$fileClean " >> MyDirectory/directoryContents.txt
done
File will look like this:
MyDirectory/FirstJavascriptFile MyDirectory/SecondJavascriptFile
MyDirectory/ThirdJavascriptFile
Problem with my script! Puts an extra " " at the end, that messes things up! Make sure to remove the excess space at the end of directoryContents.txt
2) Then in your Client side JS code:
do a "GET" request to retrieve the text file
For each entry (split by the space), 'require' that file:
.
$.get( "MyDirectory/directoryContents.txt", {}, function( data ) {
var allJsFilesInFolder = data.split(" ");
for(var a=0; a<allJsFilesInFolder.length; a++)
{
require([allJsFilesInFolder[a]], function(jsConfig)
{
//Done loading this one file
});
}
}, "text");
I was having a problem with this code not finishing before my other code, so Here's my extended answer:
define([''], function() {
return {
createTestMenu: function()
{
this.loadAllJSFiles(function(){
//Here ALL those files you need are loaded!
});
},
loadAllJSFiles: function(callback)
{
$.get( "MyDirectory/directoryContents.txt", {}, function( data ) {
var allJsFilesInFolder = data.split(" ");
var currentFileNum = 0;
for(var a=0; a<allJsFilesInFolder.length; a++)
{
require([allJsFilesInFolder[a]], function(jsConfig)
{
currentFileNum++;
//If it's the last file that needs to be loaded, run the callback.
if (currentFileNum==allJsFilesInFolder.length)
{
console.log("Done loading all configuration files.");
if (typeof callback != "undefined"){callback();}
}
});
}
}, "text");
}
}
});
What I ended up doing was everytime my Node server boots, it will run the bash script, populating directoryContents.txt. Then My client side just reads directoryContents.txt for the list of files, and requires each in that list.
Hope this helps!
There isn't really a way to do this conceptually on the fly (that I know of).
There's a few work arounds though:
Use grunt and concat and then just require that behemoth...I know, kinda sucky.
What I think is a better solution... use a require hierarchy like so:
require('/js/controllers/init', function(ctrls){
ctrls(app, globals);
});
// /js/controllers/init.js
define('js/controllers/index', 'js/controllers/posts', function(index, posts){
return function protagonist(app, globals){
var indexModule = index(app, globals);
var indexModule = posts(app, globals);
return app || someModule;
};
});
// /js/controllers/index.js
define('js/controllers/index', 'js/controllers/posts', function(index, posts){
return function protagonist(app, globals){
function method1(){}
function method2(){}
return {
m1: method1,
m2: method2
};
};
});
Note that "protagonist" function. That allows you to initialize modules before their use, so now you can pass in a 'sandbox' -- in this case app and globals.
Realistically, you wouldn't have /js/controllers/index.js... It should probably be something like /js/controllers/index/main.js or /js/controllers/index/init.js so that there is a directory adjacent to (sibling of) /js/controllers/init.js called "index". This will make your modules scalable to a given interface -- you can simply swap modules out and keep your interface the same.
Hope this helps! Happy coding!
I wrote a library to solve this problem. Eventually someone else came along and improved my library, here it is:
https://github.com/smartprocure/directory-metagen
You can use my lib with Gulp or whatever - it generates metadata for your project and RequireJS can use that metadata to require the desired files from the filesystem.
Using this lib will produce a RequireJS module that looks something like this:
define(
[
"text!app/templates/dashboardTemplate.ejs",
"text!app/templates/fluxCartTemplate.ejs",
"text!app/templates/footerTemplate.ejs",
"text!app/templates/getAllTemplate.ejs",
"text!app/templates/headerTemplate.ejs",
"text!app/templates/homeTemplate.ejs",
"text!app/templates/indexTemplate.ejs",
"text!app/templates/jobsTemplate.ejs",
"text!app/templates/loginTemplate.ejs",
"text!app/templates/overviewTemplate.ejs",
"text!app/templates/pictureTemplate.ejs",
"text!app/templates/portalTemplate.ejs",
"text!app/templates/registeredUsersTemplate.ejs",
"text!app/templates/userProfileTemplate.ejs"
],
function(){
return {
"templates/dashboardTemplate.ejs": arguments[0],
"templates/fluxCartTemplate.ejs": arguments[1],
"templates/footerTemplate.ejs": arguments[2],
"templates/getAllTemplate.ejs": arguments[3],
"templates/headerTemplate.ejs": arguments[4],
"templates/homeTemplate.ejs": arguments[5],
"templates/indexTemplate.ejs": arguments[6],
"templates/jobsTemplate.ejs": arguments[7],
"templates/loginTemplate.ejs": arguments[8],
"templates/overviewTemplate.ejs": arguments[9],
"templates/pictureTemplate.ejs": arguments[10],
"templates/portalTemplate.ejs": arguments[11],
"templates/registeredUsersTemplate.ejs": arguments[12],
"templates/userProfileTemplate.ejs": arguments[13]
}
});
You can then require modules in your front-end like so:
var footerView = require("app/js/jsx/standardViews/footerView");
however, as you can see this is too verbose, so the magic way is like so:
name the dependency above as allViews!
now you can do:
var allViews = require('allViews');
var footerView = allViews['standardViews/footerView'];
There are two advantages to requiring directories whole:
(1) in production, with the r.js optimizer, you can point to one dependency (module A) and it can then easily trace all of A's dependencies that represent a entire directory
(2) in development, you can require whole directories up front and then use synchronous syntax to require dependencies because you know they have already been loaded
enjoy "RequireJS-Metagen"
https://github.com/smartprocure/directory-metagen
https://www.npmjs.com/package/requirejs-metagen
https://github.com/ORESoftware/requirejs-metagen