Re-nagging JS modular loading COMMONJS, AMD, CMD, ES6

Source: Internet
Author: User

JavaScript modular programming has become an urgent requirement. Ideally, developers only need to implement core business logic, and others can load modules that others have already written.

The JavaScript community has made a lot of efforts to achieve the "module" effect in the existing operating environment.

CommonJS

Commonjs defined modules are divided into: module reference (require) module output (exports) module identification (module)

CommonJS Modules has 1.0, 1.1, 1.1.13 Versions:

    • node. js, Sproutcore implements Modules 1.0

    • Seajs, Avocadodb, couchdb and so on realized the modules 1.1.1

    • SEAJS, Flyscript realized the Modules/wrappings

The COMMONJS specification here refers to the COMMONJS modules/1.0 specification.

COMMONJS is a more server-side specification. Nodejs adopted this specification. A module of COMMONJS is a script file. The Require command executes the entire script the first time the script is loaded, and then generates an object in memory.

{ID: ' ... ', exports: {...}, Loaded:true, ...}

The ID is the module name, exports is the interface that the module exports, and loaded indicates whether the module is loaded. There are also many properties, which are omitted here.

When you need to use this module later, you will be taken to the exports attribute. Even if the Require command is executed again, the module is not executed again, but the value is taken to the cache.

Math.jsexports.add = function (A, b) {return a + B;}
var math = require (' math '); Math.add (2, 3); 512

Since COMMONJS is a synchronous load module, this is not a problem for the server side because all modules are placed on the local hard disk. Waiting for the module time is the hard disk read the file time, very small. However, for the browser, it needs to load the module from the server, involving speed, agent and other reasons, once the waiting time is too long, the browser is in "Suspended animation" status.

So on the browser side, not suitable for the COMMONJS specification. So in the browser side there is a specification-AMD (AMD is requirejs in the promotion process of the module definition of normalized output).

Amd

COMMONJS solves the problem of modularity, but this synchronous loading method is not suitable for the browser side.

AMD is the abbreviation for "Asynchronous module definition", which is "asynchronous module definition". It loads the module asynchronously, and the module's load does not affect the execution of the statement behind it.
Async here refers to the other tasks of not clogging the browser (DOM build, CSS rendering, etc.), while the load inside is synchronous (callback is executed immediately after loading the module).

AMD also uses the Require command to load modules, but unlike COMMONJS, it requires two parameters:

Require ([module], callback); 1

The first parameter, [module], is an array in which the member is the module to be loaded, and callback is the callback function after the load is complete. If you change the code above to the AMD way:

Require ([' math '], function (math) {Math.add (2, 3);})

Where the parameters in the callback function correspond to the members (modules) in the array.

Requirejs load module, using the AMD specification. In other words, the module must be written in the manner specified by AMD.

Specifically, module writing must be defined using a specific define () function. If a module does not depend on other modules, it can be written directly in the Define () function.

Define (ID?, dependencies, Factory); 12
    • ID: The name of the module, if not provided, the name of the module should default to the name of the specified script requested by the module loader;

    • Dependencies: The module's dependency, the array literal that has been identified by the module-defined module. The dependent parameter is optional, and if this argument is omitted, it should default to ["require", "exports", "module"] . However, if the length attribute of the factory method is less than 3, the loader chooses to call the factory method with the number of arguments specified by the function's Length property.

    • Factory: The factory function of the module, which initializes the function or object to be executed. If it is a function, it should only be executed once. If it is an object, this object should be the output value of the module.

Suppose you now have a math.js file that defines a math module. So the Math.js is written in the following way:

Math.jsdefine (function () {var add = function (x, y) {return x + y; } return {Add:add}})

The loading method is as follows:

Main.jsrequire ([' Math '], function (math) {alert (Math.add (1, 1));})

If the math module also relies on other modules, the following is the syntax:

