复杂前端组件库项目架构

本文以 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>)。
  • TypeScripttsconfig.base.json 统一 strict: truemodule: "esnext"moduleResolution: "bundler";各包通过 tsconfig.lib.json 做声明产出(emitDeclarationOnly)。
  • Vite:每个 package 独立 vite.config.mts,库模式 build.lib.entry 指向 src/index.tsrollupOptions.external 排除 vueluban-base 等,由使用方或 workspace 提供。

2.2 Monorepo 与任务编排

技术版本(参考)用途
pnpm-依赖管理、workspaces(apps/*, packages/*
Nx22.x任务图、缓存、增量构建、多 target 编排
  • pnpm:根目录 package.jsonworkspaces: ["apps/*", "packages/*"]peerDependencyRules 可对 Storybook、sass 等做忽略或放宽。
  • Nx:通过 nx.jsonplugins 注入 @nx/vite@nx/vitest@nx/cypress@nx/eslint 等,自动为各项目生成 buildteste2elint 等 target;targetDefaults.test.dependsOn: ["^build"] 保证依赖包先构建再测。

2.3 构建与类型

技术用途
vite-plugin-dtstsconfig.lib.json 产出 .d.ts 与 declaration map,与 Vite 库构建同流程
RollupVite 底层,用于 ES 格式、external、单入口打包
  • 产物:各包 dist/index.js(ESM)、index.d.tsindex.csspackage.jsonexports 中提供 @luban-ui/source 条件指向 src/index.ts,便于本仓库内开发时直连源码。

2.4 测试

技术用途
Vitest单元测试、组件测试、E2E 风格测试(jsdom)
Cypress应用级 E2E(如 apps/luban-ui-e2e)
@vue/test-utilsVue 组件挂载、断言
jsdom浏览器环境模拟
  • 包内测试目录:统一为 packages/<pkg-name>/test,其下 unit/e2e/ 分目录;每个组件/模块一个测试文件(如 button.spec.tsform.spec.tsdesigner.e2e.spec.ts)。
  • 单元测试:主 vite 配置中 test.include 覆盖 srctest 下的 *.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 ESLintVue 3 / TypeScript 规则

2.7 包管理与发布

技术用途
npm publish对 luban-base、luban-low-code 等执行发布
changesets(可选)变更集与版本管理
  • 对外包名:无 scope 的 luban-baseluban-low-codeluban-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;对外导出组件与类型(如 ButtonVariantLubanButtonProps)。
  • luban-low-code:依赖 luban-basevuesortablejs;对外导出设计器、运行时、schema 类型、registry、palette 等;打包时 external: ['vue', 'luban-base'],发布包内不打包基础库。
  • luban-utils:无 UI,可被 base / low-code 或应用层引用;仅 tslib 等轻量依赖。

3.3 单包内代码组织

  • 组件:按领域分目录(button、form、layout、content);每个组件一个目录,内含 ComponentName.vue、可选 types.tscomponent-name-types.ts、以及 ComponentName.stories.ts
  • 样式:组件内 <style scoped lang="scss">,通过 @use '.../styles/xxx.scss' 引用;主题变量集中放在 _variables.scss
  • 入口src/index.ts 统一 export 组件与类型,便于单入口构建与类型生成。

3.4 导出与入口约定

  • package.jsonexports["."] 需包含:import/default 指向 dist/index.jstypes 指向 dist/index.d.ts;条件 @luban-ui/source 指向 src/index.ts,供本仓库 Vite/TS 解析源码。
  • tsconfig.base.jsoncustomConditions: ["@luban-ui/source"] 确保类型解析时优先走源码。

4. 工程化

4.1 构建

  • 包构建nx run luban-base:buildnx run luban-low-code:build;或 nx run-many -t build -p luban-base,luban-low-code
  • 每个包各自 vite.config.mtsbuild.lib.entrybuild.outDirrollupOptions.externalvite-plugin-dtstsconfigPathentryRoot 一致,保证声明文件与源码结构对应。

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-basenx test luban-low-code;或根脚本 pnpm test:basepnpm test:lowcode,对应包内 Vitest。
  • E2E 风格pnpm test:base:e2epnpm test:lowcode:e2e,执行各包下 vitest run -c vitest.e2e.config.mts,只跑 test/e2e/**/*.spec.ts
  • 应用 E2Enx 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 包发布

  • 发布前必须先 buildtest;各包 package.jsonnx.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)

按需可增加:lintlint:fixtypecheck(如 nx run-many -t typecheck)等。


6. 扩展建议

  • 新增包:在 packages/ 下新建目录,配置 package.json(name、exports、nx.tags)、vite.config.mtstsconfig.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 testpnpm test:base:e2epnpm test:lowcode:e2epnpm test:e2e 以及各包 buildlint,保证主分支质量。

7. 小结

复杂前端组件库在 luban-ui 的实践下,形成「多包 Monorepo + Nx 编排 + Vite 构建 + Vitest/Cypress 测试 + Storybook 文档」的闭环:代码组织 清晰分层(base / low-code / utils + apps),工程化 覆盖构建、类型、测试与发布,便捷脚本 统一在根目录,便于日常开发与 CI。技术栈上以 Vue 3、TypeScript、Vite、Nx 为核心,配合 SASS、ESLint、Prettier 与 npm 发布,即可支撑企业级组件库的迭代与复用。

0%