NodeJS入门4-模块系统

本篇博客,在CSND和Github上均可访问。

附上github博客地址

目前在Github上有 JS深入系列读后感、NodeJS系列,如果您感兴趣,不妨star一下吧,当然如果有任何表达不当或者讲述不清的地方,请及时留言,我们互相探讨,一起进步~

模块系统

在计算机程序的开发过程中,随着程序代码越写越多,在一个文件里代码就会越来越长,越来越不容易维护。

为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式。在Node环境中,一个.js文件就称之为一个模块(module)。

使用模块有什么好处?

最大的好处是大大提高了代码的可维护性。其次,编写代码不必从零开始。当一个模块编写完毕,就可以被其他地方引用。我们在编写程序的时候,也经常引用其他模块,包括Node内置的模块和来自第三方的模块。

在Node中,模块分为三种。

  • 具名的核心模块,如:fs、http等。
  • 用户自己编写的文件模块(JS文件)。
  • 第三方模块。
    • art-template
    • 通过npm下载的

Node中没有全局作用域,只有模块作用域。

  • 外部访问不到内部,内部访问不到外部。

加载

var 自定义变量名称 = require('模块')

两个作用:

  • 加载文件模块,并执行里面的代码。
  • 拿到被加载的文件模块导出的接口对象。

require中的相对路径必须加 ./,不可省略

  • 可以省略后缀名
  • 相对路径
    如果你只写文件名,则Node会依次在内置模块、全局模块和当前模块下查找hello.js,你很可能会得到一个错误:
module.js
   throw err;
         ^
Error: Cannot find module 'hello'
   at Function.Module._resolveFilename**
   at Function.Module._load
   ...
   at Function.Module._load
   at Function.Module.runMain

遇到这个错误,你要检查:

  • 模块名是否写对了;
  • 模块文件是否存在;
  • 相对路径是否写对了。

require加载规则

我们先来看一个例子:

main文件引入a和b文件,a文件因为b文件,运行main文件的执行结果是什么呢?

结果是
我是a文件 我是b文件
这样的原因是什么呢?

  • 执行顺序

    • 我们引入a文件,a文件执行输出console,同时引入b文件,b文件输出。
    • 我们引入b文件,此时Node发现已经加载过b了,它回看一下是否有缓存,优先从缓存加载,不会重复执行
    • 拿到其中的接口对象。
  • 这样做的目的

    • 避免重复加载,提高模块加载效率。
  • 判断模块标符

    • 核心模块
    • 第三方模块
    • 自己写的
  • 解决方案

    • 如果他不是核心模块,就按路径的形式进行加载。

      • 路径形式的模块判断
      • ./ 当前目录(不可省略) …/ 上一级目录(不可省略)
      • js 后缀名可以省略
    • 如果它是核心模块,(核心模块已经被便已到了二进制文件中,我们直接引用)

    • 如果是第三方模块
      他的查找规则为:

      • 首先需要 npm 来下载。
      • 使用的时候通过 require('art-template‘) 的方式加载(拿art-template举例子)。
      • 1.先找到当前文件下的 node_modules 找 ‘art-template’文件夹。
      • 2.打开 node_modules/art-template/package.json ,找到main属性指向的 文件。
      • 3.index.js文件为实际执行文件。
      • 如果 main 内没有指定入口模块,或者说 package.json不存在,那么node会自动去寻找该目录下的 index.js 文件。
      • 如果以上条件都不成立,则会向 上一级的node_modules 目录重新查找。
      • 直到当前根目录仍无法找到,报错 can not find module xxx
      • 我们一个项目 有且只有一个 node_modules,放在项目根目录中,以使得所有子目录都可以引用 第三方包。

导出

  • Node中是模块作用域 ,默认文件中的所有成员指在当前文件中有效。
  • 所有每个文件模块中都提供了一个对象: exports。它是是一个默认空对象。
  • 对于希望可以访问的模块成员,需要将其挂载到exports接口对象中即可(成为它的一个属性)。

第一种方法 (导出多个成员)

exports.foo = 123;
exports.add = function (x, y) {
    return x + y;
};
exports.c = 'hello';
exports.d = {name: 'jace',age : 21}

这里有一个特殊的地方,如果我只希望a可以拿到b里声明的函数add,我可不可以 在b中写成 exports = add 呢?

答案是否定的,此时我们又引出了第二个导出(暴露)方法。

第二种方法 (导出单个成员 例如:字符串、函数)

如果某个模块需要直接导出某个成员,而非挂载的方式,我们必须使用 module.exports = add 的方式。

module.exports = add;

**注意! 以下情况会覆盖 **

module.exports = 'hello';
module.exports = function(){} //后者会覆盖前者

exports 和 module.exports的区别

因为本人也是第一次接触Node,且也是第一次接触服务端,总结的不到位的请多指正。

  • 每一个模块中都有一个 module 对象
  • module 对象中有一个 exports 对象
  • 我们可以把需要导出的成员都 挂载 到 module.exports 接口对象中。
  • 起初的挂载方式 module.exports.xxx = xxx 较麻烦,Node为了开发者的考虑,在每个模块中提供了一个成员叫 exports
  • exports === module.exports return true
  • 注意 当一个模块需要导出单个成员时,必须使用 module.exports = xxx 的方式
  • exports = xxx 这样的方法是无效的,exports仅仅是module.exports的引用,你对exports赋值,对最终结果没有影响。
  • 如果你非要这么做, 你可以添加 exports = module.exports 这个用来重新建立引用关系、

例如:

module.exports = {
   a: 123
}//第一条

exports = module.exports //第三条 (此时 第二条生效) //结果为exports = {a: 123, foo: bar}

exports.foo = 'bar // 第二条(此时它无效)

点击进入下一篇5.npm和package.json

你可能感兴趣的:(Node.JS,【Node.JS入门】)