Math.jsdefine ([' dependencemodule '], function (dependencemodule) {//...})

When the require () function loads the math module, the Dependencemodule module is loaded first. When there are multiple dependencies, all dependencies are written in the first parameter array of the Define () function, so that AMD is dependent on the predecessor. this differs from the CMD specification, which is dependent on the nearest.

Cmd

CMD is highly dependent on the nearest, deferred execution . You can write your dependencies in any line of code, as follows:

Define (Factory)

factoryAs a function, the representation is the construction method of the module. By executing this construction method, you can get the interface that the module provides to the outside. When the factory method executes, it passes three parameters by default: Require, exports, and module.

Cmddefine (function (Require, exports, module) {var a = require ('./a ');  A.dosomething ();  var B = require ('./b '); B.dosomething ();})

If you use AMD, the following:

Amddefine ([' A ', ' B '], function (A, b) {a.dosomething (); B.dosomething ();})

This specification is actually for the promotion of SEAJS and then out. Then look at the SEAJS is the matter, basic is to know this specification.

The same seajs is also preloaded to rely on JS with AMD specifications at Preload this point is the same, obviously different place is called, and the Declaration relies on the place. Both AMD and CMD are used difine and require, but the CMD standard tends to rely on the use process, that is, no matter where the code is suddenly found to rely on another module, it is in the current code with the introduction of require can be, the specification will help you fix the preload, you can write it. But the AMD standard allows you to write well ahead of the head dependency parameter section (not written well?). Go back and write it down.) This is the most obvious difference.

Sea.js sea.use() to load the module.

Seajs.use (ID, callback?)
ES6

ES6 module features, recommended see Nanyi Teacher's: ECMAScript 6 Getting Started-module syntax

Speaking of ES6 module characteristics, then first talk about the difference between the ES6 module and the CommonJS module.

    • The ES6 module outputs a reference to the value, the output interface is dynamically bound, and the CommonJS output is a copy of the value

    • ES6 modules are executed at compile time, while CommonJS modules are always loaded at run time

CommonJS copy of output value

The CommonJS module outputs a copy of the value (a copy of the original value), that is, once a value is output, changes within the module do not affect this value.

A.jsvar B = require ('./b '); Console.log (B.foo); SetTimeout (() = {Console.log (b.foo); Console.log (Require ('./b '). foo);},//B.jslet foo = 1;settimeout (() = {foo = 2;}, +); module.exports = {Foo : foo,};//Execution: node a.js//execution Result://1//1//1

The above code shows that after the B module is loaded, its internal Foo changes will not affect the output of the Exports.foo. This is because Foo is a primitive type of value that is cached. So if you want to dynamically get the values in the module in CommonJS, then you need to take advantage of the feature of function delay execution.

A.jsvar B = require ('./b '); Console.log (B.foo ()); SetTimeout (() = {Console.log (B.foo ()); Console.log (Require ('./b '). Foo ());},//B.jslet foo = 1;settimeout (() = {foo = 2;}, +); module.exports = {F  OO: () = {return foo; },};//Execution: node a.js//execution Result://1//2//2

So we can summarize:

    • Modules that are repeatedly introduced by the CommonJS module are not repeated and get the module directly to get exposed Module.exports objects

    • If you want to get the latest value in the module everywhere, you can update the value of Module.exports every time you update the data.

    • If the Module.exports attribute of your exposure is an object, then there is no problem.

So if you want to get the newest value in the module everywhere, you can update the value of Module.exports every time you update the data, for example:

A.jsvar B = require ('./b '); Console.log (B.foo); SetTimeout (() = {Console.log (b.foo);   Console.log (Require ('./b '). foo);};//B.jsmodule.exports.foo = 1; Same as Exports.foo = 1 setTimeout (() = {Module.exports.foo = 2;}, 500);//Execution: node a.js//execution Result://1//2//2
ES6 a reference to the output value

In the ES6 module, however, it is no longer a copy of the output object, but the value in the dynamic correlation module.

ES6 static compilation, CommonJS run-time loading

With regard to the 2nd, the ES6 module compile-time execution results in the following two features:

The import command is statically parsed by the JavaScript engine and takes precedence over other content within the module.

    • The Export command has the effect of declaring variables in advance.

      Import Priority execution:

From the first article, importing the import module anywhere in the file will be advanced to the top of the file.

A.jsconsole.log (' a.js ') import {foo} from './b ';//b.jsexport let Foo = 1;console.log (' b.js first execution ');//Execution Result://B.js First execution/ /a.js

From the execution result we can see very intuitively, although import of a module is introduced later than Console.log (' a '), but it by the JS engine through static analysis, refer to the front of the module execution, better than the other parts of the module execution.

Because import is static, import has an elevated effect that the import command's position in the module does not affect the output of the program.

/a.jsimport {foo} from './b '; Console.log (' a.js '); export Const BAR = 1;export Const BAR2 = () = {Console.log (' bar2 ‘);} Export function Bar3 () {console.log (' Bar3 ');} B.jsexport let foo = 1;import * as a from '. a '; Console.log (a);//Execution Result://{bar:undefined, bar2:undefined, bar3: [Fun CTION:BAR3]}//a.js

From the above example can be very intuitive to see that a module refers to the B module, B module also refers to the A module, the export declaration of the variable is better than the execution of other contents of the module, but the specific assignment of variables need to wait until the execution of the corresponding code. (Of course, the function declaration and expression declaration is different, this is the same as the nature of the JS function, there is not too much explanation)

Well, after the difference between the ES6 module and the CommonJS module, let's talk about the same point:

Modules do not repeat

This is a good understanding, whether it is the ES6 module or the CommonJS module, when you repeatedly introduce the same module, the module will only be executed once.

CommonJS Module Cycle Dependent

A.jsconsole.log (' a starting '); Exports.done = False;const B = require ('./b '); Console.log (' in a, B.done = ', b.done); Expo Rts.done = True;console.log (' a Done ');//B.jsconsole.log (' B starting '); Exports.done = False;const a = require ('./a '); Console.log (' in B, A.done = ', a.done); exports.done = True;console.log (' b done ');//node a.js//execution Result:/a starting//B star ting//in B, a.done = false//b done//in a, B.done = true//a Done

In conjunction with the previously described features, when you want to introduce a module from B, because node has already loaded a module before, so it will not repeat the a module, but directly to generate the current a module spit Module.exports object, because a module introduced B module first To re-assign a value to done, so the value of done in the Module.exports output in the current a module is still false. The done value in the B module is true when the B module is executed when the Do value is output in module A.

From the above execution, we can see that in the CommonJS specification, when the require () statement is encountered, the code in the Require module executes, and the results of the execution are cached, and the results are not repeated the next time they are loaded, but instead are taken directly from the cache. Because of this, there is no infinite loop call when there is a cyclic dependency. While this module-loading mechanism avoids cyclic-dependent times errors, it is likely that the code is not executed as we thought it would be. Therefore, it is necessary to plan carefully when writing code to ensure that the dependency of the Loop module will work correctly.

So what can you do to avoid confusion when you have a cyclical dependency? One solution is to write each module first exports syntax, then write the requre statement, using the CommonJS caching mechanism, before the require () other modules to export their own content, so that the other modules can be used to get the correct value. Like what:

A.jsexports.done = True;let B = require ('./b '); Console.log (B.done)//B.jsexports.done = True;let a = require ('. a '); con Sole.log (A.done)

This is simple and clear, the drawback is to change the way each module is written, and most of the students are accustomed to the beginning of the file to write require statements.

ES6 Module Cycle Dependent

As with the CommonJS module, ES6 will not be able to perform repeated loading of the module, and due to the ES6 dynamic output binding characteristics, can ensure that ES6 at any time to obtain the current value of other modules.

A.jsconsole.log (' A starting ') import {foo} from './b '; Console.log (' in B, foo: ', foo); export Const BAR = 2;console.log (' A Done ');//B.jsconsole.log (' B starting '); import {bar} from './a '; Export const FOO = ' foo '; Console.log (' In A, bar: ', bar); s Ettimeout (() = {Console.log (' in A, setTimeout bar: ', bar);}) Console.log (' B done ');//Babel-node a.js//Execution Result://b starting//in a, bar:undefined//b done//a starting//in B, Foo:fo o//a done//in a, setTimeout bar:2
Dynamic import ()

The ES6 module is statically parsed at compile time, which takes precedence over other content within the module, so we can't write code like this:

if (some condition) {Import a From './a ';} else {import b from './b ';} Or import a from (str + ' B ');

Because compile-time static analysis causes us to fail in conditional statements or splicing string modules, because these are the results that need to be determined at run time in the ES6 module is not allowed, so the dynamic introduction of import () emerged.

Import () allows you to dynamically introduce the ES6 module at runtime, and you might think of this syntax as well, but the use of require.ensure is quite different.

    • The advent of require.ensure is the product of Webpack because the browser needs an asynchronous mechanism to load the module asynchronously, thus reducing the initial volume of the loaded file, so if the server is require.ensure, it will be useless. Because there is no asynchronous loading module on the server, the module can be loaded synchronously to satisfy the usage scenario. The CommonJS module can confirm the module load at run time.

    • The import () is different, it is mainly to solve the ES6 module cannot determine the reference relationship of the module at runtime, so it is necessary to introduce import ()

Let's look at its usage first:

    • Dynamic import () provides a Promise-based API

    • Dynamic import () can be used anywhere in the script

    • Import () accepts string literals, and you can construct specifiers based on your needs

To give a simple example of use:

A.jsconst str = './b '; const flag = TRUE;IF (flag) {import ('./b '). Then (({foo}) = {Console.log (foo); })}import (str). Then (({foo}) = {Console.log (foo);}) B.jsexport const FOO = ' foo ';//babel-node a.js//Execution result//foo//foo

Of course, if the use of import () on the browser side will become more extensive, such as the asynchronous loading of modules on demand, then the Require.ensure function is similar.

Because it is based on Promise, if you want to load multiple modules at the same time, it can be promise.all for parallel asynchronous loading.

Promise.all ([Import ('./a.js '), import ('./b.js '), import ('./c.js '),]). Then (([A, {default:b}, {c}]) = = {Console.    Log (' A.js is loaded dynamically ');    Console.log (' B.js is loaded dynamically '); Console.log (' C.js is loaded dynamically ');});

There is also the Promise.race method, which checks which Promise is first resolved or reject. We can use import () to check which CDN is faster:

Const CDNS = [{name: ' jquery.com ', url: ' Https://code.jquery.com/jquery-3.1.1.min.js '}, {name: ' Googleapis . com ', url: ' https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js '}];console.log ('------'); Console.log (' JQuery is: ${window.jquery} '); Promise.race ([Import (Cdns[0].url). Then (() =>console.log (cdns[0].name, ' loaded ')], import (Cdns[1].url). then (() = >console.log (cdns[1].name, ' loaded ')]). then (() = {Console.log (' JQuery version: ${window.jquery.fn.jquery} ') ;});

Of course, if you think this writing is not elegant enough, you can also combine async/await syntax sugar to use.

Async function Main () {const MyModule = await import ('./mymodule.js ');  const {EXPORT1, Export2} = await import ('./mymodule.js ');      const [Module1, Module2, module3] = await promise.all ([Import ('./module1.js '), import ('./module2.js '), Import ('./module3.js '),]);}

Dynamic import () gives us the extra functionality to use the ES module asynchronously. Dynamically or conditionally loading them according to our needs, which allows us to create more advantageous applications faster and better.

Export

A module is a separate file. All variables inside the file cannot be retrieved externally. If you want the external file to be able to read the variables of the module, you need to export the variables within the module using the Export keyword. Such as:

profile.jsexport var a = 1;export var B = 2;export var c = 3;1234

The following notation is equivalent, which is clearer (at the bottom you can see which variables are exported):

var a = 1;var b = 2;var C = 3;export {A, B, c}1234
Import

The import command imports the exported parts of other modules through export.

var a = 1;var b = 2;var C = 3;export {A, B, C}//main.jsimport {A, B, c} from './abc '; Console.log (A, B, c);

If you want to re-name the imported variable, use the AS keyword (which can also be used in the export).

Import {A as AA, B, C};console.log (AA, B, c) 12

If you want to enter and then output a module in a module, the import statement can be written together with the export statement.

Import {A, B, c} form './abc '; export {A, B, c}//use ligatures, readability is not good, not recommended export {A, B, c} from './abc '; 12345
Overall load of the module

Use the * keyword.

Import * from as ABC form './abc ';
Export default

When exporting the content, if you output multiple variables at the same time, you need to use braces {} , and the import also requires curly braces. When you use output, you do not need curly braces export defalut , and export default you do not need curly braces for the input (import) output variables.

Abc.jsvar a = 1, b = 2, c = 3;export {A, b};export default c;1234
Import {A, B} from './abc '; import c from './abc '; No curly braces required Console.log (A, B, c)//1 2 3123

Essentially, the export default output is a variable or method called default, and no curly braces are required to enter the default variable.

Abc.jsexport {A as default};//main.jsimport a from './abc ';//This is also possible import {default as AA} from './abc ';//This is also possible C Onsole.log (AA); 123456789

It's the right place. About cyclic loading (module interdependence) not written, Commonjs and ES6 are handled differently.

Reference article:

    • JavaScript modular Commonjs, AMD, CMD, UMD, ES6

    • In-depth understanding of ES6 module mechanisms

    • How to understand AMD, CMD,COMMONJS specification –javascript Modular Loading Learning Summary

    • Amd/cmd and front-end specifications

    • Front-end Modular Journey (ii): CommonJS, AMD and CMD

    • Study the JavaScript Module specification (commonjs/amd/cmd)

    • JavaScript modular Programming (i): How to format a module

    • JavaScript modular Programming (II): AMD specifications

    • JavaScript modular Programming (III): Usage of require.js

    • Module

Reprint Please indicate the source, and then nagging JS Modular loading COMMONJS, AMD, CMD, Es6-javascript easy to get into the difficult, migrant workers seemingly the road is a trap everywhere-Zhou Lujun's personal website :https:/ /www.zhoulujun.cn/html/webfront/ecmascript/js/2016_0203_528.html there is something wrong with the article, hope to tell you, thank you

Re-nagging JS modular loading COMMONJS, AMD, CMD, ES6

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.