美文网首页
Node中的模块

Node中的模块

作者: 折枝赠远方 | 来源:发表于2019-03-31 13:24 被阅读0次

    转自:https://nodejs.lipengzhou.com/04-module.html

    模块系统(module)

    模块化编程,将大的功能拆分成小的模块组成

    什么是模块化

    问题

    当你的网站开发越来越复杂代码越来越多的时候会经常遇到什么问题?

    • 烦人的命名冲突
    • 繁琐的文件依赖

    模块化优点

    • 生产效率高
    • 可维护性好(功能模块易于更改)

    模块间通信

    需要在其他模块间使用,调用某个模块的方法,提供方法共别的模块使用

    通信规则

    关键词:

    • import (es标准的写法)
    • require
    • exports
    require模块导入
    const fs = require('fs');
    
    exports模块导出

    导出多个成员

    1. 写法一:(麻烦 不推荐)
    module.exports.a = 123;
    module.exports.b = 222;
    module.exports.c = 888;
    
    1. 写法二:(推荐)

      node 为了减低开发人员的痛苦,所以为module.exports提供了一个别名 exports

    console.log(exports === module.exports) // => true
    exports.a = 123;
    exports.b = 222;
    exports.c = 789;
    exports.fn = function () {
        console.log('hello');
    }
    
    1. 写法三

      代码少可以,代码多了就不推荐了

    module.exports = {
        a: 123,
        b: 222,
        c: 888,
        fn: function() {
            console.log('hello');
        }
    }
    

    导出单个成员

    • 导出的错误写法与正确写法对比
    // 导出单个成员,错误写法 因为每个模块最终导出的是 module.exports 而非 exports 所以
    // 下面这种写法错误
    
    // exports = function (x, y) {
    //     return x + y;
    // }
    
    // 正确写法
    module.exports = function (x, y){
        return x + y;
    }
    

    为什么 exports = xxx不行
    exportsmodule.exports的一个引用

    // fn可以想象成一个模块
    function fn() {
      // 每个模块内部有一个 module 对象
      // module 对象中有一个成员 exports 也是一个对象
      var module = {
        exports: {}
      }
    
      // 模块中同时还有一个成员 exports 等价于 module.exports
      var exports = module.exports
    
      console.log(exports === module.exports) // => true
    
      // 这样是可以的,因为 exports === module.exports
      // module.exports.a = 123
      // exports.b = 456
    
      // 这里重新赋值不管用,因为模块最后 return 的是 module.exports
      // exports = function () {
      // }
    
      // 这才是正确的方式
      module.exports = function () {
        console.log(123)
      }
    
      // 最后导出的是 module.exports
      return module.exports
    }
    
    var ret = fn()
    
    console.log(ret)
    

    exportsmodule.exports的区别

    • 每个模块中都有一个 module 对象
    • module 对象中有一个 exports 对象
    • 我们可以把需要导出的成员都挂载到 module.exports 接口对象中也就是:moudle.exports.xxx = xxx 的方式,但是每次都 moudle.exports.xxx = xxx 很麻烦,点儿的太多了;所以 Node 为了你方便,同时在每一个模块中都提供了一个成员叫:exports这个exports === module.exports 结果为 true
    • 多个成员导出:moudle.exports.xxx = xxx 的方式 完全可以
    • 单个成员导出:expots.xxx = xxx当一个模块需要导出单个成员的时候,这个时候必须使用:module.exports = xxx 的方式 不要使用 exports = xxx不管用因为每个模块最终向外 return 的是 module.exports
    • exports 只是 module.exports 的一个引用所以即便你为 exports = xx 重新赋值,也不会影响 module.exports
    • 但是有一种赋值方式比较特殊:exports = module.exports 这个用来重新建立引用关系的

    特殊的导出方式

    // 重新建立引用关系
    exports = module.exports = function () {
      console.log('默认函数被调用了')
    }
    
    exports.ajax = function () {
      console.log('ajax 方法被调用了')
    }
    
    exports.get = function () {
      console.log('get 方法被调用了')
    }
    

    模块分类

    在开始了解具体的规则之前,我们先来了解一下在 Node 中对不模块的一个具体分类,一共就三种类别;

    node中的模块类别

    1. 核心模块
      • 由 Node 本身提供,著名的,例如 fs 文件操作模块、http 网络操作模块
    2. 第三方模块
      • 由第三方提供,使用的时候我们需要通过 npm 进行下载然后才可以加载使用,例如我们使用过的 mimeart-templatemarked
      • 注意:不能有第三方包的名字和核心模块的名字是一样的,否则会造成冲突
    3. 用户模块
      • 我们在文件中写的代码很多的情况下不好编写和维护,所以我们可以考虑把文件中的代码拆分到多个文件中,那这些我们自己创建的文件就是用户模块
    核心模块

    参考文档:https://nodejs.org/dist/latest-v9.x/docs/api/

    • 核心模块就是 node 内置的模块,需要通过唯一的标识名称来进行获取。
    • 每一个核心模块基本上都是暴露了一个对象,里面包含一些方法供我们使用
    • 一般在加载核心模块的时候,变量的起名最好就和核心模块的标识名同名即可
      • 例如:const fs = require('fs')
    • 核心模块本质上也是文件模块
      核心模块已经被编译到了 node 的可执行程序,一般看不到
      可以通过查看 node 的源码看到核心模块文件
      核心模块也是基于 CommonJS 模块规范

    Node 中都以具名的方式提供了不同功能的模块,例如操作文件就是:fs
    核心模块(系统模块)由 Node 提供,使用的时候都必须根据特定的核心模块名称来加载使用。例如使用文件操作模块: fs

    例如:

    const fs = require('fs');
    

    常见核心模块

    模块名称 作用
    fs 文件操作
    http 网络操作
    path 路径操作
    url url地址操作
    os 操作系统信息
    net 更加底层的网络操作方式
    querystring 解析查询字符串模块
    util 工具函数模块
    ... ...
    用户自定义模块(基于文件的模块)

    以 ./ 或 ../ 开头的模块标识就是文件模块,一般就是用户编写的。

    第三方模块
    • moment
    • marked
    • ...

    一般就是通过 npm install 安装的模块就是第三方模块。

    加载规则如下:

    • 如果不是文件模块,也不是核心模块node 会去 node_modules 目录中找(找跟你引用的名称一样的目录),例如这里 require('underscore')
    • 如果在 node_modules 目录中找到 underscore 目录,则找该目录下的 package.json 文件;如果找到 package.json 文件,则找该文件中的 main 属性,拿到 main 指定的入口模块
    • 如果过程都找不到,node 则取上一级目录下找 node_modules 目录,规则同上。。。
    • 如果一直找到代码文件的根路径还找不到,则报错。。。

    深入模块加载机制

    模块记载流程图
    nodejs-require.851bca4a.jpg
    2015-07-15_55a6794639322.1416ddab.jpg
    模块加载概述
    1. 简而言之,如果require绝对路径的文件,查找时不会去遍历每一个node_modules目录,其速度最快。其余流程如下:

    2. module path数组中取出第一个目录作为查找基准。直接从目录中查找该文件,如果存在,则结束查找。如果不存在,则进行下一条查找。

    3. 尝试添加.js.json.node后缀后查找,如果存在文件,则结束查找。如果不存在,则进行下一条。

    4. 尝试将require的参数作为一个包来进行查找,读取目录下的package.json文件,取得main参数指定的文件。

    5. 尝试查找该文件,如果存在,则结束查找。如果不存在,则进行第3条查找。

    6. 如果继续失败,则取出module path数组中的下一个目录作为基准查找,循环第1至5个步骤

    7. 如果继续失败,循环第1至6个步骤,直到module path中的最后一个值。

    8. 如果仍然失败,则抛出异常。

    整个查找过程十分类似原型链的查找和作用域的查找。所幸Node.js对路径查找实现了缓存机制,否则由于每次判断路径都是同步阻塞式进行,会导致严重的性能消耗。

    相关文章

      网友评论

          本文标题:Node中的模块

          本文链接:https://www.haomeiwen.com/subject/ssgybqtx.html