
# 🧩 csit-vue3 组件库 - 组件开发 - 开发者遵循规范

- 本文档专为参与维护/扩展该组件库的开发者编写，包含目录结构说明、组件编写规范、打包配置、文档写法等内容。
- 本文档用于指导组件库的开发规范，避免多人工程混乱，确保`按需引入`、自动生成 `exports`等机制正常运作。
- 本文档目的是让参与开发这个组件库的同事们 **明确规则、统一规范、提升效率，减少出错**。

## 1：组件目录规范说明

> 为了确保组件库的 **构建一致性**、**按需引入支持** 以及 **自动化导出脚本正常工作**，我们对组件的目录结构和命名方式做出了以下强制性约定。

### 1.1. 所有组件必须放置在

**`src/components/components-project/`**

### 1.2. 每个组件必须是一个**独立的文件夹**，且目录名必须满足以下要求

- 使用 `kebab-case` 命名风格，示例：`ci-blue-line-title`
- 以 `ci-` 开头（如 `ci-upload`, `ci-page-header`）
- 可支持多层级分类（可根据组件所属类别进行归类，清晰明了）（如 `ci-echarts/ci-echart-bar`、`ci-element-plus/ci-el-date-picker-daterange`）

![logo](./public/images-readme-dev/demo-directory-ex.png)

> [!NOTE]
>
> **原因：**
>
> - ci- 前缀用于标识该目录为一个“最终组件目录”，即该目录下的内容会被视为一个完整组件用于打包。
> - 自动导出脚本会以 ci- 为特征开始递归查找组件文件结构，并生成对应的 package.json > exports 配置，**非 ci- 开头目录将被忽略**。
> - 确保多人协作时命名风格统一，避免命名混乱。

## 2：每个组件目录必须具备的内容

| 文件名              | 说明                                                     |
| ------------------- | -------------------------------------------------------- |
| `index.ts`          | 组件安装文件（用于 Vue 插件化安装）                      |
| `index.vue`         | 组件主文件（模板 + 逻辑）                                |
| `type.ts`           | 可选：暴露组件用到的类型，如 props 类型等                |
| `styles/index.less` | 可选：组件样式，最终打包后会变成 `index.css`，被自动导出 |

> [!NOTE]
>
> **原因：**
>
> - index.vue + index.ts 命名固定后，在使用 preserveModules 打包策略时，最终输出的文件名也会是 index.js 和 index.css，便于按需导入时路径规则一致。
> - 自动导出脚本是基于是否存在 index.js 来判断当前目录是否为组件终点节点（leaf node），如果文件命名不一致，将导致按需导出失效。
> - 确保团队在开发新组件时保持一致性，避免遗漏必要的导出或类型文件。

### 2.1. 简单组件开发模块化分类示例

<img src="./public/images-readme-dev/demo-easy-com.png" alt="alt text"/>

### 2.2. 复杂组件开发模块化分类示例

<img src="./public/images-readme-dev/demo-complex-com.png" alt="alt text"/>

> [!NOTE]
>
> 当开发复杂组件时，代码行数控制在 **500 ~ 600** 行之间，能分模块提取的内容参照`ci-upload`示例进行提取，提取的文件名必须遵循`见名知意`即可。
>
> 比如：**pages/components/views** (看到这些大家第一时间联想到的是 .vue 文件存放目录)、**model**（联想到数据层目录）、**styles**（联想到样式层目录）、**utils**（联想到工具层目录）、**hooks**（联想到组合式API层目录）等等。
>
> **代码需要加详细注释，以便于后续维护**

### 2.3. 编写`.vue`组件规范（以`index.vue`为核心）

> 为确保组件统一风格、可维护性强、结构清晰，所有组件的 .vue 文件需遵循以下编写规范。

#### 2.3.1. 命名与基础结构

> 组件名必须显式声明，且遵循目录名的大驼峰命名法（PascalCase）

