乐闻世界logo
搜索文章和话题

面试题手册

pnpm 的 shamefully-hoist 配置是什么?什么时候需要使用?

shamefully-hoist 是 pnpm 的一个配置选项,用于创建类似 npm/Yarn 的扁平化 node_modules 结构。默认行为 vs shamefully-hoist:# 默认 pnpm 结构(严格)node_modules/├── .pnpm/│ └── lodash@4.17.21/└── lodash -> .pnpm/lodash@4.17.21/node_modules/lodash# 只能访问声明的依赖const lodash = require('lodash'); // ✅ 正常const debug = require('debug'); // ❌ 错误(未声明)# shamefully-hoist=true 结构(扁平化)node_modules/├── .pnpm/├── lodash/├── debug/ # 被提升上来└── ...# 可以访问所有依赖const lodash = require('lodash'); // ✅ 正常const debug = require('debug'); // ✅ 可以访问(幽灵依赖)配置方法:# .npmrcshamefully-hoist=true# 或只提升特定包shamefully-hoist-pattern[]=webpackshamefully-hoist-pattern[]=*types*使用场景:遗留项目迁移# 项目依赖幽灵依赖,暂时无法修改# .npmrcshamefully-hoist=true# 迁移完成后应关闭# shamefully-hoist=false特定工具兼容# 某些工具需要扁平化结构# 如某些 webpack 插件、IDE 等shamefully-hoist=true混合使用# 只提升特定包public-hoist-pattern[]=*eslint*public-hoist-pattern[]=*prettier*public-hoist-pattern[]=*types*相关配置对比:| 配置 | 作用 | 推荐度 ||------|------|--------|| shamefully-hoist | 完全扁平化 | ⭐⭐ || public-hoist-pattern | 部分扁平化 | ⭐⭐⭐⭐⭐ || hoist-pattern | 内部扁平化 | ⭐⭐⭐⭐ |public-hoist-pattern 推荐配置:# .npmrc# 提升类型定义包public-hoist-pattern[]=*types*# 提升构建工具public-hoist-pattern[]=*eslint*public-hoist-pattern[]=*prettier*public-hoist-pattern[]=*webpack*# 提升测试工具public-hoist-pattern[]=*jest*public-hoist-pattern[]=*vitest*为什么不推荐完全扁平化:// shamefully-hoist=true 的问题// 1. 幽灵依赖const someDep = require('some-dep');// 实际未在 package.json 中声明// 可能导致 CI/CD 失败// 2. 版本冲突// 不同包依赖同一包的不同版本// 扁平化后只能有一个版本// 3. 失去 pnpm 的优势// 磁盘空间节省减少// 依赖管理不严格最佳实践:# 推荐配置# .npmrc# 默认不扁平化shamefully-hoist=false# 只提升必要的包public-hoist-pattern[]=*eslint*public-hoist-pattern[]=*prettier*public-hoist-pattern[]=*types*# 严格模式strict-peer-dependencies=true迁移策略:# 1. 先启用 shamefully-hoist 迁移# .npmrcshamefully-hoist=true# 2. 运行项目,检查是否有问题pnpm installpnpm buildpnpm test# 3. 逐步修复幽灵依赖# 找到所有未声明的依赖pnpm ls --depth=10# 4. 添加到 package.jsonpnpm add missing-dep# 5. 关闭 shamefully-hoist# .npmrcshamefully-hoist=false检查幽灵依赖:# 使用 depcheck 检查cd projectnpx depcheck# 或使用 pnpm 检查pnpm ls --depth=0总结:shamefully-hoist 是迁移过渡方案长期使用应关闭,保持 pnpm 的严格性使用 public-hoist-pattern 替代完全扁平化逐步修复幽灵依赖,回归标准模式
阅读 0·3月7日 12:16

pnpm workspace 如何配置和使用?

