nodejs进阶

文章目录

    • 写在前面
    • 一、dependencies、devDependencies和peerDependencies区别:
    • 二、需要牢记的npm命令
      • 2.1 npm init
      • 2.2 npm config list
      • 2.3 npm配置镜像源
    • 三、npm install 的原理
    • 四、package-lock.json的作用
    • 五、npm run 的原理
    • 六、npx
      • 6.1 npx是什么
      • 6.2 npx的优势
      • 6.3 npm和npx区别
    • 七、发布npm包
    • 八、搭建npm私服
    • 九、模块化
      • 9.1 CommonJS规范
      • 9.2 ESM规范
      • 9.3 CommonJS和ESM的区别
    • 十、全局变量和API
    • 十一、CSR SSR SEO
    • 十二、内置模块path
    • 十三、内置模块os
    • 十四、内置模块process
    • 十五、子进程模块child_process
      • 1. 介绍
      • 2. exec()和execSync()
      • 3. spawn()和spawnSync()
    • 十六、多媒体处理工具ffmpeg
    • 十七、events事件触发器
    • 十八、待更

写在前面

npm是Node Package Manager(Node.js包管理器)的缩写,是Node.js社区最流行的包管理工具之一。它不仅提供了包管理的功能,还提供了发布和共享代码、管理依赖、版本控制等功能,可以帮助开发者更方便地开发和分享代码。

npm可以用来从全球的代码库中获取并安装Node.js模块,这些模块可以用于构建应用程序、工具和包等。开发者可以通过npm来搜索、安装、更新和卸载各种Node.js模块。npm包管理器还支持各种依赖关系管理工具,如开发依赖、生产依赖、测试依赖等,可以帮助开发者更好地管理自己的项目。

npm的使用非常广泛,已经成为了前端开发的标配之一。无论是开发Web应用、移动应用、命令行工具还是桌面应用程序,npm都可以为开发者提供丰富的功能和支持。

一、dependencies、devDependencies和peerDependencies区别:

在NPM中使用npm init生成的package.json文件里,dependencies、devDependencies和peerDependencies是用于管理项目依赖的三个重要概念。

dependencies项目中依赖的包列表,这些包会在项目运行时自动安装,也可以理解是生产环境所需要的依赖,如vue、vuex、vue-router、md5等

devDependencies开发依赖列表。项目开发过程中所所需要的包的列表,这些包不会随项目一起发布,而是只在开发时使用。 安装在此的包通常使用npm i 包名 --save-dev或者简写npm i 包名 -D安装。什么是开发依赖呢?比如说 webpack vite rollup 等配置工具,只会在打包时使用,生产环境不需要使用,这类依赖就是开发依赖

peerDependencies:给编写插件人员或者编写npm包的开发人员去使用的;比如你要开发vite的插件,那么vite plugin插件是不能凭空运行的,需要依赖宿主环境,也就是依赖vite,要把vite安装下来才能使用插件,因此就要把vite安装在peerDependencies里。

二、需要牢记的npm命令

更多npm命令参考博客:满神之Npm Package json

2.1 npm init

npm init用于初始化生成一个新的package.json文件。它会让用户设置一些配置,如包名、版本、描述等…可以一路回车使用默认配置,尾缀带-f表示force、-y表示yes,则跳过配置阶段,直接生产一个新的package.json文件,创建好之后可以在package.json直接查看或修改更新。

在这里插入图片描述

2.2 npm config list

npm config list用于列出所有的npm配置信息。执行该命令可以查看当前系统和用户级别的所有npm配置信息,以及当前项目的配置信息(如果在项目目录下执行该命令)

在这里插入图片描述

2.3 npm配置镜像源

npm config set registry 镜像源配置镜像源

npm get registry查看已配置的镜像源

在这里插入图片描述

扩展安装满神自己写的包xmzs,可以实现快捷切换各种镜像

在这里插入图片描述

在这里插入图片描述

如果公司有自定义的镜像 也可以使用这个包添加镜像

在这里插入图片描述

三、npm install 的原理

参考博客:满神——Npm install 原理

package.json和package-lock.json文件内的包版本一致或不一致时,包的安装版本依据是怎么样的?

不一致时:

会根据package.json的版本号去下载对应版本,并更新lock文件

一致时:

根据package-lock.json中的版本号去下载

其实包在下载前都会检查缓存中是否有对应包,如果有就直接解压到node_modules,如果没有就下载包资源,检查包的完整性后添加到缓存中再更新package-lock.json

node_modules中的很多包都会依赖其他的包,因此这样的包里面也会有node_modules文件夹,因此,在遍历依赖树的时候,会使用广度优先遍历算法,逐层处理每个依赖包的依赖,直到所有依赖包都被处理完毕。

安装包(依赖)的时候会采用扁平化的方式安装,简单理解就是:比如要安装两个包,分别是vue和react,它们都会依赖一个名为child子包,也可以称为二级依赖,如果这二级依赖版本是一致的,那么这个二级依赖会被提到一级依赖中,让vue和react共用这个依赖,这就是扁平化处理,如果不是一致的,那就在分别安装在vue和react的node_modules中作为二级依赖。

