惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
V
Visual Studio Blog
IT之家
IT之家
博客园 - 聂微东
The Cloudflare Blog
月光博客
月光博客
阮一峰的网络日志
阮一峰的网络日志
S
SegmentFault 最新的问题
Apple Machine Learning Research
Apple Machine Learning Research
酷 壳 – CoolShell
酷 壳 – CoolShell
爱范儿
爱范儿
H
Help Net Security
博客园 - 叶小钗
V
V2EX
WordPress大学
WordPress大学
J
Java Code Geeks
Hugging Face - Blog
Hugging Face - Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园_首页
C
Check Point Blog
B
Blog
D
DataBreaches.Net
美团技术团队
罗磊的独立博客

kshao-blog-前端知识记录

win or mac clash 无 TUN 让 Antigravity、Chrome 强制 proxy(解决 Antigravity 无法加载选择 model、自动更新无法登录、跳转) 【大杂烩】在 pnpm 中直接修改 node_modules(.pnpm) 中的依赖项,项目中持久化 - pnpm 中的依赖处理、幽灵依赖、寻址规则等 在 html 中直接使用 Esm、Jsx 脚本快速调试和使用 React@19 和 Vue@3 源码,解决 React19 UMD 构建等问题 一键在本地批量检测并升级更新 package.json 中的模块依赖,ncu(npm-check-updates)在 npm、pnpm 或 workspace 项目中的使用教程 解决 Mac Docker Desktop 中启动出现的问题合集 通过阿里云、腾讯云无服务器搭建自定义的企业域名邮箱,实现在 QQ邮箱 收发等功能(附腾讯 SMTP 和 IMAP) 解决使用代理(clash 等)进行 SSH 连接(如 Github ssh key clone/push)出现 kex_exchange_identification 错误 静态文件资源 cdnjs, jsdelivr 抖音字节国内快速 CDN 镜像推荐【2025】- 仍在使用 bootcdn 和 staticfile CDN 请注意验证资源的完整性(SRI) pnpm monorepo 中管理依赖的最佳实践,与 Catalogs(目录)协议的使用(monorepo 中统一版本管理) Web 安全中的 Secure Contexts(安全上下文)- 解决在本地中使用 clipboard 或 Crypto 等 API 限制或关闭上下文限制 使用 serve 配合 openssl 或 mkcert 创建本地自签名可信任的证书 - 创建本地 TLS\SSL https 协议服务 利用 Github Actions 和 Acme 自动申请、更新和部署至阿里云、腾讯云 CDN Lets Encrypt SSl\TLS ECC RSA 双证书 【CSS】解决在 flex 容器中使用 align-content 或 justify-content 属性 center 居中时的溢出滚动和截断问题 - 理解 safe 关键字 在线工具 - 一键获取下载抖音无水印视频、抖音去水印解析工具、下载抖音无水印高清图集【2025 最新】 【React Router】v6 data router 在非组件(或工具方法)中如何优雅的跳转路由 【React】为什么路由跳转时页面滚动高度不会被重置(保留上个页面高度)?理解 history scrollRestoration 的场景与使用,以及如何使用 React Router 重置和跳转前保留滚动高度 【React】在本地 Html 中快速 debug(调试)React 源码 【React】结合源码和 EventLoop 分析 - 为什么 useLayoutEffect 会阻止 DOM 重绘(而 useEffect 闪烁)?为什么其内部 useState 会“同步”执行? React Compiler - 解放在函数中编程时的性能焦虑(React Conf 2024)附 Next 在线演示 在 github actions 中获取时间,并转换为中国标准时间(中国时区) 【npm】npm ci - npm clean install,在 CI、CD 中保持构建的一致性和可重复性 eslint 9.x 升级或使用指南,eslint.config.js 配置,包含 react、typescript、prettier 等常用配置升级迁移 使用 Spicetify 自定义 Spotify - 歌词翻译、全屏展示、主题替换 在 node 中快速代理请求(Proxy),解决跨域或请求转发问题 - http-proxy-middleware 修改请求体和返回 在 Hexo 中使用 AI(Gemini、deepseek、Azure)生成文章摘要,支持自定义模板。hexo-ai-summaries 插件文档(默认适配主题 Butterfly) 解决在 webstorm 或 idea 等 jetbrains 工具中遇到 Git 无法 force push,或 force push 灰色禁用无法点击(protected branches) hexo-seo-submit,Hexo 博客 SEO 优化插件 - 每日定时自动或手动提交链接至百度、Bing、Google,支持 Github Actions 和 Coding Jenkins 等CI(Hexo 插件编写) 【Node】Corepack - 解决 pnpm 或 yarn 的多版本管理、解决本地版本与 packageManager 中的版本一致性问题 解决 npm、cnpm 或 pnpm install 遇到 certificate has expired (证书已过期) npm、yarn、pnpm 设置最新国内镜像源(附官方镜像源和最新阿里源),以及 nrm 的使用教程【2025】
一文吃透 pnpm 如何使用 workspace 构建 monorepo,与 npm、y...
kshao · 2024-05-29 · via kshao-blog-前端知识记录

