前端如何统一开发环境

统一不同的同事之间,本地和 CI 之间的开发环境有利于保证运行效果一致和 bug 可复现,本文聚焦于前端最基本的开发环境配置:nodejs 和 包管理器。

nodejs

首先推荐使用 fnm 管理多版本 nodejs。

对比 nvm

  • 支持 brew 安装,更新方便
  • 跨平台,windows 也能用 winget 安装

使用 fnm 一定要记得开启根据当前 .nvmrc 自动切换对应的 nodejs 版本,也就是在在 .zshrc 中加入:

shell 复制代码
eval "$(fnm env --use-on-cd)"

包管理器

尽管 npm 一直在进步,甚至 7.x 已经原生支持了 workspace。但是我钟爱 pnpm,理由:

  • 安全,避免幽灵依赖,不会将依赖的依赖平铺到 node_modules 下
  • 快,基于软/硬链接,node_modules 下是软连接,硬链接到 .pnpm 文件夹下的硬链接
  • 省磁盘,公司配的 mac 只有 256G
  • pnpm 的可配置性很强,配置不够用还可以用 .pnpmfile.js 编写 hooks
  • yarn2 node_modules 都看不到,分析依赖太麻烦了
  • 公司用的 vue,而 vue3/vite 用 pnpm(政治正确)

推荐使用 Corepack 管理用户的包管理器,其实我一开始知道有 corepack 这个 nodejs 官方的东西的时候,我就在想:为啥不叫 npmm(node package manager manager) 呢?

corepack 目前官方觉得功能没稳定,所以默认没开启,需要用户通过 corepack enable 手动开启,相关的讨论:enable corepack by default

有了 corepack 我们就可以轻松的在 npm/yarn/pnpm 中切换,安装和更新不同的版本。还有一个非常方便的特性就是通过在 package.json 中声明 packageManager 字段例如 "pnpm@8.14.1",当我们开启了 corepack,cd 到该 package.json 所在的 package 的时候,运行 pnpmcorepack 会使用 8.14.1 版本的 pnpm

corepack 是怎样做到的呢?nodejs 安装文件夹有个的 bin 目录,这个目录会被添加到 path 环境变量,其中包含了 corepack 以及 corepack 支持的包管理器的可执行文件:

shell 复制代码
❯ tree ../../Library/Caches/fnm_multishells/17992_1705553706619/bin
../../Library/Caches/fnm_multishells/17992_1705553706619/bin
├── corepack -> ../lib/node_modules/corepack/dist/corepack.js
├── node
├── npm -> ../lib/node_modules/npm/bin/npm-cli.js
├── npx -> ../lib/node_modules/npm/bin/npx-cli.js
├── pnpm -> ../lib/node_modules/corepack/dist/pnpm.js
├── pnpx -> ../lib/node_modules/corepack/dist/pnpx.js
├── yarn -> ../lib/node_modules/corepack/dist/yarn.js
└── yarnpkg -> ../lib/node_modules/corepack/dist/yarnpkg.js

可以看到 pnpm 被链接到了 corepack 的一个 js 文件,查看 corepack/dist/pnpm.js 内容:

shell 复制代码
#!/usr/bin/env node
require('./lib/corepack.cjs').runMain(['pnpm', ...process.argv.slice(2)]);

可以看到其实 corepack 相当于劫持了 pnpmyarn 命令,然后根据 packageManager 字段配置自动切换到对应的包管理器,如果已经安装过就使用缓存,没有就下载。

怎样统一 nodejs 和 包管理器

问题

虽然我在项目中配置了 .nvmrc 文件,在 package.json 中声明了 packageManager 字段,但是用户可能没有安装 fnm 以及配置根据 .nvmrc 自动切换对应的 nodejs,还有可能没有开启 corepack,所以同事的环境还是有可能和要求的不一致。我一向认为,不应该依靠人的自觉去遵守规范,通过工具强制去约束才能提前发现问题和避免争论。

解决办法

最开始是看到项目中使用了 only-allow 用于限制同事开发时只能用 pnpm,由此我引发了我一个灵感,为什么我不干脆把事情做绝一点,把 nodejs 的版本也给统一了

于是我写了一个脚本用于检查用户本地的 nodejs 的版本,包管理器的版本必须和要求一致。最近封装成了一个 cli:check-fe-env。使用方式很简单,增加一个 preinstall script:

json 复制代码
{
  "scripts": {
    "preinstall": "npx check-fe-env"
  }
}

工作原理

  • 用户在运行 pnpm install 之后,install 依赖之前,包管理器会执行 preinstall 脚本
  • cli 会检测:
    • 用户当前环境的 nodejs 版本和 .nvmrc 中声明的是否一样
    • 用户当前使用的包管理器种类和版本是否和 package.jsonpackageManager 字段一样

获取当前环境的 nodejs 版本很简单,可以用 process.version。想要获取执行脚本时的包管理器可以通过环境变量:process.env.npm_config_user_agent,如果一个 npm script 是通过 pnpm 运行的,那么这个环境变量会被设置为例如 pnpm/8.14.1 npm/? node/v20.11.0 darwin arm64,由此我们可以获取当前使用的包管理器和版本。

为了加快安装速度,我特意把源码和相关依赖给一起打包了,整个 bundle 大小 8k 左右。

局限性

最新的 npmpnpm 目前貌似都有一个 bug,都是安装完依赖才执行 preinstall hooks,具体看这: Preinstall script runs after installing dependencies

这个方案对于 monorepo 项目或者说不需要发包的项目是没啥问题的,但是不适用于一个要发包的项目。原因是 preinstall script 除了会在本地 pnpm install 时执行,别人安装这个包,也会执行这个 preinstall script,就和 vue-demi 用的 postinstall script 一样。主要是确实没找到一个:只会在本地运行 pnpm install 后且在安装依赖前执行的 hook。

相关推荐
我要洋人死1 小时前
导航栏及下拉菜单的实现
前端·css·css3
科技探秘人1 小时前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人1 小时前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR1 小时前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香1 小时前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q2498596931 小时前
前端预览word、excel、ppt
前端·word·excel
小华同学ai1 小时前
wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器
前端·钉钉·飞书
Gavin_9151 小时前
【JavaScript】模块化开发
前端·javascript·vue.js
懒大王爱吃狼3 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
逐·風7 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#