Node.js学习笔记
Node.js学习笔记
Node.js 是一个开源、跨平台的 JavaScript 运行时环境。
概述
适合IO密集型运算(libuv和cpp底层的功劳),不适合CPU密集型计算(单线程),可用于前后端开发。
前端:Vue Angular React Nuxtjs
后端:
Serverless
Web应用:Express,Nextjs,koa
RPC服务:gRPC
爬虫:Puppeteer
BFF层
及时性应用:Socket.io
桌面端:
Electron
Tauri
NW.js
移动端:
Weex
Ionic
React Native
基建端:
Webpack Vite
less scss
babel swc
inquire
CI/CD 反代 单元测试等都可以使用Node.js.
我靠,怎么Nodejs什么都能干啊!
npm
npm是Nodejs的包管理工具。
常用npm命令
npm init:初始化空白包,创建package.jsonnpm run script: 运行npm命令(需要在package.json定义)npm install:安装一个包或一组包,并会在当前目录存放一个node_modules,简写为npm i ...npm install <package-name> --save:安装指定包,并将其添加到package.json,npm uninstall <package-name>:卸载指定包npm config list:查看Node npm版本,npm源,runtime目录等信息npm config registry:查看npm源npm config set registry URLs:设置npm源npm login:登录npmnpm publish:将包发布到npm
关于package.json
"version":"1.0.0"第一位主版本号,针对重大更新或改动
第二位次版本号,针对功能更新
第三位修订号。针对bug修复
"scripts":{...}
运行npm命令"repository:{...}"
设置仓库信息和地址"author":...
作者信息"license":...
项目许可证关于”dependencies”
"devDependencies":{...}
开发所需的依赖,通过npm i <package-name> -D添加到package.json,例如webpack,vite,rollup等开发环境需要的依赖"dependencies:{...}"
生产环境所需的依赖"peerDependencies":{...}
给插件编写人员或npm包的开发人员使用
npm install原理
首先安装的依赖都会存放在根目录的node_modules,默认采用扁平化的方式安装,并且排序规则.bin第一个然后@系列,再然后按照首字母排序abcd等,并且使用的算法是广度优先遍历,在遍历依赖树时,npm会首先处理项目根目录下的依赖,然后逐层处理每个依赖包的依赖,直到所有依赖都被处理完毕。在处理每个依赖时,npm会检查该依赖的版本号是否符合依赖树中其他依赖的版本要求,如果不符合,则会尝试安装适合的版本
详细见:Npm install原理