pnpm workspace 是 pnpm 内置的 monorepo 解决方案,用于管理多个包在一个仓库中。基本配置:# pnpm-workspace.yamlpackages: - 'packages/*' # 所有 packages 目录下的包 - 'apps/*' # 所有 apps 目录下的应用 - 'shared/*' # 共享模块项目结构示例:my-monorepo/├── pnpm-workspace.yaml├── package.json├── pnpm-lock.yaml├── packages/│ ├── ui/│ │ ├── package.json│ │ └── src/│ └── utils/│ ├── package.json│ └── src/└── apps/ ├── web/ │ ├── package.json │ └── src/ └── server/ ├── package.json └── src/包间依赖引用:// apps/web/package.json{ "name": "@my-org/web", "dependencies": { "@my-org/ui": "workspace:*", // 使用 workspace 协议 "@my-org/utils": "workspace:^1.0.0" }}workspace 协议类型:{ "dependencies": { "package": "workspace:*", // 使用最新版本 "package": "workspace:^", // 兼容版本 "package": "workspace:~", // 近似版本 "package": "workspace:^1.0.0" // 指定版本范围 }}常用命令:# 安装所有依赖pnpm install# 在特定包中运行命令pnpm --filter @my-org/ui buildpnpm -F @my-org/ui build # 简写# 在所有包中运行命令pnpm -r build # 递归执行# 只在有变化的包中运行pnpm -r --filter "...[origin/main]" build# 添加包间依赖pnpm --filter @my-org/web add @my-org/ui优势对比:| 特性 | pnpm workspace | Lerna | Yarn Workspaces ||------|---------------|-------|-----------------|| 内置支持 | ✅ | ❌ 需安装 | ✅ || 依赖共享 | ✅ 硬链接 | ❌ | ✅ 符号链接 || 磁盘效率 | 最高 | 一般 | 中等 || 配置复杂度 | 简单 | 复杂 | 简单 |最佳实践:# 使用 changesets 管理版本pnpm add -Dw @changesets/clipnpm changeset init# 发布流程pnpm changeset # 记录变更pnpm changeset version # 更新版本pnpm -r publish # 发布所有包
阅读 0·3月6日 23:39

pnpm 如何处理 peer dependencies?与 npm 有什么不同?

pnpm 对 peer dependencies 的处理更加严格和正确,这是其重要特性之一。什么是 Peer Dependencies:// react-dom/package.json{ "peerDependencies": { "react": "^18.0.0" // 需要宿主项目提供 react }}npm/Yarn 的问题:# 项目依赖{ "dependencies": { "react": "^17.0.0", "react-dom": "^18.0.0" # 需要 react 18 }}# npm/Yarn 可能静默安装,导致版本不匹配# 运行时才发现问题pnpm 的严格处理:# 安装时报错pnpm install# ERR_PNPM_PEER_DEP_ISSUES Unmet peer dependencies# react-dom@18.0.0 requires react@^18.0.0 but you have react@17.0.0解决方案:正确安装匹配版本pnpm add react@18 react-dom@18使用 pnpm.overrides// package.json{ "pnpm": { "overrides": { "react": "^18.0.0" } }}忽略 peer dependency(不推荐){ "pnpm": { "peerDependenciesMeta": { "react": { "optional": true } } }}peerDependencies 的正确使用:// 插件包的 package.json{ "name": "my-react-plugin", "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" }, "peerDependenciesMeta": { "react-dom": { "optional": true // 可选的 peer dependency } }}monorepo 中的处理:# pnpm-workspace.yamlpackages: - 'packages/*'# packages/plugin/package.json{ "peerDependencies": { "react": "^18.0.0" }}# packages/app/package.json{ "dependencies": { "react": "^18.0.0", "@my-org/plugin": "workspace:*" }}# pnpm 会自动将 app 中的 react 提供给 plugin自动安装 peer dependencies:# .npmrcauto-install-peers=true # 自动安装 peer dependenciesstrict-peer-dependencies=false # 不严格检查对比总结:| 特性 | npm/Yarn | pnpm ||------|----------|------|| 检查时机 | 可能延迟到运行时 | 安装时立即检查 || 错误提示 | 可能不明显 | 明确的错误信息 || 版本冲突 | 可能静默忽略 | 严格报错 || monorepo 支持 | 一般 | 优秀 |
阅读 0·3月6日 23:39

