frontend-mandatory-standards

【必读·强制】公司前端开发强制规范。编写或修改任何前端代码前必须先阅读本技能。涵盖 Composition API 强制规范、命名约定、接口调用规则、Pinia 状态管理、组件拆分等全部开发约束。

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "frontend-mandatory-standards" with this command: npx skills add giikin/skills/giikin-skills-frontend-mandatory-standards

前端开发规范

技术栈

  • 语言:TypeScript
  • 框架:Vue 3 Composition API (<script setup>)
  • CSS:Tailwind CSS(避免使用 <style> 标签,Tailwind class 覆盖不了的场景除外)
  • 状态管理:Pinia
  • UI 框架:Giime(基于 Element Plus 增强,el-*gm-*

命名规范

场景风格示例
类型/接口PascalCaseUserInfo, ApiResponse
变量/函数/文件夹camelCaseuserName, handleSubmit()
环境常量UPPER_CASEVITE_BASE_URL
组件文件PascalCaseUserProfile.vue
Composablesuse 前缀useUserInfo, useFormValidation
布尔值辅助动词前缀isLoading, hasError, canSubmit
事件处理handle 前缀handleClick, handleSubmit

变量命名:避免 data/info/list/result/status 等通用名称,使用 formData/userInfo/taskList 等具体业务名称。

通用编码规则

  • 注释:每个方法和变量添加 JSDoc 注释,使用中文;函数内部添加适量单行中文注释
  • 函数声明:优先使用 const 箭头函数而非 function,除非需要重载
  • 异步:优先 async/await,不用 Promise 链式调用
  • 现代 ES:优先使用 ?.??Promise.allSettledreplaceAllObject.groupBy
  • 守卫语句:条件提前退出,减少嵌套深度
  • 函数参数:1-2 个主参数 + options 对象,避免参数超长
// ✅ 函数参数设计示例
const urlToFile: (url: string, options?: FileConversionOptions) => Promise<File>;
  • 工具库优先:减少造轮子
    • 日期dayjs
    • Vue 工具vueusetryOnMounteduseEventListeneruseLocalStorage 等)
    • 数据处理lodash-escompactcloneDeepuniqBy 等 ES 未提供的方法)
  • Git 操作:不要操作 git 命令,除非用户明确要求
  • 格式化:修改后执行 npx eslint --fix <文件路径>
  • 类型检查:执行 npx vue-tsc --noEmit -p tsconfig.app.json

更多示例见 coding-conventions.md

Composition API 规范

1. 减少 watch,优先事件绑定

watch 是隐式依赖,难以追踪变更来源。优先在事件回调中直接处理逻辑,数据流更清晰:

// ❌ 隐式监听,难以定位谁触发了变更
watch(count, () => {
  console.log('changed');
});

// ✅ 在事件中直接处理,数据流清晰
const handleCountChange = () => {
  console.log('changed');
};
// <gm-input v-model="count" @change="handleCountChange" />

2. 使用 defineModel

Vue 3.4+ 引入的 defineModel 将 props + emit + computed 三件套简化为一行,减少样板代码:

// ❌ 三件套写法,样板代码多
const props = defineProps<{ modelValue: string }>();
const emit = defineEmits<{ 'update:modelValue': [value: string] }>();

// ✅ defineModel 一行搞定
const value = defineModel<string>({ required: true });

3. 使用 useTemplateRef

Vue 3.5+ 推荐使用 useTemplateRef 获取模板引用,类型安全且避免 ref 变量名与模板 ref 属性名耦合:

// ❌ 变量名必须与 template 中的 ref="inputRef" 完全一致
const inputRef = ref<FormInstance | null>(null);
// ✅ 类型安全,解耦变量名和模板 ref
const inputRef = useTemplateRef('inputRef');

4. 优先 ref,避免无理由 reactive

reactive 在解构时会丢失响应性,整体替换时需要逐字段赋值。ref 行为更可预测,通过 .value 可以直接整体替换:

// ❌ reactive 解构丢失响应性,整体替换需 Object.assign
const profileData = reactive({ name: '', age: 0 });
// ✅ ref 行为统一,profileData.value = newData 即可整体替换
const profileData = ref({ name: '', age: 0 });

5. Props 直接解构

Vue 3.5+ 支持 defineProps 解构时保持响应性并设置默认值,withDefaults 已不再需要:

// ❌ withDefaults 在 Vue 3.5+ 已不必要
const props = withDefaults(defineProps<{ size?: number }>(), { size: 10 });

// ✅ 直接解构,简洁且响应式
const { size = 10 } = defineProps<{ size?: number }>();

6. Dialog 暴露 openDialog

弹窗组件通过 defineExpose 暴露 openDialog 方法,父组件通过 ref 调用,避免通过 props 控制 visible 导致的状态同步问题:

const dialogVisible = ref(false);

const openDialog = (data?: SomeType) => {
  dialogVisible.value = true;
};

defineExpose({ openDialog });

7. 自动导入,不要显式 import Vue API

refcomputedwatchonMounted 等已通过 unplugin-auto-import 自动导入。显式 import { ref } from 'vue' 会产生冗余代码,且与项目配置不一致。

8. 样式用 Tailwind CSS

所有样式通过 Tailwind 的 class 实现。只有 Tailwind 无法覆盖的场景(如深度选择器 :deep()、复杂动画)才使用 <style>

9. 配置数据抽离

选项列表、表单规则、tableId 等与页面逻辑无关的配置数据,在 modules/**/composables/useXxxOptions.ts 中抽离,保持组件专注于交互逻辑:

export const useXxxOptions = () => {
  const tableId = 'xxx-xxx-xxx-xxx-xxx';
  const xxxOptions = [{ label: '选项1', value: 1, tagType: 'primary' as const }];
  const rules = {
    xxx: [{ required: true, message: 'xxx不能为空', trigger: 'blur' }],
  };

  return { tableId, xxxOptions, rules };
};

10. 组件代码结构

统一使用 templatescriptstyle 顺序。

代码拆分规范

每次向已有 .vue 文件添加新功能、或创建新模块时,都应首先阅读 directory-structure.md 了解拆分原则。

何时触发拆分

触发场景操作
新建增删改查模块先阅读 crud.md 学习完整的 CRUD 代码模板和文件拆分方式,按模板生成所有文件
向已有页面添加功能(新增表单、弹窗等)先检查当前文件行数,超过 300 行应拆分;评估新增内容是否应作为独立子组件
新建模块/页面先规划目录结构(components/composables/stores/),按功能区域预拆分
修改迭代已有功能如果发现当前文件已臃肿(>300 行),在完成需求的同时顺带拆分,不要让文件继续膨胀

核心原则

  • 一般 .vue 文件不超过 300 行:除入口级别文件(如 index.vue)外,子组件、弹窗等文件应控制在 300 行以内
  • 入口文件可适当放宽index.vue 作为模块入口承担编排职责,行数可适当超出,但也应尽量精简
  • 按功能区域拆分 UI 组件:每个独立的功能区域(搜索栏、表格、弹窗、表单区等)应作为独立子组件
  • 核心业务逻辑保留在 index.vue:主页面负责数据获取、状态管理、子组件编排
  • UI 展示逻辑下沉到子组件:子组件只负责渲染和用户交互
  • 避免过度传参:当 props 层级超过 2 层时,使用 Pinia 替代深层 props 传递

标准模块目录结构

modules/xxx/
├── index.vue                    # 主页面(编排子组件、管理数据)
├── components/                  # UI 子组件
│   ├── Search.vue
│   ├── Table.vue
│   └── EditDialog.vue
├── composables/                 # 逻辑复用
│   └── useXxxOptions.ts
└── stores/                      # 状态管理(需要时)
    └── useXxxStore.ts

详细的拆分策略、示例代码和 Props 传递原则见 directory-structure.md

接口调用规范

文件限制

/src/api 下的文件由代码生成工具自动生成,可以简单修改但不能新建和删除

文件结构