npm install 原理总结:执行该命令时会安装package.json/package-lock.json记录的依赖,在安装对应依赖前它会判断package.jsonpackage-lock.json内记录的包版本是否一致,从而决定根据package.json还是从-lock.json记录的版本下载,如果不一致会根据package.json下载,并更新-lock.json文件,如果一致则根据-lock.json下载【其实就是根据package.json下载】,下载前会使用广度优先遍历算法逐层遍历依赖树,判断是否要做扁平化处理,再检查缓存中是否有对应包,如果有就直接解压到node_modules,如果没就下载资源包并检查包完整性后添加到缓存并更新package-lock.json

四、package-lock.json的作用

  1. 锁定版本,记录依赖树详细信息
  2. 实现依赖缓存,它会通过name + version + integrity信息生成一个唯一的key,这个key能找到对应的index-v5下的缓存记录,如果有缓存记录就找到对应二进制文件解压到node_modules中。

包详细信息:

在这里插入图片描述

五、npm run 的原理

在这里插入图片描述

如上图,执行npm run dev,它会运行对应的可执行命令vite --mode dev,这些可执行命令是无法直接运行的,在package.json中配置完成后,在node环境下通过npm run scriptName 运行。那么这个可执行命令是如何运行的呢?这里以上面的vite举例:

它会从node_modules中找到.bin文件夹下的vite命令,可执行命令都在.bin文件夹下,如下图

在这里插入图片描述

这里的vite可执行命令还有cmd和powershell的版本,因此可以在cmd和powershell命令行内运行npm run dev命令,常规的vite可执行命令由于node是跨平台的,因此也可以给unix、Linux以及MacOs去使用,window比较特殊因此会有cmd和ps版的命令,

当然这个可执行命令也不是凭空产生的,它是在用户安装vite时,根据vite依赖的package.json文件下的bin配置生成的。

在这里插入图片描述

以上讲述了运行npm run命令会在当前项目的node_modules/.bin中运行可执行命令,如果没有.bin目录他还会从其他目录中找可执行命令,如下查找优先级

  1. 当前项目先去找有没有node_modules/.bin
  2. 全局的node_modules下面去找。使用npm config list查看配置,找到全局依赖的路径【prefix】,去这个路径找
  3. 去环境变量找
  4. 报错

在这里插入图片描述

npm run 的生命周期prevpost

npm run也是有生命周期的,以dev举例:如下图,这里运行npm run dev命令

在这里插入图片描述

如下,切换脚本代码顺序,依旧是先执行predev,接着dev,最后再执行postdev

在这里插入图片描述

应用场景:比如说运行npm run build命令,可以写个前置脚本实现清除dist文件夹,也可以写个后置脚本实现CI操作提交代码。

比如说熟悉的vue脚手架vue-cli就在使用pre前置命令,如下图:

在这里插入图片描述

所以说npm run生命周期的应用场景是非常多的。

六、npx

6.1 npx是什么

npx是一个命令行工具,它是npm5.2.0版本中新增的功能。它允许用户在不安装全局包的情况下,运行已安装在本地项目中的包或者远程仓库中的包。

npx的作用是在命令行中运行node包中的可执行文件,而不需要全局安装这些包。这可以使开发人员更轻松地管理包的依赖关系,并且可以避免全局污染的问题。它还可以帮助开发人员在项目中使用不同版本的包,而不会出现版本冲突的问题。

6.2 npx的优势

  • 避免全局安装:npx运行你执行npx package,而不需要你先全局安装package
  • 总是使用最新版本:如果你没有在本地安装相应的npm packagenpx会从npm的包仓库中下载并使用最新版,并且使用完会自动删除。
  • 执行任意的npm包:npx不仅可以执行在package.jsonscripts部分定义的命令,还可以执行任何npm package
  • 执行GitHub gist:npx甚至可以执行GitHub gist或者其他公开的JavaScript文件。

6.3 npm和npx区别

npx侧重于执行命令,执行某个模块命令。虽然会自动安装模块,但是重在执行某个命令。

npm侧重于安装或者卸载某个模块。重在安装,并不具备执行某个模块的功能。

这里卸载vite,并写了一个简易的html文件,执行npx vite可以看到成功使用vite运行index.html文件,并且package.json文件中并没有看到新增vite依赖。

在这里插入图片描述

在这里插入图片描述

七、发布npm包

发布npm包的好处是什么?

  • 方便团队或者跨团队共享代码,使用npm包就可以方便的管理,并进行版本控制
  • 做开源造轮子必备技术,否则你做完的轮子如何让别人使用,难道是u盘拷贝?
  • 面试题会问,字节就问过
  • 增加个人IP,让更多的人知道你的技术能力和贡献

发布npm包前需要注册npm账号,可以去npm官网注册,也可以命令行输入npm adduser

输入npm adduser会自动跳转npm账号注册网站,需要注意的是,注册时一定要将镜像源切换成npm官方镜像源https://npmjs.com/registry/,如下:

在这里插入图片描述

如未使用npm官方镜像源注册是会提示如下错误:

在这里插入图片描述

注册完成后使用npm login命令登录:

在这里插入图片描述

登录成功后使用npm publish命令发布包,如果发布时报403错误,表示包重名了需要重命名或者是版本重合,需要提升版本序号,如下:

在这里插入图片描述

下面我们更改包的名称,重新发布,如下:

在这里插入图片描述

接着,我们在npm官网搜索刚发的包,可以看到自己刚发布的包如下:是不是非常神奇?!

在这里插入图片描述

八、搭建npm私服

