Skip to content

从零开始使用monorepo搭建组件库

Monorepo

第一步:创建npmjs组织

istrcn

Monorepo 和 Multirepo

单一仓库(Monorepo)架构,可以理解为:利用单一仓库来管理多个packages的一种策略或手段;与其相对的是多仓库(Multirepo)架构

  • Monorepo 目录中除了会有公共的package.json依赖以外,在每个sub-package子包下面,也会有其特有的package.json依赖。

兄弟模块之间可以通过模块 package.json 定义的 name 相互引用,保证模块之间的独立性

  • Multirepo 更倾向与在项目制中,将一个个项目使用不同的仓库进行隔离,每一个项目下使用独有的package.json来管理依赖

Monorepo 项目搭建

安装包管理工具

初始化项目

  1. 创建一个新的项目目录 istr-node-package,根目录运行 pnpm init 创建 package.json 文件

  2. 然后根目录新建一个文件夹 packages,用于存储子包

    2.1. 新建 packages/istr-shared( 共享包 ),用于存放多个项目或组件之间共享的代码 。运行 pnpm init 创建 package.json 文件,修改 package.json 的 name 为 "@istr/shared";修改 package.json 的 main 入口文件路径字段为`"src/index.js"

    2.2. 新建 packages/istr-ui( 公共组件包 ),即UI组件库。运行 pnpm init 创建 package.json 文件,修改 package.json 的 name 为 "@istr/ui";修改 package.json 的 main 入口文件路径字段为`"src/index.js"

  3. 配置workspace

    根目录新建一个 pnpm-workspace.yaml,将 packages 下所有的目录都作为包进行管理

    yaml
    packages:
      # all packages in direct subdirs of packages/
      - 'packages/*'

pnpm-monorepo 最终项目结构

pnpm-monorepo/
├── packages/
│   ├── libc-shared/
│   ├── libc-ui/
│   ├── vue-dome1/
│   └── vue-dome2/
├── package.json
└── pnpm-workspace.yaml

子包共享

此时,pnpm-workspace.yaml工作空间下的每个子包都可以共享我们的公共依赖了。还有个问题是,兄弟模块之间如何共享呢? 之前我们说过,子包之间可以通过 package.json 定义的 name 相互引用,一起看下两个实际场景

  1. 如何把子包 libc-shared 共享出去?

--workspace参数去安装共享子包,会去 workspace工作空间中找依赖项并安装

sql
sql

 体验AI代码助手
 代码解读
复制代码pnpm install @libc/shared --workspace -w

package.json 中就会自动添加如下依赖,"workspace:" 只会解析本地 workspace 包含的 package

less
x"dependencies": {
   "@libc/shared": "workspace:^"
 }

此时,vue 项目就可以使用公共包 libc-shared 里的方法,import 引入即可

javascript
import { isObject } from '@libc/shared'
  1. 如何把子包 libc-ui 共享出去?

重复一下上面的步骤,然后我们去引用一个 button组件,发现报错了Failed to resolve import "./base" from "../libc-ui/src/components/typography/title.vue". Does the file exist? vite.config.js 中添加 extensions 即可解决,配置一下省略的扩展名列表

css
resolve: {
  extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
}

虽然 button 组件引用成功了,但是发现没有任何样式效果。在libc-ui/src/index.js 文件中导入一下样式文件就行了

js
import "./styles/index.less";

公共依赖

全局安装公共依赖 lodash。需要加-w(在工作空间的根目录中启动 pnpm)

bash
$ pnpm install lodash -w
$ pnpm add rimraf -D -w

这样,vue-dom1vue-dom2 这两个 vue项目就都可以使用 lodash 库了

局部依赖

如果只有 vue-dom1 项目用到了 lodash,我们也可以安装到vue-dom1 项目内部,不作为公共依赖项,有两种方法可以实现

  1. cd 到 src/packages/vue-dom1目录下,直接安装
bash
$ pnpm install lodash
  1. 在任意目录下,使用 --filter 参数进行安装
bash
$ pnpm install lodash --filter vue-demo1

shared包

typescript封装js帮助类

bash
$ pnpm install vite -w
$ pnpm install vite-plugin-dts -D -w

修改 package.json

调整 package.json 以支持组件库发布:

js
{
	"name": "xxh-v3-tempcom",
	 // 是否为私人组件,这里需要修改为 fase 
	"private": false,
	 // 版本,每次提交之前都需要修改,否则提交失败
	"version": "1.0.1",
	 // 类型必须为 module
	"type": "module",
   // 该组件默认指向路径
	"main": "dist/xxh-v3-tempcom.js",
   // 当以类似 import 方式引入时的默认目标, 如: import xxx from "xxh-v3-tempcom"
	"module": "dist/xxh-v3-tempcom.umd.cjs",
   // 类型定义文件,这个必须要写,要不然也无法自动提示
	"types": "dist/index.d.ts",
   // 当运行 npm publish 时,需要提交到 npm 的文件 
	"files": [
		"package.json",
		"README.md",
		"dist"
	],
	"author": "xxh-h",
	"license": "MIT",
	"description": "用于测试打包提示的项目",
	  // 用于在 npm 中搜索的关键字
	"keywords": [
		"vite",
		"vue3",
		"typescript",
		"demo"
	],
	"scripts": {
		"dev": "vite",
		"build": "vue-tsc -b && vite build",
		"preview": "vite preview"
	},
	"dependencies": {
		"vue": "^3.5.13"
	},
	"devDependencies": {
		"@vitejs/plugin-vue": "^5.2.1",
		"@vue/tsconfig": "^0.7.0",
		"typescript": "~5.7.2",
		"vite": "^6.1.0",
		"vite-plugin-dts": "^4.5.0",
		"vue-tsc": "^2.2.0"
	}
}
json
{
  "main": "./dist/index.cjs",       // CommonJS 格式的入口文件
  "module": "./dist/index.esm.js", // ES Modules 格式的入口文件
  "exports": {
    "import": "./dist/index.esm.js",  // 用于现代工具链(如 Vite)
    "require": "./dist/index.cjs"     // 用于 CommonJS 环境
  },
  "types": "./dist/index.d.ts"      // TypeScript 类型定义文件
}

https://cn.vitejs.dev/guide/build.html#library-mode

发布

pnpm login --registry https://registry.npmjs.org
pnpm publish --registry https://registry.npmjs.org
pnpm -r publish --access public --no-git-checks
  • -r--recursive:对所有子包执行 publish

  • --access public

    • 如果发布的包是公开的(默认是私有包),需要显式指定为 public
    • 私有作用域包(如 @scope/package)默认是私有的,必须设置 --access public 才能发布为公开包。

高级

统一依赖版本

使用pnpm catelogs,文档: https://www.pnpm.cn/catalogs

丰富项目

  • .npmrc
  • .editorconfig

将父级目录定义成 "private": true,避免被publish发布

遇到的问题

发布失败

npm notice Publishing to https://registry.npmjs.org/ with tag latest and public access
npm error code E404
npm error 404 Not Found - PUT https://registry.npmjs.org/@istrcn%2fshared - Not found
npm error 404
npm error 404  '@istr/shared@1.0.0' is not in this registry.
npm error 404
npm error 404 Note that you can also install from a
npm error 404 tarball, folder, http url, or git url.

原因: 没有在npmjs中注册私有scope

https://juejin.cn/post/7415776780076105779

https://ksh7.com/posts/pnpm-use-workspace/index.html#项目结构