每个接口生成两个文件:

  • postXxxYyyZzz.ts — 原始 axios 方法
  • usePostXxxYyyZzz.ts — useAxios 响应式封装

文件名通过 "请求方法+路由地址" 生成:post /open/v1/system/listpostOpenV1SystemList.ts + usePostOpenV1SystemList.ts

选择规则

场景使用版本原因
默认useXxx 封装版本提供响应式数据(dataisLoading)和自动取消竞态
循环/批量请求原始版本(无 use 前缀)useAxios 会取消前次请求,循环调用时只有最后一个生效
// ✅ 默认:useAxios 封装
const { exec: getListExec } = usePostXxxV1ListPage();

await getListExec();
// ✅ 批量:原始接口 + Promise.all
await Promise.all(ids.map(id => deleteXxxV1Item({ id })));

API 导入规则

所有接口和类型统一从 controller/index.tsinterface/index.ts 导入,降低耦合度,方便后期重构:

// ✅ 从 controller 统一导入
import type { PostGmpV1CrowdListInput } from '@/api/gmp/controller';
import { postGmpV1CrowdList } from '@/api/gmp/controller';
// ✅ 公共类型从 interface 导入
import type { CrowdItemVo } from '@/api/gmp/interface';
// ❌ 不要从具体文件导入
import { postGmpV1CrowdDetail } from '@/api/gmp/controller/RenQunGuanLi/postGmpV1CrowdList';

错误处理

  • 无需 try/catchcreateAxios 拦截器自动弹出错误提示,业务代码不需要手动 catch
  • 无需判断 code:非正常响应码会被拦截器自动 reject,不需要 if (code !== 200)
  • 需要 finally 时:使用 try/finally(不写 catch),用于清理副作用

使用流程

  1. 如果提供了接口地址,第一步找到 @/api/xxx/controller 中定义的请求方法
  2. 仔细阅读接口文件中的类型定义,不要自己编参数
  3. 根据场景选择 useXxx 版本或原始版本
  4. 下拉框、单选框等数据源可从接口文档注释获取,获取后在 useXxxOptions 中抽离复用

更多用法示例见 api-conventions.md

Pinia 状态管理

Store 文件组织

modules/xxx/
├── stores/
│   └── useXxxStore.ts    # 模块专用 store
└── index.vue

何时使用

Pinia 适合跨多层组件共享状态、异步任务轮询等场景。不要过度使用——只在父子组件间传递的数据用 props/emits,局部状态用 ref,简单表单数据用 v-model。

使用规范

// 命名体现业务含义,不要用 const store = useXxxStore
const xxxStore = useXxxStore();

xxxStore.resetStore(); // 进入页面时重置,确保干净状态

跨组件共享状态

当组件嵌套超过 2 层且需要共享状态时,用 Pinia 替代 props 层层传递:

index.vue → Result.vue → ResultSuccess.vue → ImageSection.vue
                    ↓
              各组件直接访问 store,不需要 props 逐层传递

Store 标准写法、轮询任务模式等见 pinia-patterns.md

参考文档

主题说明参考
CRUD 代码模板新建增删改查页面时必读,完整的文件拆分和代码模板crud.md
编码约定详解守卫语句、函数参数设计、工具库用法等完整示例coding-conventions.md
接口调用指南useAxios 用法、类型定义、导入规则完整说明api-conventions.md
Pinia 使用模式Store 创建、重置、跨组件共享等模式pinia-patterns.md
组件拆分规范何时拆分、目录结构、Props 传递原则directory-structure.md
字典模块规范useDictionary、Store 创建、命名规范dictionary.md
环境变量配置.env 分层原则、域名规则、多环境构建env.md

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

git-auto-commit-push

No summary provided by upstream source.

Repository SourceNeeds Review
General

giime-components

No summary provided by upstream source.

Repository SourceNeeds Review
General

apifox-mock

No summary provided by upstream source.

Repository SourceNeeds Review
General

git-create-mr

No summary provided by upstream source.

Repository SourceNeeds Review
frontend-mandatory-standards | V50.AI