The preface

Module. Exports, export, export default, require, define, import, etc. Many people are still confused about these fields. The concept is very vague, so I want to write such an article, one is to help myself sort out the knowledge, the other is to grow up with everyone. If there is something wrong, please mention it and I will correct it in time.

Just began to write when some do not know how to start, one is because knowledge too much, two is not enough to help you from the deep analysis of JS modular differences, as well as its implementation principle, thought. This is an article of my own study notes, I can only take you to understand the front-end modular, distinguish them and use them correctly.

A couple of things to throw out:

  • CommonJS:NodeJSThe cornerstone of concrete implementation of modular systems.
  • AMD: Asynchronous module specification, yesRequireJSIn the process of promotion, the standardized output of module definition is highly recommended.
  • UMDCompatible:AMDandcommonJSAt the same time, but also compatible with the global reference way;
  • CMDIs:SeaJSIn the process of promotion, the standardized output of module definition is recommended to rely on nearby;
  • ES6: The design idea of ES6 module is as static as possible, so that the dependencies of the module, as well as the input and output variables can be determined at compile time;

CommonJS specification

The CommonJS website says it wants JS to run not just in a browser, but anywhere, giving it the ability to develop large applications.

javascript: not just for browsers any more!

CommonJS defines modules as:

  1. Module reference (require)
  2. Module definition (exports)
  3. Module identification (module)

He can do:

  • Server-side JavaScript applications
  • Command line tool
  • Graphical interface applications
  • Hybrid applications (e.g., Titanium or Adobe AIR)

The CommonJS module has the following features

  • All code runs in the module scope and does not pollute the global scope.
  • Modules can be loaded multiple times, but only run once on the first load, and then the results are cached and read directly from the cache when they are loaded later. For the module to run again, the cache must be cleared.
  • The order in which modules are loaded, in the order they appear in the code.

Let’s talk about the concept of a bag

Node.js is implemented based on the CommonJS specification. NPM is familiar to all of you. It implements the CommonJS package specification.

Package specification

Package specifications, compared to Git repositories, can be understood as follows:

  • git initHidden files are generated in the current folder.gitLet’s call thatGit repository.
  • npm initThe command generates a configuration file in the current folderpackage.json“, which describes the current package. We call this file package.

Package structure

Strictly following the CommonJS specification, the package directory should contain the following files or directories.

  • package.json: package description file, which exists in the package top-level directory
  • bin: Directory for storing executable binaries
  • lib: Directory for storing JS code
  • doc: Directory for storing documents
  • test: directory for storing unit test case code

Package. json is a configuration file that describes information about the package.

NodeJS module

Since Node.js is implemented based on CommonJS, let’s take a quick look at NodeJS module principles.

Recently, I participated in a training organized by the company to cultivate structural thinking. Everything can be classified. Once things are classified, it is more conducive to people’s cognition of things and convenient for people to remember them. So let’s take a look at the module classification of Node šŸµ.

Generally classified