环境与版本

  • platform => mac os
  • node => v22.2.0
  • npm => 10.8.0
  • pnpm => 9.1.3
  • yarn => 1.22.22

名词解释

利用单一仓库来管理多个 packages 的一种策略,如早期的 lerna

workspace

由上述单仓多包催生的管理方式,workspace(工作空间) 是 npmyarnpnpm 等包管理工具提供的一种特性,用于管理多个包的依赖关系。
合理配置 workspace 后,包之间互相依赖不需要使用 npm link,将在 install 时中处理

pnpm 中使用 workspace

A workspace must have a pnpm-workspace.yaml file in its root. A workspace also may have an .npmrc in its root.
如文档描述,启用 pnpmworkspace 需要在项目根目录创建 pnpm-workspace.yaml

项目结构

my-monorepo
├── docs
├── apps
│   └── web
├── packages
│   ├── ui
│   ├── eslint-config
│   └── shared-utils
├── pnpm-workspace.yaml
├── .npmrc => optional
└── sdk

根目录 package.json

/my-monorepo/package.json

{ "name": "my-monorepo", "private": true, "script": { "dev": "pnpm -r dev" } }

pnpm-workspace.yaml

在该项目中指定位于 my-monorepo/apps/my-monorepo/packages/ 内的直接子目录为工作区如 webui
docs 本身则为一个工作区,则不需要通配符

pnpm-workspace.yaml

packages: - "docs" - "apps/*" - "packages/*"

具体语法 - glob 通配符

pnpm-workspace.yaml

pnpm-workspace.yaml

packages: # 选择 packages 目录下的所有首层子目录的包 - 'packages/*' # 选择 components 目录下所有层级的包 - 'components/**' # 排除所有包含 test 的包 - '!**/test/**'

安装依赖

/my-monorepo/

pnpm install

在根目录中安装依赖

–workspace-root

/my-monorepo/

pnpm add <package-name> -w # or pnpm add <package-name> --workspace-root

给指定 workspace(工作空间) 安装依赖

--filterpackage.json name

/my-monorepo/

pnpm add <package-name> --filter <workspace-name> # or pnpm add lodash --filter docs

更新依赖

更新根目录依赖,看执行路径

pnpm update <package-name> [-w]

更新指定 workspace 依赖

pnpm update <package-name> --filter <workspace-name>
# or
pnpm update lodash --filter docs

卸载依赖

pnpm uninstall <package-name> [-w]
# or
pnpm uninstall <package-name> --filter <workspace-name>
# or
pnpm uninstall lodash --filter docs

执行脚本

执行 workspace 中的脚本

pnpm dev --filter docs

执行所有 workspace 中脚本

pnpm -r dev
# or
$ pnpm --recursive dev

或者在根目录的 package.json 中配置

/my-monorepo/package.json

{ "name": "my-monorepo", "script": { "docs:dev": "pnpm dev --filter docs" } }

直接执行

pnpm docs:dev

安装内部 workspace 依赖

pnpm add <package-name> --filter <workspace-name>
# or
pnpm add web --filter docs
  • 请注意你当前的 pnpm 版本,在 9.0pnpm 修改 link-workspace-packages 的默认值为 false。该属性开启后,你在安装依赖时优先在本地链接,而不是从 registry(远程) 中下载。
  • 所以在这个版本你若需要使用命令安装一个新的 workspace 中的依赖需要在 .npmrc 中启用 link-workspace-packages
  • 当然主动在 package.json 中声明的依赖不受影响,如 web: "workspace:*"pnpm 还是会自动处理,这种不确定性的执行结果可能是导致 pnpm 在该版本中禁用了该值

https://github.com/pnpm/pnpm/issues/7954#issuecomment-2062830615
9.x pnpm link-workspace-packages
8.x pnpm link-workspace-packages

.npmrc

.npmrc

link-workspace-packages = true

或临时启用

pnpm add <package-name> --filter <workspace-name> --link-workspace-packages=true
# or
pnpm add web --filter docs --link-workspace-packages=true

执行结果

/my-monorepo/packages/docs/package.json

{ "name": "docs", "dependencies": { "web": "workspace:^" } }

什么是 workspace:^ - semver 版本

你可能好奇 workspace:^ 是怎么生成的,后面 pnpm 是怎么如何转化的?

