目录

  • 模块化概念
  • 模块的分类
  • 加载模块
  • 模块作用域
  • 向外共享模块作用域成员
  • module对象
  • module.exports对象
  • exports对象
  • 共享总结
  • 模块规范化模块
  • npm与包
  • 查看npm版本和node版本
  • npm安装第三方模块
  • 使用第三方模块moment
  • 包的版本
  • 包管理配置文件
  • 包的分类
  • 项目包
  • 全局包
  • 规范的包结构
  • 模块的加载机制
  • 内置模块加载机制
  • 自定义模块加载机制
  • 第三方模块加载机制
  • 目录作为模块


模块化概念

自顶向下把系统划分为若干个模块的过程叫模块化。对系统来说,模块是可以组合、分解、和更换的单元。
提高了代码的复用性,代码的可维护性,可以实现按需加载。

模块的分类

按模块的来源分为三类:
1、内置模块:fs,path,http
2、自定义模块:用户创建的js文件
3、第三方模块:第三方开发,使用需要下载

加载模块

使用require加载模块,加载模块时会执行模块中的代码

// 加载内置模块
const fs = require('fs')
// 加载用户自定义模块,可以省略.js文件后缀名
const user= = require('./a.js')
// 加载第三方模块
const moment = require('moment')

模块作用域

只能在当前模块访问,这种限制叫做作用域。
防止了全局变量污染的问题。

// b.js
const username = 'xiaoming'
console.log(username)
// a.js
const aa = require('./b.js')
console.log(aa)  //  {}
console.log(aa.username)  //undefined
console.log(username)  //报错,username is not defined


// 终端输入命令
node a.js
// 打印xiaoming {} 然后报错

向外共享模块作用域成员

module对象

每个.js自定义模块中都有一个module对象,存储了当前模块的有关信息。
在使用require导入一个模块的时候得到的变量(成员),就是module.exports指向的对象,默认为{}

const aa = require('./b.js')
console.log(aa)
console.log(module)
console.log(module.exports)
module.exports对象

使用module.exports对象将自定义模块的成员共享出去。

// b.js
const age = 10
function func(){
	console.log('func')
}
// 在module.exports对象上挂载属性和方法
module.exports.username = 'xiaoming'
module.exports.age = age
module.exports.test = function(){
	console.log('this is b.js')
}
module.exports.func=func

// a.js
const aa= require('./b.js')
console.log(aa)
console.log(aa.age)   //10

// 终端运行a.js
node a.js
// 打印一个对象,包含username,age,test方法

注意:使用require导入模块时,导入的结果永远以module.exports指向的对象为准。

// b.js
const age = 10
// 在module.exports对象上挂载属性和方法
module.exports.age = age
module.exports.test = function(){
	console.log('this is b.js')
}

// 让module.exports指向一个新对象
module.exports={
	name:'xiaoming',
	sayHi(){
		console.log('hi')
	}
}

// a.js
const aa= require('./b.js')
console.log(aa)

// 终端运行a.js
node a.js
// 打印一个对象,包含name,sayHi方法
exports对象

默认情况下module.exports对象和exports对象指向同一个对象,共享的结果还是以module.exports对象指向的为准。

console.log(module.exports === exports)  //true

情况一:

// b.js
exports.username='xiaoming'
module.exports={
	sex:'male',
	syaHi(){
		console.log('hi')
	}
}

// a.js
const aa = require('./b.js')
console.log(aa)  // { sex: 'male', syaHi: [Function: syaHi] }

情况二:

// b.js
module.exports.username='xiaoming'
exports={
	sex:'male',
	syaHi(){
		console.log('hi')
	}
}

// a.js
const aa = require('./b.js')
console.log(aa)  // { username: 'xiaoming' }

情况三:

// b.js
module.exports.username='xiaoming'
exports.sex='male'

// a.js
const aa = require('./b.js')
console.log(aa)  // { username: 'xiaoming', sex: 'male' }

情况四:

// b.js
exports={
	sex:'male',
	username:'zs'
}
module.exports=exports
module.exports.age=10

// a.js
const aa = require('./b.js')
console.log(aa)  // { sex: 'male', username: 'zs', age: 10 }

建议不要在一个模块里使用module.exports和exports对象。

共享总结
// 共享属性
module.exports.属性 = 属性
exports.属性 = 属性

// 共享方法
module.exports.方法名 = function(){
	// 方法体
}
module.exports.方法名= 已经定义了的方法名
exports.方法名 = function(){
	// 方法体
}
exports.方法名= 已经定义了的方法名
模块规范化模块

Node.js遵循COmmonJS模块化规范。
1、每个模块内部,module变量代表当前模块
2、module变量是一个对象,module.exports是对外的接口
3、加载某个模块其实是加载该模块的module.exports属性,require方法用于加载模块

npm与包