npm私服:通过npm install命令下载前端项目依赖时,每次都需要从淘宝等第三方npm服务器下载,速度慢,耗时长;第三方npm服务器一般不支持包的上传,公司内部开发的公共包只能通过拷贝的方式添加到各个程序员开发的前端项目内,效率低,不方便;因此,搭建npm私有服务器(简称npm私服)显得尤为重要。

npm私服工作原理:用户通过 npm install 命令安装某个模块时,npm会先检查 node_modules目录中是否已经存在该模块,如果存在,则结束该模块安装,否则向npm私服发起请求,npm私服先查询该模块是否是我们自己的私有模块或已经缓存过的公共模块,如果是则直接将其返回给用户,如果不是,则继续向上游npm服务器(如淘宝、yarnpkg和npmjs等npm服务器)查找,如果找到,则将该模块返回给用户并将其缓存至npm私服,否则响应用户“npm ERR! 404 Not Found”错误。

npm私服的优势

  • 可以离线使用,你可以将npm私服部署到内网集群,这样离线也可以访问私有的包
  • 提高包的安全性,使用私有的npm仓库可以更好的管理你的包,避免在使用公共的npm包的时候出现漏洞
  • 提高搞的下载速度,使用私有的npm仓库,你可以将经常使用的npm包缓存到本地,从而显著提高包的下载速度,减少依赖包的下载时间。这对于团队内部开发和持续集成、部署等场景非常有用

如何搭建npm私服

Verdaccio是一个通过Node.js创建的企业级npm私有仓库程序,可以安装运行在Windows系统和Linux系统,为了更好地让大家理解npm私有仓库用法,这里将其安装运行在Linux系统中。

官网:https://verdaccio.org/zh-CN/

安装指令:npm install verdaccio -g

使用方式非常简单:直接运行verdaccio即可。

在这里插入图片描述

运行verdaccio生成的默认端口是4873,打开此端口如下:可以点击设置更改翻译为中文

在这里插入图片描述

使用verdaccio --listen 自定义端口号可以指定开启端口,如下

在这里插入图片描述

下面我们可以根据启动的端口中所给指示创建用户并发布包:

在这里插入图片描述

在这里插入图片描述

刷新端口页面,可以成功看到新发布的包,再去查看npm官网中我们先前发布的包,可以看到版本还是1.0.0,说明npm私服并不会影响到npm官网中上传的包。

在这里插入图片描述

在这里插入图片描述

每次发包都要需要加上--registry http://localhost:2270/显然非常麻烦,因此我们可以使用mmp将端口存储端口镜像,如下:

在这里插入图片描述

刷新端口页面验证结果如下:

在这里插入图片描述

这样,以后我们想将包发布私服或者发布到npm官网,只需要切换镜像源即可。

九、模块化

Nodejs的模块化规范遵循两套规范,分别是CommonJS规范和ESM规范。

9.1 CommonJS规范

使用此规范package.json文件中设置"type": "commonjs"

引入模块require

它只支持这四种格式的引入:

  1. 内置模块,例如http os fs child_process等nodejs内置模块
  2. 第三方模块express md5 koa
  3. 自己编写的模块./index.js
  4. 支持引入addon C++扩展模块 .node文件
const fs = require('node:fs');  // 导入核心模块
const express = require('express');  // 导入 node_modules 目录下的模块
const myModule = require('./myModule.js');  // 导入相对路径下的模块
const nodeModule = require('./myModule.node');  // 导入扩展模块

导出模块exportsmodule.exports

module.exports = {
    hello: function() {
        console.log('Hello world!')
    }
}

如果不想导出对象,直接导出值

module.exports = 123

9.2 ESM规范

使用此规范package.json文件中设置"type": "module"

引入模块importimport必须写在头部

import fs from 'node:fs'

如果要引入json文件,需要做特殊处理,增加断言并且指定类型为json,node低版本是不支持的。

import data from './data.json' assert { type: "json" }
console.log(data)

在这里插入图片描述

导入模块的整体对象

import * as all from 'demo.js'

动态导入模块:import静态加载(也就是写在头部的那种导入方式)不支持掺杂在逻辑中,因此想要动态加载请使用import('')这里函数模式。

if(true) {
    import('./demo.js').then()
}

模块导出

导出默认对象

export default {  // 导出一个默认对象 只能有一个,不能重复export default
    name: 'test'
}

导出变量

export const a = 1  // 可以有多个

9.3 CommonJS和ESM的区别

  • CommonJS是基于运行时的同步加载,ESM是基于编译时的异步加载
  • CommonJS是可以修改值的,ESM值只读不可修改
  • CommonJS支持树摇tree Shaking,ESM不支持树摇
  • CommonJS中顶层的this指向这个模块本身,而ES6中顶层this指向undefined

十、全局变量和API

如何在nodejs定义全局变量?

在nodejs中使用global定义全局变量,定义的变量,在引入的文件中也可以访问,例如:

demo.js

global.value = '2270'
require('index.js')  // 引入使用全局变量的测试文件
console.log(global);  // 输出global对象

index.js

console.log(value)

在这里插入图片描述

需要注意的是,引入文件的代码要使用全局变量就必须要放在定义全局变量的后面

在浏览器中我们定义的全局变量都在window对象下,而nodejs是定义在global下,不同的环境还需要判断是浏览器还是nodejs,于是在ECMAScript 2020 出现了一个globalThis全局变量,在nodejs环境会自动切换成global对象,浏览器环境自动切换window对象,非常的方便。