每个组件的 name 属性必须显式声明，并保持与组件目录名一致（转为大驼峰命名）：

```vue
<script lang="ts">
export default {
  name: 'CiBlueLineTitle', // 必须与目录 ci-blue-line-title 对应
}
</script>
```

好的示例：

```bash
ci-blue-line-title/
└── index.vue   // name: 'CiBlueLineTitle'
```

错误示例：

- 缺少 name 定义
- 命名与目录不一致，如 BlueLine、TitleComponent

#### 2.3.2. 样式规范

> 禁止在.vue中直接写样式，必须模块化样式至styles/文件夹中(参考: 2.2)，或者简单样式模块化至当前组件目录中(参考: 2.1)

组件样式必须单独存放在 styles/ 目录下，并通过模块方式引入：

```vue
<style lang="less" scoped>
@import url('./styles/xxx.less'); // 如果样式文件放到styles目录下
</style>


<style lang="less" scoped>
@import url('./xxx.less');        // 如果样式文件放到当前目录下
</style>
```

推荐结构：

```bash
ci-blue-line-title/
├── index.vue
└── styles/
    └── index.less
```

禁止以下写法：

```vue
<style lang="less" scoped>
/* ❌ 不允许在.vue中直接写样式 */
.CiNoDataStatus {
  width: 100%;
  height: 100%;
  /* ... */
}
</style>
```

#### 2.3.3. Props 处理方式

> 必须使用defineProps + withDefaults 的组合式 props 定义方式（支持类型推导和默认值设置）来定义默认值

标准写法：

```vue
<script lang="ts">
import { defineProps, withDefaults } from 'vue'
import type { BlueLineTitleProps } from './type'

const props = withDefaults(defineProps<BlueLineTitleProps>(), {
  title: '默认标题',
  color: '#3573f3',
})
</script>
```

不建议使用的旧写法（🚫 错误）

```vue
<script lang="ts">
// ❌ 不推荐
defineProps({
  title: {
    type: String,
    default: '默认标题'
  }
})
</script>
```

使用 withDefaults 的好处：

- 更强的类型推导
- 更符合 Vue 3 SFC 的最佳实践
- 默认值更直观、可维护性更强

#### 2.3.4. `.vue`组件体积控制与逻辑拆分

> 单文件组件`.vue`文件 不应超过500 ~ 600行
>
> 为保持代码可读性和团队协作效率，组件内容超过一定行数时，必须拆分。

拆分建议如下（示例结构参考：2.2）：

| **情况**              | **拆分建议**                                               |
| --------------------- | ---------------------------------------------------------- |
| template 模板区域过长 | 将 UI 拆分为子组件                                         |
| setup 中逻辑函数过多  | 拆分为模块化函数文件，例如：useXXX.ts、logic.ts、helper.ts |
| 响应式变量过多        | 考虑适当合并或封装成 composable                            |

#### 2.3.5. 总结

请严格遵循上述约定，统一团队开发风格，确保以下目标：

- 支持自动化导出和按需引入
- 保持组件结构清晰可维护
- 符合现代 Vue 3 开发最佳实践

如有不符合规范的代码，提交前请调整为统一格式，或通过代码审查发现后统一修正。

### 2.4. `index.ts`文件介绍和编写示例

#### 2.4.1. 介绍

> “每个组件都必须有 index.ts 文件，否则将无法支持按需引入。”

**`index.ts`是组件的导出入口文件，主要职责如下**

- 注册组件（如添加 .install 方法，支持 app.use() 全局注册）
- 导出组件本体（默认导出 Vue 组件）
- 导出相关类型（通过 export * from './type' 等方式）
- index.ts 的存在对于支持组件**全量引入**与**按需引入**非常关键。

#### 2.4.2. 示例

