Skip to content

编码规范

本文档基于 AGENTS.md 中的规范,为贡献者提供编码指南。

TypeScript 规范

类型定义

优先使用 interface

typescript
// ✅ 推荐
interface DesignerConfig {
  ueditorPath?: string
}

// ❌ 避免
type DesignerConfig = {
  ueditorPath?: string
}

类型导入

typescript
// ✅ 推荐
import type { DesignerConfig } from '../types'

// ❌ 避免
import { DesignerConfig } from '../types'

类型注解

为公共 API 添加完整的类型注解:

typescript
// ✅ 推荐
export async function loadMaterials(
  params?: MaterialQueryParams
): Promise<StyleListData> {
  // Implementation
}

// ❌ 避免(缺少返回类型)
export async function loadMaterials(params?) {
  // Implementation
}

JSDoc 注释

所有导出的函数、接口和类型必须有 JSDoc 注释:

typescript
/**
 * Load materials from API
 * 
 * @param params - Query parameters
 * @returns Promise resolving to material list data
 */
export async function loadMaterials(
  params?: MaterialQueryParams
): Promise<StyleListData> {
  // Implementation
}

Vue 组件规范

文件结构

vue
<template>
  <!-- Template content -->
</template>

<script setup lang="ts">
// 1. Vue core imports
import { ref, computed, onMounted } from 'vue'

// 2. Local composables
import { useMaterial } from '../composables/useMaterial'

// 3. Local components
import MaterialPanel from './MaterialPanel.vue'

// 4. Type imports
import type { DesignerConfig } from '../types'

// 5. Props definition
interface Props {
  config?: DesignerConfig
}

const props = withDefaults(defineProps<Props>(), {
  config: () => ({})
})

// 6. Emits definition
const emit = defineEmits<{
  ready: []
  change: [content: string]
}>()

// 7. Component logic
</script>

<style scoped>
/* Styles */
</style>

命名规范

typescript
// ✅ 变量/函数: camelCase
const currentSection = ref(null)
const loadMaterials = async () => {}

// ✅ 组件: PascalCase
import MaterialPanel from './MaterialPanel.vue'

// ✅ 常量: PascalCase 或 UPPER_SNAKE_CASE
const Config = { /* ... */ }
const MAX_SIZE = 100

// ✅ CSS 类: kebab-case
<div class="material-panel"></div>

Props 定义

使用 TypeScript interface + withDefaults:

vue
<script setup lang="ts">
interface Props {
  config?: DesignerConfig
  isEnabled?: boolean
}

const props = withDefaults(defineProps<Props>(), {
  config: () => ({}),
  isEnabled: false
})
</script>

Emits 定义

使用 TypeScript 类型定义:

vue
<script setup lang="ts">
const emit = defineEmits<{
  ready: []                    // 无参数
  change: [content: string]    // 一个参数
  update: [id: number, data: any] // 多个参数
}>()
</script>

Composables 规范

命名和结构

typescript
// ✅ 文件名: useMaterial.ts
// ✅ 函数名: useMaterial

export function useMaterial(config: DesignerConfig) {
  // Reactive state
  const materials = ref<MaterialItem[]>([])
  const loading = ref(false)
  
  // Methods
  const loadMaterials = async () => { /* ... */ }
  
  // Lifecycle
  onMounted(async () => {
    await loadMaterials()
  })
  
  // Grouped return object
  return {
    // Data
    materials,
    
    // Loading states
    loading,
    
    // Methods
    loadMaterials
  }
}

JSDoc 注释

typescript
/**
 * Material management composable
 * 
 * Handles loading and managing material data including
 * categories and material items.
 * 
 * @param config - Designer configuration
 * @returns Material state and methods
 */
export function useMaterial(config: DesignerConfig) {
  // Implementation
}

错误处理

标准模式

typescript
try {
  await someAsyncOperation()
} catch (error) {
  console.error('Failed to perform operation:', error)
  // Set fallback state
  materials.value = []
  hasMore.value = false
}