pnpm 常用命令有哪些?与 npm 命令有什么区别?

pnpm 提供了丰富的 CLI 命令来管理依赖和项目。安装命令:# 安装所有依赖pnpm installpnpm i# 安装单个包pnpm add lodashpnpm add lodash@4.17.21# 安装到不同依赖类型pnpm add lodash --save-prod # dependencies (默认)pnpm add lodash --save-dev # devDependenciespnpm add lodash --save-optional # optionalDependenciespnpm add lodash -D # devDependencies 简写pnpm add lodash -O # optionalDependencies 简写# 全局安装pnpm add -g lodashpnpm add --global lodash更新命令:# 更新单个包pnpm update lodashpnpm up lodash# 更新所有依赖pnpm updatepnpm up# 更新到最新版本pnpm update --latestpnpm up -L# 交互式更新pnpm update --interactivepnpm up -i删除命令:# 删除包pnpm remove lodashpnpm rm lodash# 删除多个包pnpm remove lodash express# 删除全局包pnpm remove -g lodash运行脚本:# 运行 package.json 中的脚本pnpm run buildpnpm run test# 简写pnpm buildpnpm test# 传递参数pnpm build -- --watchMonorepo 相关命令:# 在特定包中运行命令pnpm --filter <package-name> buildpnpm -F <package-name> build# 在所有包中运行pnpm -r build# 并行运行pnpm -r --parallel build# 只在有变化的包中运行pnpm -r --filter "...[origin/main]" build# 递归执行命令pnpm -r exec rm -rf node_modules查询命令:# 查看包信息pnpm info lodashpnpm view lodash# 查看已安装的包pnpm listpnpm ls# 查看全局安装的包pnpm list -g# 查看依赖树pnpm list --depth=2# 查看过时的包pnpm outdatedStore 管理命令:# 查看 store 路径pnpm store path# 清理 storepnpm store prune# 验证 storepnpm store verify其他常用命令:# 创建项目pnpm create react-app my-apppnpm create vite# 执行包命令pnpm dlx create-react-app my-app# 导入其他锁文件pnpm import# 链接本地包pnpm link ../local-package# 检查依赖问题pnpm audit# 为什么安装了这个包pnpm why lodash配置命令:# 查看配置pnpm config list# 设置配置pnpm config set store-dir /path/to/store# 删除配置pnpm config delete store-dir命令对比:| npm | yarn | pnpm ||-----|------|------|| npm install | yarn | pnpm install || npm add lodash | yarn add lodash | pnpm add lodash || npm run build | yarn build | pnpm build || npm update | yarn upgrade | pnpm update || npm remove lodash | yarn remove lodash | pnpm remove lodash |
阅读 0·3月6日 23:39

pnpm 的 overrides 和 resolutions 有什么区别?如何使用?