```ts
// 引入 Vue 的 App 类型，用于为组件添加 install 方法时提供类型支持
import type { App } from 'vue'
// 引入当前组件的 vue 文件
import _CiBlueLineTitle from './index.vue'

/**
 * 这个变量带上 install 方法 & 类型断言
 *    1: app.use CiBlueLineTitle 时，Vue 会调用这个 install 方法来注册组件
 *    2: 防止 app.use CiBlueLineTitle 时，Vue 无法识别组件类型
 */
const CiBlueLineTitle = _CiBlueLineTitle as typeof _CiBlueLineTitle & {
  install: (app: App) => void
}

// 为组件添加 install 方法，使其可以通过 app.use() 全局注册
CiBlueLineTitle.install = (app: App) => {
  // 注册组件，组件名称通过 name 属性指定
  app.component(CiBlueLineTitle.name as string, CiBlueLineTitle)
}

// 具名导出(完整生态约定)
export { CiBlueLineTitle }

// 默认导出(完整生态约定)
export default CiBlueLineTitle

// 将 type.ts 中定义的类型全部具名导出，方便外部在按需导入组件时也能使用相关类型(完整生态约定)
export * from './type'

```

## `番外：`自动生成 `exports` 规则说明

我们提供了一个自动脚本，会递归分析 `dist/lib` 和 `dist/types/...` 生成 package.json 中的 `exports` 字段，方便支持按需引入。

### 脚本规则

- 从 `dist/lib/` 开始递归，**只识别以 `ci-` 开头**的目录为组件。
- 组件目录下必须有 `index.js` 表示是一个“组件终点”。
- 如果有 `index.css`，则自动生成 `./ci-xxx/style/index.css` 入口。
- 类型文件根据 `dist/types/src/components/components-project/...` 匹配生成。

## 3：组件开发过程中，如何进行组件效果的预览规范说明

> 为了规范组件开发过程中的调试方式，提升协作效率，业务组件库提供了统一的**组件预览机制**。开发者需按照以下规则进行组件预览页的创建与管理。

### 3.1 预览目录结构规范

**当你在 src/components/components-project/ 下开发完成一个组件后，需在 src/views-demo/ 下创建对应的预览模块以进行调试与功能验证。**

| **项目**        | **说明**                                                                                                                                                 |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| src/views-demo/ | 专用于放置组件开发过程中的预览用例，每个组件对应一个子目录                                                                                               |
| 子目录命名要求  | 必须以 `usedemo-`开头 + 组件目录名，采用 `kebab-case` 命名规则 示例：`usedemo-ci-blue-line-title`                                                        |
| index.vue       | 预览页的入口组件，所有人统一命名为 `index.vue`                                                                                                           |
| 可选目录结构    | `- model/`：存放当前预览模块的状态模型或数据`- api/`：存放当前预览模块的接口请求逻辑`- mock/`：如需本地 mock 接口，请放于此`- styles/`：模块样式专用目录 |

#### 3.1.1. 示例结构

```bash
src/
├── components/
│   └── components-project/
│       └── ci-blue-line-title/
│           ├── index.vue
│           └── index.ts
├── views-demo/
│   └── usedemo-ci-blue-line-title/
│       ├── index.vue
│       ├── model/
│       ├── api/
│       ├── mock/
│       └── styles/
```

#### 3.1.2. 示例结构图

![logo](./public/images-readme-dev/demo-usedemo-com-ex.png)

### 3.2 路由管理规范

**所有用于预览的组件模块，都需要在统一的路由管理文件中注册。**

| **文件路径**                   | **说明**                                   |
| ------------------------------ | ------------------------------------------ |
| src/views-demo/routes/index.ts | 统一维护所有组件预览模块的路由配置入口文件 |

#### 3.2.1 子模块路由定义方式

每个预览模块内需提供 routes/index.ts 路由定义文件，并在主路由入口中集中注册。

示例：src/views-demo/usedemo-ci-blue-line-title/routes/index.ts

```ts
export default [
  {
    path: 'usedemoCiBlueLineTitle', // 必须以 usedemo 开头 + CamelCase 命名
    name: 'usedemoCiBlueLineTitle',
    component: () => import('../index.vue'),
  },
]
```

