likes
comments
collection
share

跟着小满zs学 nodejs —— 总结篇(一)

作者站长头像
站长
· 阅读数 4

介绍

  • Nodejs并不是JavaScript应用,也不是编程语言,它是JavaScript运行时环境,是开源、跨平台的
  • Nodejs是构建在V8引擎之上的,V8引擎是由C/C++编写的,因此我们的Js代码需要有C/C++转化后再执行

Npm Package.json

  • npm(全称 Node Package Manager)是 Node.js 的包管理工具,它是一个基于命令行的工具,用于帮助开发者在自己的项目中安装、升级、移除和管理依赖项

    npm 命令

  • npm init

  • npm install

  • npm install <package-name>

  • npm install <package-name> --save

  • npm install <package-name> --save-dev

  • npm install -g <package-name>

  • npm update <package-name>

  • npm uninstall <package-name>

  • npm run <script-name>

  • npm search <keyword>

  • npm info <package-name>

  • npm list:列出当前项目中安装的所有包

  • npm outdated:列出当前项目中需要更新的包

  • npm publish

  • npm login

  • npm logout

  • npm config list

  • npm get registry

Npm install 原理

  1. 首先安装的依赖都会存放在根目录的node_modules,默认采用扁平化的方式安装,并且排序规则.bin第一个然后@系列,再然后按照首字母排序abcd等,并且使用的算法是广度优先遍历

  2. 在遍历依赖树时,npm会首先处理项目根目录下的依赖,然后逐层处理每个依赖包的依赖

  3. 在处理每个依赖时,npm会检查该依赖的版本号是否符合依赖树中其他依赖的版本要求,如果不符合,则会尝试安装适合的版本

    扁平化安装

  • 理想情况下,安装某个二级模块时,若发现第一层级有相同名称,相同版本的模块,便直接复用那个模块

  • 非理想情况下,需要安装不同版本的模块,不能进行复用,会单独在一级模块下搞一层node_modules

    npm install 后续流程

跟着小满zs学 nodejs —— 总结篇(一)

Npm run 原理

  • npm run vite举例

    1. 先从当前项目的 node_modules/.bin 去查找可执行命令vite
    2. 如果没找到就去全局的 node_modules 去找可执行命令vite
    3. 如果还没找到就去环境变量查找
    4. 再找不到就进行报错

Npm 生命周期

    "predev": "node prev.js",
    "dev": "node index.js",
    "postdev": "node post.js"

执行 npm run dev 命令的时候 predev 会自动执行 他的生命周期是在dev之前执行,然后执行dev命令,再然后执行postdev,也就是dev之后执行

模块化

  • Nodejs 环境 原生支持CommonJS模块,这意味着你可以使用require来引入模块、module.exports来导出模块。从 Node.js 14开始,Node.js也开始支持 ES Modules,你可以使用importexport关键字来导入导出模块
  • 浏览器环境 原生支持ES Modules,这意味着你可以使用 importexport 。但是,浏览器环境不支持Node.js独有的requiremodule.exports,也就是不支持CommonJS模块

在vite项目中,根目录下的JavaScript文件(如vite.config.js或者index.js)运行的环境主要是Nodejs环境,该环境用于处理项目配置、插件加载、开发服务器启动等任务。src目录下的JavaScript文件运行的环境通常是浏览器环境。这意味着你可以使用浏览器提供的API,例如:documentwindow等对象。但是Nodejs环境下的一些模块和方法,例如:requiremodule.exportsprocess等在浏览器环境中是无法使用的

Nodejs 全局变量

  • 在 Nodejs 中使用global定义全局变量,定义的变量,可以在引入的文件中直接使用,但要注意引入的顺序要在定义之后。
global.xxx = 'xxx';
  • 在浏览器中我们定义的全局变量都在window,nodejs在global,不同的环境还需要判断,于是在ECMAScript 2020 出现了一个globalThis全局变量,在nodejs环境会自动切换成global ,浏览器环境自动切换window

由于 Nodejs 中没有DOM和BOM,除了这些API,其他的ECMAscript API都能用

Nodejs 内置全局API

  • __dirname
  • __filename
  • require module
  • process
  • BUffer

CSR SSR SEO

  • 上面讲到 nodejs 环境无法直接操作DOM和BOM,但是使用第三方库可以做到
  • npm i jsdomjsdom是一个模拟浏览器环境的库,可以在 nodejs 中使用 DOM API
const fs = require('node:fs')
const { JSDOM } = require('jsdom')

const dom = new JSDOM(`<!DOCTYPE html><div id='app'></div></html>`)

const document = dom.window.document

const window = dom.window

fetch('https://api.thecatapi.com/v1/images/search?limit=10&page=1').then(res => res.json()).then(data => {
    const app = document.getElementById('app')
    data.forEach(item=>{
       const img =  document.createElement('img')
       img.src = item.url
       img.style.width = '200px'
       img.style.height = '200px'
       app.appendChild(img)
    })
    fs.writeFileSync('./index.html', dom.serialize())
})
  • 上述 demo 属于SSR(Server-side Rendering 服务端渲染),请求数据和拼接都在服务端完成
  • 现在常用的 vue、react 等框架是在客户端完成渲染拼接的,属于CSR(Client-Side Rendering 客户端渲染)
  • SEO(搜索引擎优化),CSR应用对SEO不友好