pnpm 提供了强大的依赖覆盖机制,主要通过 overrides 和 resolutions 来实现。pnpm.overrides:用于强制覆盖依赖版本,无论原始依赖声明什么版本。// package.json{ "pnpm": { "overrides": { "lodash": "^4.17.21", "react": "^18.0.0" } }}使用场景:修复安全漏洞{ "pnpm": { "overrides": { "minimist@<1.2.6": "^1.2.6" } }}强制统一版本{ "pnpm": { "overrides": { "typescript": "^5.0.0" } }}替换包{ "pnpm": { "overrides": { "node-sass": "sass" } }}路径覆盖:{ "pnpm": { "overrides": { "react": "$react", // 使用项目中的版本 "webpack>lodash": "^4.17.21" // 只覆盖 webpack 的 lodash } }}resolutions(Yarn 兼容):// package.json{ "resolutions": { "lodash": "^4.17.21" }}区别对比:| 特性 | pnpm.overrides | resolutions ||------|----------------|-------------|| 适用范围 | 所有依赖 | 所有依赖 || 优先级 | 高 | 中 || Yarn 兼容 | ❌ | ✅ || 路径指定 | ✅ | ❌ || 版本引用 | ✅ | ❌ |实际应用示例:// 复杂覆盖场景{ "dependencies": { "react": "^18.0.0", "react-dom": "^18.0.0", "antd": "^5.0.0" }, "pnpm": { "overrides": { // 确保所有包使用相同 React 版本 "react": "$react", "react-dom": "$react-dom", // 修复 antd 的某个依赖漏洞 "antd>rc-util": "^5.30.0", // 替换废弃的包 "request": "axios" } }}验证覆盖效果:# 查看实际安装的版本pnpm list react# 查看依赖树pnpm list --depth=10# 查看为什么安装了这个版本pnpm why lodash注意事项:谨慎使用全局覆盖// ❌ 不推荐:全局覆盖可能导致不兼容{ "pnpm": { "overrides": { "react": "^18.0.0" } }}// ✅ 推荐:路径指定更精确{ "pnpm": { "overrides": { "some-package>react": "^18.0.0" } }}更新锁文件# 修改 overrides 后需要重新安装pnpm install# 或强制更新pnpm install --force
阅读 0·3月6日 23:39

pnpm 如何处理依赖版本冲突?

pnpm 通过其独特的 node_modules 结构和依赖解析机制来处理版本冲突。版本冲突场景:// 项目依赖{ "dependencies": { "package-a": "^1.0.0", // 依赖 lodash@^4.17.0 "package-b": "^2.0.0" // 依赖 lodash@^3.10.0 }}npm/Yarn 的处理方式(有问题):# 扁平化结构只能有一个版本node_modules/├── lodash@4.17.21/ # 只有一个版本├── package-a/└── package-b/ # 可能使用错误版本!pnpm 的处理方式(正确):# 每个包有自己的依赖版本node_modules/├── .pnpm/│ ├── lodash@3.10.1/│ ├── lodash@4.17.21/│ ├── package-a@1.0.0/│ │ └── node_modules/│ │ ├── package-a/│ │ └── lodash -> ../../lodash@4.17.21/node_modules/lodash│ └── package-b@2.0.0/│ └── node_modules/│ ├── package-b/│ └── lodash -> ../../lodash@3.10.1/node_modules/lodash├── package-a -> .pnpm/package-a@1.0.0/node_modules/package-a└── package-b -> .pnpm/package-b@2.0.0/node_modules/package-bpnpm 的依赖解析机制:// package-a 使用 lodash@4.17.21const lodash = require('lodash');// 解析路径:// node_modules/.pnpm/package-a@1.0.0/node_modules/lodash// package-b 使用 lodash@3.10.1const lodash = require('lodash');// 解析路径:// node_modules/.pnpm/package-b@2.0.0/node_modules/lodash查看依赖树:# 查看依赖树pnpm listpnpm ls# 查看特定包的依赖pnpm why lodash# 查看详细依赖树pnpm list --depth=10# 查看重复依赖pnpm list --depth=Infinity | grep lodash解决版本冲突:使用 overrides 强制统一版本{ "pnpm": { "overrides": { "lodash": "^4.17.21" } }}路径指定覆盖{ "pnpm": { "overrides": { "package-b>lodash": "^4.17.21" } }}使用 resolutions(Yarn 兼容){ "resolutions": { "lodash": "^4.17.21" }}依赖去重:# pnpm 自动去重# 如果两个包依赖相同版本的 lodash# 只会在 store 中存储一份# 手动检查重复pnpm list --depth=Infinity | sort | uniq -dpeer dependencies 冲突:// package-a{ "peerDependencies": { "react": ">=16.8.0" }}// package-b{ "peerDependencies": { "react": ">=17.0.0" }}# pnpm 会报错ERR_PNPM_PEER_DEP_ISSUES# 解决方案pnpm add react@18# 或使用 overrides{ "pnpm": { "overrides": { "react": "^18.0.0" } }}最佳实践:定期检查依赖# 查看过时的包pnpm outdated# 更新依赖pnpm update锁定版本{ "dependencies": { "lodash": "4.17.21" // 精确版本 }}使用 pnpm-lock.yaml# 确保版本一致性pnpm install --frozen-lockfile对比总结:| 特性 | npm/Yarn | pnpm ||------|----------|------|| 多版本支持 | ❌ 扁平化冲突 | ✅ 独立存储 || 自动隔离 | ❌ | ✅ || 依赖解析 | 可能错误 | 精确正确 || 磁盘占用 | 高(多份) | 低(硬链接) |
阅读 0·3月6日 21:35

