本文以 luban-ui 为例,介绍一套适合复杂前端组件库的项目架构,重点包括代码组织、工程化与便捷脚本,并详细说明技术栈选型与使用方式。适用于需要多包协作、严格类型与测试、且要对外发布 npm 的 Vue 3 组件库场景。
1. 概述与目标
- 定位:底层组件库(基础组件 + 低代码组件 + 设计器 + 运行时渲染),不包含业务应用。
- 目标:多包 Monorepo、统一构建/测试/发布、类型声明完整、单元测试与 E2E 覆盖、文档与 Demo 分离。
- 约束:组件风格遵循 Material Design,响应式设计;新增/修改组件必须补齐单元测试与 E2E,并通过根目录脚本可执行。
2. 技术栈详解
2.1 核心运行时与框架
| 技术 | 版本(参考) | 用途 |
|---|
| Vue | ^3.5.x | 组件开发、Composition API、<script setup> |
| TypeScript | ~5.9.x | 类型安全、.d.ts 声明、严格模式 |
| Vite | ^6.x | 开发服务器、库模式构建、测试运行器底座 |
- Vue 3:单文件组件(.vue)用 template 编写,不使用 h() 为主实现;模板中组件调用统一 kebab-case(如
<luban-form>)。 - TypeScript:
tsconfig.base.json 统一 strict: true、module: "esnext"、moduleResolution: "bundler";各包通过 tsconfig.lib.json 做声明产出(emitDeclarationOnly)。 - Vite:每个 package 独立
vite.config.mts,库模式 build.lib.entry 指向 src/index.ts,rollupOptions.external 排除 vue、luban-base 等,由使用方或 workspace 提供。
2.2 Monorepo 与任务编排
| 技术 | 版本(参考) | 用途 |
|---|
| pnpm | - | 依赖管理、workspaces(apps/*, packages/*) |
| Nx | 22.x | 任务图、缓存、增量构建、多 target 编排 |
- pnpm:根目录
package.json 的 workspaces: ["apps/*", "packages/*"];peerDependencyRules 可对 Storybook、sass 等做忽略或放宽。 - Nx:通过
nx.json 的 plugins 注入 @nx/vite、@nx/vitest、@nx/cypress、@nx/eslint 等,自动为各项目生成 build、test、e2e、lint 等 target;targetDefaults.test.dependsOn: ["^build"] 保证依赖包先构建再测。
2.3 构建与类型
| 技术 | 用途 |
|---|
| vite-plugin-dts | 从 tsconfig.lib.json 产出 .d.ts 与 declaration map,与 Vite 库构建同流程 |
| Rollup | Vite 底层,用于 ES 格式、external、单入口打包 |
- 产物:各包
dist/ 下 index.js(ESM)、index.d.ts、index.css;package.json 的 exports 中提供 @luban-ui/source 条件指向 src/index.ts,便于本仓库内开发时直连源码。
2.4 测试
| 技术 | 用途 |
|---|
| Vitest | 单元测试、组件测试、E2E 风格测试(jsdom) |
| Cypress | 应用级 E2E(如 apps/luban-ui-e2e) |
| @vue/test-utils | Vue 组件挂载、断言 |
| jsdom | 浏览器环境模拟 |
- 包内测试目录:统一为
packages/<pkg-name>/test,其下 unit/ 与 e2e/ 分目录;每个组件/模块一个测试文件(如 button.spec.ts、form.spec.ts、designer.e2e.spec.ts)。 - 单元测试:主 vite 配置中
test.include 覆盖 src 与 test 下的 *.spec.ts 等。 - E2E 风格:包内单独
vitest.e2e.config.mts,仅 include: ['test/e2e/**/*.spec.ts'],与单元隔离。
2.5 文档与预览
| 技术 | 用途 |
|---|
| Storybook | 组件文档与交互预览,左右布局(组件列表 + 当前内容) |
| @storybook/vue3-vite | 与 Vite 集成,stories 从 `packages//src/**/.stories.@(ts |
- Demo 与默认 schema 仅放在
apps/*,不放入 packages;Storybook 只引用组件与少量示例数据。
2.6 样式与规范
| 技术 | 用途 |
|---|
| SASS (.scss) | 组件样式;主题相关变量放入 _variables.scss |
| ESLint | 代码规范,根与各包可有 eslint.config.mjs |
| Prettier | 格式化,与 ESLint 配合 |
| Vue ESLint | Vue 3 / TypeScript 规则 |
2.7 包管理与发布
| 技术 | 用途 |
|---|
| npm publish | 对 luban-base、luban-low-code 等执行发布 |
| changesets(可选) | 变更集与版本管理 |
- 对外包名:无 scope 的
luban-base、luban-low-code、luban-utils;内部开发可用 Vite alias @luban-ui/luban-base 等直连源码。
3. 代码组织
3.1 仓库结构概览
luban-ui/
├── apps/
│ ├── luban-ui/ # 主应用:开发/联调、Demo 页
│ │ ├── src/
│ │ ├── vite.config.mts
│ │ └── package.json
│ └── luban-ui-e2e/ # Cypress E2E 项目
│ ├── src/e2e/
│ ├── cypress.config.ts
│ └── package.json
├── packages/
│ ├── luban-base/ # 基础组件(按钮、表单、布局等)
│ │ ├── src/
│ │ │ ├── index.ts # 统一导出
│ │ │ ├── lib/
│ │ │ │ ├── button/
│ │ │ │ ├── form/
│ │ │ │ ├── layout/
│ │ │ │ └── content/
│ │ │ └── styles/
│ │ ├── test/
│ │ │ ├── unit/
│ │ │ └── e2e/
│ │ ├── vite.config.mts
│ │ ├── vitest.e2e.config.mts
│ │ └── package.json
│ ├── luban-low-code/ # 低代码:设计器、运行时、schema、注册表
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── lib/
│ │ │ │ ├── LubanDesigner.vue
│ │ │ │ ├── RuntimeRenderer.vue
│ │ │ │ ├── registry.ts
│ │ │ │ ├── schema.ts
│ │ │ │ └── ...
│ │ ├── test/unit/ & test/e2e/
│ │ └── package.json # 依赖 luban-base、vue、sortablejs
│ └── luban-utils/ # 工具函数与通用逻辑
│ └── src/
├── .storybook/
│ ├── main.ts # stories 路径、Vite 集成、fs.allow
│ └── preview.ts
├── docs/ # 设计/架构/提交规范文档
├── nx.json
├── tsconfig.base.json
├── package.json # workspaces、根脚本
└── pnpm-workspace.yaml / pnpm 配置
3.2 包职责与依赖关系
- luban-base:纯基础 UI 组件,仅依赖
vue;对外导出组件与类型(如 ButtonVariant、LubanButtonProps)。 - luban-low-code:依赖
luban-base、vue、sortablejs;对外导出设计器、运行时、schema 类型、registry、palette 等;打包时 external: ['vue', 'luban-base'],发布包内不打包基础库。 - luban-utils:无 UI,可被 base / low-code 或应用层引用;仅
tslib 等轻量依赖。
3.3 单包内代码组织
- 组件:按领域分目录(button、form、layout、content);每个组件一个目录,内含
ComponentName.vue、可选 types.ts 或 component-name-types.ts、以及 ComponentName.stories.ts。 - 样式:组件内
<style scoped lang="scss">,通过 @use '.../styles/xxx.scss' 引用;主题变量集中放在 _variables.scss。 - 入口:
src/index.ts 统一 export 组件与类型,便于单入口构建与类型生成。
3.4 导出与入口约定
package.json 的 exports["."] 需包含:import/default 指向 dist/index.js,types 指向 dist/index.d.ts;条件 @luban-ui/source 指向 src/index.ts,供本仓库 Vite/TS 解析源码。tsconfig.base.json 的 customConditions: ["@luban-ui/source"] 确保类型解析时优先走源码。
4. 工程化
4.1 构建
- 包构建:
nx run luban-base:build、nx run luban-low-code:build;或 nx run-many -t build -p luban-base,luban-low-code。 - 每个包各自
vite.config.mts:build.lib.entry、build.outDir、rollupOptions.external、vite-plugin-dts 的 tsconfigPath 与 entryRoot 一致,保证声明文件与源码结构对应。
4.2 类型
- 库构建时由
vite-plugin-dts 产出 dist/**/*.d.ts;消费方通过 "types": "./dist/index.d.ts" 或 exports.types 使用。 - 包内开发与测试共用
tsconfig.lib.json(仅含 src,排除 spec);应用或 E2E 使用各自 tsconfig.json。
4.3 测试分层
- 单元:
nx test luban-base、nx test luban-low-code;或根脚本 pnpm test:base、pnpm test:lowcode,对应包内 Vitest。 - E2E 风格:
pnpm test:base:e2e、pnpm test:lowcode:e2e,执行各包下 vitest run -c vitest.e2e.config.mts,只跑 test/e2e/**/*.spec.ts。 - 应用 E2E:
nx e2e luban-ui-e2e 或根脚本 pnpm test:e2e,跑 Cypress。
4.4 规范与格式化
- ESLint:根与各包可继承/扩展,Vue3 + TypeScript 规则;Nx 的
@nx/eslint 提供 lint target。 - Prettier:与 ESLint 集成,统一换行与引号;提交前可配合 husky + lint-staged 做格式化。
4.5 包发布
- 发布前必须先
build 与 test;各包 package.json 的 nx.targets.publish 可配置 dependsOn: ["build", "test"],再执行 npm publish --access public。 - 版本号:可用
nx run luban-base:version 等执行 pnpm version patch --no-git-tag-version;或使用 changesets 管理版本与 CHANGELOG。
5. 便捷脚本(根 package.json)
| 脚本 | 含义 |
|---|
pnpm dev | 启动主应用(如 nx run @luban-ui/luban-ui:dev),用于开发与联调 |
pnpm storybook | 启动 Storybook(如 storybook dev -p 6006) |
pnpm build-storybook | 构建 Storybook 静态站 |
pnpm test | 运行主应用测试(或指定 Nx 的 test target) |
pnpm test:e2e | 运行 Cypress E2E(luban-ui-e2e) |
pnpm test:base | 仅跑 luban-base 单元测试 |
pnpm test:base:e2e | 仅跑 luban-base 的 e2e 风格用例 |
pnpm test:lowcode | 仅跑 luban-low-code 单元测试 |
pnpm test:lowcode:e2e | 仅跑 luban-low-code 的 e2e 风格用例 |
pnpm release:packages | 构建 base + low-code 后依次 npm publish(示例,可按需加版本与 tag) |
按需可增加:lint、lint:fix、typecheck(如 nx run-many -t typecheck)等。
6. 扩展建议
- 新增包:在
packages/ 下新建目录,配置 package.json(name、exports、nx.tags)、vite.config.mts、tsconfig.lib.json,并在根 workspaces 内;Nx 会自动发现。 - 新增组件:在对应包
src/lib/<domain>/ 下新增 .vue 与类型,在 src/index.ts 中导出;同时新增 test/unit/<name>.spec.ts 与可选 test/e2e/<name>.e2e.spec.ts 及 *.stories.ts。 - CI:可配置在 MR 上运行
pnpm test、pnpm test:base:e2e、pnpm test:lowcode:e2e、pnpm test:e2e 以及各包 build 与 lint,保证主分支质量。
7. 小结
复杂前端组件库在 luban-ui 的实践下,形成「多包 Monorepo + Nx 编排 + Vite 构建 + Vitest/Cypress 测试 + Storybook 文档」的闭环:代码组织 清晰分层(base / low-code / utils + apps),工程化 覆盖构建、类型、测试与发布,便捷脚本 统一在根目录,便于日常开发与 CI。技术栈上以 Vue 3、TypeScript、Vite、Nx 为核心,配合 SASS、ESLint、Prettier 与 npm 发布,即可支撑企业级组件库的迭代与复用。