Skip to content

组件库开发规范

适用于 apps/ 下所有 @nexa/* 组件库包。 组件库的目标消费者是 products/tactics-sim-engine 及未来的产品应用。

目录结构

每个组件库必须遵循以下结构:

apps/<package-name>/
├── package.json          # name: @nexa/<name>, private: false
├── tsconfig.json         # 引用 tsconfig.app.json + tsconfig.node.json
├── tsconfig.app.json     # 库代码的 TS 配置
├── tsconfig.node.json    # vite.config 的 TS 配置
├── vite.config.ts        # 构建配置(library + app 双模式)
├── index.html            # dev playground 入口
├── src/
│   ├── main.ts           # dev playground 启动文件
│   ├── App.vue           # dev playground 根组件(引入 src/lib 演示)
│   └── lib/              # 【库代码】对外发布的代码
│       ├── index.ts      # 公共 API 入口(export * from)
│       ├── types.ts      # 公共类型定义
│       ├── <Component>.vue   # 主组件(PascalCase)
│       └── components/   # 内部子组件

组件开发铁律

必须遵守

  • 每个组件必须支持 embedded prop:控制是否独立渲染标题栏。true 时组件作为嵌入式面板,隐藏自有标题和关闭按钮,由宿主管理。
  • 每个组件必须 emit close 事件:宿主可通过该事件响应关闭操作。
  • 使用 <script setup lang="ts">:禁止 Options API。
  • Props 使用 TypeScript 泛型defineProps<{ ... }>()
  • CSS Modules:推荐使用 <style module> 避免样式污染宿主。如 $style.panel$style.header
  • 公共 API 从 src/lib/index.ts 导出:组件、composable、类型定义均从此文件 re-export。

禁止事项

  • ❌ 在组件中硬编码路由(组件库不拥有路由)
  • ❌ 直接操作全局状态(不引入 Pinia store)
  • ❌ 依赖特定的布局/容器结构
  • ❌ 在库代码中引用 src/main.tssrc/App.vue(仅限 dev playground)

组件模式

主组件模板

vue
<script setup lang="ts">
import type { XxxProps } from './types'

const props = withDefaults(defineProps<XxxProps>(), {
  embedded: false,
})

const emit = defineEmits<{
  close: []
  // 其他事件...
}>()

// 内部逻辑...
</script>

<template>
  <div :class="[$style.panel, embedded && $style.embedded]">
    <div v-if="!embedded" :class="$style.header">
      <span :class="$style.title">面板标题</span>
      <n-button quaternary @click="emit('close')">✕</n-button>
    </div>
    <div :class="$style.body">
      <!-- 核心内容 -->
    </div>
  </div>
</template>

<style module>
.panel { /* 独立模式样式 */ }
.embedded { /* 嵌入模式样式(更紧凑) */ }
.header { display: flex; justify-content: space-between; align-items: center; }
.title { font-weight: 600; }
.body { flex: 1; overflow: auto; }
</style>

Props 类型定义(types.ts)

typescript
export interface XxxProps {
  /** 是否作为嵌入式面板(隐藏标题栏和关闭按钮),默认 false */
  embedded?: boolean
  // 其他 props...
}

export interface XxxEmits {
  close: []
}

依赖规则

  • 内部依赖:通过 workspace:* 引用 @nexa/*
  • 外部框架vuenaive-uicesium 必须声明为 peerDependencies
  • 构建工具vitevite-plugin-cesiumvite-plugin-dtsvue-tsc 放在 devDependencies

构建

  • build:libvite build --mode library → 输出 ES + UMD 到 dist/lib/
  • buildvue-tsc -b && vite build --mode library → 含类型检查的完整构建
  • devvite → 启动 dev playground(使用 src/App.vue 作为入口)
  • UMD 外部依赖必须配置 rollupOptions.output.globals,特别是 @nexa/* 内部依赖

验收标准

  • [ ] 包名符合 @nexa/<name> 规范
  • [ ] 主组件支持 embedded prop 和 close emit
  • [ ] src/lib/index.ts 正确导出所有公共 API
  • [ ] peerDependencies 声明完整(vue/naive-ui/cesium)
  • [ ] devDependencies 不含运行时依赖
  • [ ] pnpm build:lib 构建成功
  • [ ] dev playground 可独立运行(pnpm dev