AMD's CommonJS Wrapping

Source: Internet
Author: User
Tags define function

In fact, the title of this article should be "Why I do not recommend the use of AMD simplified CommonJS wrapping", but too long not good-looking, for the sake of beauty I can only cut off a section. "

What is it?

In order to reuse the existing CommonJS module, AMD has defined the simplified CommonJS wrapping, and then Requirejs implemented it (not necessarily on the order of precedence). It provides a way to define a module similar to CommonJS, as follows:

Jsdefine(function(require, exports, module) {    var A = require(‘a‘); return function () {};});

In this way, the dependency of the module can be defined as "near" as CommonJS. But it is this seemingly both ways of doing things that have brought a lot of trouble to everyone.

What did it do?

Since Requirejs is the most popular AMD loader, subsequent discussions are based on Requirejs.

Look directly at Requirejs this part of the logic:

Js//If no name, and callback is a function, then figure out if it a//CommonJS thing with dependencies.if (!deps && isFunction(callback)) {    deps = [];    if (callback.length) {        callback            .toString()            .replace(commentRegExp, ‘‘) .replace(cjsRequireRegExp, function (match, dep) { deps.push(dep); }); deps = (callback.length === 1 ? [‘require‘] : [‘require‘, ‘exports‘, ‘module‘]).concat(deps); }}

As you can see, in order to support CommonJS Wrapper this way, the Define function needs to do these things:

    1. By factory.toString() getting the source of factory;
    2. Remove comments from the source code (avoid matching to commented-out dependency modules);
    3. The require dependent information is obtained by means of regular matching;

Write the module as a require reserved word. Both the module loader and the build tool implement the above logic.

For Requirejs, the module that was first defined in this article will eventually become:

Jsdefine([‘a‘], function(require, exports, module) {    var A = require(‘a‘); return function () {};});

Equivalent to:

Jsdefine([‘a‘], function(A) {    return function () {};});

The conclusion is that CommonJS Wrapper is only written in a CommonJS-compatible way, and the logic of the module's operation does not change.

AMD Run Policy

AMD Runtime Core thinking is "early executing", which is the early implementation of dependency. This good understanding:

Js//main.jsdefine([‘a‘, ‘b‘], function(A, B) { //运行至此,a.js 和 b.js 已下载完成(运行于浏览器的 Loader 必须如此); //A、B 两个模块已经执行完,直接可用(这是 AMD 的特性); return function () {};});

Personally, this feature of AMD is good and bad:

First, early execution of dependencies can identify errors early. In the above code, if an exception is thrown in a module, then Main.js will receive an error before calling the factory method, factory will not execute, and if the dependency is performed on demand, the result is: 1) No error occurs when a branch using the A module is not entered, 2) when an error is encountered, the Main.js The factory method is probably half done.

In addition, early implementation of dependencies often leads to a better user experience and is prone to waste. For example, module a relies on another module B, which needs to load the data asynchronously, and executing B early can make the wait time shorter, and if B is not used at the end, the bandwidth and memory overhead are wasted; In this scenario, the on-demand execution of dependencies avoids waste, but leads to longer wait times.

I personally prefer AMD to this approach. To cite a less appropriate example: Chrome and Firefox for a better experience, for some types of files, click will ask whether to save, this time has actually started the download. Sometimes wait a long time to confirm, will be happy to find that the file has been good, if the point is canceled, the browser will cancel the download, the downloaded part is wasted.

Once you understand the AMD feature, take a look at the code:

Js//mod1.jsdefine(function() {    console.log(‘require module: mod1‘); return { hello: function() { console.log("hello mod1"); } };});
 js  //mod2.jsdefine (function () { console.log ( ' require module:mod2 '); return {hello: function () {console.log ( "Hello mod2");}});  
 js  //main.jsdefine ([ ' mod1 ',  ' mod2 '],  function (MOD1, mod2) {//run here, Mod1.js and Mod2.js have been downloaded, //mod1, mod2 two modules have been completed, directly available;  require Module:main '); Mod1.hello (); Mod2.hello (); return {hello: function () {console.log ( ' hello main ');}});   