然后在 src/views-demo/routes/index.ts 中统一引入并注册：

```ts
import usedemoCiBlueLineTitle from '../usedemo-ci-blue-line-title/routes'

export default [...usedemoCiBlueLineTitle]
```

> [!IMPORTANT]
>
> **注意事项：**
>
> 所有预览路径必须以 `usedemo` 开头，方便统一管理。
>
> 路由名称统一采用 `CamelCase` 命名方式，例如：`usedemoCiBlueLineTitle`

#### 3.2.2 总结要求

| **类型**       | **要求**                                                                     |
| -------------- | ---------------------------------------------------------------------------- |
| 模块目录命名   | `usedemo-` + 组件目录名（`kebab-case`）                                      |
| 路由 path/name | 必须 `usedemo` 开头，`CamelCase` 命名                                        |
| 模块主入口文件 | 必须存在 `index.vue`                                                         |
| 可选支持目录   | `model/`、`api/`、`mock/`、`styles/`                                         |
| 路由配置注册   | 每个模块提供 `routes/index.ts`，并统一注册到 `views-demo/routes/index.ts` 中 |

## 4：左侧抽屉菜单 - 组件菜单维护说明

> 为了方便开发者在本地调试和预览自己编写的组件，组件库提供了**菜单渲染机制**，用于在菜单栏中显示“组件开发预览入口”。

### 4.1. 整体效果示例图

![logo](./public/images-readme-dev/demo-menus-tree.png)

### 4.2. 目录结构说明

开发者的本地预览菜单配置，统一放在如下目录：

```bash
src/layout/mock/local-dev-menus-tree/
├── index.ts                    // 所有开发者菜单的汇总入口
├── menus-lisi.ts               // 示例：由 李四 编写的本地菜单配置
├── menus-zhangsan.ts           // 示例：由 张三 编写的本地菜单配置
└── ...
```

> 所有开发者的菜单文件 **必须以 menus- 为前缀命名**，并使用**全拼**命名，如：menus-zhangsan.ts、menus-lisi.ts。

### 4.3. 如何在左侧抽屉菜单中添加自己的组件预览菜单？

#### 4.3.1. 在目录中创建自己的菜单模块文件

例如，你的名字是 “李四”，则新建一个文件：

```bash
src/layout/mock/local-dev-menus-tree/menus-lisi.ts
```

#### 4.3.2. 每位开发者的菜单文件结构

每个开发者的菜单模块，必须默认导出一个“树形结构”的菜单数据数组。**以下字段规则必须遵守**：

| **字段名**   | **类型** | **是否必填** | **说明**                                                                                                    |
| ------------ | -------- | ------------ | ----------------------------------------------------------------------------------------------------------- |
| menuId       | string   | ✅ 必填       | 菜单唯一标识（需自行定义，确保唯一）                                                                        |
| menuTitle    | string   | ✅ 必填       | 菜单名称。顶级菜单填写**你的名字**，子级菜单填写**功能或组件名**                                            |
| menuUrl      | string   | ✅ 必填       | 页面路由地址（仅子级菜单需填写，顶级菜单无需填写）<br /><br />页面路由地址：'/' + views-demo/routes中的path |
| menuIcon     | string   | ❌ 可选       | 菜单图标，默认使用系统图标                                                                                  |
| menuOrderNum | number   | ❌ 可选       | 菜单排序号，当前未启用                                                                                      |
| menuDescrib  | string   | ❌ 可选       | 菜单描述，当前未启用                                                                                        |
| menuParentId | string   | ✅ 必填       | 父级菜单的 menuId，**顶级菜单请统一填写 'iadw'**                                                            |
| menuType     | string   | ❌ 可选       | 菜单类型，当前未启用                                                                                        |
| target       | string   | ❌ 可选       | 打开方式，当前未启用                                                                                        |
| keepAlive    | boolean  | ❌ 可选       | 是否缓存页面，当前未启用                                                                                    |
| children     | array    | ❌ 可选       | 子菜单数组，结构同上，支持多级嵌套                                                                          |

