New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

What's the best way to conditionally load a library? #451

Closed
Integralist opened this Issue Sep 8, 2012 · 10 comments

Comments

Projects
None yet
2 participants
@Integralist

Integralist commented Sep 8, 2012

I'm building an application using latest requirejs version, and I want to load jQuery for Internet explorer browsers and zeptojs for all other browsers.

What is the best way to achieve this?

Some other things to note...

  1. I will want to use the r.js build script to concatenate and minify my code.
  2. I will want to load backbone.js which depends on jQuery or zeptojs being loaded first.

Any feedback appreciated.

@jrburke

This comment has been minimized.

Member

jrburke commented Sep 11, 2012

I would create a loader plugin, call it dollar.js, something along the lines of:

define(['module'], function (module) {
  return {
    load: function (id, require, load, config) {
        var dollar = module.config().dollar || (isIE() ? 'jquery': 'zepto');
        require([dollar], load);
    });
  };
});

Where isIE is some test you do to know if you need jQuery. by using module config you can override in a build to force building in either zepto or jquery.

Then, this is the part I have not explicitly tried, I would set up a map config so that for all the other modules, 'jquery' is mapped to this branch loader plugin, but when it asks for jquery it really gets jquery:

requirejs.config({
  map: {
    '*': {
      'jquery': 'dollar!'
    },
    'dollar': {
      'jquery': 'jquery'
  }
});

So hopefully, all the modules you use can just use 'jquery' as the dependency ID, but which one is loaded is based on the loader plugin. Going to close this issue, but if the above does not work, we can reopen if something needs to be fixed. Also please feel free to continue discussion in this issue.

@jrburke jrburke closed this Sep 11, 2012

@Integralist

This comment has been minimized.

Integralist commented Sep 13, 2012

@jrburke worth also mentioning that I want to use Backbone.js so I'm not sure how the following would fit into your above solution...

requirejs.config({
    paths: {
        Backbone: '../Utils/Libraries/backbone'
    },
    shim: {
        'Backbone': {
            deps: ['../Utils/Libraries/lodash', '../Utils/Libraries/jquery'], // load dependencies
            exports: 'Backbone' // use the global 'Backbone' as the module value
        }
    }
});

...as really I assume that I'd need to swap out the reference to jquery with dollar! some how? Sorry I didn't really understand how to implement the plugin. Do I just have a javascript file called "dollar.js" with that code inside of it and then reference it somehow within the map config (which was something else I didn't fully understand how it worked).

Not sure if using Backbone like I do in the example above changes the solution or whether you can help me understand how I would use your solution with how I'm using RequireJs/Backbone.

Thanks

@jrburke

This comment has been minimized.

Member

jrburke commented Sep 13, 2012

I would create paths config for 'lodash' and 'jquery' with the paths you used in the deps array, then just use deps: ['lodash', 'jquery']. That will also allow the map config to work correctly since it is targeting 'jquery'.

Yes, that loader plugin code is just dropped into a dollar.js, and put in your baseUrl. Then implement the isIE() function inside that module, before the return call, and do the switching logic you use to determine if jquery or zepto should be used.

Add that map config to the config you have above (with the jquery and lodash paths changes).

So using shim config hopefully should not matter, but I have not explicitly tried it, along with the map config to a plugin resource. I expect it to work, but if it does not, put the test case up on github somewhere and I can then debug it more.

@Integralist

This comment has been minimized.

Integralist commented Sep 15, 2012

@jrburke that didn't work for me - causes the following errors in Google Chrome (this wasn't even getting to a point where I could run the 'r.js' build script - that'll follow on from here)...

GET file:///Users/markmcdonnell/Dropbox/Sites/Test%20Script%20Loading/dollar!.js  require.js:1884
Uncaught Error: Script error  require.js:194
Uncaught Error: Load timeout for modules: Backbone  require.js:194

I've set-up a repo for you to look at (so you can clone and test) https://github.com/Integralist/RequireJS-Test-Conditional-Script-Loading

Could you potentially advise on what I'm doing wrong/missing.

Also, once we can get this running as it is (without a build script), would you be able to give me an example of the build script and how I would write it to package this up (because I wouldn't want jquery and zepto to be included in the build file as I would think that for performance reasons it would be better to have them loaded async - as there is no point in loading jquery and zepto together on a device that doesn't need one or the other).

Many thanks!

@jrburke

This comment has been minimized.

Member

jrburke commented Sep 17, 2012

Thanks for the test repo! There was a bug with allowing plugin values in map config. I filed #466 for it, and it is fixed on the dev2.1 branch now, if you want to try it:

https://raw.github.com/jrburke/requirejs/dev2.1/require.js

and r.js:

https://raw.github.com/jrburke/r.js/dev2.1/dist/r.js

@Integralist

This comment has been minimized.

Integralist commented Sep 23, 2012

Just so you know it looked like that worked for correctly loading either jQuery or Zepto (depending on whether the browser is IE or not).

I'm just going to try running a build script on it to see how it processes it all and then after that I'll try seeing what's involved with switching to Almond?

@Integralist

This comment has been minimized.

Integralist commented Sep 23, 2012

@jrburke I've updated the example repo so you can take a look at the files and the build script.

I'm trying to first get the build script to run and then after that I'll see if adding insertRequire: ['app'] will work to switch it over to using almond.js (if it is indeed as simple as that?)

But I'm not able to get the build script to run as I normally would...

Marks-MacBook-Pro:require markmcdonnell$ node r.js -o build.js

Tracing dependencies for: app
ReferenceError: document is not defined
In module tree:
    app
      dollar

ReferenceError: document is not defined
In module tree:
    app
      dollar

    at eval (eval at <anonymous> (/Library/WebServer/Documents/require/r.js:13611:38))

...any ideas on what I'm missing?

Kind regards,
Mark

@jrburke

This comment has been minimized.

Member

jrburke commented Sep 25, 2012

dollar.js needs to do extra checks for when running in node as part of the build. In that case, document is not available. So, maybe only execute the isIE test if typeof document !== 'undefined'.

@Integralist

This comment has been minimized.

Integralist commented Sep 30, 2012

@jrburke the build script completed successfully but the compiled file in the /build/ directory included ZeptoJS in side of it?

What I want to do is to only load ZeptoJs or jQuery depending on the result of the isIE function.

Because if I'm loading this application on a mobile I don't want to load jQuery, I would want to load a more mobile friendly library such as ZeptoJs. And alternatively on a desktop device I would want to load jQuery and not ZeptoJs.

Any ideas on how to achieve this?

@jrburke

This comment has been minimized.

Member

jrburke commented Oct 1, 2012

The plugin should test for the build case, via config.isBuild and if true, then just call the load callback immediately and not call require(), which would indicate to the optimizer that required module needs to be included in the build:

    return {
        load: function (id, require, load, config) {
            if (config.isBuild) {
                load();
            } else {
                var dollar = module.config().dollar || (isIE ? 'jquery' : 'zepto');
                require([dollar], load);
            }
        }
    };

You may want to adjust that logic to allow a module.config().dollar value to be used in a build setting, so that you could for instance allow building both branches of the logic in two different builds, but the basic idea is to just inform the optimizer during a build that "yes, this resource has been satisifed, continue on".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment