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

本文档专为参与维护/扩展该组件库的开发者编写，包含目录结构说明、组件编写规范、打包配置、文档写法等内容。

本文档用于指导组件库的开发规范，避免多人工程混乱，确保`按需引入`、自动生成 `exports`等机制正常运作。

本文档目的是让参与开发这个组件库的同事们 **明确规则、统一规范、提升效率，减少出错**。

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

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

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

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

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

1：使用 `kebab-case` 命名风格，示例：`ci-blue-line-title`

2：以 `ci-` 开头（如 `ci-upload`, `ci-page-header`）

3：可支持多层级分类（可根据组件所属类别进行归类，清晰明了）（如 `ci-echarts/ci-echart-bar`、`ci-element-plus/ci-el-date-picker-daterange`）

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

:::tip
**原因：**

1：`ci-` 前缀用于标识该目录为一个`最终组件目录`，即该目录下的内容会被视为一个完整组件用于打包。

2：自动导出脚本会以 `ci-` 为特征开始递归查找组件文件结构，并生成对应的 `package.json > exports` 配置，**非 `ci-` 开头目录将被忽略**。

3：确保多人协作时命名风格统一，避免命名混乱。
:::

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

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

:::tip
**原因：**

1：`index.vue + index.ts` 命名固定后，在使用 `preserveModules` 打包策略时，最终输出的文件名也会是 `index.ts` 和 `index.css`，便于按需导入时路径规则一致。

2：自动导出脚本是基于是否存在 `index.ts` 来判断当前目录是否为组件终点节点`（leaf node）`，如果文件命名不一致，将导致按需导出失效。

3：确保团队在开发新组件时保持一致性，避免遗漏必要的导出或类型文件。
:::

### 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"/>

:::tip
1：当开发复杂组件时，代码行数控制在 `500 ~ 600` 行之间，能分模块提取的内容参照`ci-upload`示例进行提取，提取的文件名必须遵循`见名知意`即可。

2：比如：`pages/components/views` (看到这些大家第一时间联想到的是 `.vue` 文件存放目录)、`model`（联想到数据层目录）、`styles`（联想到样式层目录）、`utils`（联想到工具层目录）、`hooks`（联想到组合式API层目录）等等。

3：**代码需要加详细注释，以便于后续维护**
:::

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

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

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

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

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

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

:::tip 好的示例：

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

:::

:::warning 错误示例
缺少 `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>
```

:::tip
使用 `withDefaults` 的好处：

1：更强的类型推导

2：更符合 `Vue 3 SFC` 的最佳实践

3：默认值更直观、可维护性更强
:::

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

单文件组件`.vue`文件 不应超过`500 ~ 600`行

为保持代码可读性和团队协作效率，组件内容超过一定行数时，必须拆分。

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

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

<br/>

#### 2.3.5. 总结

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

:::tip
支持自动化导出和按需引入

保持组件结构清晰可维护
  
符合现代 Vue 3 开发最佳实践
:::

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

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

<br/>

#### 2.4.1. 介绍

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

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

1：注册组件（如添加 `.install` 方法，支持 `app.use()` 全局注册）

2：导出组件本体（默认导出 `Vue` 组件）

3：导出相关类型（通过 `export * from './type'` 等方式）

4：`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` 字段，方便支持按需引入。

:::tip 脚本规则
1：从 `dist/lib/` 开始递归，**只识别以 `ci-` 开头**的目录为组件。

2：组件目录下必须有 `index.js` 表示是一个“组件终点”。

3：如果有 `index.css`，则自动生成 `./ci-xxx/style/index.css` 入口。

4：类型文件根据 `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 | 统一维护所有组件预览模块的路由配置入口文件 |

<br/>

#### 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]
```

:::warning **注意事项：**

所有预览路径必须以 `usedemo` 开头，方便统一管理。

路由名称统一采用 `CamelCase` 命名方式，例如：`usedemoCiBlueLineTitle`

:::

#### 3.2.2 总结要求

<br/>

| **类型**       | **要求**                                                                     |
| -------------- | ---------------------------------------------------------------------------- |
| 模块目录命名   | `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. 如何在左侧抽屉菜单中添加自己的组件预览菜单？

<br/>

#### 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 启动开发环境

:::tip

GitLab项目地址: `http://10.132.17.80/front-group/csit-base-components-v3`(放到了前端组下，因此每个人都有访问权限)

1.`git clone http://10.132.17.80/front-group/csit-base-components-v3.git`

2.`nvm use 18+` Node.js建议使用20+版本

3.`npm ci`

4.0.`npm run dev-mock` --> 启动: 组件开发阶段

4.1. `npm run docs:dev` --> 启动: 组件文档开发阶段
:::

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

| **命令**             | **说明**                                                                                       |
| -------------------- | ---------------------------------------------------------------------------------------------- |
| 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 构建组件库产物

<br/>

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

### 5.4 发布组件库到 NPM

:::tip
**发布前的认证：必须先登录一次**

执行一次：`npm run login:internal`

输入你的账号密码，npm会把token 缓存到本地（~/.npmrc 用户级别），下次发布就可以直接使用缓存的 token，`之后每次 publish 都不需要再输入账号密码了`

账号: `public` 密码: `public` 邮箱: `public@csits.org.cn`
:::

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

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

1.**本地构建成功**（运行 `npm run build` 无误）

2.**功能自测通过**（组件功能正常、无明显 bug）

3.**代码已 Review**（经团队审查通过）

4.**文档已完善**（至少包含组件说明和示例）

5.**版本号已更新**（遵循 [Semver 语义化版本规范](https://semver.org/lang/zh-CN/)）

:::