当你将依赖项添加到 package.json 中时,pnpm 根据 .npmrc 或命令行中的 save-workspace-protocol 字段来决定是否使用 workspace: 协议,并根据 save-prefix 字段来决定版本的前缀(semver

例如,save-prefix"~"save-workspace-protocoltrue

save-workspace-protocol

{
  "name": "docs",
  "dependencies": {
    "web": "workspace:~1.0.0"
  }
}

转化

{
	"dependencies": {
		"foo": "workspace:*",
		"bar": "workspace:~",
		"qar": "workspace:^",
		"zoo": "workspace:^1.5.0"
	}
}
{
	"dependencies": {
		"foo": "1.5.0",
		"bar": "~1.5.0",
		"qar": "^1.5.0",
		"zoo": "^1.5.0"
	}
}

总结

常用命令

# 安装依赖
$ pnpm install

# 给指定 workspace 安装依赖
$ pnpm add <package-name> --filter <workspace-name>

# 卸载依赖
$ pnpm uninstall <package-name> --filter <workspace-name>

# 更新依赖
$ pnpm update <package-name> --filter <workspace-name>

# 给根目录安装依赖 - -w 为安装 -workspace-root
$ pnpm add <package-name> -<D>w

# 内部包的互相引用 - 前提 .npmrc 中配置 link-workspace-packages = true
# 若未配置需手动
$ pnpm add <package-name> --filter <workspace-name>

# 执行 workspace 中的脚本, 或者在根目录的 package.json 中配置
$ pnpm dev --filter docs
# 执行所有 workspace 中的脚本 
$ pnpm -r dev
# or
$ pnpm --recursive dev

# 删除 workspace 中所有的 node_modules
rm -rf node_modules && pnpm -r exec -- rm -rf node_modules

npm 中使用 workspace

项目结构

my-monorepo
├── docs
├── apps
│   └── web
├── packages
│   ├── ui
│   ├── eslint-config
│   └── shared-utils
├── package.json
└── sdk

package.json 中配置 workspace

路径语法与 pnpm 一致 => 具体语法 - glob 通配符

{
  "name": "my-monorepo",
  "workspaces": [
    "apps/*",
    "packages/*"
  ]
}

命令初始化子包

根据提供的路径创建 workspace(路径不存在,则创建),并在根目录的 package.json 中添加 workspace 路径(若已经配置相关路径,则不会)

npm init -w ./newFolder/hooks -y
# or
npm init -w ./packages/hooks -y

workspace 添加、更新、移除依赖

npm install lodash -w docs
npm uninstall lodash -w docs
npm update lodash -w docs

# or
npm install lodash --workspace=docs

依赖内部的 workspace

pnpm 不同的是 npm 是直观的版号,当然你需要修改 semver 规则,可参考 save-prefix

npm install ui -w docs

/my-monorepo/packages/docs/package.json

{ "name": "docs", "dependencies": { "ui": "^1.0.0" } }

执行脚本

npm run dev -w docs
# run many
npm run dev -w docs -w ui

# or
npm run dev --workspace=docs
npm run test --workspace=docs --workspace=ui

批量执行 workspace 内的脚本

--if-present 避免 workspace 中不含当前脚本的报错

npm run dev

# or
npm run dev --if-present

在根目录 package.json 中运行 workspace 中的脚本

在此处配置一些常用的脚本命令

/my-monorepo/package.json

{ "name": "my-monorepo", "script": { "dev": "npm run dev", "docs:dev": "npm run dev -w docs" } }

总结

# 新增子包
npm init -w ./packages/docs -y

# 为子包添加依赖
npm install lodash -w docs

# 运行子包的dev脚本
npm run dev -w docs

# 运行所有 workspace dev脚本
npm run dev

yarn 中使用 workspace

yarnnpm 大体一致,此处不阐述相同点。

命令

yarn workspaces info - 查询 workspace 之间依赖关系

yarn workspaces info

执行结果:

{
  "docs": {
    "location": "docs",
    "workspaceDependencies": [
      "eslint-config",
      "typescript-config",
      "ui"
    ],
    "mismatchedWorkspaceDependencies": []
  },
  "web": {
    "location": "web",
    "workspaceDependencies": [
      "eslint-config",
      "typescript-config"
    ],
    "mismatchedWorkspaceDependencies": []
  }
}

yarn workspace <package-name> <command> - 给 workspace 安装依赖

# 添加依赖
yarn workspace docs add lodash

# 移除依赖
yarn workspace docs remove lodash

yarn add <package-name> -W - 根目录安装依赖

yarn add lodash -w

yarn workspace <package-name> add <package-name@version> - 安装内部依赖

yarn workspace docs add ui@1.0.0
# or
yarn workspace docs add ui@^1.0.0

此处必须加上版本号,你可以手动加上 semver 前缀,但需要与 package.json 中的版本号保持一致即可,否则会去远程下载。

相关链接

pnpm monorepo 中管理依赖的最佳实践