RequireJS randomly loads wrong filename with AMD m

2019-07-22 20:37发布

问题:

I have my config (main.js) defined as:

require.config({
    urlArgs: "bust=" + (new Date()).getTime(),
    paths: {
        jquery: 'jquery-1.8.3.min',
        knockout: 'knockout-2.2.0',
        komapping: 'knockout.mapping-latest',
        token: 'jquery.tokeninput'
    },
    shim: {
        'token': ['jquery']
    }
});

EDIT main.js is a shared configuration. I have several pages that all use the same setup and I don't want to need to modify version-specific filenames all over the place in my project. /EDIT

and included in the page as:

<script src="/AnswersDev/Scripts/require.js" data-main="/AnswersDev/Scripts/main"></script>

add-report.js is included as:

<script type="text/javascript">
    require(['Views/add-report']);
</script>

EDIT That appears likely to be the cause. The inline script is sometimes running before main, and therefore the aliases it is expecting to find are not defined, so it just goes looking for .js. /EDIT

jquery.tokeninput is AFAIK, the only non-AMD module I'm using. A working request results in the following sequence of request (Fiddler capture):

  1. require.js
  2. main.js
  3. Views/add-report.js
  4. Views/add-report-wizard-model.js
  5. knockout-2.2.0
  6. jquery-1.8.3.min.js
  7. Views/add-report-wizard-model-parameter.js
  8. Views/add-report-wizard-model-step.js
  9. knockout.mapping-latest.js

A non-working request has a sequence like e.g.:

  1. require.js
  2. Views/add-report.js
  3. main.js
  4. Views/add-report-wizard-model.js
  5. knockout.js
  6. jquery.js
  7. Views/add-report-wizard-model-step.js
  8. Views/add-report-wizard-model-parameter.js
  9. knockout.mapping-latest.js

add-report.js:

define(['jquery', 'knockout', 'Views/add-report-wizard-model'], function ($, ko, ViewModel) {
... snip ...
});

Note that in the non-working sequence, add-report.js is requested before require.js (I expected that non-deterministic load order applied after require.js is loaded) and both knockout and jquery (both having AMD support built in) are being requested with the wrong file names. I have observed other sequences where require is requested before add-report and the knockout and jquery file names are still wrong. The only thing I am doing between requests is refreshing the browser (I'll also note that once it works, it tends to keep working, and once it breaks, it tends to stay broken). I've tried removing urlArgs from the config and still observe the same issue. On this particular page, jquery.tokeninput is not used. Removing it from the config file does not appear to have any effect either.

At this point, RequireJS is completely unreliable for me and I certainly cannot release my code to production in the current state. Given the number of people who seem to be using RequireJS quite successfully, I can only assume that the problem is in my code somewhere, but I'm at a loss as to where that might be.

Anyone have any suggestions where I might have gone wrong? Thanks.

EDIT: As requested, excerpt from knockout.mapping-latest.debug.js:

// Module systems magic dance.

if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
    // CommonJS or Node: hard-coded dependency on "knockout"
    factory(require("knockout"), exports);
} else if (typeof define === "function" && define["amd"]) {
    // AMD anonymous module with hard-coded dependency on "knockout"
    define(["knockout", "exports"], factory);
} else {
    // <script> tag: use the global `ko` object, attaching a `mapping` property
    factory(ko, ko.mapping = {});
}

回答1:

When you request your files like this:

<script src="/AnswersDev/Scripts/require.js" data-main="/AnswersDev/Scripts/main"></script>
<script type="text/javascript">
    require(['Views/add-report']);
</script>

Chances are that the inline require is called before the main.js script (where is your config if I understand correctly).

So you all need to load from the main.js files. You can use the config deps option to load some files. Or you could also all include them inline, but that defeat the purpose.



回答2:

This is what I've got working now (thanks Simon).

tl;dr version: create config in global require var in <head>, then include page-specific module/require.js as usual.

main.js:

var require = {
    baseUrl: '/Scripts',
    paths: {
        jquery: 'jquery-1.8.3.min',
        knockout: 'knockout-2.2.0',
        komapping: 'knockout.mapping-latest',
        token: 'jquery.tokeninput'
    },
    shim: {
        'token': ['jquery']
    }
};

AddReport:

<head>
    ...
    <script src="/Scripts/main.js" type="text/javascript"></script>
</head>
<body>
    ...
    <script src="/Scripts/require.js" data-main="Views/add-report.js"></script>