Node.js 是基于谷歌 V8( 一种JavaScript 引擎 )构建的一种 JavaScript 运行环境,由 Ryan Dahl 开发

Node.js 使 JavaScript 脱离了浏览器,可以实现服务器开发的一种 JavaScript 运行环境

Node.js 的安装

官方地址:https://nodejs.org/zh-cn/

安装完成后,输入 node -v 命令检测是否安装成功

1
2
$ node -v #显示node版本号
v17.6.0

通常 nodejs 安装时会自动安装 npm(node package manager)。输入 npm -v 检查 npm 是否安装

1
2
$ npm -v #显示npm版本号
8.5.1

Node.js 版本管理

推荐下面这款工具来管理 Node.js 的版本

fnm 的使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 查看已安装的所有版本及版本别名(包括系统内安装的版本)
fnm ls

# 查看官方发布的所有可用版本
fnm ls-remote

# 安装指定版本
fnm install 21.6.1

# 安装最新的 LTS 版本
fnm install --lts

# 使用国内源安装最新的 LTS 版本
fnm install --lts --node-dist-mirror=https://npmmirror.com/mirrors/node

# 使用指定版本
fnm use 21.6.1

# 查看当前正在使用的版本
fnm current

# 设置已安装版本的别名
fnm alias 20.8.1 dev # fnm use dev 切换至 20.8.1 版本

# 设置默认版本
fnm default 18.21.1 # fnm use default 切换至 18.21.1 版本

# 卸载指定版本, 可以使用别名
fnm uninstall 18.21.1 # 不指定版本号则会卸载当前正在使用的版本

# 使用指定版本来执行某个全局命令
fnm exec --using=18 node -v

# 在应用目录写入文件,指定特定版本
echo '16.20.2' > .node-version # fnm use . 使用 .node-version 文件中指定版本

fnm env
# 获取 fnm 所有环境变量, 用于删除 fnm

模块(Module)

Node.js 是基于 CommonJS 模块化规范实现的模块系统

在 CommonJS 模块化规范中一个文件就是一个模块,每个模块都有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见

每个模块中必须有一个变量 module,它是一个对象,代表当前模块自身,通过 module.exportsexports 对象可以导出当前模块的方法或者变量

exports 和 module.exports 默认为相等关系:exports === module.exports

1
2
3
4
5
6
7
8
// module.js
var name;
exports.setName = function(thyName){
name = thyName
};
exports.sayHello = function(){
console.log('Hello ' + name)
}

通过 module.require() 方法 或 require() 函数来引入某个模块,函数会返回被引入模块中的 module.exports 对象

require 和 module.require 默认为相等关系:require === module.require

1
2
3
4
// getModule.js
var myModule = require('./module');
myModule.setName('Timeic');
myModule.sayHello()

在模块中,变量this 默认指向当前模块的导出对象

1
2
console.log(this == module.exports); // true
console.log(this == exports); // true

在Node.js中,模块分为两类:

一类是Node.js自身提供的模块,称为核心模块;另一类是用户编写的模块,称为文件模块

核心模块部分在Node 源代码的编译过程中,被编译成了二进制执行文件。在Node 进程启动时,核心模块就被直接加载进内存中,所以核心模块的加载速度是最快的,如:http、fs、url等

文件模块则是在运行时动态加载,需要完整的路径分析、文件定位、编译执行过程。速度相比核心模块稍微慢一些

包(Package)

包是实现模块系统的重要规范,Node.js 中包就是一个目录,一个包中包含了模块所需的所有文件,符合 CommonJS 模块化规范的包目录,应该包含以下文件:

  • package.json:包的描述文件(必要文件‼️),必须在包的根目录下

  • bin:可执行二进制文件放在该目录下

  • lib:JavaScript代码放在该目录下

  • doc:文档放在该目录下

  • test:单元测试放在该目录下

npm 命令行工具

npm 是一个命令行工具,它是 JavaScript 的包管理工具,借助 npm 我们可以实现 JavaScript 模块的发布,安装和依赖等

