Global
Nodejs 全局中存在一些变量可以直接使用
暴露的原生模块
我们在 nodejs 中经常使用的 process 和 console 其实是直接暴露出来的原生模块,虽然我们大部分时候都是直接使用的,但是完整的写法是先导入再使用:
import console from 'node:console'
import process from 'node:process'
console.log('hello')
process.exit(0)global
global 中提供了一些我们常用的方法,例如定时器,微任务等,这些方法都暴露在了全局中。所以除了通过 global. 访问,我们更常用方式的还是直接访问这些方法
console.log(global)
/*
<ref *1> Object [global] {
global: [Circular *1],
queueMicrotask: [Function: queueMicrotask],
clearImmediate: [Function: clearImmediate],
setImmediate: [Function: setImmediate] {
[Symbol(nodejs.util.promisify.custom)]: [Getter]
},
structuredClone: [Function: structuredClone],
clearInterval: [Function: clearInterval],
clearTimeout: [Function: clearTimeout],
setInterval: [Function: setInterval],
setTimeout: [Function: setTimeout] {
[Symbol(nodejs.util.promisify.custom)]: [Getter]
},
atob: [Function: atob],
btoa: [Function: btoa],
performance: Performance {
//...
},
fetch: [AsyncFunction: fetch]
}
*/global 和 GlobalThis
两者在 Nodejs 环境中是一样的
console.log(global === globalThis) //=> true in nodejs但是推荐使用 GlobalThis,因为这个变量在浏览器环境也是有效的
console.log(window === globalThis) //=> true in browserperformance
performance 对象同时存在于 Nodejs 和浏览器环境,它能提供比 new Date() 更高的精度,可以用于统计某些耗时短的任务消耗的时间
performance.now() // 程序运行开始的到执行这句代码的时间,单位是 ms
// => 55.3364999294281举个例子
console.log(
performance.now() //=> 65.53837490081787
)
let str: string = ''
for(let i = 0; i < 10e6; i++) {
str += 'abcdef'
str += 'ghijkl'
str += 'mnopqrs'
str += 'tuvwxyz'
}
console.log(
performance.now() //=> 1919.1480419635773
)structuredClone
structuredClone 属于一个较新的 API,是 JS 中原生提供的深拷贝函数
虽然原生提供了拷贝的 API,但 structuredClone 其实存在诸多不足:
- 不会拷贝原型链
- 函数将被丢弃
- Error、DOM 节点等是不能被拷贝的
- setter、getter 可能不会被复制
- ...
所以使用起来需要谨慎一些
在此之前,如果是较为简单的数据,我们可以通过 JSON.parse(JSON.stringify(obj)) 来拷贝对象。对于复杂的数据,可以使用 lodash 提供的拷贝方法
atob/btoa
这两个方法用于 ASCII 和 binary 的互相转换,也是浏览器可用
- btoa 是
binary to ascii,是 base64 的编码过程 - atob 是
ascii to binary,是 base64 的解码过程
node prefix
推荐使用 NodeJs 原生模块的时候,加上 node 这个 namespace,防止潜在的模块冲突
- import path form 'node'
+ import path form 'node:path'process.env
接触前端最开始的时候,与之相关的是我们在 package.json 中的 scripts 字段中设置环境变量,例如:
{
"scripts": {
"build": "NODE_ENV=development vite build",
"build:prod": "NODE_ENV=production vite build",
}
}通过上面的代码将 process.env.NODE_ENV 注入到了 node 进程中,从而可以让我们通过代码访问这个变量来判断当前的环境
const isDev = process.env.NODE_ENV === 'development'除了自己注入的变量,process.env 对象本身也有大量的预置值,例如我当前环境下,用 node 执行 js 脚本直接输出的值:
console.log(process.env)
/**
{
BUN_INSTALL: '/Users/peterroe/.bun',
COLORTERM: 'truecolor',
COMMAND_MODE: 'unix2003',
CUSTOM_ENV: 'prod',
DENO_INSTALL: '/Users/peterroe/.deno',
GIT_ASKPASS: '/Applications/Visual Studio Code.app/Contents/Resources/app/extensions/git/dist/askpass.sh',
HOME: '/Users/peterroe',
HOMEBREW_CELLAR: '/opt/homebrew/Cellar',
HOMEBREW_PREFIX: '/opt/homebrew',
HOMEBREW_REPOSITORY: '/opt/homebrew',
...
}
*/注入的原理是什么?npm run script 发生了什么?JS 中访问的 process.env 是从哪里继承的?
shell进程
当我们执行 npm run xxx 脚本后,首先启动一个 shell 进程,这个启动的 shell 和你的平台有关,在 macOS 和 Linux 上,它是 /bin/sh,在 Windows 上是 cmd.exe。比如说我当前的系统是 MacOS,并且我将 zsh 设置成了默认的 shell,这个 node 进程就是在 zsh 环境下执行的
默认的 process.env 的值就是从当前 shell 进程中的 env 继承过来的,例如你是 MacOs/Linux 的话,可以直接在 shell 中输入如下命令,查看当前 shell 中的所有的 env
$ env
MallocNanoZone=0
USER=peterroe
SECURITYSESSIONID=186a5
COMMAND_MODE=unix2003
__CFBundleIdentifier=com.microsoft.VSCode
# ...在当前 shell 中运行的 JS 脚本都能通过 process.env.xxx 访问到上面输出的任意一个值。
对于上面 build 脚本命令:NODE_ENV=development vite build,实际上是一个 shell 语法,而其中的 NODE_ENV=development,就是声明 env 变量的语法。所以我们在 JS 中访问的 process.env.NODE_ENV,是通过注入到 shell 的 env 中,再由 process.env 继承过去的
这意味着下面两个命令在这种情况下是是等价的:
$ npm run build$ export PATH=$PWD/node_modules/.bin:$PATH && NODE_ENV=development vite buildNPM包的命令
除了 shell 中已经存在的 PATH,npm run 还会将 node_modules/.bin 添加到 PATH 中,这样我们就可以在 package.json 中直接使用 vite 这样的命令了(而不用借助 npx)。