I have been reading about how the dojo 1.7 loader uses an AMD API/framework here and here too, and I came across this quote on one of the slides: "AMD(’s) greatest benefit isn’t being able to load scripts on-demand, as some people may think, the greatest benefit is the increase of the code organization/modularity and also the reduced need for globals/namespacing." But my question is, can't global variables already be avoided by using normal js functions, and maybe dojo.hitch() if you need to access another function's execution context (and another function's 'private' variables)? Put another way, other than asynchronously loading only what you need, what is the benefit of the AMD framework?
The benefits of AMD are the benefits of having a module system, analogous to a namespace system in other languages. In JavaScript, we often faked this with global variables, but modules give a number of specific benefits:
These modules are offered privacy of their top scope, facility for importing singleton objects from other modules, and exporting their own API.
--- From the CommonJS Modules/1.1.1 spec, which started it all.
Key here is the import and export facilities. Previously everyone was doing this ad-hoc, with globals (like window.jQuery, window._, etc.). To get at jQuery's exported functionality, you had to know the magic name, hope nobody conflicted with it, and be sure that the jQuery script was loaded before your script. There was no way of declaratively specifying your dependency on jQuery, and jQuery had no way of saying "this is what I export" apart from just stuffing them onto a global window.jQuery object.
A module format fixes this: each module exports specific functions, e.g.
// math.js
define(function (require, exports, module) {
exports.add = function (a, b) { return a + b; };
});
and each module can require specific other modules, e.g.
// perimeter.js
define(function (require, exports, module) {
var math = require("math");
exports.square = function (side) {
return math.add(math.add(side, side), math.add(side, side));
};
});
On why AMD should be the module system of choice, James Burke, the author of RequireJS---an AMD loader much like Dojo has---wrote a blog post detailing why he thinks AMD is the best.
Related
I've recently started to use RequireJS, which enables me to organize my code in a nice way. So, define and require became my best friends. But now I see one problem, which I do not know how to solve in terms of RequireJS, or in terms of some particular Design Pattern. So, imagine, I have a really huge module containing zillions of methods. I define it like so:
define(function(BIG_MODULE){
return {
property_0: "value_0",
property_1: "value_1",
....
property_zillion: "value_zillion",
method_0: function(){...},
...
method_zillion: function(){...}
}
});
Please, do not ask me, why I have such a huge module - it's just an abstraction. And, so, the question now is - if it is possible to import or require not the entire module, but some of its properties and methods? Lets say I somehow assigned my module to some local instance and if I investigate the internals of this instance, then I can see, that it contains only some particular properties and methods. Is it possible?
One thing you definitely should do is not export anything that is not meant to be part of the public API of your module.
This being said, RequireJS has no notion of importing only part of a module. When you list a module as a dependency, RequireJS loads the module, loads and executes its dependencies, calls its factory function (this is the function passed to define) with the resolved dependencies, and records what the module exported. Then when you use it somewhere else, the module's export value is bound to the corresponding parameter in the callback. So in this code
require(["foo"], function (foo) {...
you get as foo all of what was exported by the module "foo".
If you use ES6 (aka ES2015) and have the ES6 modules converted to AMD modules (by Babel, for instance), then you can have some language-based notion of a partial import. For instance, if foo exports like this return { bar: 1, baz: 2, bwip: 3 } then you could import only bar like this:
import { bar } from "foo";
console.log(bar);
This would output 1 to the console. Note, however, that this does not change how the module is loaded and processed by RequireJS. RequireJS reads the whole module and executes all of the factory function. The import above only affects how the code that loads "foo" gets to access the exported values.
I'm working with a very large project that uses:
Legacy JSP pages that includes javascript files with script tags
Backbone models and views that uses other javascript modules without RequireJS
We now want to start using RequireJS with jQuery, BackboneJS and UnderscoreJS for everything we develop from now on, but we don't have the resources to rewrite all the legacy JSP pages. We may have time to rewrite the Backbone models and views we have already developed.
The problem is that for our legacy code (both 1 and 2 above) we include all our javascript files in a huge file and ship to the browsers. This huge file must be able to co-exist with our new RequireJS templates, but how can I e.g. separate certain parts of the huge file, so I can use them with the templates using RequireJS? Without having to rewrite all pages that uses this part of the file, or having duplicate code.
Thanks!
I don't know if I fully grasp the problem at hand, but I think a the shim or map functions of RequireJS will help you out.
Extract the parts you want in a new module from your huge javascript file. Then tell RequireJS that your huge javascript file is a dependecy for this new module using shim. Something like:
requirejs.config({
shim: {
'new-module': {
deps: ['huge-javascript-file'],
exports: 'NewModule'
}
});
Shim documentation: http://requirejs.org/docs/api.html#config-shim
The map function might be useful when only portions of your new code have to use your old huge file. Check out this documentation: http://requirejs.org/docs/api.html#config-map
I don't think there is One True Way to achieve this, but I've approached a similar problem by defining module "facades" around the globally scoped code. Let's say your legacy scripts define a global variable called foo. You can define a AMD module and export that variable from it:
//foo.js
define(function() {
return window.foo;
});
//bar.js
define(['foo'], function(foo) {
//do something with foo
});
This way you only need to write a single-line facade every time you need to use a new piece of the existing, globally defined code, without breaking existing code which expects the same code to be globally defined. Over time you can move and refactor the actual implementation into the module without breaking consumer code.
I started to use Web Essentials and I see there's an option "Use the AMD module". I am using typescript for an ASP MVC4 application. Can someone explain what the AMD module is all
about. Is this something that I should know about?
AMD is one way to format and load modular JavaScript. See here: http://addyosmani.com/writing-modular-js/ and especially here: http://requirejs.org/docs/whyamd.html
To quote from that latter source:
The AMD format comes from wanting a module format that was better than
today's "write a bunch of script tags with implicit dependencies that
you have to manually order" and something that was easy to use
directly in the browser.
Essentially AMD allows you to load JavaScript modules on demand, and provides a format for encapsulating their content so the Global namespace isn't polluted.
In TypeScript, with the AMD compiler switch that you've discovered set to 'on', you export a module like this:
export module pe.components {
export class Component { // 'export' makes this visible outside the module
}
class FriendComponent { // no 'export' so this is only visible inside the module.
}
}
And in another file you import this module like this:
import c = module('relative-path-to-file/pe.components');
And then use it like this:
var component:Component = new c.pe.components.Component(); // Works, because Component is exported
... but not this:
var friend:FriendComponent = new c.pe.components.FriendComponent(); // Shouldn't work* because FriendComponent is not exported.
(* there was a bug that made non-exported interfaces visible outside their declaring modules. I think this has been fixed in TS 0.8.1).
As to the second part of your question - this is really too broad. If your architecture requires you to load new functionality (plug-ins or applets within a single page application, for example), then yes, AMD modules and a loading framework such as RequireJS may be the way to go. If, on the other hand, you know in advance all the functionality your users are going to require, you might be better off just minifying your scripts thoroughly and loading them in advance as a single file.
I don't think the fact that you are working with MVC is relevant here: the question is whether your client-side architecture warrants an asynchronous, modular approach.
Regarding AMD (Asynchronous Module Definition ) I read the phase like this:
The AMD format comes from wanting a module format that was better than
today's "write a bunch of script tags with implicit dependencies that
you have to manually order" and something that was easy to use
directly in the browser.
What is the the purpose in javascript context? Can you make some example? pro et contro of using AMD?
Long before JavaScript gained a native module system, the only way to put scripts onto a page were <script> elements. These executed in sequence, in the order they appear on the HTML. This means that if your script relied on jQuery, then jQuery's <script> has to come before your script's <script>. Otherwise, it blows up.
It's not uncommon to logically split an app into multiple files, especially as the app grows. But using this system of manually ordering scripts becomes a nightmare quickly. Your scripts have implicit dependencies whose management is defined elsewhere. This is where AMD comes in.
AMD is a module specification and RequireJS is an implementation of such system. Simply put, it's a wrapper around your code that 1) keeps your script inert until invoked, 2) allows your script to explicitly define its dependencies and, 3) allows the module system to work out which dependencies execute in what order.
Here's a rough example:
// your-app.js
define(['jquery', 'underscore'], function($, _){
// Your script sits in this "wrapper" function.
// RequireJS now knows app.js needs jquery and underscore.
// It loads and executes them first before your script.
})
// jquery.js
define('jquery', [], function(){
// jQuery stuff
return jQuery
})
// underscore.js
define('underscore', [], function(){
// underscore stuff
return underscore
})
// Then on your HTML, load up your app.
<script data-main="path/to/app.js" src="path/to/require.js"></script>
It's common for Javascript libraries that depend on each other to require that they are loaded in a specific order. For example, the script tag that includes the jQuery library has to come before the script tag that includes the jQuery UI library.
If the libraries were using AMD, they could be included in any order. The AMD library would take care of initialising the libraries in the correct order, because you specify which library depenends on which.
(Somewhat ironically, the script tag that includes the AMD library of course has to come before the code that include any libraries using AMD...)
I'm new to Dojo so I don't quite understand all of Dojo's features. Here are some questions, but I'm sure some of them seem really stupid since I don't quite get how Dojo is structured yet:
How do you create multiple modules within a single js file and access the module in the file it's created? Also, how do you access specific modules from a file containing multiple modules?
What is the difference between require and define?
I successfully required a module from a file, but I can't figure out how to get the variables out of the file, how do you do this?
I was looking at how Dojo required its modules and notice that it does a http request for each file, but isn't that really inefficient when you're dealing with lots of modules and/or on a large site, you really would like to minimize the number of http requests necessary? What's the way around this?
Reading through The Dojo Loader will provide answers.
Basically module = file and very often (as a matter of best practice) module = file = class (more precisely public class defined via dojo/_base/declare).
Ad #4: You will need to employ The Dojo Build System, that will resolve all the dependencies and put all your modules into a single file (or more files, this depends on your build profile). Have a look at Dojo Boilerplate project, it may help with building your application.
How do you create multiple modules within a single js file?
While this is possible to do, and is done by the build system when it creates layer files, it is not recommended. Putting multiple modules in a single file means you have to use a different form of define that gives an explicit ID to eahc of them. This is less flexible then having the module IDs automatically derived from the file name and path (this, together with relative paths makes it much easier to move and rename AMD modules, compared to old-style modules)
What is the difference between require and define?
define is a beefed up version of require that defines a new module with its return value.
I successfully required a module from a file, but I can't figure out how to get the variables out of the file.
I am not sure wha toyu mean with this (without giving a concrete runnable example) but all you have to do is create an object to be the value of your module and return it from define. This is not very far off from how you would define modules the old way, with manual "namespaces"
//foo.js
define([], function(){
var M = {}; //the exported stuff will go here
var myVariable = 16; //private function variables are private (like they are everywhere else)
//and cannot be seen from the outside
M.anumber = myvariable + 1; //things on the returned object can be seen from the outside
return M; //people who require this module will get this return value
});
//bar.js
define(['foo'], function(foo){
console.log( foo.anumber );
});
I was looking at how Dojo required its modules and notice that it does a http request for each file...
As phusick pointed out, the build system can be used to bundle all the modules into a single file (giving you the best of both worlds - modularity during development and performance during deployment). It can also do other cool stuff, like bundling CSS and passing the Javascript through a minifier or through the Closure compiler, checking ifdef for creating platform-dependent builds, etc.