pnpm 如何使用硬链接和符号链接来节省磁盘空间?

pnpm 通过硬链接和符号链接的组合实现高效的磁盘空间利用:硬链接(Hard Links)硬链接是指向文件系统中同一文件的多个引用。# pnpm 的硬链接机制# 全局 store 位置~/.pnpm-store/v3/files/00/abc123... # 实际文件# 项目中的硬链接project-a/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash.jsproject-b/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash.js# 两者都指向同一个物理文件,不占用额外空间特点:多个硬链接共享同一个 inode删除一个链接不影响其他链接修改会反映到所有链接符号链接(Symbolic Links/Soft Links)符号链接是指向文件路径的特殊文件。# pnpm 的符号链接结构node_modules/lodash -> .pnpm/lodash@4.17.21/node_modules/lodash# 这是一个指向相对路径的符号链接特点:类似于快捷方式可以跨文件系统原文件删除后链接失效pnpm 的组合使用:项目结构:node_modules/├── lodash -> .pnpm/lodash@4.17.21/node_modules/lodash [符号链接]└── .pnpm/ └── lodash@4.17.21/node_modules/ └── lodash.js [硬链接 → 全局 store]实际效果:// 查看 inode 验证硬链接const fs = require('fs');const stat1 = fs.statSync('project-a/node_modules/.pnpm/lodash@4.17.21/lodash.js');const stat2 = fs.statSync('project-b/node_modules/.pnpm/lodash@4.17.21/lodash.js');console.log(stat1.ino === stat2.ino); // true,同一个 inode空间节省示例:# npm 方式:10个项目使用 lodash10 × 1.4MB = 14MB# pnpm 方式:10个项目使用 lodash1 × 1.4MB = 1.4MB(节省 90%)
阅读 0·3月6日 21:35

pnpm-lock.yaml 的作用是什么?如何管理锁文件?