不要抛出错误(composables)

typescript
// ✅ 推荐
const loadMaterials = async () => {
  try {
    const data = await fetch('/api/materials')
    return data
  } catch (error) {
    console.error('Load failed:', error)
    return { records: [], total: 0, page: 1, pageSize: 20 }
  }
}

// ❌ 避免(除非必要)
const loadMaterials = async () => {
  const data = await fetch('/api/materials')
  return data // 可能抛出错误
}

导入组织

按以下顺序组织导入:

typescript
// 1. Vue core imports
import { ref, computed, onMounted } from 'vue'

// 2. Third-party libraries
import axios from 'axios'

// 3. Local utilities/composables
import { useMaterial } from '@/composables/useMaterial'

// 4. Local components
import MaterialPanel from '@/components/MaterialPanel.vue'

// 5. Type imports
import type { DesignerConfig, MaterialItem } from '@/types'

// 6. CSS/SCSS imports
import '@/styles/index.scss'

路径别名

使用 @/ 别名访问 src 目录:

typescript
// ✅ 推荐
import { Config } from '@/utils/config'

// ❌ 避免
import { Config } from '../../../utils/config'

最佳实践

1. 避免 any 类型

typescript
// ✅ 推荐
function process(data: MaterialItem): void {
  // ...
}

// ❌ 避免
function process(data: any): void {
  // ...
}

2. 使用 const 和 let

typescript
// ✅ 推荐
const MAX_SIZE = 100
let currentIndex = 0

// ❌ 避免
var MAX_SIZE = 100

3. 清理资源

typescript
onUnmounted(() => {
  // Clean up event listeners
  window.removeEventListener('resize', handleResize)
  
  // Clear timers
  clearTimeout(timer)
  
  // Unsubscribe
  subscription?.unsubscribe()
})

4. 使用 computed 而非 watch

typescript
// ✅ 推荐
const filteredMaterials = computed(() => {
  return materials.value.filter(m => m.categoryId === currentCategoryId.value)
})

// ❌ 避免(除非必要)
watch([materials, currentCategoryId], () => {
  filteredMaterials.value = materials.value.filter(m => 
    m.categoryId === currentCategoryId.value
  )
})

5. 具名导出 vs 默认导出

typescript
// ✅ Vue 组件: 默认导出
export default defineComponent({
  name: 'MaterialPanel'
})

// ✅ 工具函数: 具名导出
export function loadUEditor() { }
export function processStyles() { }

CSS/SCSS 规范

类名前缀

scss
// Page builder sections
.pb-section { }
.pb-section-active { }

// UEditor Plus Designer components
.upd-toolbar { }
.upd-panel { }

Scoped 样式

组件样式使用 scoped:

vue
<style scoped>
.material-panel {
  /* Component-specific styles */
}
</style>

全局样式

全局样式放在 src/styles/:

scss
// src/styles/index.scss
.pb-section {
  position: relative;
  cursor: pointer;
}

提交规范

Commit 消息格式

<type>(<scope>): <subject>

<body>

<footer>

Type 类型

  • feat: 新功能
  • fix: 修复 bug
  • docs: 文档更新
  • style: 代码格式(不影响代码运行)
  • refactor: 重构
  • test: 测试
  • chore: 构建过程或辅助工具的变动

示例

feat(material): add material search functionality

Add search input in material panel to filter materials by keywords.
The search is debounced by 300ms to improve performance.

Closes #123

代码审查检查清单

  • [ ] 所有公共 API 都有 TypeScript 类型定义
  • [ ] 所有导出的函数/接口都有 JSDoc 注释
  • [ ] 没有使用 any 类型(除非必要)
  • [ ] 错误处理得当
  • [ ] 资源清理(event listeners, timers)
  • [ ] 遵循命名规范
  • [ ] 导入顺序正确
  • [ ] 运行 npm run type-check 无错误

相关文档

Released under the Apache License 2.0. 压缩版免费使用,商用或源码需授权。