Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why use alternate requirejs define: define(function(require) { ... }

Tags:

requirejs

I have seen people use an alternate define "syntax" in require js than what is described in the require js' documentation, or in many tutorials.

The usual define "syntax":

define(['module/first'], function (firstModule) {
   //Module code with a dependency on module/first goes here.
});

The alternate define "syntax":

<script data-main="app/config" src="assets/js/libs/require.js"></script>

file: config.js:

require.config({
   paths: {
      jquery:      '../assets/js/libs/jquery'
   }
});
require(['app']);

file: app.js:

define(function(require) {
     var FirstModule = require('modules/first');
     //Module code with a dependency on module/first goes here.

What are the advantages, and disadvantages of using this alternate "syntax"?

like image 606
joholo Avatar asked Dec 14 '12 03:12

joholo


People also ask

What is the use of RequireJS?

RequireJS is a JavaScript library and file loader which manages the dependencies between JavaScript files and in modular programming. It also helps to improve the speed and quality of the code.

What is define () in JavaScript?

Advertisements. The define() function can be used to load the modules (module can be an object, function, class or a code which is executed after loading a module). You can load different versions of the same module in the same page.

Is RequireJS still relevant?

RequireJS has been a hugely influential and important tool in the JavaScript world. It's still used in many solid, well-written projects today.

How use HTML file require?

To include the Require. js file, you need to add the script tag in the html file. Within the script tag, add the data-main attribute to load the module. This can be taken as the main entry point to your application.


1 Answers

I think your explanation is a bit misleading: in both cases, you will have a top-level require call with a data-main attribute specifying a file to kick-start the process of requiring different modules.

So typically you will have this in your HTML:

<script data-main="app/config" src="assets/js/libs/require.js"></script>

Then, in both cases, you would have a file app/config which sets your configuration (although you could do this directly in the HTML) and more importantly calls require on your modules:

require.config({
  paths: {
    jquery:      '../assets/js/libs/jquery'
  }
});
require(['app']);

Now, it's when we get to defining modules with dependencies that these styles differ. In the amd style you pass in the module names (paths) as an array, and a function which takes the same number of arguments:

app.js

define(['module/first', 'module/second', 'module/third'], function (firstModule, secondModule, thirdModule) {
  // use firstModule, secondModule, thirdModule here
});

In the simplified CommonJS syntax, you just pass require into define and then require whatever modules you need inline:

app.js

define(function(require) {
  var firstModule = require('modules/first');
  var secondModule = require('modules/second');
  var thirdModule = require('modules/third');
  // use firstModule, secondModule, thirdModule here

}

Getting back to your original question, the advantages of the CommonJS style over the amd style should be clear.

For one thing, with the conventional syntax, if there are many modules being required, it is very easy to mistakenly assign modules to the wrong variable names. Consider this common case:

define(['jquery', 'underscore', 'backbone', 'modules/first', 'modules/second', 'modules/third', 'i18n', 'someOtherModule'], function ($, _, Backbone, first, second, third, I18n, someOtherModule) {
  // ...
});

Right away, you can see that when we add a new module to this list, we have to be very careful that the corresponding new function argument appears in the right place, or else we can have jQuery assigned to Backbone, etc. In some cases this can create very subtle bugs that are hard to track down.

Now consider the CommonJS syntax:

define(function(require) {
  var $ = require('jquery');
  var _ = require('underscore');
  var Backbone = require('backbone');
  var firstModule = require('modules/first');
  var secondModule = require('modules/second');
  var thirdModule = require('modules/third');
  var I18n = require('i18n');
  var someOtherModule = require('someOtherModule');
  // ...
}

Note that:

  1. The pairing of module to variable name is very clear.
  2. The order of the require statements is not important, since the variable names are being paired separately rather than as a mapping between an array and a function.
  3. The modules do not need to be assigned first. They can be assigned anywhere, so long as it is before the module is actually used.

Those are just a few reasons that come to mind, I'm sure there are others. Basically, if you just have one or two dependencies, either syntax will do fine. But if you have a complex network of module dependencies, the CommonJS syntax is probably preferable.

Note that in the RequireJS docs, they mention this small caveat:

Not all browsers give a usable Function.prototype.toString() results. As of October 2011, the PS 3 and older Opera Mobile browsers do not. Those browsers are more likely to need an optimized build of the modules for network/device limitations, so just do a build with an optimizer that knows how to convert these files to the normalized dependency array form, like the RequireJS optimizer.

But this is not a major issue:

Since the number of browsers that cannot support this toString() scanning is very small, it is safe to use this sugared forms for all your modules, particularly if you like to line up the dependency names with the variables that will hold their module values.

like image 50
Chris Salzberg Avatar answered Oct 24 '22 10:10

Chris Salzberg