path 模块

主要用于处理和转换文件路径,这个API提供了多个方法来操作字符串形式的路径

  1. path.basename(path, [ext]):返回路径的最后一部分,如果提供了ext参数,那么返回结果会去除该扩展名
  2. path.dirname(path):返回path的目录名,就是路径中消除文件名后的部分
  3. path.extname(path):返回扩展名,比如'.txt'
  4. path.join(path1, path2,..):拼接路径,支持 ../ 操作
  5. path.resolve([from ...] , to):用于将给定的路径转化为绝对路径
const path = require('node:path');
path.resolve(__dirname,'./index.js')
  1. path.parse(pathString):返回path字符串的对象表示
  2. path.format(pathObject):从对象中返回路径字符串。这与 path.parse 功能相反

os 模块

用于跟操作系统交互

  1. os.type():返回操作系统的类型
  2. os.platform():返回操作系统平台
  3. os.release():返回操作系统版本
  4. os.arch():返回操作系统cpu架构
  5. os.cpus():返回cpu线程以及详细信息
  6. os.networkInterfaces():返回网络信息

process API

与其他API不同的是,process对象是一个全局对象,无需导入或定义,用于操作当前进程和控制当前进程

  1. process.arch:与 os.arch() 一样
  2. process.cwd():返回当前的工作目录
  3. process.argv:获取执行进程后面的参数,返回的是一个数组
  4. process.memoryUsage:获取当前进程的内存使用情况
  5. process.exit():强制进程尽快退出
  6. process.kill():与 exit 类似,用来杀死一个进程,接收一个参数进程id,可以通过 process.pid 获取
  7. process.env:读取操作系统所有的环境变量,也可以修改和查询环境变量,但不会真正影响操作系统的变量,只在当前进程生效。可以配合 cross-env 第三方库使用

child_process 子进程模块

子进程是 Nodejs 核心API,可以用来执行 shell 命令,编写前端工程化工具,处理 cpu 密集型应用 Nodejs 创建子进程共有7个API,Sync是同步API,不加Sync是异步API

  1. exec执行命令
  2. execSync同步执行命令
const { exec, execSync } = require('child_process');
// exec 异步方法,回掉函数返回buffer,可以执行shell命令,或者跟软件交互
// execSync 同步方法
// 只执行较小的shell命令,想要立即拿到结果的shell,字节上线200kb,超过报错

exec('node -v', (err, stdout, stderr) => {
    if (err) {
        return err;
    }
    console.log(stdout);
})

const nodeVersion = execSync('node -v');
console.log('sync', nodeVersion.toString());

// sync v18.16.0
// v18.16.0
  1. spawn执行命令
  2. spawnSync同步执行命令
// spawn 用于执行一些实时获取的信息,没有字节上线,返回的是个流
// spawnSync 用的较少

const { stdout } = spawn('netstat', ['-a'], {
    // options配置信息
});
stdout.on('data', (msg) => {
    console.log(msg.toString());
})
// spawn在执行完成后会抛出close事件监听,并返回状态码
stdout.on('close', (msg) => {
    console.log('结束了');
})

options 配置信息,exec和spawn都有

cwd <string> 子进程的当前工作目录。
env <Object> 环境变量键值对。
encoding <string> 默认为 'utf8'
shell <string> 用于执行命令的 shell。 在 UNIX 上默认为 '/bin/sh',在 Windows 上默认为 process.env.ComSpec。 详见 Shell RequirementsDefault 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执行可执行文件
  2. execFileSync同步执行可执行文件
execFile(path.resolve(process.cwd(),'./bat.cmd'),null,(err,stdout)=>{
    console.log(stdout.toString()) 
})
echo '开始'

mkdir test 

cd ./test

echo console.log("test1232131") >test.js

echo '结束'

node test.js

exec 底层是通过 execFile 实现,execFile 底层是通过 spawn 实现

  1. fork创建node子进程,只能接收js模块,适合大量的计算,或者容易阻塞主进程操作的一些代码

    index.js

const {fork} = require('child_process')
const testProcess = fork('./test.js')

testProcess.send('我是主进程')

testProcess.on("message",(data)=>{
    console.log('我是主进程接受消息111:',data)
})

test.js

process.on('message',(data)=>{
    console.log('子进程接受消息:',data)
})

process.send('我是子进程')
  • send 发送信息 ,message接收消息,可以相互发送接收

更高效的子进程管理:Execa 模块

Execa 模块用于执行外部命令,构建在 child_process 模块之上,提供了简洁的API和更好的错误处理机制。但它属于第三方模块,需要 npm 额外安装

使用 Promise 封装,默认支持 Promise API; 提供了更好的错误处理机制,能够捕获并处理命令执行过程中的错误

  • 先看demo
const execa = require('execa');

async function runCommand() {
    try {
        const { stdout } = await execa('ls', ['-l', '-a'], { cwd: '/path/to/directory' });
        console.log('命令执行结果:', stdout);
    } catch (error) {
        console.error('命令执行失败:', error);
    }
}

runCommand();

Execa模块的execa()函数接收三个参数:

  • command(String):要执行的外部命令,例如‘ls’、‘echo’等
  • args(Array):传递给外部命令的参数,以数组形式传入
  • option(Object 可选):见上面