Skip to content
0

Global

Nodejs 全局中存在一些变量可以直接使用

暴露的原生模块

我们在 nodejs 中经常使用的 processconsole 其实是直接暴露出来的原生模块,虽然我们大部分时候都是直接使用的,但是完整的写法是先导入再使用:

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 browser

performance

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 的互相转换,也是浏览器可用

  1. btoa 是 binary to ascii,是 base64 的编码过程
  2. 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 build

NPM包的命令

除了 shell 中已经存在的 PATHnpm run 还会将 node_modules/.bin 添加到 PATH 中,这样我们就可以在 package.json 中直接使用 vite 这样的命令了(而不用借助 npx)。

Released under the MIT License.