2

My project includes the following files:

./index.html
./js/main.js
./js/vendor/require.js
./js/viewmodel/vm.js

The index.html has the following relevant snippet:

<script data-main="js/main.js" src="js/vendor/require.js"></script>
<script type="text/javascript">
    require(['viewmodel/vm', 'ko'], 
        function(viewmodel, ko) {
            ko.applyBindings(viewmodel);
        }
    );
</script>

The js/main.js file is as follows:

var root = this;
define('jquery', ['http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.3.js'], function () { return root.$; });
define('ko', ['http://ajax.aspnetcdn.com/ajax/knockout/knockout-2.1.0.js'], function (ko) { return ko; });

The js/viewmodel/vm.js file...

define(['jquery', 'ko'], 
    function($, ko) {
        return {
            subject: 'world',
            greeting: 'hello'
        }
    }
);

When you open a browser to index.html, then the browser tries to load a file called js/ko.js instead of using the module defined in main.js. It seems like the js file pointed to by the data-main attribute is not guaranteed to run before dependency resolution. This does not seem correct to me since one purpose of the data-main js file is to define require configuration (i.e. path, shim, etc). I am using require v2.1.2.

This works perfectly fine if I copy the contents of my main.js file into the script block in index.html. By "perfectly fine" I mean that it resolved ko to be a module and finds the appropriate CDN link to resolve ko instead of trying to download ./js/ko.js.

3 Answers 3

12

to use the data-main attribute for configuring your whole application, it is necessary that it is the single entry point for all your code.

your 2nd script block breaks this requirement by providing a 2nd entry point. since these entry points will resolve independently of each other (and asynchronously), you cannot rely on one to affect the other.

to resolve it, refactor your code in a way that provides a single entry point to your application and do your configuration via this entry point.

Sign up to request clarification or add additional context in comments.

3 Comments

Does this mean that if i have many pages, and or many js files I need to concate them to a single js file for this to work? Or repeat the require.config in each and every page?
this practice of a single point of entry is only relevant to a single page. if your application spans a number of distinct URLs then what scripts you load at one URL does not affect the logic of a script used at another URL.
yes but i'd have to span the require.config() config on each page right? Is there any way to globalize it?
7

That's because requirejs sets the async. Attribute on the script.

The boolean async attribute on script elements allows the external JavaScript file to run when it's available, without delaying page load first.

This means that both scripts are loaded and evaluated parallel, so none of the two scripts can access methods or functions from the other one. If you want to define requirejs variables in one script you mustn't load that script with require js.

For me there are three possibilities how you can solve that problem:

  • Add the content of main.js to your page (as you mention)
  • Load the main.js file without requirejs as normal script
  • Define the require config before loading the scripts (link to requirejs docu )

1 Comment

Thank you for the explanation of the browser's asynchronous execution. What I meant to ask in my question was something to the effect: "Is it correct that Requirejs is not blocking execution of all modules prior to completing the data-main referenced script?" and @neonstalwart correctly answered this for me.
1

I had the same problem. The architecture of the site that i was working was components that was loading asynchronous at each part of the page. Each component has its own html, css, and js code. So, my solution is to keep a guard function for all the required dependency code, to protect them from running before the main javascript file:

index.html

<head>
<script type="text/javascript">
    window.BeforeMainGuard = {
        beforeMainLoadedFunctions: [],
        hasMainLoaded: false,
        guard: function( func ) {
            console.assert( typeof func === 'function' );
            if( this.hasMainLoaded ) {
                func();
            }else {
                this.beforeMainLoadedFunctions.push( func );
            }
        },
        onMainLoaded: function() {
            for( var i = 0; i<this.beforeMainLoadedFunctions.length; ++i ) {
                var beforeMainLoadedFunction = this.beforeMainLoadedFunctions[i];
                  beforeMainLoadedFunction();
            }
            this.beforeMainLoadedFunctions = null;
            this.hasMainLoaded = true;
        }
    };
</script>
<script data-main="js/main.js" src="js/vendor/require.js"></script>
<script type="text/javascript">
    window.BeforeMainGuard.guard( function() {
        require(['viewmodel/vm', 'ko'], 
            function(viewmodel, ko) {
                ko.applyBindings(viewmodel);
            }
        );
    });
</script>
</head>

js/main.js

require.config({
  // your config
});

require( [ 'AppLogic' ], function( AppLogic ){      
    AppLogic.Init();
    window.BeforeMainGuard.onMainLoaded();
} );

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.