Html<!--index.html--><script>    require([‘main‘], function(main) { main.hello(); });</script>

In local testing, the usual result is this:

Bashrequire module: mod1require module: mod2require module: mainhello mod1hello mod2hello main

This result is in line with expectations. But is that all? Use Fiddler to delay 200 test the mod1.js request again, this time output:

Bashrequire module: mod2require module: mod1require module: mainhello mod1hello mod2hello main

This is because the Main.js mod1 and MOD2 two modules are loaded in parallel and executed after loading, so the first two lines of output depend on which JS is loaded first. If you have to make mod2 execute after mod1, you need to declare dependencies on the Define module, or configure dependencies by Require.config:

Jsrequire.config({    shim: {        ‘mod2‘: {            deps : [‘mod1‘]        }    }});
Serious problem!

We go back and see what CommonJS Wrapper will bring. As mentioned earlier, in the AMD specification, the above is main.js equivalent to this:

JSMain.jsdefine (function (require, exports, module) {//run to this point, mod1.js and mod2.js have been downloaded, console.log ( ' require Module:main '); var mod1 = require ( './mod1 '); Span class= "Hljs-comment" >//here to execute mod1? Mod1.hello (); var mod2 = require ( './mod2 '); Span class= "Hljs-comment" >//here to execute MOD2? Mod2.hello (); return {hello: function () {console.log ( ' hello main ');}});   

This reliance on "near" writing makes it very easy to think that Main.js executes mod1 or MOD2 when executing to the corresponding require statement, but this is wrong, because CommonJS Wrapper does not change the nature of the "dependency" of amd"early execution!

In fact, for an on-demand loader that relies on execution, such as SEAJS, the above code results must be:

Bashrequire module: mainrequire module: mod1hello mod1require module: mod2hello mod2hello main

So, to understand the CommonJS or CMD module specifications of the classmate, see the use of CommonJS Wrapper way to write AMD module, easy to produce understanding deviation, thus mistakenly think Requirejs has a bug.

I think that "early execution" or "on-demand" strategies do not have obvious merits and demerits, but it is foolish for AMD to "imitate others and offer different characteristics." This year, do yourself the most important!

Other questions

There is also a small problem to mention: By default, the AMD module is defined by passing in a dependency list via parameters, which is simple to rely on. After using CommonJS Wrapper, Requirejs need to factory.toString() extract dependencies from the regular, complex and error-prone. This code, such as Requirejs, will go wrong:

Jsdefine(function(require, exports, module) {    ‘/*‘;    var mod1 = require(‘mod1‘), mod2 = require(‘mod2‘); ‘*/‘; mod1.hello();});//Uncaught Error: Module name "mod1" has not been loaded yet for context: _

Of course, this is because the Requirejs is not written well, the normal sentence as a comment to filter, SEAJS with the regular processing of the above code is not a problem, but also a lot of complexity.

Although the above code is hard to come by in real-world projects, it is simpler and more reliable to write the AMD loader if you give up CommonJS Wrapper support for your brain residue. For example, the rainy night with a knife classmate write seed, the code is very concise; build tools are often based on string parsing, still need to filter comments, but can use UGLIFYJS compression and other trickery methods.

Given that not every AMD Loader supports CommonJS Wrapper, the use of parameters to define dependencies also guarantees better module versatility. As for the "nearest" definition of dependency, I have always felt dispensable, when we write PHP or Python, the Include and import will be placed on the top, so that the code can be seen at a glance to see all dependencies, modification is also convenient.

Some examples of this article come from the biggest difference between seajs and Requirejs, thanks!

This article link: https://imququ.com/post/amd-simplified-commonjs-wrapping.html, participate in the review?

--eof--

AMD's CommonJS Wrapping

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.