在这里插入图片描述

在这里插入图片描述

关于其他全局API

由于nodejs中没有DOM和BOM,因此nodejs没有windowdocument相关的api,除了这些api,其他的ECMAScript Api基本都能使用,例如:

setTimeout setInterval Promise Math console Date等。

nodejs内置全局API

__dirname

它表示当前模块的所在目录的绝对路径。

__filename

它表示当前模块文件的绝对路径,包括文件名和文件扩展名,也就是说比__dirname多了一个文件名和文件扩展名。

process

process 模块是 nodejs 提供给开发者用来和当前进程交互的工具,它的提供了很多实用的 API,如获取命令行参数、获取当前工作目录、获取环境变量等…

  • process.argv:这是一个包含命令行参数【命令行参数指的是,控制台输入命令行后面追加的参数】的数组。第一个元素是Node.js的执行路径,第二个元素是当前执行的JavaScript文件的路径,之后的元素是传递给脚本的命令行参数。
  • process.env:这是一个包含当前环境变量的对象。您可以通过process.env访问并操作环境变量。
  • process.cwd():这个方法返回当前工作目录的路径。
  • process.on(event, listener):用于注册事件监听器。您可以使用process.on监听诸如exituncaughtException等事件,并在事件发生时执行相应的回调函数。
  • process.exit([code]):用于退出当前的Node.js进程。您可以提供一个可选的退出码作为参数。
  • process.pid:这个属性返回当前进程的PID(进程ID)。

这些只是process对象的一些常用属性和方法,还有其他许多属性和方法可用于监控进程、设置信号处理、发送IPC消息等。

需要注意的是,process对象是一个全局对象,可以在任何模块中直接访问,无需导入或定义。

Buffer

Buffer主要用于处理数据。之后会详细讲

十一、CSR SSR SEO

这三篇博客讲解的非常细致

  1. https://xiaoman.blog.csdn.net/article/details/132273569?spm=1001.2014.3001.5502
  2. https://juejin.cn/post/6844903961091112968?searchId=2023092420374812D6B5A036826B3B1E95
  3. https://juejin.cn/post/6844903824428105735?searchId=2023092420374812D6B5A036826B3B1E95

十二、内置模块path

NodeJs的path模块在不同的操作系统是有差异的(Windows|posix)

posix(Portable Operating System Interface og UNIX)表示可移植操作系统接口,是IEEE为要在各种UNIX操作系统上运行软件,而定义API的一系列互相关联的标准的总称。遵守这套标准的操作系统有(unix,like unix,linux,macOs,windows wsl)。为什么要定义这套标准,比如在Linux系统启动一个进程需要调用fork函数,在windows启动一个进程需要调用createprocess函数,这样跨系统运行就会存在问题,比如我在linux写好了代码,移植到windows发现函数不统一,posix标准的出现就是为了解决这个问题。

windows并没有完全遵循posix标准,windows在设计上采用了不同于posix的路径表示方法。

**在windows系统中,路径使用反斜杠(\)作为路径分隔符。这与posix系统使用的正斜杠(/)是不同的。**这是windows系统的历史原因所致,早期的windows操作系统采用了不同的设计选择。

下面我们直接看代码来学习path的多个api:

const path = require("path");
// 1. path.basename() 返回给定路径的最后一部分 xm.html
// 需要注意的是:windows系统 路径默认是F:\project\node\path.js 这种反斜杠的格式
// 但是这里我们用的是正斜杠的写法 这表明windows是兼容正斜杠的写法的
console.log(path.basename("F:/project/node/path.js")); // path.js
// 这里我们再试试反斜杠的写法 反斜杠需要转义一下
console.log(path.basename("F:\\project\\node\\path.js")); // path.js
// 但是反斜杠的写法在posix系统下是无法处理的
console.log(path.posix.basename("F:\\project\\node\\path.js")); // F:\project\node\path.js 无法处理 输出的是整个路径
// 假设现在老板有个需求:现在是MocOs环境 你需要处理windows的路径 那么我们可以使用path.win32来指定win32环境处理方式
console.log(path.win32.basename("F:\\project\\node\\path.js")); // path.js  成功处理
// 2. path.dirname() 返回路径的目录名 和basename刚好是互补的 F:\project\node\path.js
console.log(path.dirname("F:/project/node/path.js")); // F:/project/node
// 3. path.extname() 返回路径的扩展名 .html
// 返回值是带点的
// 如果没有 返回空字符串
// 如果有多个点 返回最后一个点后面的内容
console.log(path.extname("F:/project/node/path.js")); // .js
console.log(path.extname("F:/project/node/path")); //
console.log(path.extname("F:/project/node/path.js.js.txt")); // .txt
// 4. path.join() 拼接路径
console.log(path.join("F:/project/node/path.js", "F:/project/node/path.js")); // F:\project\node\path.js\F:\project\node\path.js
// 5. path.resolve() 将一些 路径/路径段 解析为绝对路径 总是返回一个以相对于当前工作目录的绝对路径
// 多个绝对路径 返回最后一个
console.log(
  path.resolve("F:/project/node/path.js", "F:/project/node/path2.js")
); // F:\project\node\path2.js
// 如果只有一个相对路径 返回当前工作目录的绝对路径
console.log(path.resolve("./path.js")); // C:\Users\22706\Desktop\temp\path.js
// 如果有绝对路径 也有相对路径 返回当前工作目录拼接相对路径所得的绝对路径
console.log(path.resolve(__dirname, './index.js'));  // C:\Users\22706\Desktop\temp\index.js
// 6. path.parse() 解析路径 返回一个对象 path.format()是path.parse()的逆向操作
console.log(path.parse("F:/project/node/path.js"));
// {
//     root: 'F:/',  // 根目录
//     dir: 'F:/project/node',  // 文件所在目录
//     base: 'path.js',  // 文件名加后缀
//     ext: '.js',  // 后缀名
//     name: 'path'  // 文件名
// }

