CommonJS specification

NodeJs applications are modular using CommonJS. Each file is a module, with its own scope, variables, and methods, invisible to other modules. The CommoJS specification loads modules synchronously.

Starting with Node.js v13.2, ES6 module support is enabled by default. However, ES6 modules are required to use the.mjs suffix.

The module object

NodeJs automatically creates a Module object when running certain modules and adds an exports object to the Module object, which is initialized with {}.

module.exports = {}
Copy the code

Exports and module.exports can be called directly from NodeJs modules.But exports is just a reference to module.exports.

The use of exports

// add.js
function add(a, b){
    return a + b;
}
exports.plus = plus; It's equivalent tomodule.exports = {
    plus: plus
}

// Reference add.js in main.js
let Add = require('add.js')
Add.add(1.2) / / 3
Copy the code

The use of the module. Exports

// add.js
function add(a, b){
    return a + b;
}
module.exports = add;

// Reference add.js in main.js
let add = require('add.js')
add(1.2) / / 3
Copy the code

The loading mechanism for the require syntax

NodeJs caches a module the first time it is loaded in NodeJs. When the module is loaded later, the module’s module.exports property is fetched directly from the cache without dynamic updates.

Here are a few things to note:

  • Exports is just a reference to the exports method on the Module object
  • Module. Exports is returned when require references modules instead of exports

Modularity in ES6 (import, export, export default)

In a file, there can be multiple export and import files, but only one export default file.

Description and use of export

The export command specifies the external interface and must establish a one-to-one correspondence between variables inside the module. The interface output by the export statement is dynamically bound to its corresponding value, that is, the real-time value inside the module can be obtained through this interface. That is, when the value of the imported object changes in the module, the imported object changes accordingly.

/ / add. Js export
export varVariable = valueexport{variable}export{variableasOutput variable}/ / reference
import{variable}from 'add.js' 
Copy the code

Syntax for export default

Export above is dynamically bound. Import Default is not dynamic. The default export results in values rather than references. The reason is that the default export can be viewed as a special case of “default assignment”, which is essentially an assignment, so it gets a value rather than a reference.

/ / add. Js export
export default {
    add(){}}export default var count = 1 ` `
/ / reference
import add from 'add.js'
Copy the code

Export default is used to export values, not references. Except for the following exceptions:

export default function load() {
    console.log('doing something')}Copy the code

Export default Function is a special case that results in exporting references instead of values. If we export Function in the normal way, we still export the value, even if the exported object is a Function.

function onload() {}

export default onload;
Copy the code

Import command and dynamic import

The variables entered by the import command are read-only because they are essentially input interfaces. It is not allowed to rewrite interfaces in scripts that load modules.

The standard use of import is that the imported module is static and will be all imported modules that are compiled at load time. In some scenarios where we want to import modules conditionally or on demand, we can use dynamic imports instead of static ones. The keyword import can be used to dynamically import modules as if they were functions. In this way, a promise is returned.

// add.js
export let value = 'oldValue';

import('./add.js').then((module) = > {
    // Do something with the module.
})
/ / or
let module = await import('./add.js')
let { value } = await import('./add.js')

console.log(module.value)
console.log(value)
Copy the code

As we know from the above, export exports references. So when we use the import command line to import export, the exported object must be a reference?

The answer is no. For imports, {} = await import() is equivalent to reassignment, so references to concrete objects are lost, that is, asynchronous imports are reassigned. The let Module = await import() reference does not change because the module itself is an object, and the reference to module.value remains the same, even if the module is reassigned.

AMD specification

With NodeJS based on the CommonJS specification, the concept of server modules has been formed, and it is natural that people want client modules. Because the CommonJS specification loads synchronously, if the load resource is too large it will take too long to load and the whole application will just sit there and wait. This is not a problem on the server side because all modules are stored on the local disk and can be loaded synchronously, and the waiting time is the disk read time.

However, it is a big problem for the browser, because modules are placed on the server side and the wait time depends on the speed of the network, which can be a long time, so the browser side cannot use “synchronous loading”.

To solve this problem, only asynchronous loading can be used. This is the background from which the AMD specification was born. AMD defines a set of JavaScript modules that rely on asynchronous loading standards to solve the problem of synchronous loading.

In other words, before the current module can be parsed and executed, the module author must execute the module from which the current module comes, as shown in the call structure of the require function:

define(['./a'.'./b'].function(a, b) = >{ 
    a.doSomething(); 
    b.doSomething(); 
})
Copy the code

Example: A module is defined in a closure using the define function in the following format

define(id? : string, dependencies? : string[],factory: Function | Object)
Copy the code

Dependencies specifies the list of modules to depend on. It is an array and an optional parameter, and the output of each dependent module is passed as a parameter to the Factory. If no dependencies are specified, the default is [“require”, “exports”, “module”]

Example: Define a myModule module that depends on the jQuery module. Using the define and the require need to introduce the require. Js file, the file address: requirejs.org/docs/releas…

<scritpt src='./require.js' ></script>
define('myModule'['jquery'].function($) {$('body').text('hello world');
    return true;
})
/ / use
require(['myModule'].function(myModule) {
    console.log(myModule) // true
})
Copy the code

Note that asynchronously loading dependencies should use arrays to list the dependencies

require(['myModule']) //true
require('myModule') // false
Copy the code

Currently, there are two JavaScript libraries that implement the AMD specification: require.js and curl

CMD specification

CMD is the specification recommended by SEAJS, while CMD relies on proximity and requires when used. Format is as follows

define(id? : string, dependencies? : string[],factory: Function | Object)
Copy the code

The biggest difference between CMD and AMD is that the execution time of dependent modules is different, rather than the loading time or way is different, both are asynchronous loading modules.

AMD dependency front, JS can easily know who the dependency module is, immediately load.

CMD depends on nearby modules, which modules need to be parsed by turning them into strings.

Reference article: Close reading “Default, named export differences”