包:第三方模块
包是基于内置模块封装出来的,提供了更高级更方便的API,极大提高了开发效率。
在https://www.npmjs.com/搜索自己需要的包
在https://registry.npmjs.org/服务器下载自己需要的包

查看npm版本和node版本

npm 是包管理工具,Node Package Manager
在终端输入命令

npm -v
node -v

npm安装第三方模块

使用第三方模块moment

在终端输入下载安装命令

npm install 包名[@版本号]
npm i 包名[@版本号]  //简写
// 终端
npm i moment
// a.js
const moment = require('moment')
const date = moment().format('YYYY-MM-DD HH:mm:ss')
console.log(date)

安装完成后,项目文件夹下多了一个叫node_module的文件夹和package-lock.json文件。
node_module的文件夹存放所有已经安装到项目的包,require导入第三方包找的就是这个文件夹。
package-lock.json文件记录node_module文件夹里的每个包的下载信息,包括名字、版本、下载地址等。
不要手动修改这两个,npm会自动管理维护。

包的版本

三位数字,采用点分十进制。
大版本.功能版本.bug修复版本
比如:2.12.0
大改动版本为2,新增功能12次,修复bug次数0。
只要前面的版本号变动了,后面的版本号就归零。

包管理配置文件

npm规定,在项目根目录必须提供一个叫package.json的包管理配置文件,记录了项目的名称、版本号、描述,用到了哪些包,哪些包只会在开发期间使用,哪些包会在开发和上线都使用等
在多人项目开发中,要把node_module文件夹添加到.gitignore忽略文件中。
在项目的根目录中,创建一个叫package.json的配置文件可以用来记录项目安装了哪些包,方便删除node_module文件夹后团队成员共享项目。使用npm install命令时,npm会自动把包的信息写入package.json文件中。

// 在执行命令的目录下快读创建package.json文件,只能在英文目录下运行,项目名称不能包括中文和空格
npm init -y

dependencies节点:package.json文件中的一个节点,专门记录使用npm install安装了哪些包。记录在开发和上线中使用的包

// 一次性安装所有的包,当拿到一个没有node_module文件夹的包时要先使用这个命令安装需要的包
npm install
npm i  // 简写
// 卸载包,命令执行后也会把package.json文件里的dependencies里对应的信息删除
npm uninstall [包名]

devDependencies节点:记录某些只在开发阶段使用的包信息。

// 安装包并把信息记录到devDependencies节点
npm install 包名 --save-dev
npm i 包名 -D  //简写

包的分类

项目包

安装到node_modules目录的包。
分为开发依赖包(devDependencies节点里)和核心依赖包(dependencies节点里)。

npm i 包名 -D    // 开发依赖包,记录到devDependencies节点
npm i 包名      // 核心依赖包,记录到dependencies节点
全局包
npm i 包名 -g    // 全局安装指定包
npm uninstall 包名 -g     // 卸载全局安装的包

注意:
只有工具性质的包才有全局安装的必要性。
判断某个包是否需要全局安装后才能使用可以参考官方文档。

规范的包结构

1、包必须以单独的目录存在
2、包的顶级目录必须包含package.json文件
3、package.json中必须包含name, version, main 这三个属性,分别代表包的名字、版本号、包的入口

模块的加载机制

模块在第一次加载后会被缓存,即多次使用require()不会导致模块被执行多次。

// b.js
console.log('bbb')

// a.js
require('./b.js')
require('./b.js')
require('./b.js')

// 终端
node a.js
// 只打印一次bbb

内置模块加载机制

内置模块的加载优先级最高,比如require(‘fs’),如果有一个自定义模块也叫fs,那么加载的是内置模块。

自定义模块加载机制

必须以./ 或者 . ./ 为路径开头,如果没有这样指定,会被当作内置模块或者第三方模块加载。
如果省略了文件扩展名,按以下顺序尝试加载文件:
1、确切文件名
2、.js
3、.json
4、.node
5、加载失败,报错

第三方模块加载机制

从当前模块的父目录开始尝试从/node_moules文件夹中加载。
如果没有找到对应的第三方模块,就移动到上一级父目录中,直到找到或不能再向上。
比如在"C:\Users\a\b\c\project\test.js"调用require(‘tools’):
先找到:C:\Users\a\b\c\project\node_modules\tools
找不到:C:\Users\a\b\c\node_modules\tools
找不到:C:\Users\a\b\node_modules\tools
找不到:C:\Users\a\node_modules\tools
找不到:C:\Users\node_modules\tools
找不到:C:\node_modules\tools

目录作为模块

三步:
1、在被加载的目录下找package.json文件,寻找main文件作为require()的加载入口
2、被加载的目录没有package.json文件,或者main入口不存在或无法解析,则尝试加载目录下的index.js文件
3、以上两步都失败了,报错:Error:Cannot find module ‘xxx’