In RequireJS - Cannot alias jQuery name in path

2019-06-18 05:34发布

问题:

I'm a RequireJS noob. When I use "require.config" and include a path to jQuery with a name different than jQuery, results are not as expected.

Here's a very simple example to help explain my issue.

Structure of files

root
├── Index.htm
└── scripts
    ├── libs
    │   ├── jquery-1.7.1.js
    │   └── require.js
    ├── main.js
    └── someModule.js

index.htm

<html>
<head>
    <title>BackboneJS Modular app with RequireJS</title>
    <script data-main="scripts/main" src="scripts/libs/require.js"></script>
</head>
<body>
    <h3>BackboneJS is awesome</h3>
</body>
</html>

Here the script tag references require in scripts/libs. When require gets ran the JavaScript file called main.js in the scripts directory should be executed.

main.js

require.config({
   "paths": {
           "mod1": "someModule"
   }
});
require(["mod1"], function (sm) {
    console.log(sm.someValue);
});

In my experience the "mod1" can be anything as long as it's referenced the same in the require.config path and in the require method.

someModule.js

define([], function () {
    console.log();
    return { someValue: "abcd" };
});

Just for completeness I included someModule.js

The perceived inconstancy occurs when I include JQuery.

In the following main.js I added jQuery to the config and the require method.

Main.js

require.config({
    "paths": {
        "jquery": "libs/jquery-1.7.1"
        ,"mod1": "someModule"
    }
});

require(["mod1", "jquery"], function (sm, $) {
    console.log(sm.someValue);
    console.log($);
});

With the additional of jQuery everything seems to still works. The "console.log($)" writes the jQuery function.

Now the kicker. In the following code I change "jquery" to "jqueryA" in both the paths and require

require.config({
    "paths": {
        "jqueryA": "libs/jquery-1.7.1"
        ,"mod1": "someModule"
    }
});

require(["mod1", "jqueryA"], function (sm, $) {
    console.log(sm.someValue);
    console.log($);
});

Now "console.log($)" writes null.

Should this be expected? Is there a reason why the name must be jquery, but for mod1 it can be anything?

I can work-around this without a problem, but this issue seems odd. I know I can use the combined RequireJS and jQuery file, but when jQuery has an update I don't want to be dependent on RequireJS to include the new jQuery.

回答1:

In jQuery 1.7 they decided to support AMD loading. To do this, it defines a module named 'jquery' which passes back a reference to the jQuery object. When you define your path to jquery with another name (eg 'jqueryA'), things aren't exactly breaking as you think they are.

The jquery script always defines itself as a module named 'jquery', which is registered with require for your app. When you named your path shortcut 'jquery' and 'jquery' was loaded as a dependency, require was actually referencing the 'jquery' module defined by jquery-1.7.1.js, which does pass back the correct reference. When you name your module shortcut jqueryA, you are now referencing an undefined reference, because the jquery script itself does not pass back a reference, except via the module named 'jquery'. It's silly, I know.

The jquery script defines the module as 'jquery' and expects that you will simply reference it as 'jquery'. If you want to reference it as another name (and as a bonus, keep it from conflicting with other loaded jquery libraries), use this method:

Use requirejs and jquery, without clobbering global jquery?



回答2:

Here's my workaround, based on the implementation I read of Require.JS 2.1.0:

define.amd.jQuery = false;

require.config({
    ...

    shim: {
        "jQuery-1.8.2": {
            exports: "jQuery"
        }
    }

    ...
});


回答3:

I believe I found the answer to my issue.

Optionally call AMD define() to register module https://github.com/documentcloud/underscore/pull/338#issuecomment-3253751

Here's a quote from the previous link. Even though it pertains to underscore, I believe it relates to JQuery also.

all AMD loaders allow mapping a module ID to a partial path, usually the configuration is called 'paths', so to do what you want:

requirejs.config({ paths: underscore: 'js/libs/underscore-1.2.3.min' } }); require(['underscore'], function () {}); Since underscore is used by other higher-level modules, like backbone, a common dependency name needs to be used to communicate a common dependency on underscore, and it makes sense to call that dependency 'underscore'. The paths config gives a way to do the mapping to a specific URL you want to use for that dependency.

Here's a rant that does a very good job of describing the issues with AMD and named modules.

AMD modules with named defines. So much pain for what gain? http://dvdotsenko.blogspot.com/2011/12/amd-modules-with-named-defines-so-much.html

Quote from the link above

If the only way to consume the module properly is to force the end-developer to hard-code its name again in a config file, at the consumption point, (in that respect only) why waste time, effort and hard-code the name in the module in the first place (let alone cause grief to those devs who DO need to load the module under different name / from alternate sources)?

In this post James Burk recommends not using name module. https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon

Normally you should not register a named module, but instead register as an anonymous module:

This allows users of your code to rename your library to a name suitable for their project layout. It also allows them to map your module to a dependency name that is used by other libraries. For instance, Zepto.js can be mapped to fulfill the module duty for the 'jquery' module ID.

There are some notable exceptions that do register as named modules:

•jQuery •underscore

Exception suck. Exceptions makes it difficult for noobs.