Is there a way to avoid loading modules that could already exist into the DOM?
Example:
require.config({
paths: {
// jquery here is needed only if window.jQuery is undefined
'jquery': '//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min'
}
});
It would be great to be able to use something like this snippet
require.config({
paths: {
'jquery': {
uri: '//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min',
// if this function returns false or undefined load the script from the url
define: function(){ return window.jQuery; }
}
}
});
-----------------------------------------------------------------
UPDATE
-----------------------------------------------------------------
I sent a pull request to #jrburke on github https://github.com/jrburke/requirejs/issues/886 with my proposal. The fixed version of requirejs could be tested on here:
http://gianlucaguarini.com/experiments/requirejs/requirejs-test3.html
Here the requirejs configuration according to my API proposal
require.config({
paths: {
// jquery here is needed only if window.jQuery is undefined
'jquery':'//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min',
'lodash':'//cdnjs.cloudflare.com/ajax/libs/lodash.js/2.0.0/lodash.underscore.min',
'backbone':'//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.0.0/backbone-min'
},
shim:{
'jquery':{
// with my fix now I detect whether window.jQuery has been already defined
// in this case I avoid to load the script from the cdn
exports:'jQuery',
// if this feature is missing I need to load the new jQuery from the cdn
validate: function(){
return window.jQuery.Defferred;
}
},
'lodash':{
// lodash will be loaded only if it does not exist in the DOM
exports:'_',
// if this function returns false or undefined load the script from the cdn
validate: function() {
// is the lodash version already available in the DOM new enough for my application?
return window.parseInt(window._.VERSION) >= 2;
}
},
'backbone':{
deps:['lodash','jquery'],
// if backbone exists we don't need to load it twice
exports:'Backbone'
}
}
});
As #jrburke points out in your pull-request, the way to do it is:
require.config({});
if (typeof jQuery === 'function') {
define('jquery', function() { return jQuery; });
}
// start module loading here
require(['app'], function(app) {});
If a module is already defined, it won't be (re)loaded. Here, the definition is simply to re-use the already-loaded global jQuery object.
As jQuery is AMD compatible, if it is already in the page Require.js won't load it again.
In a broader way, Require.js only looks the path configs when a module haven't been defined yet. So as soon you have a module defined, Require.js won't load it again:
define('jquery', [], function() { /* stuff */ });
// ^ Module 'jquery' is defined here. Require.js won't load it twice.
Checkout this JsBin for a working example: http://jsbin.com/OfIBAxA/2/edit
Related
So I have been searching all over the internet to try to find a solution to this problem but I cannot find a solution that works. I'm currently using the latest version of Gulp and Browserify to bundle up JS for the website I'm working on. We previously would concatenate all the JS files together, but I'm moving to a module setup now.
The problem I am running into is duplicating certain dependencies, in this example, I'll focus on jQuery (v2.1.4). Here is my setup:
main.js (Loaded on every page)
window.jQuery = window.$ = require('jquery');
window.Vue = require('vue');
require('jquery-validation');
// More JS that loads on all pages
page.js (Each page has it's own js file for scripts relating to that page)
require('remodal'); // This requires jQuery
// Rest of the JS for this page...
The problem I am running into is that now jQuery is in both javascript bundles. With Browserify, I marked jQuery as "external" for page-speicific.js which removed jQuery from the script, but I get an error Uncaught Error: Cannot find module 'jquery' and I cannot seem to find a solution to this.
If I "exclude" jQuery with Browserify, or if I put a try block around the require('remodal'), I end up with Uncaught TypeError: $(...).remodal is not a function instead. I'm guessing since the module remodal requires jQuery and it's not loaded there, it's not seeing it's set to the window and that's why execution fails?
Well, found the answer to my question. Guess a night of rest was all I needed to be able to think clearer to search for an answer.
I checked out browserify-shim (and browserify-global-shim) at some point, but found that it would only shim top-level dependencies. If jQuery was a dependency of a dependency, this would not work. Well, once I found the answer linked below, I discovered that theres an undocumented (at least, I never found it) { global: true } you can set to have the shim propagate to all dependencies.
var b = browserify();
var globalShim = require('browserify-global-shim').configure({
'jquery': '$'
});
b.transform({ global: true }, globalShim);
After running gulp, all of my page-specific scripts now referenced jQuery as a window variable.
!(function(root, factory) {
if (typeof define === 'function' && define.amd) {
define(['jquery'], function($) {
return factory(root, $);
});
} else if (typeof exports === 'object') {
factory(root, (window.$)); // <----------------- :D
} else {
factory(root, root.jQuery || root.Zepto);
}
})(this, function(global, $) {
Source: Shimming dependencies of dependencies with browserify-shim
I tried to load Cycle DOM from their CDN through SystemJS with something like:
System.config({
map: {
'cycle-dom': 'https://unpkg.com/#cycle/dom#17.1.0/dist/cycle-dom.js',
'xstream': 'https://cdnjs.cloudflare.com/ajax/libs/xstream/10.3.0/xstream.min.js',
}
});
System.import('cycle-dom', cycleDOM => {
...
});
But I quickly found out cycle-dom needs xstream. So I try to load both:
Promise.all([
System.import('xstream'),
System.import('cycle-dom')
])
.then(([xs, cycleDOM]) => {
...
});
But I still get the same error. It looks like cycle-dom is expecting xstream to exist on window when it's first loaded. So I tried:
System.import('xstream')
.then(xs => window['xstream'] = xs)
.then(() => System.import('cycle-dom'))
.then(cycleDOM => {
...
});
I feel like I'm going about this all wrong. How can I do this?
Update:
Following martin's advice below, I tried configuring xstream as a dependency of cycle-dom.
Here's a jsbin that demonstrates. What I'm doing is loading cycle-run and cycle-dom and then running the example off the cycle home page.
But I get the error:
"TypeError: Cannot read property 'default' of undefined"
Undefined in this case is cycle-dom trying to load window['xstream'], which isn't being loaded.
Thanks.
The System.import() call returns a Promise so you need to put the callback into its then() method (the second parameter is the parent name; not a callback).
System.import('cycle-dom').then(function(cycleDOM) {
console.log(cycleDOM);
});
This prints the module exports.
I don't have any experience with cycle.js so I can't tell whether this is enough or not. Nonetheless you can set this package dependencies with meta config:
System.config({
map: {
'cycle-dom': 'https://unpkg.com/#cycle/dom#17.1.0/dist/cycle-dom.js',
'xstream': 'https://cdnjs.cloudflare.com/ajax/libs/xstream/10.3.0/xstream.min.js',
},
meta: {
'cycle-dom': {
deps: [
'xstream'
]
}
}
});
Again, I don't know whether this is enough or not. The SystemJS documentation contains pretty well explained example how to load dependencies that need to register some global variables. See https://github.com/systemjs/systemjs/blob/master/docs/module-formats.md#shim-dependencies
Edit:
In this case it's a little more complicated. The cycle-run.js script is generated probably by browserify and you can see it contains a line as follows:
var xstream_1 = (typeof window !== "undefined" ? window['xstream'] : typeof global !== "undefined" ? global['xstream'] : null);
This checks whether window['xstream'] exists when it's loaded. This means that the xstream has to be loaded before loading the cycle-run.js script. The way SystemJS works is that it loads the requested module and then loads its dependencies (you can see the order in Developer Tools). So it's the opposite order than you need (this is very similar to my question on SystemJS GitHub page).
This means you need to restructure the import calls:
System.config({
// ...
meta: {
'xstream': {
format: 'global',
exports: 'xstream',
}
}
});
System.import('xstream').then(function() {
Promise.all([
System.import('cycle-run'),
System.import('cycle-dom'),
])
.then(([cycle, cycleDOM]) => {
// ...
});
});
This registers the xstream before loading cycle-run. Also with the meta configuration for xstream this ensures that the window.xstream exists only inside these callbacks and doesn't leak to the global scope.
See your updated demo: https://jsbin.com/qadezus/35/edit?js,output
Also to use format and exports you need to use the newer SystemJS 0.20.* and not 0.19.*.
I am facing loading of JQuery dependency scripts with Require Js
I am having Jquery Plugins, jQuery Library and 2 module js
assets > js > vendor > jQuery Library,
assets > js > apps.js, pageTop.js, categoryJS.js
My apps.js:
require.config({
'baseUrl':'assets/js',
'paths':{
'jQuery':'vendor/jquery-1.11.2.min',
'viewportSize':'viewportSize-min',
'isoTopFilter':'isotope.pkgd.min',
'categoryJS' :'categoryJS'
},
shim:{
'jQuery': {
'exports':'$'
},
'viewportSize' :{
deps:['jQuery']
},
'isoTopFilter':{
deps:['jQuery']
},
'pageTop':{
deps:['jQuery']
},
'categoryJS':{
deps:['jQuery']
}
}
});//require.config
define(['jQuery','viewportSize','isoTopFilter','pageTop','categoryJS'], function($, pageTop, categoryJS) {
$(function() {
pageTop.alertProduct();
categoryJS.categorySlider();
});
});
and my pageTop.js
define([],function( ){
return {
alertProduct: function(){
alert('I am module2');
return true;
}
}
});
My categoryJs.js
define([],function( ){
return {
categorySlider: function(){
alert('I am module2');
return true;
}
}
});
The script is not loading, getting error
Uncaught TypeError: Cannot read property 'alertProduct' of undefined
Can some one please help me, I have tried by all means
You have:
define(['jQuery','viewportSize','isoTopFilter','pageTop','categoryJS'],
function($, pageTop, categoryJS)
Look at the list of dependencies and the list of arguments. pageTop is the 2nd argument so it will get the value of the viewportSize module. There's no magic here: each module listed in the dependencies is passed to the callback and it is passed in the same order as it appears in the dependencies. Assuming you do not need the values of viewportSize and isoTopFilter you could do this:
define(['jquery','pageTop','categoryJS','viewportSize','isoTopFilter'],
function($, pageTop, categoryJS)
Also,
jQuery calls define so you cannot set a shim for it. (It most likely will be ignored.) You should remove the shim you set for it.
Same with your own modules. If you call define and want to set a list of dependencies, pass the dependencies to define, do not set shim for these modules. shim is only for code that does not call define.
jQuery hardcodes its module name as jquery so you have to refer to it as jquery all lowercase. (More details here.) Change your paths so that you have jquery there instead of jQuery.
I'm upgrading from jQuery 1.8 to 1.9 and since jQuery.browser() is removed, I will use jQuery Browser Plugin.
My requirejs config file (loaded using data-main="") looks somewhat like this:
(EDITED - added more code snippets)
main-comp.js
require.config({
paths: {
jquery: 'libs/jquery/jquery1.9.1.min',
utils: 'modules/utils',
myController: "controllers/myController",
browserPlugin: 'libs/jquery/jquery.browser.min'
},
shim: {
browserPlugin: {
deps: ['jquery']
}
}
});
require(['myController', 'jquery'], function (controller, $) {
$(controller.start);
}
);
moduls/utils.js
define(['browserPlugin'], function () {
return {
browser: $.browser
};
});
myController.js
define(['utils'], function (utils) {
function start() {
console.log(utils.browser.msie)
}
return {
start: start
};
});
Everything seemed to work properly, but then I saw that sometimes in IE only I get a 'jQuery' is undefined (it's a capital Q there) or '$' is undefined errors from the jquery.browser.min.js file.
I thought the deps means that jquery will load before the jquery.browser file but apparently this isn't always the case. I tried following this answer and add exports: "$.fn.browser" but with no success.
When running an optimized version (minify+uglify using r.js) I haven't encountered it.
What am I doing wrong?
You need to ensure you reference $ as a parameter in the require callback. Like so:
require(['myController', 'jquery'], function (controller, $) {
$(controller.start);
}
);
This ensures that jQuery is available to use. It is a bit of an odd one as it will expose itself globally anyway so it will sometimes work regardless, but the correct way is to explicitly require it and use it inside the callback as a parameter.
It looks like you are missing jquery dependency in moduls/utils.js, please try:
define(['jquery', 'browserPlugin'], function ($) {
return {
browser: $.browser
};
});
and also, just to be on the safe side, add jquery to your shim :
jquery: {
exports: "$"
},
By the way, why don't you use $.browser in your code and just load the jquery plugin using the shim configuration?
I had the same problem, the script in data-main is loading asynchronously, that means that it may load after the scripts it defines.
The solution is to load another script with the require.config right after the require.js script.
data-main Entry Point Documentation.
I'm having issues trying to load ckeditor via requirejs (I've tried converting the main ckeditor js file into individual modules but that has just caused all hell to break loose) and so I'm now checking to see if there is a very simple way to do this that I've missed.
I know requirejs allows you to load normal js scripts so maybe just loading the ckeditor.js file (un-edited, so it's still an IIFE/self-executing function) - would that work with requirejs or if you're using requirejs for loading modules, does the entire project then need to be module based?
Any help appreciated.
Kind regards,
Mark
Alternatively, you can create a RequireJS shim to load things in the correct order, and alias proper RequireJS module names to the CKEditor distribution files.
This means your module still declares it is dependant on CKEditor, which is a lot nicer than having it just show up by magic.
require.config({
shim: {
'ckeditor-jquery':{
deps:['jquery','ckeditor-core']
}
},
paths: {
"jquery": '/javascript/jquery-1.7.1/jquery.min',
'ckeditor-core':'/javascript/ckeditor-3.6.4/ckeditor',
'ckeditor-jquery':'/javascript/ckeditor-3.6.4/adapters/jquery'
}
});
then in a module you can depend on ckeditor-jquery (or ckeditor-core for that matter, if you don't need the jQuery integration) and know it'll be available:
require(
[
"jquery",
"ckeditor-jquery"
],
function( _jquery_ ) {
$('#editorContent2').ckeditor({
customConfig : '',
skin:'office2003'
});
}
}
Another way to do that:
var require = {
"shim": {
"path/foo/ckeditor/ckeditor": { "exports": "CKEDITOR" }
}
};
define(['moduleX', 'path/foo/ckeditor/ckeditor'], function (x, ckeditor) {
ckeditor.editor.prototype.fooFunc = function() {
};
});
OK, it seems I answered my own question here.
Instead of trying to break ckeditor down into modules I just used RequireJs to load the script in it's entirety.
require(['require', 'dependancy-A', 'dependancy-B', 'dependancy-C'], function(require, A, B, C){
// this = [object DOMWindow]
// CKEDITOR_BASEPATH is a global variable
this.CKEDITOR_BASEPATH = '/ckeditor/';
require(['/ckeditor/ckeditor'], function(){
// Code to create a new editor instance
});
});
```