zoukankan      html  css  js  c++  java
  • 【原创】CommonJS 模块

    参考:

    http://nodejs.cn/api/modules.html (nodejs官方教程)

    常识性知识

    nodejs的模块有2种类型:commonjs模块和es模块;

    不可以 require() 具有 .mjs 扩展名的文件。 试图这样做会抛出错误。 .mjs 扩展名是保留给 ECMAScript 模块,无法通过 require() 加载。

    模块封装器

    在执行模块代码之前,Node.js 会使用一个如下的函数封装器将其封装

    (function(exports, require, module, __filename, __dirname) {
    // 模块的代码实际上在这里
    });
    • 它保持了顶层的变量(用 var、 const 或 let 定义)作用在模块范围内,而不是全局对象。
    • 它有助于提供一些看似全局的但实际上是模块特定的变量,例如:
      • 实现者可以用于从模块中导出值的 module 和 exports 对象。
      • 包含模块绝对文件名和目录路径的快捷变量 __filename 和 __dirname 。

      

    模块加载的顺序

    没有路径符号前缀时

    当没有以 '/'、 './' 或 '../' 开头来表示文件时,这个模块必须是一个核心模块或加载自 node_modules 目录

    加载顺序为

    //1,核心模块
    
    //2,node_modules目录
    
    先找当前文件夹下的node_modules文件夹下的module-name文件夹下的package.json 文件指定的main字段文件路径。
    ## 如果第一种情况没有找到
    找当前文件夹下的node_modules文件夹下的module.js 文件
    ## 如果第二种情况没有找到
    找当前文件夹下的node_modules文件夹下的module文件夹下的index.js 文件
    ## 如果第三种情况没有找到
    找的上一级node_modules文件夹,查找顺序与上面一样。
    
    //3,全局目录加载

    如果 NODE_PATH 环境变量被设为一个以冒号分割的绝对路径列表,则当在其他地方找不到模块时 Node.js 会搜索这些路径。 此外,Node.js 还会搜索以下的全局目录列表: 1: $HOME/.node_modules 2: $HOME/.node_libraries 3: $PREFIX/lib/node 其中 $HOME 是用户的主目录, $PREFIX 是 Node.js 里配置的 node_prefix。 强烈建议将所有的依赖放在本地的 node_modules 目录。 这样将会更快地加载,且更可靠。

    有'/'、 './' 或 '../' 前缀时

    以 '/' 为前缀的模块是文件的绝对路径。 例如, require('/home/marco/foo.js') 会加载 /home/marco/foo.js 文件。
    以 './' 为前缀的模块是相对于调用 require() 的文件的。 必须在同一目录下
    以 '../' 为前缀的模块是相对于调用 require() 的文件的。 必须在上一目录下

    加载顺序为

    文件
    module-name.js
    module-name.node
    
    目录
    module-name/package.json的main属性
    module-name/index.js
    module-name/index.node

    如果给定的路径不存在,则 require() 会抛出一个 code 属性为 'MODULE_NOT_FOUND' 的 Error。

    模块分类

    核心模块

    Node.js 有些模块会被编译成二进制。 这些模块别的地方有更详细的描述。

    核心模块定义在 Node.js 源代码的 lib/ 目录下。

    require() 总是会优先加载核心模块。 例如, require('http') 始终返回内置的 HTTP 模块,即使有同名文件。

    文件模块

    其实模块都是文件;

    按确切的文件名没有找到模块,则 Node.js 会尝试带上 .js、 .json 或 .node 拓展名再加载。

    目录作为模块

    如果存在同名文件,会优先使用同名文件;

    require('./dir-module') 传入目录名,解析顺序是

    //在该目录下查找package.json文件的main属性对应的模块
    package.json文件下的main属性
    
    //在该目录下查找index.js文件
    ./some-library/index.js
    
    //在目录下查找index.node文件
    ./some-library/index.node

    node_modules 目录下的模块

    如果传递给 require() 的模块标识符不是一个核心模块,也没有以 '/' 、 '../' 或 './' 开头,则 Node.js 会从当前模块的父目录开始,尝试从它的 /node_modules 目录里加载模块。

    如果还是没有找到,则移动到再上一层父目录,直到文件系统的根目录。

    例子,如果在 '/home/ry/projects/foo.js' 文件里调用了 require('bar.js'),则 Node.js 会按以下顺序查找:

    • /home/ry/projects/node_modules/bar.js
    • /home/ry/node_modules/bar.js
    • /home/node_modules/bar.js
    • /node_modules/bar.js

    这使得程序本地化它们的依赖,避免它们产生冲突。

    通过在模块名后包含一个路径后缀,可以请求特定的文件或分布式的子模块。 例如, require('example-module/path/to/file') 会把 path/to/file 解析成相对于 example-module 的位置。 后缀路径同样遵循模块的解析语法。

    循环加载

    module首次加载,就会生成副本,循环使用时会返回 module 的 exports 对象的 未完成的副本

    a.js

    console.log('a 开始');
    exports.done = false;
    const b = require('./b.js');
    console.log('在 a 中,b.done = %j', b.done);
    exports.done = true;
    console.log('a 结束');

    b.js

    console.log('b 开始');
    exports.done = false;
    const a = require('./a.js');
    console.log('在 b 中,a.done = %j', a.done);
    exports.done = true;
    console.log('b 结束');

    main.js

    console.log('main 开始');
    const a = require('./a.js');
    const b = require('./b.js');
    console.log('在 main 中,a.done=%j,b.done=%j', a.done, b.done);

    当 main.js 加载 a.js 时, a.js 又加载 b.js。 此时, b.js 会尝试去加载 a.js。 为了防止无限的循环,会返回一个 a.js 的 exports 对象的 未完成的副本 给 b.js 模块。 然后 b.js 完成加载,并将 exports 对象提供给 a.js 模块。

    当 main.js 加载这两个模块时,它们都已经完成加载。 因此,该程序的输出会是:

    $ node main.js
    main 开始
    a 开始
    b 开始
    在 b 中,a.done = false
    b 结束
    在 a 中,b.done = true
    a 结束
    在 main 中,a.done=true,b.done=true
  • 相关阅读:
    pycharm快捷键
    Docker
    Go语言与Elasticsearch
    Celery与APScheduler
    爬虫入门到入狱
    数据分析
    后台管理
    Linux基础与自动化运维
    微信小程序
    Git
  • 原文地址:https://www.cnblogs.com/tkzc2013/p/14538134.html
Copyright © 2011-2022 走看看