注意:
如果项目中package.json与package-lock.json的依赖和版本不一致,则拉取package.json的依赖为准,并覆盖package-lock.json
npm install在检查npm config时,遵循项目级npmrc->用户级npmrc->全局npmrc->npm内置npmrc的顺序
package-lock.json中的”name”,”integrity”,”version”信息会生产一个key索引,用于比较本地的缓存
npm run原理
当前项目搜索是否存在node/modules/.bin,其次是全局,再其次是环境变量,如果没有就报错.
npm run也有生命周期,可以在package.json中的”scripts”设置”predev”和”postdev”参数执行相关文件.
npx
npx是一个命令行工具,它是npm 5.2.0版本中新增的功能。它允许用户在不安装全局包的情况下,运行已安装在本地项目中的包或者远程仓库中的包。
npx的作用是在命令行中运行node包中的可执行文件,而不需要全局安装这些包。这可以使开发人员更轻松地管理包的依赖关系,并且可以避免全局污染的问题。它还可以帮助开发人员在项目中使用不同版本的包,而不会出现版本冲突的问题。
npx的作用
避免全局安装
总是使用最新版本
执行任意npm包
可以执行Github gist等公开的JavaScript文件
发布npm包
步骤:
创建npm账号(注意是npm官方源)
登录npm账号
npm publish发布
npm私服
好处:
可以离线使用,私有化部署
提供包的安全性,更好的管理包
提高包的下载速度
步骤:
安装verdaccio
1
npm i verdaccio -g启动npm私服服务
1
verdaccio --listen 5000创建npm私服账户
1
npm adduser --registry http://localhost:5000/发布到npm私服
1
npm publish --registry http://localhost:5000/
注:如果已经设置好registry可以直接切换镜像然后更新
Nodejs的模块化
Nodejs的模块化规范遵循两套:CommonJS 和 ESModules
CommonJS规范
package.json中设置属性"type"为"commonjs"
五种模式
使用
require()引入自己编写的模块1
require('./test.js')引入第三方模块
1
const md5 = require("md5")引入Nodejs内置模块
1
const fs = require("node:fs")引入C++扩展,addon,napi,node-gyp,.node等
引入Json文件
1
const data = require('./data.json')
使用 module.exports 导出模块,也可使用ES6的解构赋值
1 | |
ESModules规范
package.json中设置属性"type"为"module"
导入:
1 | |
ESM不支持引入json,高版本可以强兼容但需要额外的断言且会报警
1 | |
导出:
export default只能有一个
1 | |
要查看某模块导出的所有内容:
1 | |
CJS和ESM的区别
Cjs是基于运行时的同步加载,ESM是基于编译时的异步加载.一般来说,ESM下的import只能在作用域最顶层,若需要动态加载,可以使用import的函数模式(会返回一个Promise对象).
1
2
3
4
5if(true){
import('./test.js').then(res=>{
console.log(res)
})
}CJs可以修改值,但ESM不行
Cjs不可以Tree Shaking,ESM可以
Cjs的顶层
this指向模块本身,ESM顶层this指向undefined
全局变量&全局API
在浏览器环境下,可以使用var的关键词将全局变量绑定到window对象上,但是node并不是浏览器环境,因此不存在BOM和DOM,而且使用var定义全局变量也会导致状态提升的问题.
全局变量
在nodejs中使用global定义全局变量.在ECMA2020中可使用globalThis作为全局变量,可以自动切换.
1 | |
全局API
由于nodejs中没有DOM和BOM,除了这些API,其他的ECMAscriptAPI基本都能用,例如setTimeout,Promise,Math,Console等
Nodejs专有API
__dirname
表示当前模块的所在目录的绝对路径,ESM模式下不可使用,可用process.cwd()代替__filename
表示当前模块文件的绝对路径,包括文件名和文件扩展名
__dirname__filename只能在cjs使用 esm规范没有这两个全局变量
process
处理进程相关的内容process.argv: 这是一个包含命令行参数的数组。第一个元素是Node.js的执行路径,第二个元素是当前执行的JavaScript文件的路径,之后的元素是传递给脚本的命令行参数。process.env: 这是一个包含当前环境变量的对象。您可以通过process.env访问并操作环境变量。process.cwd(): 这个方法返回当前工作目录的路径。process.on(event, listener): 用于注册事件监听器。您可以使用process.on监听诸如exit、uncaughtException等事件,并在事件发生时执行相应的回调函数。process.exit([code]): 用于退出当前的Node.js进程。您可以提供一个可选的退出码作为参数。process.pid: 这个属性返回当前进程的PID(进程ID)。
这些只是
process对象的一些常用属性和方法,还有其他许多属性和方法可用于监控进程、设置信号处理、发送IPC消息等。
需要注意的是,process对象是一个全局对象,可以在任何模块中直接访问,无需导入或定义。Buffer- 创建
Buffer实例:
Buffer.alloc(size[, fill[, encoding]]): 创建一个指定大小的新的Buffer实例,初始内容为零。fill参数可用于填充缓冲区,encoding参数指定填充的字符编码。Buffer.from(array): 创建一个包含给定数组的Buffer实例。Buffer.from(string[, encoding]): 创建一个包含给定字符串的Buffer实例。
- 读取和写入数据:
buffer[index]: 通过索引读取或写入Buffer实例中的特定字节。buffer.length: 获取Buffer实例的字节长度。buffer.toString([encoding[, start[, end]]]): 将Buffer实例转换为字符串。
- 转换数据:
buffer.toJSON(): 将Buffer实例转换为JSON对象。buffer.slice([start[, end]]): 返回一个新的Buffer实例,其中包含原始Buffer实例的部分内容。
- 其他:
Buffer.isBuffer(obj): 检查一个对象是否是Buffer实例。Buffer.concat(list[, totalLength]): 将一组Buffer实例或字节数组连接起来形成一个新的Buffer实例。
- 创建
CSR SSR SEO
概述
在node环境下无法直接操作DOM和BOM,可以通过jsdom实现,模拟浏览器的环境
1 | |
CSR/SSR
我们上面的操作属于SSR (Server-Side Rendering)服务端渲染请求数据和拼装都在服务端完成,而我们的Vue,react 等框架这里不谈(nuxtjs,nextjs),是在客户端完成渲染拼接的属于CSR(Client-Side Rendering)客户端渲染.
CSR 和 SSR 区别
页面加载方式:
- CSR:在 CSR 中,服务器返回一个初始的 HTML 页面,然后浏览器下载并执行 JavaScript 文件,JavaScript 负责动态生成并更新页面内容。这意味着初始页面加载时,内容较少,页面结构和样式可能存在一定的延迟。
- SSR:在 SSR 中,服务器在返回给浏览器之前,会预先在服务器端生成完整的 HTML 页面,包含了初始的页面内容。浏览器接收到的是已经渲染好的 HTML 页面,因此初始加载的速度较快。
内容生成和渲染:
- CSR:在 CSR 中,页面的内容生成和渲染是由客户端的 JavaScript 脚本负责的。当数据变化时,JavaScript 会重新生成并更新 DOM,从而实现内容的动态变化。这种方式使得前端开发更加灵活,可以创建复杂的交互和动画效果。
- SSR:在 SSR 中,服务器在渲染页面时会执行应用程序的代码,并生成最终的 HTML 页面。这意味着页面的初始内容是由服务器生成的,对于一些静态或少变的内容,可以提供更好的首次加载性能。
用户交互和体验:
- CSR:在 CSR 中,一旦初始页面加载完成,后续的用户交互通常是通过 AJAX 或 WebSocket 与服务器进行数据交互,然后通过 JavaScript 更新页面内容。这种方式可以提供更快的页面切换和响应速度,但对于搜索引擎爬虫和 SEO(搜索引擎优化)来说,可能需要一些额外的处理。
- SSR:在 SSR 中,由于页面的初始内容是由服务器生成的,因此用户交互可以直接在服务器上执行,然后服务器返回更新后的页面。这样可以提供更好的首次加载性能和对搜索引擎友好的内容。
CSR 应用例如 ToB 后台管理系统 大屏可视化 都可以采用CSR渲染不需要很高的SEO支持
SSR 应用例如 内容密集型应用大部分是ToC 新闻网站 ,博客网站,电子商务,门户网站需要更高的SEO支持
Path-路径模块
path模块在不同的操作系统是有差异的(Windows/POSIX)
POSIX
Unix,like-unix,macOS,Linux,WSL都遵循POSIX标准,使用正斜杠/
Win32
使用双反斜杠\\
兼容性处理方法
1 | |
Path模块常用方法
basename:返回给定路径的文件名(含文件扩展名)dirname:返回路径的目录名extname:返回路径的扩展名(带点,形如.html)
如果没有,返回空字符串;如果有多个点,返回最后一个点后面的内容path.join(params):将多个路径字符串合并为一个字符串path.resolve(params):解析路径,返回绝对路径;若有多个路径参数则返回最后一个;若只有一个相对路径,返回当前工作目录的绝对路径.1
2
3
4
5
6
7
8const path = require('path')
console.log(path.resolve('/a'))
// D:\a
console.log(path.resolve('./index.js'))
// D:\Project\Program\Node\index.js
console.log(path.resolve('D:/Project/Program/Node','./node_modules','./.bin/tldts'))
// D:\Project\Program\Node\node_modules\.bin\tldtspath.parse():解析路径,返回对象path.format():根据传入的对象解析路径
OS-系统模块
OS模块主要是和操作系统交互的模块
| 序号 | API | 作用 |
|---|---|---|
| 1 | os.type() | 它在 Linux 上返回 'Linux',在 macOS 上返回 'Darwin',在 Windows 上返回 'Windows_NT' |
| 2 | os.platform() | 返回标识为其编译 Node.js 二进制文件的操作系统平台的字符串。 该值在编译时设置。 可能的值为 'aix'、'darwin'、'freebsd'、'linux'、'openbsd'、'sunos'、以及 'win32' |
| 3 | os.release() | 返回操作系统的版本例如10.xxxx win10 |
| 4 | os.homedir() | 返回用户目录 例如c:\user\xiaoman 原理就是 windows echo %USERPROFILE% posix $HOME |
| 5 | os.arch() | 返回cpu的架构 可能的值为 'arm'、'arm64'、'ia32'、'mips'、'mipsel'、'ppc'、'ppc64'、's390'、's390x'、以及 'x64' |
| 6 | os.cpus() | 返回CPU的线程及详细信息 |
| 7 | os.networkInterfaces() | 返回网络信息 |
Process-进程模块
process.arch():返回操作系统CPU架构process.cwd():返回当前的工作目录,可代替__dirname使用process.argv():获取执行进程后面的参数 返回是一个数组 后面我们讲到命令行交互工具的时候会很有用,各种cli脚手架也是使用这种方式接受配置参数例如webpackprocess.memoryUsage():用于获取当前进程的内存使用情况。该方法返回一个对象,其中包含了各种内存使用指标,如 rss(Resident Set Size,常驻集大小)、heapTotal(堆区总大小)、heapUsed(已用堆大小)和 external(外部内存使用量)等1
2
3
4
5
6
7
8{
rss: 30932992, // 常驻集大小 这是进程当前占用的物理内存量,不包括共享内存和页面缓存。它反映了进程实际占用的物理内存大小
heapTotal: 6438912, //堆区总大小 这是 V8 引擎为 JavaScript 对象分配的内存量。它包括了已用和未用的堆内存
heapUsed: 5678624, //已用堆大小
external: 423221, //外部内存使用量 这部分内存不是由 Node.js 进程直接分配的,而是由其他 C/C++ 对象或系统分配的
arrayBuffers: 17606 //是用于处理二进制数据的对象类型,它使用了 JavaScript 中的 ArrayBuffer 接口。这个属性显示了当前进程中 ArrayBuffers 的数量
}process.exit():退出进程process.kill(pid):杀死进程process.env():获取或修改当前操作系统的环境变量,但修改只在当前进程生效,不会真正影响系统的环境变量.
注: 可以使用第三方库cross-env实现环境变量的切换
child_process-子进程模块
exec('command',(err,stdout,stderr)=>{})在shell执行命令,异步方法,有回调函数,stdout是输出,stderr是错误信息.execSync():功能同上,但是同步方法,不需要回调函数.spawn():执行命令(shell命令无上限,返回流,实时返回)spawnSync():执行命令(同步方法)
cwd <string> 子进程的当前工作目录。
env <Object> 环境变量键值对。
encoding <string> 默认为 ‘utf8’。
shell <string> 用于执行命令的 shell。 在 UNIX 上默认为 ‘/bin/sh’,在 Windows 上默认为 process.env.ComSpec。 详见 Shell Requirements 与 Default Windows Shell。
timeout <number> 默认为 0。
maxBuffer <number> stdout 或 stderr 允许的最大字节数。 默认为 200*1024。 如果超过限制,则子进程会被终止。 查看警告: maxBuffer and Unicode。
killSignal <string> | <integer> 默认为 ‘SIGTERM’。
uid <number> 设置该进程的用户标识。(详见 setuid(2))
gid <number> 设置该进程的组标识。(详见 setgid(2))
1 | |
execFile()执行脚本文件execFileSync():同上,同步方法fork():创建子进程,但仅限于js模块,父子进程可相互通信
Events-事件模块
Node.js核心API都采用异步事件驱动架构,通过有效的方法监听事件状态的变化,在变化时做出相应的动作.
事件模型
发布-订阅设计模式
当一个发布者有新消息时,就将这个消息发布到调度中心。调度中心就会将这个消息通知给所有订阅者。这就实现了发布者和订阅者之间的解耦,发布者和订阅者不再直接依赖于彼此,他们可以独立地扩展自己.
1 | |
监听消息数量默认是10个,可以通过调用setMaxListeners传入数量.
Util-工具模块
util是Nodejs内部提供的实用工具类API.
util.promisify():将回调函数包装为Promise类型
注: 如果有多个返回值,res为js对象;若只有一个返回值,res即是本身
1 | |
util.callbackify:将promise类型的方法变成 回调函数。util.format:输入格式化%s:String将用于转换除BigInt、Object和-0之外的所有值。BigInt值将用n表示,没有用户定义的toString函数的对象使用具有选项{ depth: 0, colors: false, compact: 3 }的util.inspect()进行检查。%d:Number将用于转换除BigInt和Symbol之外的所有值。%i:parseInt(value, 10)用于除BigInt和Symbol之外的所有值。%f:parseFloat(value)用于除Symbol之外的所有值。%j: JSON。 如果参数包含循环引用,则替换为字符串'[Circular]'。%o:Object. 具有通用 JavaScript 对象格式的对象的字符串表示形式。 类似于具有选项{ showHidden: true, showProxy: true }的util.inspect()。 这将显示完整的对象,包括不可枚举的属性和代理。%O:Object. 具有通用 JavaScript 对象格式的对象的字符串表示形式。 类似于没有选项的util.inspect()。 这将显示完整的对象,但不包括不可枚举的属性和代理。%c:CSS. 此说明符被忽略,将跳过任何传入的 CSS。%%: 单个百分号 ('%')。 这不消费参数。
Fs-文件模块
1. 读取文件
fs.readFile():异步方法
1 | |
fs.readFileSync():同步方法
1 | |
fs2.readFile().then():Promise包装方法
1 | |
fs.createReadStream():流式读取文件(适合大文件)
1 | |
2. 文件夹操作
fs.mkdirSync():创建文件夹,recursive:true允许多层文件夹嵌套
1 | |
fs.rmdirSync():删除文件夹,若要删除单文件可用fs.rmSync()
3.重命名
fs.renameSync('old','new'):重命名文件
4.文件监听
fs.watch(filePath,(event,filename)=>{}):监听文件事件变化
1 | |
fs.watchFile(filePath,(cur,pre)=>{}):监听文件的元数据信息变化
5.写入文件
fs.writeFileSync(fileName,contentString):向指定路径下文件写入内容(默认覆盖方法)
注:,第三个参数{flag:'a'}表示追加,fs.appendFileSync()也可实现相同功能
'a': 打开文件进行追加。 如果文件不存在,则创建该文件。'ax': 类似于'a'但如果路径存在则失败。'a+': 打开文件进行读取和追加。 如果文件不存在,则创建该文件。'ax+': 类似于'a+'但如果路径存在则失败。'as': 以同步模式打开文件进行追加。 如果文件不存在,则创建该文件。'as+': 以同步模式打开文件进行读取和追加。 如果文件不存在,则创建该文件。'r': 打开文件进行读取。 如果文件不存在,则会发生异常。'r+': 打开文件进行读写。 如果文件不存在,则会发生异常。'rs+': 以同步模式打开文件进行读写。 指示操作系统绕过本地文件系统缓存。
这主要用于在 NFS 挂载上打开文件,因为它允许跳过可能过时的本地缓存。 它对 I/O 性能有非常实际的影响,因此除非需要,否则不建议使用此标志。
这不会将fs.open()或fsPromises.open()变成同步阻塞调用。 如果需要同步操作,应该使用类似fs.openSync()的东西。'w': 打开文件进行写入。 创建(如果它不存在)或截断(如果它存在)该文件。'wx': 类似于'w'但如果路径存在则失败。'w+': 打开文件进行读写。 创建(如果它不存在)或截断(如果它存在)该文件。'wx+': 类似于'w+'但如果路径存在则失败。
fs.createWriteStream(filePath):创建可写流,适合大量数据的写入.
1 | |
6.软连接/硬链接
fs.linkSync(filePath,newFilePath):创建硬链接文件,类似共享文件,可用于文件备份
fs.symlinkSync(filePath,newFilePath):创建硬链接,类似win的快捷方式,源文件删除后无效.
Zlib-解压缩
在 Node.js 中,zlib 模块提供了对数据压缩和解压缩的功能,以便在应用程序中减少数据的传输大小和提高性能。该模块支持多种压缩算法,包括 Deflate、Gzip 和 Raw Deflate。
Gzip流式压缩:
1 | |
Gzip流式解压:
1 | |
Defalte同理,改为createDefalte()压缩,createInfalte()解压即可.
Gzip文件解压:
1 | |
Http-http服务
“http” 模块是 Node.js 中用于创建和处理 HTTP 服务器和客户端的核心模块。它使得构建基于 HTTP 协议的应用程序变得更加简单和灵活。
- 创建 Web 服务器:你可以使用 “http” 模块创建一个 HTTP 服务器,用于提供 Web 应用程序或网站。通过监听特定的端口,服务器可以接收客户端的请求,并生成响应。你可以处理不同的路由、请求方法和参数,实现自定义的业务逻辑。
- 构建 RESTful API:”http” 模块使得构建 RESTful API 变得简单。你可以使用 HTTP 请求方法(如 GET、POST、PUT、DELETE 等)和路径来定义 API 的不同端点。通过解析请求参数、验证身份和权限,以及生成相应的 JSON 或其他数据格式,你可以构建强大的 API 服务。
- 代理服务器:”http” 模块还可以用于创建代理服务器,用于转发客户端的请求到其他服务器。代理服务器可以用于负载均衡、缓存、安全过滤或跨域请求等场景。通过在代理服务器上添加逻辑,你可以对请求和响应进行修改、记录或过滤。
- 文件服务器:”http” 模块可以用于创建一个简单的文件服务器,用于提供静态文件(如 HTML、CSS、JavaScript、图像等)。通过读取文件并将其作为响应发送给客户端,你可以轻松地构建一个基本的文件服务器。
1 | |
Http请求
1 | |