轻松掌握基于pnpm构建单体仓库monorepo模式
文/晨风不可依米 (2025.07。02)
重要概念
简单理解
- 单体仓库通过工作区配置文件(
pnpm-workspace.yaml)的字段packages记录所有模块包,又通过hoist、hoistWorkspacePackages、hoistPattern等字段进行符号连接绑定,解决字段packages所定义模块包的寻址问题。 - 单体仓库相当于中枢系统,集中管理所有模块包的共同资源,并共享给所有模块包。也就是说,模块包不需要安装与单体仓库相同的依赖包,可在模块包直接使用。
- 模块包相当于独立库,继承了单体仓库所共享出来的资源内容,并完成实现特定需求的功能(
工具库、组件库、业务逻辑库、基于特定平台开发的功能库)。模块包一般都是功能明确、可独立发行的模块库(方便维护、细粒控制)。
架构优点
- 所有相关的工程项目形成子包管理,独立打包发行;可实现跨仓使用。
- 技术栈高度统一,工程模块功能分类明确,减少重复代码实现。
- 统一遵守一套代码质量(
eslint、prettier)规范标准。 - 工程模块的依赖、版本、构建等统一管理。
- 采用自动化脚本统一部署流程。
工程模块的划分
- 基建模块:针对普遍性的工具函数构建模块(
请求、文件、数据转换与加密)。 - 组件模块:自研框剪或基于特定框架(
vue、react)所开发的组件库。 - 业务模块:针对特定业务流程构建的功能模块(
授权认证、支付流程)。 - 环境模块:针对特定环境所开发的功能模块(
ios、android、browser)。
具体搭建流程(案例:搭建具有 monorepo 架构的组件库 ymui)
技术栈选型
- 构建工具:
vite. - 框剪选择:
vue@3.5.13。 - 代码质量规范:
eslint、prettier。 pnpm常用命令:- 单体仓库中安装依赖:
pnpm install -w [-S | -D] <repository>。 - 单词仓库指定模块包安装依赖:
pnpm --filter <package> add [-S -D] <repository | workspace>。 - 单词仓库指定模块包运行脚本:
pnpm --filter <package> run <command>。
- 单体仓库中安装依赖:
构建流程
初始化单体仓库(ymui)
使用终端命令在合适的位置创建单体仓库(ymui),并初始化包管理配置(package.json)和工作区配置(pnpm-workspace.yaml)等文件。
## 创建并进入创建单体仓库(`ymui`)根目录
mkdir ymui && cd ymui
## 初始化包管理配置
pnpm init
## 初始化工作区配置
touch pnpm-workspace.yaml
## 初始化组件库示例
cd demo
pnpm create vite (选择 vue、typescript;查看安装依赖,并交给单体仓库去安装)
cd ..
## 由于 ymui 基于 vue@3.5.13 框架开发 + 样式处理 Sass,因此需要安装必要依赖,以及相应的开发依赖。
## 生产依赖
pnpm install -wS vue@3.5.13
## 开发依赖
pnpm install -wD sass@^1.89.2
## 开发依赖(依赖版本与初始化组件库示例的依赖保持一致;此处我就不带版本号了)
pnpm install -wD @vitejs/plugin-vue @vue/tsconfig typescript vue-tsc vite
模块划分
定义工作区配置(pnpm-workspace.yaml);即声明所有工程模块的路径(可指定、正则模糊匹配)。
packages:
- docs ## 组件库文档服务
- demo ## 组件库示例服务
- packages/* ## 组件库的各个工程模块
规范模块包结构目录
为单体仓库(ymui)下,创建所有工程模块并初始化包配置文件,目录结构的最终效果如下:
ymui(单体仓库)
├── package.json(单体仓库的包配置文件)
├── pnpm-workspace.yaml(工作区配置文件)
│
├── tsconfig.json(单体仓库的配置,会引用 tsconfig.node.json、 tsconfig.app.json 等配置文件)
├── tsconfig.node.json(单体仓库的包配置文件)
├── tsconfig.app.json(单体仓库的包配置文件)
│
├──.gitignore(远程仓库推送的忽略项规则配置)
├── LICENSE(版权许可证类型,自行查找下载)
├── README.md(组件库主页介绍,自行创建)
│
├── docs(组件库文档服务;通过 vitepress 初始化项目;好像是这个)
├── demo(组件库示例服务;进入该目录,通过 pnpm create vite,选择 vue、typescript 即可)
│
├── packages(所有独立开发的工程模块的腹肌目录)
│ ├── global(组件库整体模块包)
│ ├── dist(打包自动生成构建产物)
│ ├── src(源码实现目录)
│ ├── index.ts(功能模块的入口文件,通过依赖项导出其它工程模块)
│ ├── package.json(global 工程模块的包配置文件)
│ └── vite.config.ts(global 工程模块的构建配置文件)
│
│ ├── theme(组件库主题模块包)
│ ├── dist(打包自动生成构建产物)
│ ├── src(源码实现目录)
│ ├── modules(功能模块的具体实现)
│ ├── datatype.ts(数据类型工具函数)
│ ├── index.ts(功能模块的入口文件,会引用目录 modules 里的模块并导出所有实现内容)
│ ├── package.json(theme 工程模块的包配置文件)
│ └── vite.config.ts(theme 工程模块的构建配置文件)
│
│ ├── shared(组件库工具函数包)
│ ├── dist(打包自动生成构建产物)
│ ├── src(源码实现目录)
│ ├── modules(功能模块的具体实现)
│ ├── dayjs.ts(第三方依赖包)
│ ├── datatype.ts(数据类型工具函数)
│ ├── index.ts(功能模块的入口文件,会引用目录 modules 里的模块并导出所有实现内容)
│ ├── package.json(shared 工程模块的包配置文件)
│ └── vite.config.ts(shared 工程模块的构建配置文件)
│
│ ├── button(组件库按钮组件包)
│ ├── dist(打包自动生成构建产物)
│ ├── src(源码实现目录)
│ ├── modules(功能模块的具体实现)
│ ├── index.vue(默认基础组件)
│ ├── submit.vue(提交类型组件;可能封装多种类型 button 组件,此处举了个栗子)
│ ├── index.ts(功能模块的入口文件,会引用目录 modules 里的模块并导出所有实现内容)
│ ├── package.json(button 工程模块的包配置文件)
│ └── vite.config.ts(button 工程模块的构建配置文件)
│
│ ├── 可自行封装更多的组件模块包
│
│──develop.md(开发笔记)
- 单体仓库(
ymui)的包配置文件(package.json)内容,如下:
{
"name": "ymui",
"version": "0.0.1",
"description": "Building Vue3 Component Library Based on Monorepo Architecture Pattern",
"author": "chenfengbukeyimi",
"license": "ISC",
"packageManager": "pnpm@10.4.1",
"keywords": [
"ui",
"vue",
"library",
"monorepo"
],
"scripts": {
"test": "echo welcome to use ymui"
},
"dependencies": {
"vue": "^3.5.13"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.2.3",
"@vue/tsconfig": "^0.7.0",
"typescript": "~5.8.3",
"vite": "^6.3.5",
"vue-tsc": "^2.2.8"
},
"main": ""
}
- 实现组件库工具函数包(
shared)
特别提醒:开辟新的终端命令面板,并切换到ymui/packages/shared目录;组件库工具函数包(shared)的相关命令操作,都在该终端面板操作。
安装第三方依赖包(dayjs)
pnpm add -S dayjs
对第三方依赖包的处理,如第三方依赖包(dayjs),详见文件ymui/packages/shared/src/modules/dayjs.ts内容,如下:
import dayjs from 'dayjs'
// 通过函数原封不动的将第三方依赖包内容返回;该函数约定以 use 开头来拼接第三方依赖包名。
function useDayJs () {
return dayjs
}
对自定义封装工具函数的处理,如工具函数(datatype),详见文件ymui/packages/shared/src/modules/datatype.ts内容,如下:
// 类型判断
export function getDataType(value: any): string {
return Object.prototype.toString.call(value).slice(8, -1);
}
export function isString(value: any): boolean {
return getDataType(value) === "String";
}
// 其它更多类型判断函数
组件库工具函数包(shared)的入口文件,导出所有工具函数,详见文件ymui/packages/shared/src/index.ts内容,如下:
export * from "./modules/dayjs";
export * from "./modules/datatype";
// 导出其它更多自定义的工具函数
定义组件库工具函数包(shared)的构建配置文件,详见文件ymui/packages/shared/vite.config.ts内容,如下:
import { defineConfig } from "vite";
export default defineConfig({
build: {
minify: true,
lib: {
// 模块包的入口文件
entry: "./src/index.ts",
// 根据 fileName 转换为首字母大写驼峰法。
name: "YmuiShared",
// 默认生成 <fileName>.[mjs | umd].js 文件;可指定 formats 配置,本案例不用。
fileName: "ymui-shared",
// formats: ["es", "cjs", "umd"]
},
rollupOptions: {
// 忽略打包外部依赖包,减少构建产物的体积
external: ["dayjs"],
output: {
globals: {
// 在特定环境下,可使用全局变量引用指定模块包;以 key 为模块包名,value 为全局变量名进行定义。
dayjs: "dayjs",
},
},
},
},
});
定义组件库工具函数包(shared)的包管理配置文件,详见文件ymui/packages/shared/package.json内容,如下:
{
// 字段 name 值,必须以 @<单体仓库名>/<模块包名> 命名。
"name": "@ymui/shared",
"version": "0.0.1",
"description": "YmuiShared Tools",
"author": "chenfengbukeyimi <chenfengbukeyimi@qq.com>",
"license": "ISC",
"packageManager": "pnpm@10.4.1",
"keywords": [
"vue",
"tool",
"library"
],
// 模块包的脚本命令集
"scripts": {
// (基于 vite.config.ts 文件配置)打包构建产物命令
"build": "vite build"
},
"dependencies": {
"dayjs": "^1.11.13"
},
// 打包构建产物(模块包),指定默认引用入口配置(类型声明文件、CommonJs系统模块入口、ESModule系统模块入口)
"types": "./dist/ymui-shared.d.ts",
"main": "./dist/ymui-shared.umd.js",
"module": "./dist/ymui-shared.mjs",
"exports": {
".": {
"types": "./dist/ymui-shared.d.ts",
"import": "./dist/ymui-shared.mjs",
"require": "./dist/ymui-shared.umd.js"
}
},
// 是否运行发行模块包到 npmjs 官网
"private": false,
// 发行模块包到 npmjs 官网的资源内容
"files": [
"dist",
"README.md"
],
// 发行模块包命令的相关配置
"publishConfig": {
"access": "public"
}
}
打包构建组件库工具函数包(shared)
pnpm build
## 后续将统一在单体仓库下管理所有模块的相关脚本命令
- 实现组件库模块包(
button)
特别提醒:开辟新的终端命令面板,并切换到ymui/packages/button目录;组件库模块包(button)的相关命令操作,都在该终端面板操作。
为组件库模块包(button)安装相关依赖配置,命令执行如下:
## 由于组件库模块包(`button`)是基于`vue@^3.5.13`版本开发的,因此需要安装对等依赖;详见包管理配置 package.json 字段对象 peerDependencies。(一定保证安装到这个位置,否则增大打包体积
pnpm add --save-peer vue@^3.5.13
## 由于组件库模块包(`button`)经常会使用到单体仓库(ymui)里的工具函数包(shared),因此需要安装本地模块包。
### 本地模块包安装方式一(不推荐: link:../shared 直接引用本地工作区中解析模块的版本;本地模块包变动更改,影响输出模块产物的功能)
pnpm add ../packages/shared
### 本地模块包安装方式二(推荐:workspace:^ 解析本地工作区中解析模块的最新版本;仅使用稳定的最新版本)
操作实现:手动复制("@<单体仓库名>/<模块包名>": "workspace:^")括号里的内容到包管理配置 package.json 字段对象 peerDependencies 里;然后执行以下命令:
pnpm install
定义组件库模块包(button)具体实现逻辑,详见文件ymui/packages/button/src/modules/index.vue内容,如下:
<template>
<section>Button</section>
</template>
<script setup lang="ts">
import { useDayJs, isString } from "@ymui/shared";
const DayJs = useDayJs();
// 此处可测试打包是否会 tree-shaking 成功(案例:仅使用工具函数模块包一个或两个函数分别打包测试)。
console.log(DayJs.now());
console.log(isString("123"));
interface ButtonProps {}
const props = withDefaults(defineProps<ButtonProps>(), {
color: "primary", // 后续将通过主题包的字段配置值
size: "md", // 后续将通过主题包的字段配置值
});
</script>
<style scoped lang="scss"></style>
组件库模块包(button)的入口文件,导出所有封装的组件模块,详见文件ymui/packages/button/src/index.ts内容,如下:
import Button from "./modules/index.vue";
// import Submit from "./modules/submit.vue"; 举了个栗子
export {
// Submit,
Button as default
};
定义组件库模块包(button)的构建配置文件,详见文件ymui/packages/button/vite.config.ts内容,如下:
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
export default defineConfig({
plugins: [vue()],
build: {
// 遵循本案上述规范
lib: {
entry: "./src/index.ts",
name: "YmuiButton",
fileName: "ymui-button",
},
rollupOptions: {
// 使用正则模式忽略本地模块包,以减少打包体积;若单独使用,则会提示用户自行安装相关依赖。
external: [/@ymui.*/],
output: {
globals: {
// 在特定环境下,可使用全局变量引用指定模块包;以 key 为模块包名,value 为全局变量名进行定义。
"@ymui/shared": "YmuiShared",
},
},
},
},
});
定义组件库模块包(button)的包管理配置文件,详见文件ymui/packages/button/package.json内容,如下:
{
"name": "@ymui/button",
"version": "0.0.1",
"description": "Ymui Button Component",
"author": "chenfengbukeyimi <chenfengbukeyimi@qq.com>",
"license": "ISC",
"packageManager": "pnpm@10.4.1",
"keywords": [
"ui",
"vue",
"library"
],
"scripts": {
"build": "vite build"
},
"dependencies": {
"@ymui/shared": "workspace:^"
},
"devDependencies": {},
"peerDependencies": {
"vue": "^3.5.13"
},
"types": "dist/index.d.ts",
"main": "dist/ymui-button.umd.js",
"module": "dist/ymui-button.mjs",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/ymui-button.mjs",
"require": "./dist/ymui-button.umd.js"
}
},
"private": false,
"files": [
"dist",
"README.md"
],
"publishConfig": {
"access": "public"
}
}
打包构建组件库模块包(button)
pnpm build
## 后续将统一在单体仓库下管理所有模块的相关脚本命令
- 实现组件库整体包(
global)
特别提醒:开辟新的终端命令面板,并切换到ymui/packages/global目录;组件库整体包(global)的相关命令操作,都在该终端面板操作。
为组件库整体包(global)安装相关依赖配置,命令执行如下:
## 由于组件库整体包(global)是基于 vue@^3.5.13 版本开发的,因此需要安装对等依赖;
pnpm add --save-peer vue@^3.5.13
## 由于组件库整体包(`global`)是应用于全局导入的使用场景,具有所有本地模块包的功能;
采用手动配置安装(ymui/packages 目录下)本地所有模块包,具体详见包管理配置 package.json 内容字段 dependencies 的对象内容。
定义组件库整体包(global)的包管理配置文件,详见文件ymui/packages/global/package.json内容,如下:
{
"name": "@ymui/global",
"version": "0.0.1",
"description": "Ymui Library",
"author": "chenfengbukeyimi <chenfengbukeyimi@qq.com>",
"license": "ISC",
"packageManager": "pnpm@10.4.1",
"keywords": [
"ui",
"vue",
"library"
],
"scripts": {
"build": "vite build"
},
"dependencies": {
"@ymui/shared": "workspace:^",
"@ymui/button": "workspace:^",
// 其它本地模块包(如主题包、语言包)
},
"devDependencies": {
// 使用 pnpm add --save-peer vue@^3.5.13 可能会自动在这里生成,不影响
"vue": "^3.5.13"
},
"peerDependencies": {
// 作为外部依赖,减少打包构建体积,同时以用户使用版本为基准适配
"vue": "^3.5.13"
},
"types": "dist/index.d.ts",
"main": "dist/index.cjs.js",
"module": "dist/index.esm.js",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.esm.js",
"require": "./dist/index.cjs.js"
}
},
"private": false,
"files": [
"dist",
"README.md"
],
"publishConfig": {
"access": "public"
}
}
组件库整体包(global)的入口文件,导出所有工具函数,详见文件ymui/packages/global/src/index.ts内容,如下:
export * from "@ymui/shared";
export * from "@ymui/button";
// 其它本地模块包(如主题包、语言包)
组件库整体包(global)的构建配置文件,详见文件ymui/packages/global/vite.config.ts内容,如下:
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
export default defineConfig({
plugins: [vue()],
build: {
lib: {
entry: "./src/index.ts",
name: "YmuiGlobal",
fileName: "ymui-global",
},
rollupOptions: {
external: ["vue"],
output: {
globals: {
vue: "Vue",
},
},
},
},
});
打包构建组件库整体包(global)
pnpm build
## 后续将统一在单体仓库下管理所有模块的相关脚本命令
- 实现组件库示例服务(
demo)
特别提醒:开辟新的终端命令面板,并切换到ymui/demo目录;组件库示例服务(demo)的相关命令操作,都在该终端面板操作。
初始化组件库示例服务(demo),执行命令,如下:
pnpm create vite .
## 特别提醒:终端面板里选择 Vue Typescript
## 安装项目依赖;因为要测试 组件库功能,需安装 @ymui/global 进行全局导入示例项目;由于是本地服务模块,采用手动配置 @ymui/global 模块包依赖(配置内容往下看)。
组件库示例服务(demo)的包管理配置文件,内容如下:
{
"name": "@ymui/demo",
"dependencies": {
... 省略内容
"@ymui/global": "workspace:^"
},
// 其它配置已隐藏查看
}
安装包管理配置里的相关依赖,命令执行,如下:
pnpm install
编写测试案例,详见ymui/demo/src/App.vue文件,(测试结果眼见为实),内容如下:
<template>
<section>App</section>
<Button />
</template>
<script setup lang="ts">
// 引用组件库整体包(global)里的工具函数、button 组件
import { getDataType, Button } from "@ymui/global";
console.log(getDataType(new Date()));
interface AppProps {}
const props = defineProps<AppProps>();
</script>
<style scoped lang="scss"></style>
- 统一规范化
tsconfig.json编译配置
特别提醒:请在单体仓库(ymui)根目录下操作配置。
定义通用基础场景编译配置(tsconfig.base.json)
{
"compilerOptions": {
// 基本配置
"rootDir": ".",
"baseUrl": ".",
"outDir": "dist",
"lib": [],
"types": [],
"paths": {
// 给(packages目录下的)组件模块包路径定义别名
"@ymui/*": ["packages/*/src"]
},
// 编译配置
"strict": true,
"target": "es2022",
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
// ES 功能支持库(DOM、Promise、ESNext)
// cjs 与 es 转换配置
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
// 产物配置
"sourceMap": true,
"skipLibCheck": true,
"noUnusedLocals": true,
"removeComments": true,
"isolatedModules": true
}
}
定义脚本命令场景编译配置(tsconfig.node.json)
{
// 继承 通用基础编译配置
"include": [
// 匹配到所有模块的 vite.config.ts 文件配置
"**/*.config.*"
// 用于单体仓库的脚本文件
"scripts"
],
"exclude": [
// 排除第三方依赖,构建产物
"**/dist",
"**/node_modules"
],
"compilerOptions": {
"allowJs": true,
// 允许被 references 引用
"composite": true,
// 命令脚本需要支持的功能库模块
"lib": ["ESNext"],
"types": ["node"]
}
}
定义模块包通用场景编译配置(tsconfig.module.json)
{
// 继承通用基础编译配置
"extends": "./tsconfig.base.json",
// 源码实现目录,类型申明文件
"include": ["packages/**/src"],
"compilerOptions": {
// 允许被 references 引用
"composite": true,
// 源码实现需要支持的功能库模块
"types": ["node"],
"lib": ["ESNext", "DOM", "DOM.Iterable"]
}
}
聚合集成单体仓库(ymui)场景编译配置(tsconfig.json)
{
"compilerOptions": {
"target": "es2022",
"moduleResolution": "node",
"isolatedModules": true,
"useDefineForClassFields": true
},
"files": [],
"references": [
{ "path": "./tsconfig.module.json" },
{
"path": "./tsconfig.node.json"
}
]
}
为方便查看最终编译配置结果:,可执行以下命令:
npx tsc -p <编译配置文件> --showConfig
## 比如查看 脚本命令场景(tsconfig.node.json) 的最终编译配置结果;也可以尝试替换为 模块包通用场景(tsconfig.module.json)查看最终编译配置结果。
npx tsc -p tsconfig.node.json --showConfig
自动生成*.d.ts类型声明文件
在单体仓库(ymui)根目录下执行命令,如下:
## 参数详解
## -p 表示指定编译配置文件;本例只对 模块包 生成 *.d.ts 类型声明文件;以供模块包类型适配。
## --composite 编译配置文件是否为可被引用配置;
## --declaration 生成 *.d.ts 类型声明文件(含 *.js 文件);
## --emitDeclarationOnly 只生成 *.d.ts 类型声明文件。
npx vue-tsc -p tsconfig.module.json --composite false --declaration --emitDeclarationOnly
## 为简化脚本命令执行,只需要把该命令添加到单体仓库(ymui)的包管理配置文件(package.json)的字段(scripts)命令集对象里(dts:generate)。
## 脚本命令配置详见后续最终配置效果(往下看)。
vue-tsc生成的类型声明文件目录结构,详见单体仓库(ymui)根目录下的dist目录(可与模块包目录结构对比),如下图:

