1、模块化
Node.js 中根据模块来源的不同,将模块分为了3 大类,分别是:
- 内置模块 (内置模块是由 Node.js 官方提供的,例如fs、path、http 等)
- 自定义模块 (用户创建的每个 .js 文件,都是自定义模块)
- 第三方模块(由第三方开发出来的模块,并非官方提供的内置模块,也不是用户创建的自定义模块,使用前需要先下载)
1.1 加载模块
使用强大的 require()方法,可以加载需要的内置模块、用户自定义模块、第三方模块进行使用。例如:
// 1.加载内置的 fs 块
const fs = require( 'fs' )
// 2.加载用户的自定义模块
const custom = require( ' ./custom.js')
// 3.加载第三方模块(关于第三方模块的下载和使用)
const moment = require( 'moment ' )
注意: 使用require()方法加载其它模块时,会执行被加载模块中的代码
1.2 模块作用域
和函数作用域类似,在自定义模块中定义的变量、方法等成员,只能在当前模块内被访问,这种模块级别的访问限制,叫做模块作用域。
作用: 防止全局变量污染的问题
1.3 向外共享模块作用域中的成员
module对象:在每个.js 自定义模块中都有一个 module 对象,它里面存储了和当前模块有关的信息
module.exports对象
- 在自定义模块中,可以使用 module.exports 对象,将模块内的成员共享出去,供外界使用。
- 外界用 require() 方法导入自定义模块时,得到的就是 module.exports 所指向的对象
注意: 使用 require()方法导入模块时,导入的结果,永远以 module.exports 指向的对象为准
exports对象
由于 module.exports 单词写起来比较复杂,为了简化向外共享成员的代码,Node 提供了 exports 对象。默认情况下,exports 和 module.exports 指向同一个对象。 最终共享的结果,还是以 module.exports 指向的对象为准
exports 和 module.exports 的使用误区
时刻谨记,require() 模块时,得到的永远是 module.exports 指向的对象
1.4 node.js中的模块化规范
Node.js 遵循了 CommonJS 模块化规范,CommonJS 规定了模块的特性和各模块之间如何相互依赖 CommonJS规定:
- 每个模块内部,module 变量代表当前模块。
- module 变量是一个对象,它的 exports 属性(即 module.exports) 是对外的接。
- 加载某个模块,其实是加载该模块的 module.exports 属性require()方法用于加载模块。
2、npm与包
国外有一家IT 公司,叫做npm,Inc.这家公司旗下有一个非常著名的网站: www.npmjs.com/,它是全球最大的包共享…!
到目前位置,全球约 1100 多万的开发人员,通过这个包共享平台,开发并共享了超过 120 多万个包 供我们使用。
npm,Inc.公司提供了一个地址为 registry.npmjs.org/ 的服务器,来对外共享所有的包,我们可以从这个服务器上下载自己所需要的包。
- 从 www.npmis.com/ 网站上搜索自己所需要的包
- 从 registry.npmis.org/ 服务器上下载自己需要的包
2.1 在项目中安装包命令
如果想在项目中安装指定名称的包,需要运行如下的命令:
npm install 包的完整名称
初次装包完成后,在项目文件夹下多一个叫做 node modules的文件夹和 package-lock.json 的配置文件
其中:
node modules 文件夹用来存放所有已安装到项目中的包。require0 导入第三方包时,就是从这个目录中查找并加载包 package-lock.json 配置文件用来记录 node modules 目录下的每一个包的下载信息,例如包的名字、版本号下载地址等
安装指定版本的包
默认情况下,使用 npm install 命令安装包的时候,会自动安装最新版本的包。如果需要安装指定版本的包,可以在包名之后,通过 @ 符号指定具体的版本,例如:
npm i moment@2.22.2
包的语义化版本规范
包的版本号是以“点分十进制”形式进行定义的,总共有三位数字,例如 2.24.0其中每一位数字所代表的的含义如下:
- 第1位数字:大版本
- 第2位数字:功能版本
- 第3位数字:Bug修复版本
版本号提升的规则: 只要前面的版本号增长了,则后面的版本号归零
快速创建package.json
npm 包管理工具提供了一个快捷命令,可以在执行命令时所处的目录中,快速创建 package.json 这个包管理 配置文件:
npm init -y
devDependencies 节点
如果某些包只在项目开发阶段会用到,在项目上线之后不会用到,则建议把这些包记录到 devDependencies 节点中。
与之对应的,如果某些包在开发和项目上线之后都需要用到,则建议把这些包记录到 dependencies 节点中。
可以使用如下的命令,将包记录到 devDependencies 节点中:
// 安装指定的包,并记录到 devDependencies 节点中
npm i 包名 -D
// 注意:上述命令是简写形式,等价于下面完整的写法:
npm install 包名 --save-dev
解决下载包速度慢问题
使用淘宝npm镜像源服务器
切换npm的下包镜像源
下包的镜像源,指的就是下包的服务器地址。
// 查看当前的下包镜像源
npm config get registry
// 将下包的镜像源切换为淘宝镜像源
npm config set registry https://registry.npm.taobao.org/
// 检查镜像源是否下载成功
npm config get registry
2.2 nrm
为了更方便的切换下包的镜像源,可以安装 nrm 这个小具,利用 nrm 提供的终端命令,可以快速查看和切换下包的镜像源。
// 通过 npm 包管理器,将 nrm 安装为全局可用的工具
npm i nrm -g
// 查看所有可用的镜像源
nrm ls
// 将下包的镜像源切换为 taobao 像
nrm use taobao
2.3 全局包
在执行 npm install 命令时,如果提供了 -g 参数,则会把包安装为全局包
全局包会被安装到 C:\Users\用户目录 AppData Roaming\npm\node modules 目录下
// 安装全局包
npm i 包名 -g
// 卸载全局包
npm uninstall 包名 -g
3、开发属于自己的包
3.1 初始化包的基本结构
- 新建itheima-tools 文件夹,作为包的根目录
- 在itheima-tools文件夹中,新建如下三个文件
- package.json (包管理配置文件)
- index.js (包的入口文件)
- README.md (包的说明文档)
3.2 初始化package.json
{
"name":"itheima-tools",
"version":"1.0.0",
"main": "index.js",
"description":"提供了格式化时间,HTMLEscape的功能",
"keywords":["itheima","dateFormat","escape"],
"license":"ISC"
}
3.3 在index.js中定义要导出的方法
function dateFormat (dataStr) {
const dt = new Date(dataStr)
const y = padZero(dt.getFullYear())
const m = padZero(dt.getMonth() + 1)
const d = padZero(dt.getDate())
const hh = padZero(dt.getHours())
const mm = padZero(dt.getMinutes())
const ss = padZero(dt.getSeconds())
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
}
function padZero (n) {
return n > 9 ? n : '0' + n
}
module.exports = {
dateFormat
}
3.4 将不同功能进行模块化拆分
- 将格式化时间的功能,拆分到 src -> dateFormatjs 中
- 在indexjs 中,导入两个模块,得到需要向外共享的方法
- 在indexjs 中,使用 module.exports 把对应的方法共享出去
3.5 编写包的说明文档
包根目录中的 README.md 文件,是包的使用说明文档。通过它,我们可以事先把包的使用说明,以 markdown 的格式写出来,方便用户参考
README 文件中具体写什么内容,没有强制性的要求;只要能够清晰地把包的作用、用法、注意事项等描述清楚即可。
3.6 发布包
注册npm账号
- 访问 www.npmjs.com/ 网站,点击 sign up 按钮,进入注册用户界面
- 填写账号相关的信息: Full Name、Public Email、Username、Password
- 点击 Create an Account 按钮,注册账号
- 登录邮箱,点击验证链接,进行账号的验证
登录npm账号
npm 账号注册完成后,可以在终端中执行 npm login 命令,依次输入用户名、密码、邮箱后,即可登录成功。
注意: 必须切换到官网源登录 npm set registry registry.npmjs.org/
把包发布到npm上
将终端切换到包的根目录之后,运行 npm publish 命令,即可将包发布到 npm 上(注意: 包名不能雷同)
删除已经发布的包
运行 npm unpublish 包名 --force 命令,即可从 npm 删除已发布的包。 注意:
- npm unpublish 命令只能删除 72小时以内发布的包
- npm unpublish 删除的包,在24小时内不允许重复发布
- 发布包的时候要慎重,尽量不要往 npm 上发布没有意义的包
4、模块的加载机制
4.1 优先从缓存中加载
模块在第一次加载后会被缓存。 这也意味着多次调用 require 不会导致模块的代码被执行多次
注意:不论是内置模块、用户自定义模块、还是第三方模块,它们都会优先从缓存中加载,从而提高模块的加载效率
4.2 内置模块的加载机制
内置模块是由 Node.is 官方提供的模块,内置模块的加载优先级最高
例如,require(fs)始终返回内置的 fs 模块,即使在 node modules 目录下有名字相同的包也叫做 fs
4.3 自定义模块的加载机制
使用require0加载自定义模块时,必须指定以./或./开头的路径标识符在加载自定义模块时,如果没有指定./或..这样的路径标识符,则node会把它当作内置模块或第三方模块进行加载
同时,在使用 requir() 导入自定义模块时,如果省略了文件的扩展名,则 Node.js 会按顺序分别尝试加载以下的文件:
- 按照确切的文件名进行加载
- 补全**.js** 扩展名进行加载
- 补全**.json** 扩展名进行加载
- 补全**.node** 扩展名进行加载
- 加载失败,终端报错
4.4 第三方模块加载机制
如果传递给 require()的模块标识符不是一个内置模块,也没有以或开头则 Ndejs 会当前模块的父目录开始,尝试从/node modules 文件夹中加载第三方模块
如果没有找到对应的第三方模块,则移动到再上一层父目录中,进行加载,直到文件系统的根目录。
例如,假设在 C:Users\itheima\project\foo.js' 文件里调用了 require(tools),则 Nodejs 会按以下顺序查找:
- C:\Users\itheima\project\node-modules tools
- C:\Users\itheima\node-modules\tools2
- C:\Users\node-modules\tools
- C:\node-modules\tools
4.5 目录作为模块
当把目录作为模块标识符,传递给 require()进行加载的时候,有三种加载方式:
- 在被加载的目录下查找一个叫做 package.json 的文件,并寻找 main 属性,作为 require() 加载的入口
- 如果目录里没有 package.json 文件,或者 main 入口不存在或无法解析,则 Nodejs 将会试图加载目录下的 index.js 文件.
- 如果以上两步都失败了,则Nodejs 会在终端打印错误消息,报告模块的缺失: Error: Cannot find modulex'