// 7. path.sep 返回对应操作系统下的斜杠形式 windows返回的是\ posix返回的是/
console.log(path.sep);  // \
console.log(path.posix.sep);  // /

// 8. path.delimiter 用于获取当前操作系统环境变量的分隔符
console.log(path.delimiter);  // ;
console.log(path.posix.delimiter);  // :

总结:

  1. path.basename() 返回给定路径的最后一部分
  2. path.dirname() 返回路径的目录名
  3. path.extname() 返回路径的扩展名
  4. path.join() 用于拼接路径
  5. path.resolve() 将一些 路径/路径段 解析为绝对路径 总是返回一个以相对于当前工作目录的绝对路径
  6. path.parse() 解析路径 返回一个对象 path.format()是path.parse()的逆向操作
  7. path.sep 返回对应操作系统下的斜杠形式 windows返回的是\ posix返回的是/
  8. path.delimiter 用于获取当前操作系统环境变量的分隔符

十三、内置模块os

Nodejs os模块可以跟操作系统进行交互

const os = require('os')
序号API作用
1os.platform()获取操作系统平台 win32 windows darwin(就是macos) mac linux
2os.release()获取操作系统版本
3os.arch()获取操作系统位数【cpu架构】
4os.type()返回操作系统的名字 例如,它在Linux上返回’Linux’,在macOS上返回’Darwin’,在Windows上返回’Windows_NT’
5os.hostname()返回操作系统的主机名
6os.version()返回标识内核版本的字符串。
7os.homedir()读取用户目录 底层原理:windows使用echo %userprofile% 输出 mac使用echo $HOME输出
8os.cpus()读取操作系统线程或者cpu的信息
9os.networkInterfaces()读取网络信息
const os = require("os");
// 1. os.platform() 获取操作系统平台 win32 windows darwin(就是macos) mac linux
console.log(os.platform()); // win32
// 2. os.release() 获取操作系统版本
console.log(os.release()); // 10.0.22621
// 3. os.arch() 获取操作系统位数
console.log(os.arch()); // X64
// 4. os.type() 返回操作系统的名字 例如,它在Linux上返回'Linux',在macOS上返回'Darwin',在Windows上返回'Windows_NT'。
console.log(os.type()); // Windows_NT
// 5. os.hostname() 返回操作系统的主机名
console.log(os.hostname()); // LAPTOP-VHCR78I8
// 6. os.version()  返回标识内核版本的字符串。
console.log(os.version());  // Windows 10 Home China
// 7. os.homedir() 读取用户目录 底层原理:windows使用echo %userprofile% 输出 mac使用echo $HOME输出
console.log(os.homedir());  // C:\Users\22706
// 8. os.cpus() 读取操作系统线程或者cpu的信息
console.log(os.cpus());
// 9. os.networkInterfaces()
console.log(os.networkInterfaces());
// {
//     address: '172.27.192.**',  // ip地址
//     netmask: '255.255.240.0',  // 子网掩码
//     family: 'IPv4',  // ip版本 IPv4 IPv6
//     mac: '00:15:5d:52:b9:**',  // 网卡的mac地址
//     internal: false,  // 表示是不是内网ip
//     cidr: '172.27.192.1/**'  // ip地址段
// }

知道这些信息有什么用呢?

非常经典的例子就是:webpack vite 大家应该都用过,它们有一个配置项open: true可以打开浏览器,我们来简单复刻一下它实现的源码:

const os = require("os");
// 导入执行shell命令的api
const { exec } = require("child_process");

function openBrowser(url) {
  if (os.platform() === "darwin") {
    // macOs
    exec(`open ${url}`); // macOs执行shell脚本
  } else if (os.platform() === "win32") {
    // windows
    exec(`start ${url}`); // windows执行shell脚本
  } else {
    // linux
    exec(`xdg-open ${url}`); // linux执行shell脚本
  }
}

openBrowser('https://www.baidu.com')

十四、内置模块process

process是Nodejs操作当前进程和控制当前进程的API,并且是挂载到globalThis下面的全局API。

序号API作用
1process.arch跟之前的os.arch()一样 获取操作系统位数【cpu架构】
2process.cwd()返回当前端工作目录 和__dirname类似 但是esm模式下是用不了__dirname的 可以用cwd()代替
3process.argvargv属性返回一个数组,其中包含启动Node.js进程时传递的命令行参数。第一个元素将是execPath。第二个元素是要执行的JavaScript文件的路径。剩下的元素将是任何附加的命令行参数。
4process.memoryUsage返回内存信息
5process.exit()实现进程强制退出
6process.kill()和process.exit()类似 杀死进程 需要一个参数pid 即进程id 使用process.pid获取
7process.env【最常用】获取操作系统所有的环境变量 最常用的一个属性