示例：menus-zhangsan.ts

```ts
export default {
  menuId: 'menu-zhangsan-root',
  menuTitle: '张三',
  menuParentId: 'iadw', // 顶级菜单统一使用 iadw
  children: [
    {
      menuId: 'menu-zhangsan-ci-upload',
      menuTitle: 'CiUpload 组件预览',
      menuUrl: '/usedemoCiUpload',
      menuParentId: 'menu-zhangsan-root',
    },
    {
      menuId: 'menu-zhangsan-ci-blue-line-title',
      menuTitle: 'CiBlueLineTitle 标题组件',
      menuUrl: '/usedemoCiBlueLineTitle',
      menuParentId: 'menu-zhangsan-root',
    },
  ],
}

```

> [!IMPORTANT]
>
> 也可参考示例文件：src/layout/mock/local-dev-menus-tree/menus-cuiwenqing.ts

![logo](./public/images-readme-dev/demo-menus-cui-ex.png)

#### 4.3.3. 将你的菜单文件注册到总入口

编辑 src/layout/mock/local-dev-menus-tree/index.ts：

```ts
import MenusZhangsan from './menus-zhangsan'
// 其他人的菜单引入...

export default [
  // 组件开发负责人 - 张三
  MenusZhangsan,
  
  // 其他人菜单合并...
]
```

> [!TIP]
>
> 配置完成后，你的菜单将出现在左侧菜单栏的“组件开发预览”区域，点击即可跳转到你开发中的组件页面进行调试和预览。

## 5：项目启动与构建发布命令说明

> **本章节用于指导开发者如何正确启动组件库项目、编写文档、执行打包及发布流程。所有命令均已在 package.json 中配置。**

### 5.1 启动开发环境

组件开发阶段，可选择不同环境模式进行调试：

| **命令**             | **说明**                                                                                       |
| -------------------- | ---------------------------------------------------------------------------------------------- |
| npm run dev-mock     | 启动组件库开发模式，使用本地 Mock 数据模拟后端接口，适用于**组件开发初期、接口未联调时使用**。 |
| npm run dev-liantiao | 启动组件库开发模式，使用真实后端接口联调，适用于**后期联调测试与功能验证阶段**。               |

### 5.2 文档开发与预览

配套组件文档使用 [VitePress](https://vitepress.dev/) 进行编写与构建：

| **命令**             | **说明**                                                          |
| -------------------- | ----------------------------------------------------------------- |
| npm run docs:dev     | 启动文档开发服务器，实时预览 .md 文档变更，适合文档编写阶段使用。 |
| npm run docs:build   | 构建静态文档产物，输出至 .vitepress/dist 目录，用于生产部署。     |
| npm run docs:preview | 预览构建完成后的文档站点，模拟线上访问效果。                      |

> 文档编辑完成后，建议执行 docs:preview 进行本地验收。

### 5.3 构建组件库产物

| **命令**      | **说明**                                                                                                    |
| ------------- | ----------------------------------------------------------------------------------------------------------- |
| npm run build | 执行组件库构建任务，会自动打包出：全量引入版本 + 按需引入版本 产物，并生成类型声明文件。输出至 dist/ 目录。 |

### 5.4 发布组件库到 NPM

| **命令**    | **说明**                                                |
| ----------- | ------------------------------------------------------- |
| npm publish | 将构建完成的组件库发布到 NPM 仓库，供业务项目安装使用。 |

> ⚠️ 发布前请确保以下工作已完成：

- **本地构建成功**（运行 npm run build 无误）
- **功能自测通过**（组件功能正常、无明显 bug）
- **代码已 Review**（经团队审查通过）
- **文档已完善**（至少包含组件说明和示例）
- **版本号已更新**（遵循 [Semver 语义化版本规范](https://semver.org/lang/zh-CN/)）