使用 npm 工具创建一个JavaScript项目:

1.新建一个名为 test 的目录

1
$ mkdir test

2.并在该目录下执行 npm init 命令,之后根据提示输入信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
$ cd test
$ npm init
# 输出:
This utility will walk you through creating a package.json file.
...
Press ^C at any time to quit.
package name: (test) # 输入包名/项目名称
version: (1.0.0) # 输入项目版本号
description: # 输入项目描述
entry point: (index.js) # 输入模块的入口文件
test command: # 输入测试命令
git repository: # 输入git仓库地址
keywords: # 输入项目关键词
author: # 输入作者
license: (ISC) # 输入开源协议
...
# package.json的创建内容:
{
"name": "test",
"version": "1.0.0",
"description": "一个测试项目",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Timeic",
"license": "ISC"
}

Is this OK? (yes) # 会车即可确认创建package.json

npm 常用命令

  • 初始化项目: npm init

    • -y: 使用默认配置
  • 搜索包: npm search <包名>

  • 安装包: npm install/i <包名>

    • --save:默认参数,将下载或本地的包文件解压到当前目录下的node_modules目录( 安装 ),并将其添加到项目依赖中
  • 卸载包: npm uninstall <包名>

    • 别名:npm remove/rm/r <包名>
  • 安装指定版本的npm: npm install npm@x.x.x -g

    • @latest:最新版
  • 设置淘宝镜像源: npm config set registry https://registry.npmmirror.com

  • 设置官方镜像源: npm config set registry https://registry.npmjs.org

  • 查看当前镜像源: npm config get registry

模块的实现

Node.js 运行时会将每个模块中代码包装到一个函数内执行,Node.js 会将模块所需要的接口作为该函数的参数传入,函数代码大致如下:

1
2
3
4
5
6
7
8
9
10
(function (exports, require, module, __filename, __dirname) {
// 模块中的代码
})();
/*
exports 用于导出当前模块中的变量或方法的对象
require 用于引入某个模块的函数
module 表示当前模块的对象
__filename 表示当前文件的完整名称
__dirname 表示当前目录的完整名称
*/

模块标识符:

模块标识符是用来查找和定位模块的一个字符串

文件模块的查找规则

在使用 require() 函数加载文件模块时,Node.js 会根据传入的参数,即模块标识符,对文件进行查找,查找规则如下:

1
require('xxx')

情况一:xxx是一个核心模块,则直接返回核心模块,并停止查找,比如:path、http等

情况二:xxx以./(当前目录)、../(上一级目录)、/(根目录)开头的,会将xxx作 为文件在对应目录进行查找,查找规则如下:

  • 有后缀名,按照当前后缀名查找对应的文件

  • 没有后缀名,会按照如下顺序查找:

    1. 直接查找文件xxx

    2. 查找 xxx.js文件

    3. 查找 xxx.json文件

    4. 查找 xxx.node文件

  • 如果依然没找到,则会将xxx作为目录查找,查找规则如下:

    1. 查找 xxx/index.js

    2. 查找 xxx/index.json

    3. 查找 xxx/index.node

  • 如果还没找到,则抛出异常:not found

  • 如果找到,则会使用JSON.parse()方法解析该目录下的package.json文件的内容来得到描述对象,然后根据对象中main属性( 默认为index.js )指定的模块标识符来查找文件

  • 如果没找到,则抛出异常:not found

情况三:如果 xxx是一个单词(不包含特殊符号,如:.//、空格等),并且xxx不是一个核心模块,则会将其作为一个包( 目录 )来查找,查找规则如下:

从当前模块的module.paths属性中的所有路径以及全局路径下依次查找名为 xxx的目录

require() 函数加载文件模块时采用同步加载,为了提高效率,Node.js 运行时会缓存已经加载过的文件,当发现某个文件已加载过了,则直接从缓存中加载,不会重复加载同一个文件模块

查看已缓存的模块:console.log(require.cache)