通过文章你能了解到monorepo项目的工作目录和hoisting,以及软链接内容。文章很简单,也是学习的别人的内容来的。我也懒得总结,最后用了AI的整体总结,看看就行。
项目搭建
项目目录结构:
- 核心功能包:packages
- 自测组件的工程:examples
- 项目文档:docs
xxx/
├── packages/ # 核心包目录
│ ├── components/ # UI组件库
│ ├── theme-chalk/ # 主题样式
│ └── utils/ # 工具函数
├── examples/ # 示例应用
├── docs/ # 文档站点
├── package.json # 根配置
├── pnpm-workspace.yaml # workspace配置
└── pnpm-lock.yaml # 依赖锁定
依赖管理
根目录中将内部包声明,避免从外部下载安装包,根node_modules中会生成内部包的软链接。根目录下将内部所有用到的公共库进行安装,避免在各个包中重复安装,hoisting。
{
"dependencies": {
"@xxx/components": "workspace:^", // 链接到 packages/components
"@xxx/examples": "workspace:^", // 链接到 examples
"@xxx/theme-chalk": "workspace:^", // 链接到 packages/theme-chalk
"@xxx/utils": "workspace:^", // 链接到 packages/utils
"@highlightjs/vue-plugin": "^2.1.0", // 外部依赖
"highlight.js": "^11.11.1" // 外部依赖
}
}
工作空间
workspace 就是“让 monorepo 里所有子包互相引用时,永远不用先 publish、也不用自己手动建软链”——pnpm/yarn/npm 帮你一次性把链路全部摆好。
packages:
- packages/** # 包含所有packages下的子包
- examples # 包含examples目录
它具体干的三件事
- 把“本地包”变成“虚拟包” 你在
packages/a/package.json里写"name": "@foo/a"只要pnpm-workspace.yaml里把packages/*纳入 workspace, 整个仓库就瞬间多出一个“虚拟包”@foo/a,版本号就是workspace:*。 不需要发到 npm,就能被别的子包当正式包一样依赖。 - 自动建软链 / 硬链,保证“实时同步” pnpm 安装时会把
node_modules/@foo/a -> packages/a链过去; 你一边改packages/a源码,另一边引用它的子包(或根 docs)立刻看到变化,不用重新npm link。 - 统一依赖树 + 一键批量操作
- 整个仓库只保留一份
pnpm-lock.yaml,所有子包共用,不会重复安装相同依赖。 - 一条命令就能批量跑脚本:
pnpm -r run build把每个子包的build脚本按拓扑顺序全跑完。 - 一条命令就能批量发包:
pnpm -r publish自动计算版本、打 tag、推 npm。
node_modules下软链接包名怎么来的?
软链接名称是和工作空间中子工程中package.json中的name一致。
pnpm 的安装逻辑(简化版)
- 扫描
pnpm-workspace.yaml里列出的目录,把每个目录下的package.json当成“本地包”注册表。 - 读取根
package.json(或任何子包)里声明的依赖:"@xxx/components": "workspace:*" - 发现依赖名
@xxx/components能在“本地包注册表”里匹配到,于是
- 不在远程 npm 拉包;
- 直接在根
node_modules下建一个 符号链接(软链)目录,名字就是@xxx; - 再把
@xxx/components指向仓库里的packages/components。
所以 node_modules/@xxx 这个目录名 = package.json 里的 scope (@xxx) node_modules/@xxx/components 这个目录名 = package.json 里的包名 (@xxx/components) 你看到的内容跟 packages/components 完全一致,是因为那只是个软链,磁盘上只有一份实体代码
AI总结
以下是AI总的项目内容
Monorepo 架构设计文档
📋 项目概述
本项目采用 pnpm workspace 构建的 Monorepo 架构,用于管理一个 Vue 组件库及其相关工具。通过统一的依赖管理和软链接机制,实现了高效的包间协作和开发体验。
🏗️ 项目结构
xxx/
├── packages/ # 核心包目录
│ ├── components/ # UI组件库
│ │ ├── src/
│ │ │ └── button.vue # 按钮组件
│ │ ├── index.js # 组件导出入口
│ │ └── components.js # 组件注册
│ ├── theme-chalk/ # 主题样式包
│ │ ├── index.less # 样式入口文件
│ │ └── package.json
│ ├── utils/ # 工具函数包
│ │ └── package.json
│ └── components.js # 组件统一导出
├── examples/ # 示例应用
│ ├── app.vue
│ ├── index.html
│ ├── index.js
│ └── package.json
├── docs/ # 文档站点
│ ├── .vitepress/
│ │ ├── config.mjs # VitePress 配置
│ │ └── theme/
│ │ └── index.js # 主题配置
│ ├── components/ # 文档组件
│ └── package.json
├── package.json # 根配置文件
├── pnpm-workspace.yaml # Workspace 配置
└── pnpm-lock.yaml # 依赖锁定文件
⚙️ Workspace 配置
pnpm-workspace.yaml
packages:
- packages/** # 包含所有 packages 下的子包
- examples # 包含 examples 目录
注意: docs 目录未包含在 workspace 中,但通过特殊的模块解析机制仍可使用依赖。
🔗 依赖关系与链接机制
1. 根目录依赖管理
根目录的 package.json 作为依赖管理中心:
{
"dependencies": {
"@xxx/components": "workspace:^", // 链接到 packages/components
"@xxx/examples": "workspace:^", // 链接到 examples
"@xxx/theme-chalk": "workspace:^", // 链接到 packages/theme-chalk
"@xxx/utils": "workspace:^", // 链接到 packages/utils
"@highlightjs/vue-plugin": "^2.1.0", // 外部依赖
"highlight.js": "^11.11.1" // 外部依赖
},
"devDependencies": {
"@vitejs/plugin-vue": "^6.0.1",
"less": "^4.4.1",
"less-loader": "^12.3.0",
"vite": "^7.1.6",
"vitepress": "^1.6.4"
}
}
2. 软链接实现
pnpm 自动在根目录的 node_modules/@xxx/ 下创建软链接:
node_modules/@xxx/
├── components -> ../../packages/components
├── examples -> ../../examples
├── theme-chalk -> ../../packages/theme-chalk
└── utils -> ../../packages/utils
链接类型: 符号链接(软链接)
- 使用
lrwxr-xr-x权限 - 指向相对路径
../../packages/xxx - 实现本地包的直接引用
3. workspace 协议详解
"@xxx/components": "workspace:^"
协议作用:
workspace:标识这是内部包^表示兼容版本范围- 自动解析到本地对应目录
- 避免从 npm 下载
📦 各包功能说明
@xxx/components (UI组件库)
- 位置:
packages/components/ - 功能: 提供可复用的 Vue 组件
- 导出方式: 通过
index.js统一导出所有组件 - 组件注册: 支持 Vue 插件式安装
// packages/components/index.js
import * as components from './components';
export default {
install(app) {
Object.entries(components).forEach(([key, value]) => {
app.component(key, value);
});
},
};
@xxx/theme-chalk (主题样式)
- 位置:
packages/theme-chalk/ - 功能: 提供组件的样式文件
- 文件:
index.less样式入口 - 使用方式: 在应用中直接导入样式文件
@xxx/utils (工具函数)
- 位置:
packages/utils/ - 功能:提供通用工具函数
- 状态:目前为空包,预留扩展
@xxx/examples (示例应用)
- 位置:
examples/ - 功能:展示组件使用示例
- 配置:ES模块类型
- 运行:通过 Vite 开发服务器
🔧 依赖使用场景
1. 在文档中使用
// docs/.vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme';
import TestUI from '@xxx/components'; // 使用组件库
import '@xxx/theme-chalk/index.less'; // 使用样式
import hljsVuePlugin from '@highlightjs/vue-plugin';
export default {
...DefaultTheme,
enhanceApp: async ({ app, router, siteData }) => {
app.use(TestUI); // 注册组件库
app.use(hljsVuePlugin);
},
};
2. 在示例中使用
// examples/ 中可以直接使用
import { TButton } from '@xxx/components';
⚡ 模块解析机制
1. Node.js 模块解析路径
- 当前目录的
node_modules - 父目录的
node_modules - 根目录的
node_modules← 关键
2. VitePress 特殊处理
- 命令执行:
pnpm docs:dev在根目录执行 - 工作目录: VitePress 进程的工作目录是根目录
- 依赖访问: 能够访问根目录的所有依赖
3. 为什么 docs 可以使用依赖
虽然 docs 不在 workspace 配置中,但:
- VitePress 命令在根目录执行
- 模块解析从根目录开始
- 能够找到根目录
node_modules中的依赖
🚀 开发与构建
开发环境
# 安装依赖
pnpm install
# 启动文档站点
pnpm docs:dev
# 启动示例应用
pnpm dev
# 预览文档
pnpm docs:preview
构建流程
# 构建文档
pnpm docs:build
依赖管理
# 查看依赖树
pnpm list --depth=0
# 添加依赖到根目录
pnpm add <package-name>
# 添加依赖到特定包
pnpm add <package-name> --filter @xxx/components