NodeJS
NodeJS
Node.js 是一个跨平台 JavaScript 运行环境,是一个基于 Chrome 的 V8 引擎的 JavaScript 运行环境,使开发者可以搭建服务器端的 JavaScript 应用程序。
- 非阻塞 I/O:Node.js 采用非阻塞 I/O 模型,使其能够处理大量的并发连接,而不会像传统的服务器那样为每个连接创建一个新的线程。这使得 Node.js 在处理高并发场景时表现优异。
- 事件驱动:Node.js 使用事件驱动编程模型,这意味着它会在特定的事件发生时执行相应的回调函数。这种模型使得代码更加模块化,易于理解和维护。
- 单线程:虽然 Node.js 是单线程的,但由于其非阻塞 I/O 特性,它仍然能够高效地处理大量并发请求。此外,Node.js 的 V8 引擎还使用了多种优化技术,以提高其性能。
- 跨平台:Node.js 可以在多种操作系统上运行,包括 Windows、Linux 和 macOS 等。
- 丰富的生态系统:Node.js 拥有一个庞大的生态系统,包括大量的第三方模块和工具,这些都可以通过
npm
进行安装和管理。这使得开发者能够快速地构建功能丰富的应用程序。
虽然可以使用 Node.js 编写后端程序,但在实际工作中,Node.js 更多用于前端工程化,以集成各种开发中使用的工具和技术。
安装
在官方网站下载 Node.js,然后进行安装。
需要注意:
- 安装在非中文路径下。
- 无需勾选自动安装其他配套软件。
安装完成后,打开命令提示符(cmd
),输入命令 node -v
和 npm -v
,如果能够显示出版本号,则说明安装成功。
模块化
Node.js 的模块化系统是其核心特性之一,它允许开发者将代码分割成不同的文件或模块,每个模块都有自己独立的作用域,避免了全局命名空间的污染。同时,模块之间可以通过特定的接口进行通信和协作,提高了代码的可维护性和可重用性。
Node.js 的模块主要分为以下几类:
- 内置模块:这是Node.js 官方提供的模块,也称为核心模块。在安装 Node.js 时,这些模块就已经包含在内,无需额外安装。常见的内置模块包括 fs(用于文件操作)、http(用于创建 HTTP 服务器和客户端)、path(用于处理文件路径)、os(提供与操作系统相关的功能)等。这些模块为开发者提供了丰富的API,用于处理文件、网络请求、操作系统交互等任务。
- 自定义模块:这是程序员自己编写的模块。这些模块通常根据项目的特定需求来创建,用于封装可重用的代码和功能。自定义模块可以使得代码更加模块化、易于维护和管理。
- 第三方模块:这是由其他程序员编写并发布的模块。Node.js 的生态系统中有一个庞大的第三方模块库,涵盖了各种功能,如数据库连接、模板引擎、测试框架等。开发者可以通过 npm(Node Package Manager)来安装和管理这些第三方模块,从而加速开发进程并提高代码质量。
在 JavaScript 中,有多种方式可以实现模块化,包括早期的 CommonJS 规范和现在广泛使用的 ECMAScript 模块(ESM)规范。
- CommonJS 规范:一般应用在 Node.js 项目环境中
- ECMAScript 规范:一般应用在前端工程化项目中
CommonJS 规范
CommonJS 是一种在 Node.js 环境中广泛使用的模块化规范。CommonJS 是同步加载模块的,这意味着它会阻塞代码的执行,直到模块加载完成。由于这种同步特性,CommonJS 更适合在服务器端使用。
在 CommonJS 规范中,每个文件被视为一个模块,拥有自己独立的作用域、变量以及方法等,对其他的模块都不可见。每个模块内部,module
变量代表当前模块,它是一个对象,其 exports
属性是对外的接口。加载某个模块,实际上是加载该模块的 module.exports
属性。require
方法用于加载模块,使模块能够按照规范的方式进行组织和调用。即通过 module.exports
暴露模块内部属性和方法给外部,其他模块通过 require
函数来引入并使用这个模块暴露出的内容。
语法:
- 导出:
module.exports = {}
- 导入:
require('模块名或路径')
- 内置模块直接写模块名(例如:
fs
,path
,http
),第三方模块直接写安装时指定的包名 - 自定义模块写模块文件路径(例如:
./utils.js
)
- 内置模块直接写模块名(例如:
注意:
- 使用
require()
方法加载其他模块时,会执行被加载模块中的代码。 - 模块在第一次加载后会被缓存,这也意味着多次调用
require()
方法不会导致模块的代码被执行多次。 - 内置模块是由 Node.js 官方提供的模块,加载优先级最高,被覆盖同名的自定义模块。
- 使用
require()
方法加载自定义模块时,必须指定以./
或者../
开头的路径标识符。如果没有指定以./
或者../
开头的路径标识符,则会被当成内置模块或第三方模块进行加载。 - 使用
require()
方法加载自定义模块时,如果省略了文件的扩展名,则 Node.js 会按顺序分别尝试加载以下文件:- 安装确切的文件名进行加载
- 补全
.js
扩展名进行加载 - 补全
.json
扩展名进行加载 - 补全
.node
扩展名进行加载 - 加载失败,终端报错
- 如果传递给
require()
的模块标识符不是一个内置模块,也没有以./
或者../
开头,则 Node.js 会从当前模块的父目录开始,尝试从node_modules
目录中加载第三方模块,如果没有找到,则移动到上一层父目录的node_modules
目录查找,直到文件系统的根目录。 - 当把目录作为模块标识符,传递给
require()
进行加载时,加载方式如下:- 被加载的目录下查找
package.json
文件中的main
属性,作为require()
加载的入口。 - 如果目录没有
package.json
文件,或者没有main
属性,则加载目录下的index.js
文件。
- 被加载的目录下查找
例子:创建 mathUtils.js
的模块,提供数学计算的功能
// mathUtils.js
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
module.exports = {
add: add,
subtract: subtract
};
在另一个文件 app.js
中,可以引入并使用这个 mathUtils.js
模块:
// app.js
const mathUtils = require('./mathUtils');
const sum = mathUtils.add(5, 3);
const difference = mathUtils.subtract(sum, 2);
console.log(`Sum: ${sum}`);
console.log(`Difference: ${difference}`);
终端执行:
$ node app.js
Sum: 8
Difference: 6
在上面的例子中,mathUtils.js
通过 module.exports
导出了两个函数 add
和 subtract
。然后,在 app.js
中,使用 require
函数引入了 mathUtils
模块,并将其赋值给 mathUtils
变量。之后,就可以通过 mathUtils
变量来调用 add
和 subtract
函数了。
ECMAScript 规范
除了使用 CommonJS 规范(require
和 module.exports
)进行模块化外,Node.js 还支持 ECMAScript 模块(ESM),这是 JavaScript 的官方标准模块系统。ESM 是异步加载模块的,这意味着它不会阻塞代码的执行,因此更适合在浏览器环境中使用。使用 ESM,可以通过 import
和 export
关键字来导入和导出模块。但请注意,为了使用 ESM,需要在 package.json
文件中设置 "type": "module"
或者使用 .mjs
文件扩展名。
默认导出导入
语法:
- 导出:
export default {}
- 导入:
import 变量名 from '模块名或路径'
注意:
- 默认导出可以是任何 JavaScript 对象,但每个模块只能有一个默认导出。
- 导入默认导出时,不需要使用花括号。
例子:创建 mathUtils.js
的模块,提供数学计算的功能,并默认导出
// mathUtils.js
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
export default {
add: add,
subtract: subtract
};
在另一个文件 app.js
中,使用 import
导入 mathUtils
模块:
// app.js
import mathUtils from "./mathUtils.js";
const sum = mathUtils.add(5, 3);
const difference = mathUtils.subtract(sum, 2);
console.log(`Sum: ${sum}`);
console.log(`Difference: ${difference}`);
创建 package.json
文件并设置 "type": "module"
:
{
"type": "module"
}
终端执行:
$ node app.js
Sum: 8
Difference: 6
在上面的例子中,mathUtils.js
通过 export default
导出了两个函数 add
和 subtract
。然后,在 app.js
中,使用 import
函数引入了 mathUtils
模块,并将其赋值给 mathUtils
变量。之后,就可以通过 mathUtils
变量来调用 add
和 subtract
函数了。
命名导出导入
可以使用 export
关键字来导出任何变量、函数、类或对象字面量,并为它们指定一个名称,这使得其他模块可以按需导入所需的特定功能或数据。
语法:
- 导出:
export 定义语句
- 导入:
import {导出文件同名变量} from '模块名或路径'
注意:
- 命名导出可以导出多个值,每个值都有自己的名称。
- 命名导入需要使用花括号
{}
来包围想要导入的导出名称。 - 如果一个模块同时有默认导出和命名导出,可以在导入时同时处理它们。
- 导入时,导出名称必须与导出时使用的名称完全匹配(包括大小写)
例子:创建 mathUtils.js
的模块,提供数学计算的功能,并命名导出
// mathUtils.js
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
export default {
add: add,
subtract: subtract
};
在另一个文件 app.js
中,使用 import
导入模块:
// app.js
import {add, subtract} from "./mathUtils.js";
const sum = add(5, 3);
const difference = subtract(sum, 2);
console.log(`Sum: ${sum}`);
console.log(`Difference: ${difference}`);
终端执行:
$ node app.js
Sum: 8
Difference: 6
包
在Node.js中,“包”(package)是一个重要的概念,它指的是一个可以发布和重用的代码单元。包通常包含一系列的 JavaScript 文件、模块、库、资源文件(如图片、JSON 数据等)以及相关的元数据(如包的名称、版本、描述等)。这些包被组织成一个文件结构,通常称为 “包结构” 或 “项目结构”。
Node.js 的包管理主要依赖于 npm
(Node Package Manager),这是一个开源的 JavaScript 包管理工具。npm 允许开发者从包仓库中查找、安装、更新和管理包。这些包仓库通常包含了大量的开源包,由社区成员创建和维护。
一个 Node.js 包通常包含一个 package.json
文件作为包管理配置文件,该文件是包的元数据文件,包含了包的名称、版本、描述、入口文件、依赖关系等信息。这个文件对于 npm
来说是至关重要的,它使得 npm
能够正确地识别、安装和管理包。每个 npm
的安装包中都会包含一个 package.json
文件,通常这个文件位于包的根目录下。
package.json
文件主要包含关于包的元数据(如项目名称和说明)以及功能元数据(如程序包版本号和程序所需的依赖项列表)。其中,名称(name
)和版本号(version
)是必填项,其他如应用描述(description
)、应用的配置项(config
)、作者(author
)、资源仓库地址(repository
)、授权方式(licenses
)、目录(directories
)、包入口文件(main
)、命令行文件(bin
)、应用依赖模块(dependencies
)、开发环境依赖模块(devDependencies
)、运行引擎(engines
)和脚本(scripts
)等都是可选配置项。
{
"name": "my-nodejs-app",
"version": "1.0.0",
"description": "A simple Node.js application",
"main": "index.js",
"author": "stone",
"license": "MIT",
}
注意:导入包时,默认的入口文件是 index.js
或者 main
指定的模块文件。
npm
Node.js 的 npm(Node Package Manager)是一个强大的包管理器,用于安装和管理 Node.js 项目中的库和依赖项。npm 使得开发者能够轻松地获取、更新和共享代码,从而加速开发过程,提高开发效率。
以下是 npm 的一些关键特点和功能:
- 包管理:npm 允许开发者从 npm 注册表中搜索、安装和更新各种 Node.js 包。这些包可能是由其他开发者创建并共享的,包含了各种功能,如路由处理、数据库连接、测试工具等。
- 依赖管理:npm 能够自动处理项目中的依赖关系。当一个包依赖于其他包时,npm 会递归地安装所有必需的依赖项,并确保它们的版本兼容。这使得开发者能够专注于项目的核心功能,而不必担心依赖项的管理和版本冲突问题。
package.json
文件:npm 使用package.json
文件来记录项目的元数据和依赖项。这个文件包含了项目的名称、版本、描述、入口点、脚本命令以及依赖项列表等信息。通过修改package.json
文件,开发者可以轻松地管理项目的依赖关系和其他配置。- 全局与本地安装:npm 支持全局和本地安装两种方式。全局安装将包安装到系统的全局目录中,使得这些包可以在任何 Node.js 项目中使用。而本地安装则将包安装到当前项目的
node_modules
目录中,仅供该项目使用。 - npm 注册表:npm 使用一个公共的注册表来存储和分发包。这个注册表包含了大量的开源包,供开发者免费使用。同时,开发者也可以创建私有的注册表,用于存储和管理私有包。
- 脚本和生命周期钩子:npm 支持在
package.json
文件中定义脚本命令,以便在项目构建、测试和发布等阶段执行特定的任务。此外,npm 还提供了一组生命周期钩子,允许开发者在包的安装、卸载、发布等过程中执行自定义的逻辑。 - npm 社区:npm 社区非常活跃,拥有大量的开源贡献者和用户。这使得开发者能够轻松地获取帮助、分享经验和贡献代码,共同推动 Node.js 生态系统的发展。
如果是一个新创建的项目,则在包目录(只能是英文路径,且不能有空格)下初始化清单文件:
$ npm init -y
完成后得到 package.json
:
{
"name": "npm",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
本地安装软件包,多个包使用空格分隔:
$ npm i dayjs
会自动创建 node_modules
目录,并将软件包下载到该目录,并将该软件包记录到 package.json
。
创建一个 server.js
文件,使用软件包:
const dayjs = require('dayjs')
const newDateStr = dayjs().format('YYYY-MM-DD')
console.log(newDateStr)
如果是一个别人的项目,且项目中没有 node_modules
目录,则需要使用 npm 安装 package.json
中记录的所有软件包:
$ npm i
前面安装的软件包都是本地软件包,还可以安装全局软件包,区别如下:
- 本地软件包:当前项目内使用,封装属性和方法,存在于
node_modules
目录,还可分为:- 开发依赖包:记录到
package.json
的devDependencies
节点中,只在开发期间会用到,需要使用-D
或者--save-dev
选项安装 - 核心依赖包:记录到
package.json
的dependencies
节点中,不仅在开发期间会用到,在项目上线之后也会用到。
- 开发依赖包:记录到
- 全局软件包:本机所有项目使用,封装命令和工具,存在于系统设置的位置,例如
c:\Users\用户目录\AppData\Roaming\npm\node_modules
。需要使用-g
选项安装
默认情况下,使用 npm i
命令会自动安装最新版本的包,如果需要安装指定版本的包,可以在包名之后,通过 @
符号指定具体的版本,并会覆盖之前安装的版本,例如:
$ npm i moment@2.22.2
使用 npm uninstall
命令卸载包:
$ npm uninstall moment
在使用 npm 下载包时,默认从国外的 https://registry.npmjs.org/
服务器进行下载,下载速度很慢,使用 npm config get registry
查看当前的下包镜像源:
$ npm config get registry
https://registry.npmjs.org/
使用 npm config set registry
设置下包镜像源:
$ npm config set registry=https://registry.npmmirror.com
使用 npm config get registry
查看是否配置成功:
$ npm config get registry
https://registry.npmmirror.com/
yarn
yarn 是一个广受欢迎的开源包管理器,用于管理 JavaScript 项目中的依赖项。相比 npm,速度更快。
安装:
$ npm install --global yarn
查看版本:
$ yarn --version
1.22.22
开始新项目:
$ yarn init
添加依赖
$ yarn add [package]
$ yarn add [package]@[version]
$ yarn add [package]@[tag]
将依赖添加到不同类别的依赖,分别添加到 devDependencies
、peerDependencies
、optionalDependencies
:
$ yarn add [package] -D
$ yarn add [package] -P
$ yarn add [package] -O
升级依赖:
$ yarn upgrade [package]
$ yarn upgrade [package]@[version]
$ yarn upgrade [package]@[tag]
删除依赖:
$ yarn remove [package]
安装项目所有依赖:
$ yarn
或者:
$ yarn install
pnpm
pnpm 是一个高效的 JavaScript 包管理器,旨在通过创新的设计来解决传统包管理器(如 npm 和 yarn)中的一些问题。以下是 pnpm 的主要特点和优势:
- 磁盘空间效率:pnpm 通过内容寻址存储和硬链接来共享包的不同版本,从而极大地节省了磁盘空间。这种设计使得 pnpm 在处理大型项目或具有多个子项目的 monorepo 时能够显著减少所需的存储空间。
- 快速安装:由于 pnpm 的存储结构,它通常能够更快地安装项目的依赖项。这是因为 pnpm 避免了重复下载和存储相同的包,而是利用现有的缓存和硬链接来快速安装。
- 确定的依赖解析:pnpm 通过内容寻址来确保安装的依赖项是确定的。这意味着无论在哪个时间或哪个机器上运行 pnpm,都将得到相同的结果。这有助于减少由于依赖项版本不一致而导致的问题。
- 与 npm 和 yarn 兼容:尽管 pnpm 采用了不同的存储和安装机制,但它仍然与 npm 和 yarn 的生态系统兼容。这意味着你可以使用相同的
package.json
文件格式和node_modules
目录结构,并且大多数 npm 和 yarn 命令也可以在 pnpm 中使用。 - 更少的依赖冲突:由于 pnpm 的设计,它通常能够减少依赖项之间的冲突。通过更加精确地控制依赖项的版本和安装位置,pnpm 可以帮助开发者避免一些常见的依赖问题。
- 轻量级和易于使用:pnpm 的安装和配置相对简单,而且它本身也是一个轻量级的工具,不会给系统带来过多的负担。
安装:
$ npm install -g pnpm
查看版本:
$ pnpm --version
9.0.6
开始新项目:
$ pnpm create
添加依赖
$ pnpm add [package]
$ pnpm add [package]@[version]
$ pnpm add [package]@[tag]
将依赖添加到不同类别的依赖,分别添加到 devDependencies
、optionalDependencies
:
$ pnpm add [package] -D
$ pnpm add [package] -O
升级依赖:
$ pnpm update [package]
$ pnpm update [package]@[version]
$ pnpm update [package]@[tag]
删除依赖:
$ pnpm remove [package]
安装项目所有依赖:
$ pnpm i
或者:
$ pnpm install
npm,yarn,pnpm 命令对比:
npm | yarn | pnpm | |
---|---|---|---|
安装所有依赖 | npm install | yarn | pnpm install |
安装指定依赖 | npm intall axios | yarn add axios | pnpm add axios |
全局安装依赖 | npm intall axios -D | yarn add axios -D | pnpm add axios -D |
删除依赖 | npm uninstall axios | yarn remove axios | pnpm remove axios |
启动项目 | npm run dev | yarn dev | pnpm dev |