Node中的模块系统

2021-7-19 Node.js

CommonJS模块规范,包描述文件及npm常用命令

# 什么是模块化

  • 有文件作用域
  • 通信规则
    • 加载 require
    • 导出

# CommonJS模块规范

JavaScript本身是不支持模块化的

在Node中的JavaScript还有一个很重要的概念:模块系统

  • 模块作用域
  • 使用require方法来加载模块
  • 使用exports接口对象用来导出模块的成员

# 加载require

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

两个作用:

  • 执行被加载模块中的代码
  • 得到被加载模块中的exports导出接口对象

# 导出exports

  • Node中是模块作用域,默认文件中所有的成员只在当前文件模块有效
  • 对于希望可以被其他模块访问的成员,我们就需要把这些公开的成员都挂载到exports接口对象中就可以了
exports.a = 123
exports.b = 'hello'
exports.c = function () {
  console.log('ccc')
}
exports.d = {
  foo: 'bar'
}

//或者

module.exports = {
  a: 123,
	b: 'hello',
	c: function () {
    console.log('ccc')
  },
	d: {
    foo: 'bar'
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

需求:Node中的exports是一个对象,但如果某一个模块仅导出一个方法,希望通过导入模块直接拿到该方法,而不需要通过 exports.方法 的形式(即导出单个成员

function add(x,y) {
  return x + y
}
//exports = add 这句不起作用
module.exports = add//如果多次赋值,后面会覆盖前面的
1
2
3
4
5

# exports和module.exports的区别

在Node中,每个模块内部都有一个自己的module对象,该module对象有一个默认为空的exports对象,可以默认为模块有以下代码

var exports = module.exports
console.log(exports === module.exports)//true
...
return module.exports
//所以当导出单个成员是让exports直接等于函数或者字符串会不起作用,因为改变了指针指向,没有改变module.exports的值
1
2
3
4
5

# require方法加载规则

  1. 优先从缓存加载(如果之前已引入一次,则不执行模块代码,直接获取模块的返回值)
  2. 判断模块标识(即require参数)
    • 核心模块
      • require参数为模块名
      • 核心模块文件已经被编译到了二进制文件(node.exe)中了,我们只需要按照名字加载就可以了
    • 第三方模块
      • require参数为模块名
      • 凡是第三方模块都必须通过npm来下载
      • 使用的时候通过require('包名')的方式来进行加载才可以使用
      • 不可能有任何一个第三方包和核心模块的名字是一样的
      • 先进入当前目录下的node_modules,模块加载的是node_modules/包名/package.json文件中main属性指向的文件所导出的模块
      • 如果通过上面形式找不到入口模块,则会自动找该目录下的index.js并执行
      • 如果node_modules不在当前目录下,则会一直往上一级目录找,直到当前磁盘根目录还找不到,则报错:Can not find module xxx
      • 注:一个项目有且只有一个node_modules,不会出现多个,一般放在项目根目录,这样所有项目子目录都可以访问到
    • 用户自定义模块
      • require参数为路径
      • ./当前目录,../上一级目录(都不可省略,后缀名可省略)

# 包描述文件package.json

建议每一个项目的根目录下都有一个package.json文件(包描述文件),这个文件可以通过npm init的方式自动初始化出来。

  • npm命令中的--save会在项目的package.json中生成"dependencies"(项目依赖)
  • 建议执行npm install 包名的时候都加上--save,用来保存依赖项信息
  • 如果node_modules文件不在了,可以直接通过npm install命令将package.json的dependencies中的所有依赖项都下载回来

# package.json和package-lock.json

npm5以后才加入了这个文件,当你安装包的时候,npm都会生成或者更新package-lock.json这个文件,且不用加上--save这个参数,它会默认保存依赖。

  • package-lock.json里面包含了node_modules里面所有包的依赖信息(包括版本、下载地址),这样重新下载回node_modules会更快。
  • 从文件来看,有一个lock称之为锁
    • 如果项目依赖了1.1.1版本,你重新install会加载最新版本,而不是1.1.1
    • 而package-lock.json这个lock是用来锁定版本号的,防止自动升级新版

# npm命令行工具

只要安装了node就已经安装了npm

npm也有版本,可以通过以下命令查看

npm --version
1

升级npm:

npm install --global npm
1

# 常用命令

  1. npm init
    • 生成package.json文件
    • npm init -y可以跳过向导,快速生成
  2. npm install
    • 一次性把dependencies选项中的依赖项全部安装
    • install可以简写成 i
  3. npm install 包名
    • 只下载
  4. npm install --save 包名
    • 下载并保存依赖项(package.json文件中的dependencies选项)
    • 简写:npm i -S 包名
  5. npm uninstall 包名
    • 只删除,如果有依赖项会依然保存
    • 简写:npm un 包名
  6. npm uninstall --save 包名
    • 删除的同时会把依赖信息也去掉
  7. npm help
    • 查看npm使用帮助
  8. npm 命令 --help
    • 查看指定命令的使用帮助

# 解决npm被墙问题

  • npm存储包文件的服务器在国外,有时候会被墙,速度很慢,所以我们要解决这个问题。
  • http://npm.taobao.org/淘宝的开发团队把npm在国内做了一个备份(即淘宝npm镜像),每10分钟同步一次以保证尽量与官方服务同步。

安装淘宝的cnpm:

//该命令可在任意目录下执行,--global表示安装到全局,而非当前目录
npm install --global cnpm
1
2

接下来你安装包的时候把之前的npm替换成cnpm,举个栗子:

//这里走国外的npm服务器,速度比较慢
npm install jquery

//使用cnpm就会通过淘宝的服务器来下载jquery
cnpm install jquery
1
2
3
4
5

如果不想安装cnpm又想使用淘宝的服务器来下载:

npm install jquery --registry=https://registry.npm.taobao.org
1

但每次手动加参数很麻烦,可以将该选项加入配置文件,这要以后所有的npm install都会默认通过淘宝服务器来下载:

npm config set registry https://registry.npm.taobao.org

#查看npm配置信息
npm config list
1
2
3
4