我们通过以下代码理解api:process.js

const process = require("process");
// 1. process.arch 跟之前的os.arch()一样 获取操作系统位数【cpu架构】
console.log(process.arch); // x64
// 2. process.cwd() 返回当前端工作目录 和__dirname类似 但是esm模式下是用不了__dirname的 可以用cwd()代替
console.log(__dirname); // C:\Users\22706\Desktop\temp
console.log(process.cwd()); // C:\Users\22706\Desktop\temp
// 3. process.argv argv属性返回一个数组,其中包含启动Node.js进程时传递的命令行参数。第一个元素将是execPath。第二个元素是要执行的JavaScript文件的路径。剩下的元素将是任何附加的命令行参数。
console.log(process.argv); // ['D:\\nodejs\\node.exe', 'C:\\Users\\22706\\Desktop\\temp\\process.js']
// 这里测试输入命令行的额外参数 --version 如果有--version就输出1.0.0 否则输出'无'
console.log(process.argv.includes("--version") ? "1.0.0" : "无");
// 4. process.memoryUsage 返回内存信息
console.log(process.memoryUsage());
/**
{
  rss: 21495808,  // 常驻集大小 物理内存的存量
  heapTotal: 5263360,  // v8给我们分配的堆内存的总大小 包括未使用的内存
  heapUsed: 4609688,  // 已经使用的内存
  external: 347074,  // 外部的内存 C C++使用的
  arrayBuffers: 19406  // 二进制的总量
}
*/
// 5. process.exit() 实现进程强制退出
// 比如有一个5s的定时器,5s结束会自动退出进程,有另个2s的定时器,定时器的回调内调用process.exit()实现强制退出
// 则不会再等待5s的定时器结束再结束进程
setTimeout(() => {
  console.log(5);
}, 5000);

setTimeout(() => {
  process.kill(process.pid); // 强制退出进程 不再等待上一个定时器输出5
}, 2000);

process.on("exit", () => {
  // 监听定时器退出
  console.log("进程退出了");
});

// 6. process.kill() 和process.exit()类似 杀死进程 需要一个参数pid 即进程id 使用process.pid获取
// 7. process.env 获取操作系统所有的环境变量 最常用的一个属性
// 并且可以修改环境变量 但是修改只在当前进程生效 并不会真正影响系统的环境变量
console.log(process.env);
console.log(process.env.VSCODE_INJECTION); // 1
process.env.VSCODE_INJECTION = "2270633333";
console.log(process.env.VSCODE_INJECTION); // 2270633333
// 学习环境变量有什么用呢?最常见的用途就比如:根据环境变量区分是开发环境还是生产环境 从而判断接口请求是使用http协议还是https协议
// 我们经常使用webpack vite等构建工具都会下载一个第三方库 cross-env 通过这个第三方库设置对应环境变量
// 安装完成后我们就可以在package.json中设置对应脚本
console.log(process.env.NODE_ENV == "development" ? "开发环境" : "生产环境");

学习环境变量有什么用呢?

最常见的用途就比如:根据环境变量区分是开发环境还是生产环境 从而判断接口请求是使用http协议还是https协议

我们经常使用webpack vite等构建工具都会下载一个第三方库 cross-env 通过这个第三方库设置对应环境变量。

安装完成后我们就可以在package.json中设置对应脚本:

"scripts": {
    "dev": "cross-env NODE_ENV=development node process.js",
    "build": "cross-env NODE_ENV=production node process.js"
}

上面的脚本实现了运行脚本npm run dev后,cross-env会设置名为NODE_ENV的环境变量值为development,并运行脚本node process.js在node环境下运行上面的process.js文件,运行npm run build同理,实现了运行不同脚本从而通过NODE_ENV来设置不同环境变量值,当然,环境变量名不一定都得取NODE_NEV,可以自定义随意取。

下面我们执行脚本npm run dev测试结果如下:

在这里插入图片描述

cross-env的原理就是如果是windows就调用set,如果是posix就调用export设置环境变量:

set NODE_ENV=production  # windows
export NODE_ENV=production  # posix

因此,使用第三方模块cross-env不需要考虑兼容性问题。

十五、子进程模块child_process

1. 介绍

子进程是Nodejs的核心Api,如果你会shell命令,它会有非常大的帮助,或者你喜欢编写前端工程化工具之类,它也有很大的用处,以及处理CPU密集型应用。

创建子进程

Nodejs创建子进程共有7个API,带Sync的是同步API,不加的是异步API:

  1. spwan:执行命令
  2. exec:执行命令
  3. execFile:执行可执行文件
  4. fork:创建node子命令
  5. execSync:执行命令 同步执行
  6. execFileSync:执行可执行文件 同步执行
  7. spawnSync:执行命令 同步执行

2. exec()和execSync()

exec()异步的方法 可以帮我们执行shell命令 或者跟软件交互 回调函数返回一个buffer。

我们首先介绍exec()execSync()命令的第一个用途:执行shell命令

我们尝试使用exec()执行shell命令输出node版本

const { exec } = require("child_process");
exec("node -v", (err, stdout, stderr) => {
  if (err) {
    console.log(err);
    return;
  }
  // 返回的是buffer 转成字符后输出
  console.log("exec", stdout.toString()); // v16.17.0
});