Let me tell you a little bit about the classification of modules

  • The core module

    • Core modules are the binary modules that are compiled into Node
    • Preset in Node, it provides basic Node functions, such as FS, HTTP, and HTTPS.
    • The core module is implemented by C/C++, and the external module is packaged by JS
  • Third-party modules

    • NodeuseNPM(Node Package Manager) Install third-party modules
    • NPMThe module will be installed (or downloaded) to the application root directorynode_modulesfolder
    • When the module loads,nodeWill first search in the core module folder, and then tonode_modulesFolder to search
  • File module

    • Files can be placed anywhere
    • Add the path when loading the module file
  • Folder modules (more on nodeJS loading rules later)

    • Node first searches for package.json files in that folder,

      • If it does, Node attempts to parse it and load the module file specified by the main property
      • Does not exist (orpackage.jsonIs not definedmainBy default, Node loads the index.js file in this folder (mainProperties in factNodeJSAn extension of,CommonJSThis field is not included in the standard definition.

It is estimated that the concept of folder module is relatively vague, it is actually equivalent to a custom module, give you an example šŸ¤” :

We need to use a custom folder module in /main.js under the root directory. We put all the custom folder modules in the root directory under /module. One of them is the /module/demo folder that we need to import.

| - main. Js | -- -- the module | - demo | -- package. Json | -- demo. JsCopy the code

The package.json file contains the following information:

{" name ":" demo ", "version" : "1.0.0", "main" : ". / demo. Js "}Copy the code

In the/main. In js:

let demo = require("./modules/demo");Copy the code

/modules/demo/demo.js; /modules/demo/demo.

This is the simplest package, with a folder as a module.

NodeJS module with CommonJS

The module properties
  • module.idThe module’s identifier, usually the module’s file name with an absolute path.
  • module.filenameThe file name of the module, with an absolute path.
  • module.loadedReturns a Boolean value indicating whether the module has finished loading.
  • module.parentReturns an object representing the module that called the module.
  • module.childrenReturns an array representing other modules used by this module.
  • module.exportsRepresents the output value of the module.

Let’s do a test to see what a Module really is (write in detail, high level self-filter).

  1. Create a new folder namedmodulePractice
  2. Command line accesscd modulePractive/folder
  3. npm init, input information, at this point we have created a package
  4. npm install jqueryTo installjqueryTo do the test
  5. newmodulePractice/test.js
| - modulePractice | - node_module | -- package. Json | -- test. JsCopy the code
// test.js var jquery = require('jquery'); exports.$ = jquery; console.log(module); //module is an object inside the current module, representing the current objectCopy the code

The terminal executes the file

node test.jsCopy the code

The following information is displayed:

Module {
  id: '.',
  exports: { '$': [Function] },
  parent: null,
  filename: '/Applications/practice/nodepractice/modulePratice/test.js',
  loaded: false,
  children:
   [ Module {
       id: '/Applications/practice/nodepractice/modulePratice/node_modules/jquery/dist/jquery.js',
       exports: [Function],
       parent: [Circular],
       filename: '/Applications/practice/nodepractice/modulePratice/node_modules/jquery/dist/jquery.js',
       loaded: true,
       children: [],
       paths: [Array] } ],
  paths:
   [ '/Applications/practice/nodepractice/modulePratice/node_modules',
     '/Applications/practice/nodepractice/node_modules',
     '/Applications/practice/node_modules',
     '/Applications/node_modules',
     '/node_modules' ] }Copy the code

Now we can see that the parent property of the current module is null, which proves that the current module is an entry script.

Let’s look at importing other file modules in test.js. What does the module output

6. Create a modulePractice/child.js file

| - modulePractice | - node_module | -- package. Json | -- test. Js | -- child. JsCopy the code
//child.js
var str = "I'm child";
exports.str = str;
console.log(module);Copy the code

Execute again:

node test.jsCopy the code

Module. js and module. test.js

/ / this is child. Js output information Module in the {id: '/ Applications/practice/nodepractice modulePratice/child. The js', exports: {STR: 'I\'m child' }, parent: Module { id: '.', exports: {}, parent: null, filename: '/Applications/practice/nodepractice/modulePratice/test.js', loaded: false, children: [ [Circular] ], paths: [ '/Applications/practice/nodepractice/modulePratice/node_modules', '/Applications/practice/nodepractice/node_modules', '/Applications/practice/node_modules', '/Applications/node_modules', '/node_modules' ] }, filename: '/Applications/practice/nodepractice/modulePratice/child.js', loaded: false, children: [], paths: [ '/Applications/practice/nodepractice/modulePratice/node_modules', '/Applications/practice/nodepractice/node_modules', '/Applications/practice/node_modules', '/Applications/node_modules', Module {id: '.', exports: {'$': [Function]}, parent: null, filename: '/node_modules']} '/Applications/practice/nodepractice/modulePratice/test.js', loaded: false, children: [ Module { id: '/Applications/practice/nodepractice/modulePratice/child.js', exports: [Object], parent: [Circular], filename: '/Applications/practice/nodepractice/modulePratice/child.js', loaded: true, children: [], paths: [Array] }, Module { id: '/Applications/practice/nodepractice/modulePratice/node_modules/jquery/dist/jquery.js', exports: [Function], parent: [Circular], filename: '/Applications/practice/nodepractice/modulePratice/node_modules/jquery/dist/jquery.js', loaded: true, children: [], paths: [Array] } ], paths: [ '/Applications/practice/nodepractice/modulePratice/node_modules', '/Applications/practice/nodepractice/node_modules', '/Applications/practice/node_modules', '/Applications/node_modules', '/node_modules' ] }Copy the code

As you can see

  • child.jsIn theparentProperty output istest.jsthemoduleInformation,
  • whiletest.jsIn thechildrenAttributes, includingjqueryandchild.jstwomoduleinformation
  • test.jsIn theparentProperties fornull;

From this, we can use module.parent to determine whether the current module is an entry script

Of course, there are other ways to determine the entry script, such as using require.main:

Child-.js is modified as follows:

//child.js
var str = "I'm child";
exports.str = str;
console.log(require.main);Copy the code
node test.jsCopy the code
Module {
  id: '.',
  exports: {},
  parent: null,
  filename: '/Applications/practice/nodepractice/modulePratice/test.js',
  loaded: false,
  children:
   [ Module {
       id: '/Applications/practice/nodepractice/modulePratice/child.js',
       exports: [Object],
       parent: [Circular],
       filename: '/Applications/practice/nodepractice/modulePratice/child.js',
       loaded: false,
       children: [],
       paths: [Array] } ],
  paths:
   [ '/Applications/practice/nodepractice/modulePratice/node_modules',
     '/Applications/practice/nodepractice/node_modules',
     '/Applications/practice/node_modules',
     '/Applications/node_modules',
     '/node_modules' ] }Copy the code

As you can see, require.main directly prints the entry script, since we printed require.main in child.js, So we can’t get the exports of the test.js entry script and only see the children module of the current entry script.

To test it another way, let’s print require.main in test.js and see what it prints;

Test.js is modified as follows:

var child = require("./child.js");
var jquery = require('jquery');
exports.$ = jquery;
console.log(require.main);Copy the code

perform

node test.jsCopy the code

Get the following information:

Module {
  id: '.',
  exports: { '$': [Function] },
  parent: null,
  filename: '/Applications/practice/nodepractice/modulePratice/test.js',
  loaded: false,
  children:
   [ Module {
       id: '/Applications/practice/nodepractice/modulePratice/child.js',
       exports: [Object],
       parent: [Circular],
       filename: '/Applications/practice/nodepractice/modulePratice/child.js',
       loaded: true,
       children: [],
       paths: [Array] },
     Module {
       id: '/Applications/practice/nodepractice/modulePratice/node_modules/jquery/dist/jquery.js',
       exports: [Function],
       parent: [Circular],
       filename: '/Applications/practice/nodepractice/modulePratice/node_modules/jquery/dist/jquery.js',
       loaded: true,
       children: [],
       paths: [Array] } ],
  paths:
   [ '/Applications/practice/nodepractice/modulePratice/node_modules',
     '/Applications/practice/nodepractice/node_modules',
     '/Applications/practice/node_modules',
     '/Applications/node_modules',
     '/node_modules' ] }Copy the code

That is, in a real entry file, the require.main information printed is complete information;

You can also use the parent property of the module output from require.main to determine if it is an entry script.

Modulerequire. main === module; if true, it is executed directly (node xxx.js).

Exports properties

Now that we know about module properties, what are module.exports and exports?

Module exports exports: Module exports: Module exports: Module exports: Module exports: Module exports: Module exports That is, the module.exports property represents the interface that the current module exports, and other files that load the module actually read the module.exports variable.

The exports variable, which nodeJS actually provides for convenience, is an exports variable for each module pointing to module.exports. This equates to a line of command in the header of each module.

var exports = module.exports;Copy the code

Therefore, we can add methods directly to exports objects

exports.area = function (r) {
  return Math.PI * r * r;
};

exports.circumference = function (r) {
  return 2 * Math.PI * r;
};Copy the code

Note:

  • Can’t directly transferexportsThe variable points to avalueIs equal to cut offexportswithmodule.exportsIt is no longer an interface, but only a local variable in the current module. All the other things you write in the current module at this pointexportsThe exported interface will be invalid. And onlymodule.exportsCan expose the external interface of the current module.

Exports: / / exports: / / module. Exports: / / exports: / / module. Exports: / / module. The final interface we exported was the module.exports property.

Load the rule, require method

The basic function of the require command is to read and execute a JavaScript file and then return the exports object of that module. If no specified module is found, an error is reported.

The require command is used to load other modules in the CommonJS specification. It is not a global command, but points to the module’s module.require command, which in turn calls Node’s internal command module._load

  • require(): Loads external modules
  • require.resolve(): Resolves the module name to an absolute path
  • require.main: points to the main module
  • require.cache: points to all cached modules
  • require.extensions: Calls different execution functions according to the file name extension

The require command is used to load a file. The default suffix is.js.

var foo = require('foo'); Var foo = require('foo.js');Copy the code

The introduction of this approach (not an absolute path, and not a relative path) will search the following rules for loading;

/usr/local/lib/node/foo.js
/home/user/projects/node_modules/foo.js
/home/user/node_modules/foo.js
/home/node_modules/foo.js
/node_modules/foo.jsCopy the code

That is, the default core module (node) will be searched first, and then the current module in node_modules will be searched further up the hierarchy. This allows different modules to localize their dependencies.

And if it’s a:

require('example-module/path/to/file')Copy the code
  1. Will be found firstexample-module, and then use it as a parameter to find the following path.
  2. Find if there is a file folder

    • If you find it, trypackage.jsonAnd with itsmainProperty to the directory specified as the entry file, otherwise under the current directoryindex.js | index.nodeAs an entry file
    • If not found, thenNodeAttempts will be made to add the file name.js,.json,.nodeAfter, go searching again..jsThe document will be in text formatJavaScriptScript file parsing,.jsonThe file willJSONFormat text file parsing,.nodeFiles are parsed as compiled binaries.
  3. If no discovery is found, an error is reported.

Node caches a module the first time it is loaded. The module.exports property is fetched directly from the cache when the module is loaded later.

CommonJS module loading method

The CommonJS specification loads modules synchronously, meaning that subsequent operations cannot be performed until the load is complete. So in general, the CommonJS specification does not work in a browser environment. However, this is not a problem on the server side because all modules are stored on the local hard disk and can be loaded synchronously, and the wait time is the hard disk read time. For browsers, however, this is a big problem because modules are on the server side and the wait time can be long depending on the speed of the network, leaving the browser in a state of “suspended animation”.

Therefore, browser-side modules cannot be synchronous, they can only be asynchronous. This is the background from which the AMD specification was born.

The next chapter will cover the AMD specification.

Refer to the article

  • Javascript modular programming (II) : AMD specification
  • CommonJS specification and Nodejs module mechanism
  • Nodejs module loading mechanism
  • JavaScript standard Reference tutorial