需要将生成的类型声明文件迁移到相应模块包的构建产物里。由于tsc不具有清空类型声明文件目录的功能,而且ts不能直接执行脚本命令。因此借用rimraf、tsx等功能库来实现(1. 清空旧的类型声明文件目录;2.构建打包类型声明文件目录和模块包源码产物;3. 类型声明文件目录迁移到相应模块包产物目录里)等流程命令脚本。
## 安装脚本命令所需功能库依赖
pnpm add -wD tsx rimraf
编写命令脚本文件内容。在单体仓库(ymui)根目录下创建目录(scripts),用于存放脚本命令文件,并创建文件(dts-mv.tsx)实现类型声明文件迁移逻辑。
import { join } from "node:path";
import { readdir, cp } from "node:fs/promises";
function fromRoot(...paths: string[]): string {
return join(__dirname, "..", ...paths);
}
// 通过 npx vue-tsc -p tsconfig.module.json --composite false --declaration --emitDeclarationOnly 命令所生成。
// 详见单体仓库根目录下 dist 目录,目录结构与源码库所有模块包的父级目录一致。
const MODULES_DTS_DIR = fromRoot("dist/packages");
// 源码库所有模块包的父级目录()
const SOURCES_DIR = fromRoot("packages");
// 定义单个模块包的源码目录和打包目录
const MODULE_DIST_DIR = "dist";
const MODULE_ENTRY_DIR = "src";
async function match() {
const res = await readdir(MODULES_DTS_DIR, { withFileTypes: true });
return res.filter((item) => item.isDirectory()).map((item) => item.name);
}
// 将目录 MODULES_DTS_DIR 里的module.d.ts 文件复制到对应模块的源码目录下
async function mvModuleDts(moduleName: string) {
try {
// DTS 产物目录位置
const DtsSourceDir = join(MODULES_DTS_DIR, moduleName, MODULE_ENTRY_DIR);
// 模块包产物目录位置
const ModuleResolveDir = join(SOURCES_DIR, moduleName, MODULE_DIST_DIR);
// 读取对应模块包的 DTS 产物目录位置
const dtsFiles = await readdir(DtsSourceDir);
// 复制 DTS 产物到模块包产物目录下
function cpModuleDts() {
return dtsFiles.map((file) => {
const source = join(DtsSourceDir, file);
const target = join(ModuleResolveDir, file);
return cp(source, target, { force: true, recursive: true });
});
}
await Promise.all(cpModuleDts());
console.info(`${moduleName} dts move success.`);
} catch (error) {
console.error(`${moduleName} dts move failed.`);
}
}
// 主函数运行入口
async function main() {
const modules = await match();
const tasks = modules.map(mvModuleDts);
await Promise.all(tasks);
}
// 运行脚本命令主函数
main().catch((e) => {
console.error(e);
process.exit(1);
});
配置单体仓库(ymui)的包管理配置文件(package.json)的脚本命令字段集对象(scripts),内容如下:
// 单体仓库包管理配置文件
{
"name": "ymui",
// 已隐藏部分内容
// 脚本命令字段集对象
"scripts": {
// 清除类型声明文件目录
"dts:clean": "rimraf ./dist",
// 类型声明文件迁移到模块包产物(dist)目录
"dts:move": "tsx ./scripts/dts-mv.ts",
// 构建类型声明文件(构建前清除旧的目录内容)
"dts:generate": "pnpm run dts:clean && vue-tsc -p tsconfig.module.json --composite false --declaration --emitDeclarationOnly",
// 打包构建组件库所有模块(优先构建独立模块包,最后才构建组件库整体包;因为构建组件库整体包,它需要依赖其它独立模块,所以有优先级打包构建顺序)
"build:all": "pnpm --filter '!global' run build && pnpm --filter global run build",
// 打包构建组件库
"build:ymui": "pnpm dts:generate && pnpm build:all && pnpm dts:move",
// 启动测试示例服务(Vite 项目)
"start:demo": "pnpm --filter @ymui/demo run dev"
},
}
- 编辑器
IDE支持配置
在使用VueTs项目中,根据官方推荐IDE支持配置,在单体仓库(ymui)根目录下创建IDE支持配置目录(.vscode),同时在新建目录里分别创建extensions.json、settings.json等配置文件。
特别提醒:TypeScript 版本上使用 Volar 集成。默认情况下,Volar 将用于 TypeScript 5.0 及更高版本。
插件拓展配置文件(extensions.json)内容,如下:
{
"recommendations": ["Vue.volar"]
}
编辑器工作区配置文件(settings.json.json)内容,如下:
{
"typescript.tsdk": "../node_modules/typescript/lib"
}
- 统一代码规范化(
eslint9、prettier、stylelint)
特别提醒:终端命令在单体仓库(ymui)根目录下执行。
安装开发依赖,命令如下:
## 基础依赖(eslint、prettier):eslint @eslint/js typescript-eslint eslint-config-prettier
## 框架依赖(vue):globals eslint-plugin-vue
## 配置依赖(支持 *.ts 文件配置):jiti
pnpm i -wD eslint @eslint/js typescript-eslint globals eslint-plugin-vue eslint-config-prettier jiti
## 同时在单体仓库(ymui)根目录下创建代码检查配置文件 eslint.config.ts
touch eslint.config.ts
定义代码规范化配置文件(eslint.config.ts);内容如下:
// @ts-check
// 参见官方支持配置(https://eslint.vuejs.org/user-guide/#usage)设置
import globals from "globals";
import eslint from "@eslint/js";
import TsESLint from "typescript-eslint";
import ESLintPluginVue from "eslint-plugin-vue";
import ESLintConfigPrettier from "eslint-config-prettier";
// eslint.config.mjs 配置文件内容应省略定义类型声明 ConfigArray。
type ConfigArray = ReturnType<typeof TsESLint.config>;
const YmuiESLintConfig: ConfigArray = TsESLint.config([
{ ignores: ["**/dist", "**/node_modules", "*.d.ts"] },
{
extends: [
eslint.configs.recommended,
...TsESLint.configs.recommended,
...ESLintPluginVue.configs["flat/recommended"],
],
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
globals: globals.browser,
parserOptions: {
parser: TsESLint.parser,
},
},
files: ["**/*.{ts,vue}"],
rules: {
// 组件(驼峰法)命名规则
"vue/multi-word-component-names": "off",
// 组件 props 类型检查
"@typescript-eslint/no-empty-object-type": `off`,
},
},
ESLintConfigPrettier,
]);
export default YmuiESLintConfig;
为什么要用 ts 来写配置文文件(eslint.config.ts)
eslint.config.mjs配置文件在导出(export default <配置内容>)配置内容是会报错(又或者在终端problems提示修复);报错内容如下:
## 特别提示
使用变量 YmuiESLintConfig 存储配置定义,是为了更好定位问题;(若直接使用 export default <配置定义>;你有可能以为缺少某个必填配置选项的定义)
## 报错提示
The inferred type of 'YmuiESLintConfig' cannot be named without a reference to
'.pnpm/@typescript-eslint+utils@8.34.0_eslint@9.28.0_typescript@5.8.3/node_modules/@typescript-eslint/utils/dist/ts-eslint'.
This is likely not portable. A type annotation is necessary.ts(2742)
## 解决问题
### 根据官方解释 ts(2742) 原因,类型声明没有正确导出或路径引用不正确。
### 因此采用手动修复该问题,在定义需要类型提供支持;所以我就用了 ts 来写配置,而它需要安装 jiti 该项依赖。
## 若在使用 ts 来定义配置内容,没有安装 jiti 依赖,则在执行代码检验命令(pnpm eslint .)时将提示报错,如下:
Error: The 'jiti' library is required for loading TypeScript configuration files. Make sure to install it.
## 根据报错提示,可以前往官方链接 https://eslint.org/docs/latest/use/configure/configuration-files#typescript-configuration-files 页面内容解决问题。
测试代码规范化配置,执行代码检查命令,命令如下:
pnpm eslint .
## 特别提示,为简化或统一管理命令集,需在单体仓库(ymui)的包管理配置(package.json)中的命令集对象(scripts)追加该命令定义。可直接复制下方内容。
## 复制内容:"lint:eslint": "eslint ."
根据终端执行pnpm eslint命令提示统一代码规范
- 问题一:模块包(
shared)的datatype文件定义函数参数的类型为any,将any修改为unknow。 - 问题二:模块包(
demo)的App.vue文件定义interface AppProps {}为空对象;由于组件库模块包定义组件会默认定义props的类型,因此我们通过eslint.config.ts配置文件设置对应rules规则为off就行了。
配置并定义prettier规则,详见单体仓库(ymui)根目录下prettier.js配置文件,内容如下:
配置并定义stylelint规则
安装开发依赖,命令执行请在单体仓库(ymui)根目录下执行,命令如下:
## 由于项目采用 sass 样式处理模块,需安装相应依赖(stylelint-config-standard-scss);
## 详细配置可参见官网 https://stylelint.io/user-guide/get-started
pnpm i-wD stylelint stylelint-config-standard-scss
## 同时创建样式检查配置文件 stylelint.config.mjs
touch stylelint.config.mjs
定义样式检查配置文件(stylelint.config.mjs)规则,内容如下:
export default {
extends: ["stylelint-config-standard-scss"],
// 不对构建产物文件进行检查
ignoreFiles: ["**/dist/**/*.css"],
rules: {
// 指定颜色函数的别名符号(rgb[a]、hsl[a])
"color-function-alias-notation": "with-alpha",
// 指定字母值的百分比或数字表示法(数字 0.5, 百分比 50%)。
"alpha-value-notation": ["percentage", { exceptProperties: ["opacity"] }],
// 指定十六进制颜色的短或长表示法(短命名法 #fff,长命名法 #ffffff[ab])
"color-hex-length": "long",
// 颜色函数采用旧式语法(rgba(0, 0, 0, 1));即数值之间需要用逗号隔开
"color-function-notation": "legacy",
// css 样式规则值使用小写命名
"value-keyword-case": [
"lower",
{
ignoreProperties: [
// 控制文本的渲染方式,非标准样式规则(css3 没有该样式属性)
"text-rendering",
],
},
],
},
};
测试代码规范化配置,执行代码检查命令,命令如下:
npx stylelint "**/*.{css,scss}"
## 特别提示,为简化或统一管理命令集,需在单体仓库(ymui)的包管理配置(package.json)中的命令集对象(scripts)追加该命令定义。可直接复制下方内容。
## 复制内容:"lint:styleinit": "stylelint **/*.{css,scss}"
修改编辑器IDE支持配置
插件拓展配置文件(extensions.json)内容,最终内容如下:
{
"recommendations": [
"Vue.volar",
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"stylelint.vscode-stylelint"
]
}
编辑器工作区配置文件(settings.json.json)内容,最终内容如下:
{
// typescript.tsdk 支持
"typescript.tsdk": "../node_modules/typescript/lib",
// 禁用格式化
"editor.formatOnSave": false,
"editor.formatOnPaste": false,
// 不同文件的格式化支持和自动保存配置
"[vue]": {
"editor.defaultFormatter": "Vue.volar"
},
"[json]": {
"editor.formatOnSave": true
},
"[jsonc]": {
"editor.formatOnSave": true
},
"[yaml]": {
"editor.formatOnSave": true
},
// 保存时自动修复 eslint、styleint 错误
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "always",
"source.fixAll.stylelint": "always"
}
}
- 统一仓库日志(
git)提交规范
特别提醒:请在单体仓库(ymui)根目录下操作配置。
安装开发依赖,命令如下:
## 开发依赖
### huksy:处理 git 提交过程所触发的钩子函数 。
### lint-staged:负责代码检查。常用于执行 husky 的 pre-commit 钩子函数。
### @commitlint/{config-conventional,cli} :负责弹出仓库日志提交规范的终端面板。
### cz-customizable:自定义仓库日志提交规范的终端面板提示配置。
## 特别提示:本例版本 husky@^9.1.7、lint-staged@^16.1.0、@commitlint/cli@^19.8.1、@commitlint/config-conventional@^19.8.1;操作命令有部分变化!!!
pnpm i -wD husky lint-staged @commitlint/{config-conventional,cli} cz-customizable
初始化.husky相关配置
## 初始化 .husky 配置目录内容;(执行命令与老版本 npx husky install 不同)
npx husky init
## 注册 pre-commit 钩子脚本文件
echo `pnpm lint-staged` > .husky/pre-commit
## 注册commit-msg 钩子脚本文件
echo 'npx --no -- commitlint --edit \' > .husky/commit-msg
配置仓库日志提交相关配置,详见本体仓库(ymui)的包管理配置文件(package.json)内容,如下:
{
"name": "ymui",
// 隐藏部分内容
// 新增命令集
"scripts": {
"prepare": "husky",
"lint:lint-staged": "lint-staged"
},
// 代码提交检查配置
"lint-staged": {
"*.{ts,js,tsx,jsx,vue}": [
"eslint . --fix",
"prettier --write",
"git add"
],
"*.{css,scss}": [
"stylelint **/*.{css,scss} --fix",
"git add"
]
},
// 仓库日志提交规范的终端面板弹窗配置
"config": {
"commitizen": {
"path": "./node_modules/cz-customizable"
},
"cz-customizable": {
"config": "./.cz-config.js"
}
},
"commitlint": {
"extends": [
"./commitlint.config.mjs"
]
},
}
定义仓库日志提交规范的终端面板弹窗配置,详见commitlint.config.mjs、.cz-config.js等配置文件。
commitlint.config.mjs,内容如下:
export default {
extends: ["@commitlint/config-conventional"],
// 配置规则
rules: {
'subject-case': [0],
'type-enum': [
2, 'always',
// 与配置文件 .cz-config.js 内容的 types 对象顺序保持一致。
[
'fix',
'feat',
'perf',
'test',
'docs',
'style',
'build',
'revert',
'refactor',
'chore',
'cicd',
]
]
}
}
.cz-config.js,内容如下:
"use strict";
// 由于 cz-customizable 依赖不支持 ESModule,故用 CommonJS 语法来写。
module.exports = {
types: [
{ value: "fix", name: "🐛 缺陷修复 (修复 bug)" },
{ value: "feat", name: "✨ 功能新增 (新业务功能)" },
{ value: "perf", name: "⚡️ 性能优化 (产品性能提升)" },
{ value: "test", name: "✅ 测试用例 (功能测试)" },
{ value: "docs", name: "📝 文档修改 (文档更新)" },
{ value: "style", name: "💄 代码规范 (代码风格)" },
{ value: "build", name: "📦️ 打包构建 (依赖更新、构建配置等)" },
{ value: "revert", name: "⏪️ 撤销提交 (撤销远程提交)" },
{ value: "refactor", name: "♻️ 代码重构 (业务功能新方案)" },
{
value: "chore",
name: "🔨 日常事务 (非源码、测试用例改动,且不影响功能逻辑)",
},
{ value: "cicd", name: "🎡 CI/CD (持续集成/持续部署)" },
],
messages: {
type: "请选择提交类型:",
customScope: "请输入本次提交涉及的范围 (可选): ",
subject: "请简要描述提交内容:",
body: "请输入详细描述 (可选): ",
breaking: "列出 BREAKING CHANGES (可选): ",
footer: "列出相关 issues (可选): ",
confirmCommit: "确认使用以上信息提交? (y/n)",
},
subjectLimit: 100,
};
仓库日志提交规范的终端面板弹窗配置用例测试,执行命令:
git status
git add .
## 特别注意:建议用 git cz 命令来替代 git commit -m '' 命令来弹出终端弹窗提示,规范化提交仓库日志内容。
git cz
- 请选择提交类型:📦️ 打包构建 (依赖更新、构建配置等)
- 请简要描述提交内容: 基于 monorepo 搭建 vue3 组件库
- 请输入详细描述 (可选): (建议在文本写好日志内容,复制粘贴即可)
1.构建工具:vite、pnpm。
2.框架选型:vue、typescript。
3.样式规范:sass、unocss。
4.代码规范:eslint、prettier。
5.仓库日志规范:husky、lint-staged、commitlint。
6.脚本命令支持:tsx。
7.其它辅助支持:rimraf。
- 列出相关 issues (可选):(如果你是修复问题,根据 github 里的 issues 编号填写)
- 确认使用以上信息提交? (y/n):(自行决定是否提交啦)
- 统一打包流程
特别注意
- 通过
<script src=*.umd.js>方式无法直接使用,源于构建打包时缺少外部依赖配置。 - 模块包构建配置文件
vite.config.ts内容,存在重复配置内容,因此可选择自动化生成。 - 全量打包构建,需要对
build.rollupOptions.external进行处理,且引用中不采用路径别名,方便打包后而直接使用,以及沙箱测试。
特别提醒:构建流程如下;
-
规范定义;
1-1. 目录规范;构建产物输出在build/dist目录下,且目录结构保持与单体仓库目录结构一致。
1-2. 打包模式;独立打包、全量打包(.full)、全量压缩(.full-min),体现在构建产物的文件名上。
1-3. 产物格式;默认es、umd; 在build.lib.fileName只需返回模块包名[打包模式]即可,文件拓展名会自动填充。
-
流程如下;
2-1. 总纲;获取对应模块包包管理配置文件(package.json),并根据打包模式返回构建配置vite.config.ts所需的字段配置。
2-2. 包配置信息内容转换;根据对应模块包包管理配置文件(package.json)和打包模式返回以下字段,如下:- mode: 打包模式("IndependentModule " | "FullCompression" | "IndependentModule")
- minify: 代码压缩(false)
- sourcemap: 代码地图(false)
- moduleDirName: 模块名称,由小写与分隔符组合('button')
- moduleDirPath: 模块根目录路径,绝对路径('
/ymui/packages/button') - moduleFileName: 模块名称,小写与分隔符组合,需携带前缀
单体仓库名字符.('ymui-button') - moduleVariable: 模块全局变量,首字母大写驼峰命名;提供
umd环境变量引用,('YmuiButton') - moduleEntryPath: 模块程序入口,('
/ymui/packages/button/src/index.ts') - outputDir: 构建产物输出目录,规范输出到当前模块的
dist目录,产物目录结构与单体仓库目录结构一致('/ymui/build/dist/packages/button') - outputFormats: 构建产物格式,([ 'es', 'umd' ])
- outputDtsPath: 构建产物类型声明文件位置,对应包管理配置字段
types,('/ymui/build/dist/packages/button/ymui-button.d.ts') - outputEsmFilePath: 构建产物类型声明文件位置,对应包管理配置字段
module、import,('i/ymui/build/dist/packages/button/ymui-button.mjs') - outputCjsFilePath: 构建产物类型声明文件位置,对应包管理配置字段
main、require,('i/ymui/build/dist/packages/button/ymui-button.umd.js') - dependencies: 来源于对应包管理配置字段
dependencies,打包构建必须必须存在。({ '@ymui/shared': 'workspace:^' }) - peerDependencies: 来源于对应包管理配置字段
peerDependencies,({ vue: '^3.5.13' }) - external: 来源于对应包管理配置字段
peerDependencies的键值,并转换为正则式,([ /^node:.*/ ]),减少构建产物体积。 - defaults: 来源于对应包管理配置信息内容(方便校对,配置构建是否正确)。
-
配置文件模块输出
3-1. 构建打包配置vite.config.ts字段lib配置.
3-2. 构建打包配置vite.config.ts字段rollupOptions配置.
3-3. 构建打包配置vite.config.ts字段plugins配置.根据组件模块或非组件模块追加相应插件(如样式插件、图标插件);并提供预设插件以观察构建产物打包流程、输出内容等信息。
-
生产对应模块包的类型声明文件,并移动到对应构建产物的模块位置。
-
提供包管理配置·package.json·到对应构建产物的模块位置。
-
集成
unocss样式原子化库
特别提醒:在单体仓库(ymui)根目录下执行命令安装开发依赖,命令如下:
pnpm i -wD unocss
## 在单体仓库创建原子化样式配置文件(uno.config.ts)
touch uno.config.ts
参见官方文档https://unocss.dev/integrations/vite定义原子化样式配置(uno.config.ts),内容如下:
一级标题
二级分类
三级内容
重要提醒: 四级提醒
特别注意:五级强调
文章收获
- 如果觉得对你有所帮助,请点下“推荐”吧!
- 如果担心文忘记章地址,请点下“收藏”吧!
- 如果对博主文章内容喜欢,可进行“关注”博主,更好地获悉最新文章内容。

浙公网安备 33010602011771号