Skip to content
文章目录

包管理工具

www.npmjs.com 无法访问或访问速度慢,可以使用这些平台代替 包搜索平台

npm

安装,源管理,更新,卸载

安装

安装 node 时,会一同安装 npm

源管理

查看源地址:

npm config get registrynpm config get

设置源地址:

  • 淘宝源: npm config set registry https://registry.npm.taobao.org
  • 官方源: npm config set registry https://registry.npmjs.org/

更新

shell
npm -v
# 清理 npm 缓存数据
npm cache clean --force
npm install -g npm
# 更新到指定版本
npm -g install npm@6.8.0

卸载

依赖安装,更新以及删除与全局依赖管理

依赖安装

shell
# 将依赖安装到项目的dependencies
npm i xxx
# 安装指定版本
npm i xxx@xx
# 将依赖安装到项目的devDependencies
npm i xxx -D
# 安装全局依赖
npm i xxx -g

依赖删除

shell
# 删除全局依赖
npm uninstall xx -g
# 删除依赖
npm uninstall xx

全局依赖管理

全局依赖安装位置: npm root -g

更改全局包位置:

yarn

pnpm

配置 PNPM 全局安装路径

pnpm config set store-dir <global-store-path>

如:

pnpm config set store-dir E:\.pnpm-store\global

配置 PNPM 全局 bin 文件安装路径

pnpm config set global-bin-dir <store-bin-path>

配置 PNPM cache 路径

pnpm config set cache-dir <store-cache-path>

配置 PNPM state 路径 配置当前仅由更新检查器使用的 pnpm-state.json 文件的目录

pnpm config set state-dir <store-state-path>

以上命令行的方式最终会输出到~/.npmrc文件, 所以以上方式可以在~/.npmrc文件中输入

global-bin-dir=E:\.pnpm-store\bin
global-dir=E:\.pnpm-store\global
state-dir=E:\.pnpm-store\state
cache-dir=E:\.pnpm-store\cache

修改完相关目录之后,进行全局依赖安装提示类似如下错误:

pnpm now wants to use the store at " D:\.pnpm-store\2 " to link dependencies. If you want to use the new store location, reinstall your dependencies with

则需要到原的全局目录,删除该目录下的已存在内容

安装全局模块时出现类似如下警告,则使用管理员权限打开 cmd,再重新运行

WARN  EXDEV: cross-device link not permitted, link

pnpm exec

在项目范围内执行 shell 命令。

如果您将 Jest 作为项目的依赖项,则无需全局安装 Jest,只需使用 pnpm exec 运行它:

shell
pnpm exec jest

pnpm dlx

从源中获取包而不将其安装为依赖项,热加载,并运行它公开的任何默认命令二进制文件。

例如,若要在任何地方使用 Create-react-app 来初始化一个 react 应用,而不需要 来在另一个项目下安装它,您可以运行:

shell
pnpm dlx create-react-app ./my-app

查看当前项目安装包的实际版本

shell
pnpm list
# 或者
pnpm ls

依赖安装,更新,删除

依赖安装

shell
# 安装所有依赖
pnpm install
# 将依赖安装到项目的dependencies
pnpm add xxx
# 安装指定版本
pnpm add xxx@xxx
# 将依赖安装到项目的devDependencies
pnpm add xxx -D
# 安装全局依赖
pnpm add xxx -g
shell
# 安装所有依赖,且不更新lockfile
pnpm install --frozen-lockfile
shell
# 安装所有依赖,且仅从 store 中离线下载,如果store中没有,则下载失败
pnpm install --offline
shell
# 安装所有依赖,本地存在的包从store下载,不存在的包从npm仓库下载
pnpm install --prefer-offline

如果在 pnpm 的 workspace 中执行pnpm install则表示在在工作空间以及子项目都执行pnpm install, 如果只想表示执行当前项目的pnpm install, 则需要创建.npmrc配置文件, 将recursive-install设置为false

.npmrc

recursive-install=false

依赖更新

shell
# 更新所有依赖
pnpm up
# 更新到指定版本
pnpm up foo@2

依赖删除

shell
# 删除指定依赖
pnpm remove xx
# 删除全局依赖
pnpm remove xx --global

使用 pnpm 构建 Monorepo 项目

monorepo 是什么

monorepo 是把多个项目的所有代码放到一个工作空间进行管理,同一个工作空间的多个子项目之间可以非常方便的共享/共用公共代码

有什么好处?

  • 比如一个项目包含 PC 端和移动 H5 端, 那么其中共用的代码,就可以抽取到一个单独的子项目中,然后 PC 端项目和移动 h5 项目,同时引用这个单独的子项目,减少重复代码,同时也可以避免同样的功能逻辑,有两份不同的实现

  • 可以一边做项目一边抽取独立的组件,独立组件单独发布到 npm 仓库,不断增强自己的抽象能力,也不断积累自己的可复用组件

创建 Monorepo 项目

shell
mkdir test-monorepo
cd test-monorepo
pnpm init
touch pnpm-workspace.yaml

pnpm-workspace.yaml

yaml
packages:
  # 告诉pnpm从哪些目录扫描子项目,当前配置,pnpm会从packages目录和example目录扫描子项目
  - 'packages/**'
  - 'example/**'
  # 不包括在 test 文件夹下的 package
  - '!**/test/**'
shell
pnpm install vue vite -w

-w参数表示将依赖安装在整个workspace, 此种方式安装的依赖,相当于是工作空间内的全局依赖. 该包会放置在 <root>/node_modules

如果是一个开发依赖的话,可以加上 -D 参数,表示这是一个开发依赖,会装到 pacakage.json 中的 devDependencies 中,比如:

shell
pnpm install rollup -wD
shell
pnpm i dayjs -r --filter @test/web

-r --filter 表示将dayjs仅安装在@test/web子项目中,@test/webpackage.jsonname的值

对于局部依赖,最简单的办法就是

shell
# 进入子项目的根目录
cd packages/http
# 在子项目的根目录直接安装依赖,该依赖就会是局部依赖,只有这个子项目能使用
pnpm install axios

需要注意的是,--filter 参数跟着的是package下的 package.jsonname 字段,并不是目录名。

关于 --filter 操作其实还是很丰富的,比如执行 pkg1 下的 scripts 脚本:

shell
pnpm build --filter @qftjs/monorepo1

filter 后面除了可以指定具体的包名,还可以跟着匹配规则来指定对匹配上规则的包进行操作,比如:

pnpm build --filter "./packages/**"

此命令会执行所有 package 下的 build 命令。

packages/utils 子项目的package.json文件如下

json
{
  "name": "@test/utils", // <-----
  "version": "1.0.0",
  "description": "",
  "main": "index.ts",
  "author": "Innei",
  "license": "MIT",
  "dependencies": {}
}

入口文件为 index.ts, 内容如下

ts
export const add = (a: number, b: number) => a + b

将某个子项目以第三方依赖的方式,添加到该工作空间中的另一个子项目

shell
pnpm i @test/utils -r --filter @test/ui

@test/utils@test/ui都是该工作空间的子项目,上面这个语句表示:将@test/utils项目以第三方依赖的形式,添加到@test/ui中,此时@test/uipackage.json 就会多出这个@test/utils dependencies的依赖

json
{
  "name": "@test/ui",
  "version": "1.0.0",
  "description": "",
  "main": "./index.tsx",
  "scripts": {},
  "author": "Innei",
  "license": "MIT",
  "dependencies": {
    "@test/utils": "workspace:^1.0.0"
  }
}

由于是 workspace 管理的,所有有一个前缀 workspace

在设置依赖版本的时候推荐用 workspace:*,这样就可以保持依赖的版本是工作空间里最新版本,不需要每次手动更新依赖版本。

shell
pnpm add @monorepo/http@* --filter @monorepo/web

比如 web 需要依赖 http 的功能用于请求,那么这个时候需要互相依赖,为了让依赖实时更新最新版本,才用通配符更新版本

接下来则可以从 package/ui 中直接引入这个包了。

ts
import { add } from '@test/utils'

pnpm publish 的时候,会自动将 package.json 中的 workspace 修正为对应的版本号。

常用 monorepo pnpm 命令

  • 列出这个包的源码位置,被 monorepo 内部哪些项目引用了。
shell
pnpm why -r
  • 取消某个依赖的安装
shell
pnpm remove axios
pnpm remove axios --filter  @monorepo/http

只允许 pnpm

当在项目中使用 pnpm 时,如果不希望用户使用 yarn 或者 npm 安装依赖,可以将下面的这个 preinstall 脚本添加到工程根目录下的 package.json中:

json
{
  "scripts": {
    "preinstall": "npx only-allow pnpm"
  }
}

preinstall 脚本会在 install 之前执行,现在,只要有人运行 npm install 或 yarn install,就会调用 only-allow 去限制只允许使用 pnpm 安装依赖。

严格限制 node 版本和 pnpm 版本

json
// package.json
"engines": {
    "node": "16.13.0",
    "npm": "8.1.0"
}

在根目录下 .npmrc 添加 engine-strict=true

# .npmrc
engine-strict=true

根目录放一个.nvmrc切换 node 版本更方便,nvm use 就可以切换,不需要后面加版本号

# .nvmrc
v16.17.0

限制运行时 node 版本

经常会遇到,有的小伙伴 npm run dev 启动不了项目了。在服务器上运行 npm run build 报语法错误了,本地尝试却是好好的。很多时候都是因为 node 版本不兼容。

在 package.json 中添加 npm scripts 钩子。可以在运行 npm run dev 、 npm run build 之前去执行

json
// package.json
{
    "script": {
        "predev": "npm run check:node", // 开发时校验
        "prebuild": "npm run check:node" // 打包时校验
        "check:node": "node ./misc/checkNode",
    }
}

校验当前 node 版本与项目要求的 node 版本是否匹配。如果不匹配,打印错误提示信息,终止进程。

js
// misc/checkNode.js
const { engines } = require('../package')

let semver = null
try {
  semver = require('semver') // 校验版本号的一个工具
} catch (error) {}

const version = engines.node
if (semver && !semver.satisfies(process.version, version)) {
  console.error(`Required node version ${version}, got: ${process.version}.`)
  process.exit(1)
}
if (!semver && process.version !== version) {
  console.error(`Required node version ${version} and npm version ${engines.npm}, got: ${process.version}.`)
  process.exit(1)
}

启动项目

使用 node packages/component (默认执行 index.js 文件)

shell
node packages/components

更好的选择是编写 npm scripts 就像下面这样:

json
  "scripts": {
    "test": "vitest",
    "dev": "pnpm -C play dev",
    "docs:dev": "pnpm run -C docs dev",
    "docs:build": "pnpm run -C docs build",
    "docs:serve": "pnpm run -C docs serve",
  },

其中 -C \<path> 表示 在 path 下运行 npm 脚本 而不是在当前工作路径下。例如根目录下执行 npm run docs:dev 便会执行 docs/package.json dev 脚本,同理 buildserve 也是一样。

参考文章

pnpm + workspace + changesets 构建你的 monorepo 工程

使用 pnpm 构建 Monorepo 项目

使用 pnpm 做 monorepo

pnpm monorepo 实践

如何限制项目使用指定的 node 版本