execSync() 同步的方法 一般同步的用的比较多 异步写法太麻烦了。

这两种方法都比较适合执行较小的shell命令 想要立马拿到结果的shell命令

但是这两种方法有个缺点:返回的字节上限200kb 超出就会报错

使用execSync()执行shell命令输出node版本

const { execSync } = require("child_process");
const nodeVersion = execSync("node -v");
console.log("execSync", nodeVersion.toString()); // v16.17.0

使用execSync()创建文件夹test:因为mkdir命令没有返回值,因此不用接收结果

execSync("mkdir test");  // 可以看到当前目录中多了一个test文件夹

下面我们再介绍exec()execSync()命令的第二个用途:跟软件交互

这里就以execSync()为例,尝试打开chrome的百度页面:

execSync("start chrome http://www.baidu.com");

运行此代码可以成功百度页,下面我们再试试使用execSync()启动电脑上的steam,我们需拿到steam运行程序的路径:

execSync("D:\\Steam\\Steam.exe");

运行代码后等待一会【启动应用程序需要时间】可以看到steam成功启动,需要注意的是:commonjs规范下路径需要双反斜杠进行转码。

3. spawn()和spawnSync()

spawn()spawnSync()也是用来执行shell命令,并且返回的字节大小没有上限 因为返回的是个流 并且是实时返回的,而exec()execSync()需要等待shell命令运行结束,拿到所有的返回结果的才算结束。

spawnSync()用的比较少。

这里我们扩展一下netStat的相关知识:

shell命令netStat是一个监控TCP/IP网络的非常有用的工具,它可以显示路由表、实际的网络连接以及每一个网络接口设备的状态信息。

在这里插入图片描述

下面我们先使用execSync()执行shell命令netStat获取本机网络状态。

const { execSync } = require("child_process");
const netstat = execSync("netstat");
console.log(netstat.toString());  // 需要等待运行完才输出

在这里插入图片描述

可以看到网络状态并不会实时的一条一条的输出,下面我们使用spawn()执行shell命令netStat获取本机网络状态。

spwan()的用法和exec()差异不小,它需要从返回结果中解构出stdout,并使用on监听返回的数据,并且每一条数据都是buffer类型,需要转字符输出才能看得懂,另外,spwan()不仅能实时输出运行结果,使用on还能监听命令运行结束,这是exec所不具有的。

const { spawn } = require("child_process");
const { stdout } = spawn("netstat");
stdout.on("data", (data) => {
  console.log(data.toString());
});
stdout.on("close", () => {
  console.log("结束了");
});

在这里插入图片描述

可以看到控制台实时输出了每一个网络的当前状态。

增加shell命令参数的格式和exec()不同,exec()是直接在shell命令后面增加,就和在命令行输入一样,spawn()增加shell命令参数的格式如下:

const { spawn } = require("child_process");
const { stdout } = spawn("netstat", ["-a"]);  // 增加shell命令参数
stdout.on("data", (data) => {
  console.log(data.toString());
});
stdout.on("close", () => {
  console.log("结束了");
});

参考博客:

  1. https://xiaoman.blog.csdn.net/article/details/132797154?spm=1001.2014.3001.5502
  2. https://blog.csdn.net/weixin_44299027/article/details/123741176

十六、多媒体处理工具ffmpeg

请查看我的博客:前端多媒体处理工具——ffmpeg的使用

十七、events事件触发器

请查看小满的博客:events

十八、待更

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/272124.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

一个卖美妆的 一个月招了数十万代理!月销售额破亿 你敢相信吗?

商业模式永不过时 大家好,我是吴军,一家软件公司的产品经理 今天我们来聊一下这个纪炫商城 其实,说这个纪炫商城之前,我想跟各位企业家老板聊几句实在话 作为公司两百多号技术的,一个拥有五年软件开发经验的产品经理…

深入探讨Java反射:解析机制与应用场景

当谈及Java编程语言的强大功能时,反射(Reflection)是一个不可忽视的特性。反射允许程序在运行时检查和操作其自身的结构,这为开发者提供了一种动态获取信息和执行操作的途径。在本篇博客中,我们将深入探讨Java反射的原…

分支限界法求解01背包(优先队列)【java】

实验内容:运用分支限界法解决0-1背包问题 实验目的:分支限界法按广度优先策略遍历问题的解空间树,在遍历过程中,对已经处理的每一个结点根据限界函数估算目标函数的可能取值,从中选取使目标函数取得极值的结点优先进行广度忧先搜…

day42 1226

作业1&#xff1a; #include <iostream>using namespace std;namespace myspace {string str; }int length(string str) {//char *p &str.at(0);const char *p str.data();int count 0;while (*p ! 0) {p;count;}return count; } int main() {getline(cin,myspac…

元素隐式具有 “any“ 类型,因为类型为 “string“ 的表达式不能用于索引类型 “typeof

报错展示 解决办法 Object.keys(directives).forEach(k > {app.directive(k, directives[k as keyof typeof directives]) })

工具系列:TensorFlow决策森林_(3)使用dtreeviz可视化

文章目录 介绍设置安装 TF-DF 和 dtreeviz导入库 可视化分类树加载、清洗和准备数据分割训练/测试集并训练模型训练一个随机森林分类器显示决策树检查叶节点统计信息决策树如何对实例进行分类特征空间划分 可视化回归树加载、清洗和准备数据分割训练/测试集并训练模型训练一个随…