pnpm-lock.yaml 是 pnpm 生成的锁文件,用于确保依赖版本的一致性。锁文件结构:# pnpm-lock.yamllockfileVersion: '6.0'settings: autoInstallPeers: true excludeLinksFromLock: falseimporters: .: dependencies: lodash: specifier: ^4.17.21 version: 4.17.21packages: /lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LbbZUZt0P2vK6s4I6F7McA==} engines: {node: '>=6'} dev: falsesnapshots: lodash@4.17.21: {}主要部分解析:lockfileVersion标识锁文件格式版本pnpm 8 使用版本 6.0importers记录每个包的直接依赖包含 specifier(声明的版本范围)和 version(实际安装的版本)packages所有依赖包的元数据包含解析地址、完整性校验、引擎要求等snapshots依赖树的快照记录依赖关系锁文件的作用:# 开发者 A 安装依赖pnpm install# 生成 pnpm-lock.yaml# 开发者 B 克隆项目git clone projectpnpm install# 根据锁文件安装,确保版本一致版本控制:# 必须提交到版本控制git add pnpm-lock.yamlgit commit -m "add lockfile"# CI/CD 中使用冻结安装pnpm install --frozen-lockfile# 如果锁文件与 package.json 不匹配,安装失败常见问题处理:更新依赖# 更新单个依赖pnpm update lodash# 更新所有依赖pnpm update# 更新到最新版本(忽略版本范围)pnpm update --latest解决冲突# 删除锁文件重新生成rm pnpm-lock.yamlpnpm install导入其他锁文件# 从 package-lock.json 导入pnpm import# 从 yarn.lock 导入pnpm import与 npm/yarn 锁文件对比:| 特性 | pnpm-lock.yaml | package-lock.json | yarn.lock ||------|---------------|-------------------|-----------|| 格式 | YAML | JSON | 自定义格式 || 可读性 | 高 | 中 | 低 || 存储方式 | 扁平化 | 扁平化 | 扁平化 || 硬链接支持 | ✅ | ❌ | ❌ |最佳实践:# 始终提交锁文件git add pnpm-lock.yaml# CI 中使用冻结安装pnpm install --frozen-lockfile# 定期更新依赖pnpm update --interactive --latest
阅读 0·3月6日 21:35

pnpm 的 .npmrc 配置有哪些常用选项?

pnpm 的 .npmrc 配置文件提供了丰富的选项来自定义包管理行为。基础配置:# .npmrc# 注册表配置registry=https://registry.npmjs.org/# 淘宝镜像(国内加速)registry=https://registry.npmmirror.com/# 作用域包注册表@mycompany:registry=https://npm.mycompany.com/存储配置:# 全局 store 位置store-dir=/path/to/custom/store# 缓存目录cache-dir=/path/to/custom/cache# 状态目录state-dir=/path/to/custom/state安装行为配置:# 严格模式strict-peer-dependencies=true# 自动安装 peer dependenciesauto-install-peers=true# 扁平化模式(兼容 npm)shamefully-hoist=true# 只安装生产依赖production=true# 忽略 engines 检查engine-strict=false网络配置:# 并发下载数network-concurrency=16# 请求超时时间(毫秒)fetch-timeout=60000# 重试次数fetch-retries=3# 代理设置proxy=http://proxy.company.com:8080https-proxy=http://proxy.company.com:8080# 不使用代理的域名no-proxy=localhost,127.0.0.1Workspace 配置:# 递归安装时链接 workspace 包link-workspace-packages=true# 优先使用 workspace 版本prefer-workspace-packages=true# 保存 workspace 协议save-workspace-protocol=true安全与审计:# 忽略审计警告ignore-workspace-root-check=true# 允许执行 postinstall 脚本ignore-scripts=false# 只读模式(不修改 lock 文件)frozen-lockfile=true输出配置:# 日志级别loglevel=info# 不显示进度条reporter=silent# 使用颜色输出color=always常用配置组合:# 开发环境配置strict-peer-dependencies=falseauto-install-peers=trueshamefully-hoist=false# CI/CD 配置frozen-lockfile=trueprefer-offline=truereporter=silent# Monorepo 配置link-workspace-packages=trueprefer-workspace-packages=true环境变量配置:# 通过环境变量设置export npm_config_registry=https://registry.npmmirror.com/export npm_config_store_dir=/custom/store查看当前配置:# 查看所有配置pnpm config list# 查看特定配置pnpm config get registry# 设置配置pnpm config set registry https://registry.npmmirror.com/# 删除配置pnpm config delete registry配置优先级:命令行参数环境变量项目 .npmrc用户 ~/.npmrc全局 /etc/npmrc默认值实际应用示例:# 企业环境配置registry=https://npm.company.com/@company:registry=https://npm.company.com/# 使用内部代理proxy=http://proxy.company.com:8080https-proxy=http://proxy.company.com:8080# 严格模式strict-peer-dependencies=trueengine-strict=true# 性能优化network-concurrency=32fetch-retries=5
阅读 0·3月6日 21:35