CommonJS module specification


The main content of the specification

  • Define module

    According to the CommonJS specification, a single file is a module, and each module is a separate scope, that is, variables defined within the module cannot be read by other modules unless they are defined as attributes of the global object

  • Module output

    The module has only one export, the module.exports object, you need to put the content that the module wants to output into this object

  • Load module

    Load the module using the require method, which reads a file and executes it, and returns the module.exports object inside the file

analysis:

Because the require in the above CommonJS specification is synchronous, the module system needs to read the content of the module file synchronously, and compile and execute it to get the module interface. This is very simple to implement on the server side, but because the js script is inherently asynchronous, it is on the browser side. There will be many implementation problems.

1. What is modularity

With file scope and communication rules (import, export), it is said to be modular

Js in node has an important concept: module system

  • Module scope
  • Use require method to load module
  • Use exports interface object to export members in the module
2.common JS module specification
1. Load require
  1. grammar

    var 自定义变量名称 = require('模块');
    
  2. effect

    • Execute the code in the loaded module
    • Returns the exports export object in the loaded module
2. Export exports

Node is the module scope. All objects in the default file are only valid in the current file module. For members that want to be accessed by other modules, you need to mount all public members to the exports interface object.

  • Export multiple members: mount to the exports object

    exports.str = 'hello';
    exports.add = function() {
          
          
        return 1;
    }
    
    // 或者:
    module.exports = {
          
          
        str: 'hello',
        arr: []
    }
    
  • Export a single member: direct assignment

    module.exports = 'hello';
    // 由于是赋值操作,后者会覆盖前者
    module.exports = 123
    
3. Module principle (exports export object essence)

Each module in node has a module object inside, and there is a member in the module object: exports object

let module = {
    
    
    exports: {
    
    };
}

And by default there is the following code at the end of the module:

return module.exports;

So it can be seen that each loaded module actually exports the module.exports object, and mounting data on its object is generally through

module.exports.data = 'hello';
module.exports.str = 123;

When other modules load the module, they can get the data mounted on the object exported by the module. In the later stage, node defines an object exports to reference module.exports for the convenience of writing, that is

var exports = module.exports; 

And remember that exports are only a reference to module.exports, and the loaded module exports module.exports, so if you change the reference of exports and then mount the value, the value mounted after changing the exports reference will not be exported at this time

exports = {
    
    };// 改变引用
exports.add = 123;// 挂载新数据
// 此时
exports == module.exports; // false
return module.exports;// 但是并没有修改module.exports上面的数据,因此此时导出的仍然是空对象

That is, the following scenario:

border_01

And what happens if the reference to module.exports changes? Since the references of exports and module.exports were the same at the beginning, the references of module.exports were modified later, but please remember one thing: the module exports the module.exports object at the end, so no matter how you change it, the exported data is the module .exports, and exports are just a reference to module.exports for shorthand purposes. So look at the following code:

module.exports  = {
    
    
    add: 1
}
exports = {
    
    
    foo: 2
}
// 最后导出的数据为{add:1}
// -------------------------------------
module.exports = 'hello';// 此时导出的仅仅是一个字符串

From the above code, it can be concluded that the data exported by the module is determined by module.exports. If you want to export multiple members, you can mount the data on the module.exports object or change the reference to a multi-data object; if you want to export For a single member, you can directly refer to module.exports to point to basic data types or complex data types

to sum up:

  1. Export multiple members

    • Mounted on the exports object (module.exports referenced by exports at this time)

      exports.add = 123;
      exports.minus = 456;
      
    • Point to multiple data objects

      module.exports = {
              
              
          add: 123,
          minus: 456
      };// 这个地方不能用exports来引用这个对象,不然会改变exports引用,但是最后导出的module.exports对象并没有获取这个值
      
  2. Export a single member

    module.exports = 'hello';
    
4.require identifier analysis

When a file is required, the search will be performed in the following order

  1. If it is a module in the form of a path

    ./ 当前目录,不可省略
    ../ 上一级目录,不可省略
    /xxx 几乎不用
    e:/a/index.js 几乎不用
    其中首位的/ 在这里表示的是当前文件模块所属磁盘根路径
    
    也即:
    require('./foo.js');
    // 或者省略.js文件 默认访问为.js文件
    require('./foo')
    

    And the above loading method is used as the loading method of the custom module

  2. If it is a non-path form of module identification

    Check whether it is a core module. Since the core module file has been compiled into the binary file, it only needs to be loaded according to the name.

    require('http');
    
  3. It is neither a core module nor a path form module, it may be a third-party module

    1. All third-party modules must be downloaded through npm

    2. When used, it can be loaded by require('package name'); before it can be used.

    3. It is impossible for any third package to have the same name as the core module.

    4. Third-party module search rules (take require('atr-template')) as an example:

      1. First find the node_modules directory in the directory where the current file is located

      2. node_modules/art-template

      3. node_modules / art-template / package.json statement

      4. The main attribute in the node_modules/art-template/package.json file

      5. Since the entry module of art-template is recorded in the main attribute

      6. Then load and use this third-party module (usually a js file) according to the main attribute

      7. If the package.json file does not exist or the entry module specified by main cannot be found

        1. Node will automatically find the Index.js file in this directory as an alternative entry file
      8. If none of the above conditions are true, it will enter the node_modules directory in the upper level directory to search. If the upper level is not yet, look up level by level . If the root directory of the current disk cannot be found, an error will be reported: can not find module xxx

      9. Note :

        A project has one and only one node_modules, which is placed in the project root directory, so that the code in all subdirectories in the project can be loaded into the third-party package, and there will be no multiple node_modules

5.Require loading rules
  1. Load from cache first

    Consider the following scenario

    // a.js
    require('./b');
    require('./c');
    // b.js
    console.log('b.js被加载');
    require('./c');
    // c.js
    console.log('c.js被加载');
    

    Execute the a.js file, the order of execution is: load and execute b.js, print b.js被加载, in b.js, load and execute c.js, print c.js被加载, b.js is executed, return to a.js, not at this time The c.js file will be loaded again. That is, the print result is:

    b.js被加载
    c.js被加载
    // 而不是
    b.js被加载
    c.js被加载
    c.js被加载
    

    Reason: When b.js is loaded, c.js is loaded, and the exported interface data is put into the cache. When a.js wants to load c.js again, first look for it in the cache. Without this module, some will not be loaded and run, and the cached interface data will be loaded directly from the cache without running the c.js file again, so the following code becomes reasonable:

    // a.js
    require('./b');
    let cExports = require('./c');
    console.log(xExports);
    // b.js
    console.log('b.js被加载');
    let cExports = require('./c');
    console.log(cExports);
    // c.js
    console.log('c.js被加载');
    module.exports = 'hello';
    
    // 结果为:
    b.js被加载
    c.被加载
    hello
    hello
    
  2. Find modules based on require identifier analysis

Guess you like

Origin blog.csdn.net/chen__cheng/article/details/114652393