【linux】线程概念

线程概念 1.储备知识1.1再谈页表 2.线程概念2.1如何理解多线程2.2如何证明2.3什么是线程2.4线程的优点2.4线程的缺点2.5线程异常2.6进程vs线程 喜欢的点赞&#xff0c;收藏&#xff0c;关注一下把&#xff01; 1.储备知识 1.1再谈页表 在上一篇博客说过&#xff0c;页表除了用…

Oracle查询重复数据取第二行,好用来删除重复数据

Oracle查询重复数据取第二行&#xff0c;好用来删除重复数据 SELECT * FROM ( SELECT e.* , ROW_NUMBER() over(PARTITION BY product_category_id,model_size_id ORDER BY product_category_id,model_size_id) rn FROM equ_check_rules e ) s WHERE rn 2;

鸿蒙开发之图片选择器

一、使用 系统的图片选择器真的非常友好&#xff0c;这个绝对要赞一下。 pickPhotos() { //初始化一个photopicker let photoPicker new picker.PhotoViewPicker()//maxSelectNumber最多选择多少张&#xff08;默认值为50&#xff0c;最大值为500&#xff09; //MIMEType 选…

Spring-1-Spring中引入loC和DI

控制反转和依赖注入 IoC 核心是 DI 旨在提供一种更简单的机制来设置组件依赖项&#xff0c;并在整个生命周期中管理这些依赖项 需要某些依赖项的组件通常被称为依赖对象&#xff0c;或者在 IoC 的情况下被称为目标对象 通常&#xff0c; IoC可以分解为两种子类型 依赖注入和依…

Isaac Sim 仿真机器人urdf文件导入

本教程展示如何在 Omniverse Isaac Sim 中导入 urdf 一. 使用内置插件导入urdf 安装urdf 插件 方法是转到“window”->“Extensions” 搜索框中输入urdf, 并启用 通过转至Isaac Utils -> Workflows -> URDF Importer菜单来访问 urdf 扩展。 表格中的 1,2,3 对应着…

从零实现一套低代码(保姆级教程) --- 【6】在项目中使用redux状态管理

摘要 在上一篇文章中的末尾&#xff0c;我们也完成了Input组件的属性面板配置。现在我们的低代码项目已经小有成就了。但是后面的内容还是不少的。 如果你是第一次看到这篇文章&#xff0c;那么请移步到第一节&#xff1a; 从零实现一套低代码&#xff08;保姆级教程&#xf…

抖店只能做和营业执照对照的产品吗?开店基础教程,新手可收藏!

我是王路飞。 抖店的营业执照有多重要呢&#xff1f;关系到你店铺的类型、类目和产品。 尤其是适合新手做的个体店&#xff0c;不涉及对公账户&#xff0c;货款可以直接提现到你的私人银行卡里&#xff0c;保证金也只有企业店铺的一半。 &#xff08;只需要身份证就能开通的…

Python入门之数据结构篇

文章目录 准备工作一、数组1.1 简单使用1.2 数组函数1.3 数组方法1.4 列表推导1.5 数组切片 二、元组&#xff08;tup&#xff09;2.1 简单使用2.2 元组函数 三、字典&#xff08;Dictionary&#xff09;3.1 简单使用3.2 字典函数&#xff1a;关于Python技术储备一、Python所有…

Vue.js学习笔记(1)——Visual Studio Code搭建Vue.js框架

1 开通高德地图API服务 1、进入高德地图API官网&#xff08;https://lbs.amap.com/&#xff09;&#xff1a; 2、注册登录。 3、进入控制台。 4、点击“应用管理”&#xff0c;点击“我的应用”&#xff0c;创建新应用。 5、添加Key&#xff0c;服务平台选择“Web端&#xff…

MMDetection中的数据处理

CustomDataset 代码路径&#xff1a;mmdet/datasets/custom.py mmdet中的CustomDataset继承自torch的Dataset&#xff0c;因此&#xff0c;对应的需要实现3个虚函数&#xff0c;接下来我们首先看看这3个重要函数的实现。 构造函数 CLASSES None # static变量def __init__…

关于勒索软件盛行,LockBit网络攻击者气焰嚣张的动态情报

一、基本内容 在过去一周内&#xff0c;LockBit团队颇为高调&#xff0c;他们对关键组织、政府和企业发动了一系列的攻击。该勒索软件团队发动的攻击数量远远超过其他所有的勒索团队&#xff0c;CSEM事件为近期勒索事件画上了句号 二、相关发声情况 2023年8月30日&#xff0…

数据库原理知识点清晰总结

数据库基础知识 数据库系统结构 数据库管理系统 数据库设计 关系数据库 关系数据库标准语言SQL 关系数据库规范化理论 数据库保护技术 视图 存储过程 触发器 数据的锁定

[uniapp] 文件查找失败:‘./iconfont.woff?t=1703574566334‘

uniapp 报错 文件查找失败&#xff1a;‘./iconfont.woff?t1703574566334’ uniapp引入图标的时候如果没有处理,就会报这个错误,这个错误是因为字体图标用的相对路径,uniapp不允许, 所以要解决这个错误,只需要我们去到iconfont.css文件内将相对路径改为绝对路径就好 改成 …
最新文章