cdspec 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +14 -0
- package/CLAUDE.md +10 -0
- package/README.md +55 -0
- package/cdspec.config.yaml +34 -0
- package/dist/cli.js +94 -0
- package/dist/config/default.js +48 -0
- package/dist/config/loader.js +30 -0
- package/dist/config/path.js +11 -0
- package/dist/config/types.js +1 -0
- package/dist/skill-core/adapters/claudecode-adapter.js +35 -0
- package/dist/skill-core/adapters/codex-adapter.js +28 -0
- package/dist/skill-core/adapters/iflow-adapter.js +39 -0
- package/dist/skill-core/adapters/index.js +34 -0
- package/dist/skill-core/adapters/shared.js +36 -0
- package/dist/skill-core/agent-config.js +40 -0
- package/dist/skill-core/manifest-loader.js +63 -0
- package/dist/skill-core/scaffold.js +169 -0
- package/dist/skill-core/service.js +156 -0
- package/dist/skill-core/tool-interactions.js +70 -0
- package/dist/skill-core/types.js +1 -0
- package/dist/skill-core/validator.js +25 -0
- package/dist/task-core/parser.js +70 -0
- package/dist/task-core/service.js +28 -0
- package/dist/task-core/storage.js +159 -0
- package/dist/task-core/types.js +1 -0
- package/dist/utils/frontmatter.js +40 -0
- package/dist/utils/fs.js +37 -0
- package/package.json +29 -0
- package/src/cli.ts +105 -0
- package/src/config/default.ts +51 -0
- package/src/config/loader.ts +37 -0
- package/src/config/path.ts +13 -0
- package/src/config/types.ts +22 -0
- package/src/skill-core/adapters/claudecode-adapter.ts +45 -0
- package/src/skill-core/adapters/codex-adapter.ts +36 -0
- package/src/skill-core/adapters/iflow-adapter.ts +49 -0
- package/src/skill-core/adapters/index.ts +39 -0
- package/src/skill-core/adapters/shared.ts +45 -0
- package/src/skill-core/manifest-loader.ts +79 -0
- package/src/skill-core/scaffold.ts +192 -0
- package/src/skill-core/service.ts +199 -0
- package/src/skill-core/tool-interactions.ts +95 -0
- package/src/skill-core/types.ts +22 -0
- package/src/skill-core/validator.ts +28 -0
- package/src/task-core/parser.ts +89 -0
- package/src/task-core/service.ts +49 -0
- package/src/task-core/storage.ts +177 -0
- package/src/task-core/types.ts +15 -0
- package/src/types/yaml.d.ts +4 -0
- package/src/utils/frontmatter.ts +55 -0
- package/src/utils/fs.ts +41 -0
- package/templates/design-doc/SKILL.md +99 -0
- package/templates/design-doc/agents/openai.yaml +4 -0
- package/templates/design-doc/references//345/237/272/347/272/277/346/250/241/346/235/277.md +46 -0
- package/templates/design-doc/references//345/242/236/351/207/217/351/234/200/346/261/202/346/250/241/346/235/277.md +32 -0
- package/templates/design-doc/references//345/275/222/346/241/243/346/243/200/346/237/245/346/270/205/345/215/225.md +15 -0
- package/templates/design-doc/references//347/224/237/344/272/247/345/267/245/345/215/225/345/237/272/347/272/277/347/244/272/344/276/213.md +470 -0
- package/templates/design-doc/scripts/validate_doc_layout.sh +49 -0
- package/templates/frontend-develop-standard/SKILL.md +63 -0
- package/templates/frontend-develop-standard/agents/openai.yaml +4 -0
- package/templates/frontend-develop-standard/references/frontend_develop_standard.md +749 -0
- package/templates/standards-backend/SKILL.md +55 -0
- package/templates/standards-backend/agents/openai.yaml +4 -0
- package/templates/standards-backend/references/DDD/346/236/266/346/236/204/347/272/246/346/235/237.md +103 -0
- package/templates/standards-backend/references/JUC/345/271/266/345/217/221/350/247/204/350/214/203.md +232 -0
- package/templates/standards-backend/references//344/274/240/347/273/237/344/270/211/345/261/202/346/236/266/346/236/204/347/272/246/346/235/237.md +35 -0
- package/templates/standards-backend/references//345/220/216/347/253/257/345/274/200/345/217/221/350/247/204/350/214/203.md +49 -0
- package/templates/standards-backend/references//346/225/260/346/215/256/345/272/223/350/256/276/350/256/241/350/247/204/350/214/203.md +116 -0
- package/templates/standards-backend/references//350/256/276/350/256/241/346/250/241/345/274/217/350/220/275/345/234/260/346/211/213/345/206/214.md +395 -0
- package/tests/skill.test.ts +191 -0
- package/tests/task.test.ts +55 -0
- package/tsconfig.json +16 -0
- package/vitest.config.ts +9 -0
|
@@ -0,0 +1,749 @@
|
|
|
1
|
+
# 前端开发规范
|
|
2
|
+
|
|
3
|
+
## 一、Vue3 编码规范
|
|
4
|
+
|
|
5
|
+
### 1.1 命名规范
|
|
6
|
+
|
|
7
|
+
#### 1.1.1 组件命名
|
|
8
|
+
|
|
9
|
+
**文件夹和文件名**
|
|
10
|
+
- 使用小驼峰命名法(camelCase)
|
|
11
|
+
- 示例:`testList/testList.vue`
|
|
12
|
+
|
|
13
|
+
**组件引用**
|
|
14
|
+
```vue
|
|
15
|
+
<template>
|
|
16
|
+
<!-- 自闭合组件 -->
|
|
17
|
+
<test-component />
|
|
18
|
+
|
|
19
|
+
<!-- 包含内容的组件 -->
|
|
20
|
+
<container-component>
|
|
21
|
+
This is content
|
|
22
|
+
</container-component>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<script setup>
|
|
26
|
+
import TestComponent from './components/TestComponent.vue'
|
|
27
|
+
import ContainerComponent from './components/ContainerComponent.vue'
|
|
28
|
+
</script>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
#### 1.1.2 变量和常量命名
|
|
32
|
+
|
|
33
|
+
**变量命名**
|
|
34
|
+
- 使用小驼峰命名法(camelCase)
|
|
35
|
+
- 语义化,见名知意
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
// 推荐
|
|
39
|
+
let userInfo = {}
|
|
40
|
+
let infoList = []
|
|
41
|
+
let isLoading = false
|
|
42
|
+
|
|
43
|
+
// 不推荐
|
|
44
|
+
let a = {}
|
|
45
|
+
let data = []
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**常量命名**
|
|
49
|
+
- 使用全大写字母,单词间用下划线分隔
|
|
50
|
+
- 语义化,见名知意
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
// 推荐
|
|
54
|
+
const MAX_NUMBER = 10
|
|
55
|
+
const API_BASE_URL = 'https://api.example.com'
|
|
56
|
+
const DEFAULT_PAGE_SIZE = 20
|
|
57
|
+
|
|
58
|
+
// 不推荐
|
|
59
|
+
const max = 10
|
|
60
|
+
const url = 'https://api.example.com'
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
#### 1.1.3 接口类型命名
|
|
64
|
+
|
|
65
|
+
接口类型以大写字母 `C` 为前缀:
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
export interface CState {
|
|
69
|
+
roleList: Object
|
|
70
|
+
restartVisible: boolean
|
|
71
|
+
sid: string
|
|
72
|
+
dialogVisible: boolean
|
|
73
|
+
loading: boolean
|
|
74
|
+
list: CDevmemt[]
|
|
75
|
+
total: number
|
|
76
|
+
listQuery: CListQuery
|
|
77
|
+
name: string
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface CStateParams extends CState {
|
|
81
|
+
id: string
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 1.2 代码风格
|
|
86
|
+
|
|
87
|
+
#### 1.2.1 引号使用规范
|
|
88
|
+
|
|
89
|
+
- HTML 属性使用双引号 `""`
|
|
90
|
+
- JavaScript 代码使用单引号 `''`
|
|
91
|
+
|
|
92
|
+
```vue
|
|
93
|
+
<template>
|
|
94
|
+
<test-component data-attributes="test" class="container" />
|
|
95
|
+
</template>
|
|
96
|
+
|
|
97
|
+
<script setup>
|
|
98
|
+
const str = 'hello world'
|
|
99
|
+
const message = 'This is a message'
|
|
100
|
+
</script>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
#### 1.2.2 多属性换行
|
|
104
|
+
|
|
105
|
+
当元素有多个属性时,每个属性独占一行:
|
|
106
|
+
|
|
107
|
+
```vue
|
|
108
|
+
<template>
|
|
109
|
+
<a-table-column
|
|
110
|
+
data-index="name"
|
|
111
|
+
title="名称"
|
|
112
|
+
:width="180"
|
|
113
|
+
align="center"
|
|
114
|
+
:sorter="true"
|
|
115
|
+
/>
|
|
116
|
+
|
|
117
|
+
<li
|
|
118
|
+
v-for="(item, index) in testList"
|
|
119
|
+
:key="index"
|
|
120
|
+
:span="4"
|
|
121
|
+
style="margin-bottom: 10px"
|
|
122
|
+
>
|
|
123
|
+
{{ item.name }}
|
|
124
|
+
</li>
|
|
125
|
+
</template>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
#### 1.2.3 组件样式作用域
|
|
129
|
+
|
|
130
|
+
组件样式必须设置作用域,避免样式污染:
|
|
131
|
+
|
|
132
|
+
```vue
|
|
133
|
+
<style scoped lang="less">
|
|
134
|
+
.test-list {
|
|
135
|
+
padding: 20px;
|
|
136
|
+
|
|
137
|
+
.test-item {
|
|
138
|
+
margin-bottom: 10px;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
</style>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
#### 1.2.4 样式穿透
|
|
145
|
+
|
|
146
|
+
使用 `:deep()` 进行样式穿透:
|
|
147
|
+
|
|
148
|
+
```vue
|
|
149
|
+
<style scoped lang="less">
|
|
150
|
+
:deep(.ant-card-body) {
|
|
151
|
+
padding: 20px;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
:deep(.ant-table) {
|
|
155
|
+
.ant-table-thead {
|
|
156
|
+
background-color: #f5f7fa;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
</style>
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### 1.3 组件开发规范
|
|
163
|
+
|
|
164
|
+
#### 1.3.1 生命周期钩子顺序
|
|
165
|
+
|
|
166
|
+
按照执行顺序排列生命周期钩子:
|
|
167
|
+
|
|
168
|
+
```vue
|
|
169
|
+
<script setup lang="ts" name="componentName">
|
|
170
|
+
import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from 'vue'
|
|
171
|
+
|
|
172
|
+
// 1. 挂载前
|
|
173
|
+
onBeforeMount(() => {
|
|
174
|
+
console.log('onBeforeMount')
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
// 2. 挂载后
|
|
178
|
+
onMounted(() => {
|
|
179
|
+
console.log('onMounted')
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
// 3. 更新前
|
|
183
|
+
onBeforeUpdate(() => {
|
|
184
|
+
console.log('onBeforeUpdate')
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
// 4. 更新后
|
|
188
|
+
onUpdated(() => {
|
|
189
|
+
console.log('onUpdated')
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
// 5. 卸载前
|
|
193
|
+
onBeforeUnmount(() => {
|
|
194
|
+
console.log('onBeforeUnmount')
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
// 6. 卸载后
|
|
198
|
+
onUnmounted(() => {
|
|
199
|
+
console.log('onUnmounted')
|
|
200
|
+
})
|
|
201
|
+
</script>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
#### 1.3.2 自定义事件命名
|
|
205
|
+
|
|
206
|
+
- 事件名使用 `on-` 前缀 + 动词
|
|
207
|
+
- 事件处理函数使用 `handle` 前缀 + 动词
|
|
208
|
+
|
|
209
|
+
```vue
|
|
210
|
+
<!-- 父组件 -->
|
|
211
|
+
<template>
|
|
212
|
+
<my-component @on-change="handleChange" @on-submit="handleSubmit" />
|
|
213
|
+
</template>
|
|
214
|
+
|
|
215
|
+
<script setup>
|
|
216
|
+
const handleChange = (value) => {
|
|
217
|
+
console.log('value changed:', value)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const handleSubmit = (data) => {
|
|
221
|
+
console.log('form submitted:', data)
|
|
222
|
+
}
|
|
223
|
+
</script>
|
|
224
|
+
|
|
225
|
+
<!-- 子组件 -->
|
|
226
|
+
<script setup>
|
|
227
|
+
const emit = defineEmits(['on-change', 'on-submit'])
|
|
228
|
+
|
|
229
|
+
const onChange = () => {
|
|
230
|
+
emit('on-change', newValue)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const onSubmit = () => {
|
|
234
|
+
emit('on-submit', formData)
|
|
235
|
+
}
|
|
236
|
+
</script>
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### 1.4 注释规范
|
|
240
|
+
|
|
241
|
+
#### 1.4.1 模板注释
|
|
242
|
+
|
|
243
|
+
```vue
|
|
244
|
+
<template>
|
|
245
|
+
<div>
|
|
246
|
+
<!-- 用户信息表单 -->
|
|
247
|
+
<a-form>
|
|
248
|
+
<!-- 用户名 -->
|
|
249
|
+
<a-form-item label="用户名" name="username">
|
|
250
|
+
<a-input v-model:value="form.username" />
|
|
251
|
+
</a-form-item>
|
|
252
|
+
|
|
253
|
+
<!-- 邮箱 -->
|
|
254
|
+
<a-form-item label="邮箱" name="email">
|
|
255
|
+
<a-input v-model:value="form.email" />
|
|
256
|
+
</a-form-item>
|
|
257
|
+
</a-form>
|
|
258
|
+
</div>
|
|
259
|
+
</template>
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
#### 1.4.2 脚本注释
|
|
263
|
+
|
|
264
|
+
```javascript
|
|
265
|
+
// 删除用户
|
|
266
|
+
const deleteUser = (id) => {
|
|
267
|
+
// 实现逻辑
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// 新增用户
|
|
271
|
+
const addUser = (userData) => {
|
|
272
|
+
// 实现逻辑
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* 更新用户信息
|
|
277
|
+
* @param {string} id - 用户ID
|
|
278
|
+
* @param {Object} userData - 用户数据
|
|
279
|
+
* @returns {Promise} 返回更新结果
|
|
280
|
+
*/
|
|
281
|
+
const updateUser = async (id, userData) => {
|
|
282
|
+
// 实现逻辑
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### 1.5 代码质量
|
|
287
|
+
|
|
288
|
+
#### 1.5.1 移除调试代码
|
|
289
|
+
|
|
290
|
+
提交代码前必须移除:
|
|
291
|
+
- `console.log()`
|
|
292
|
+
- `debugger`
|
|
293
|
+
- 注释掉的无用代码
|
|
294
|
+
|
|
295
|
+
```javascript
|
|
296
|
+
// ❌ 不推荐
|
|
297
|
+
const fetchData = () => {
|
|
298
|
+
console.log('fetching data...')
|
|
299
|
+
debugger
|
|
300
|
+
// const oldCode = 'something'
|
|
301
|
+
return api.getData()
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// ✅ 推荐
|
|
305
|
+
const fetchData = () => {
|
|
306
|
+
return api.getData()
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### 1.6 完整示例
|
|
311
|
+
|
|
312
|
+
```vue
|
|
313
|
+
<template>
|
|
314
|
+
<div class="user-form">
|
|
315
|
+
<a-card>
|
|
316
|
+
<a-form
|
|
317
|
+
ref="formRef"
|
|
318
|
+
:model="formState"
|
|
319
|
+
:rules="rules"
|
|
320
|
+
:label-col="{ span: 4 }"
|
|
321
|
+
:wrapper-col="{ span: 20 }"
|
|
322
|
+
>
|
|
323
|
+
<!-- 名称 -->
|
|
324
|
+
<a-form-item label="名称" name="name">
|
|
325
|
+
<a-input v-model:value="formState.name" placeholder="请输入名称" />
|
|
326
|
+
</a-form-item>
|
|
327
|
+
|
|
328
|
+
<!-- 标签 -->
|
|
329
|
+
<a-form-item label="标签" name="label">
|
|
330
|
+
<a-button type="link" @click="addLabel">
|
|
331
|
+
+添加一行
|
|
332
|
+
</a-button>
|
|
333
|
+
<span>(最多 20 个标签)</span>
|
|
334
|
+
</a-form-item>
|
|
335
|
+
|
|
336
|
+
<!-- 描述 -->
|
|
337
|
+
<a-form-item label="描述" name="describe">
|
|
338
|
+
<a-textarea
|
|
339
|
+
v-model:value="formState.describe"
|
|
340
|
+
:rows="2"
|
|
341
|
+
placeholder="请输入描述"
|
|
342
|
+
/>
|
|
343
|
+
</a-form-item>
|
|
344
|
+
|
|
345
|
+
<!-- 操作按钮 -->
|
|
346
|
+
<a-form-item :wrapper-col="{ offset: 4, span: 20 }">
|
|
347
|
+
<a-button @click="handleCancel">取消</a-button>
|
|
348
|
+
<a-button type="primary" @click="handleSubmit" style="margin-left: 10px">
|
|
349
|
+
确认
|
|
350
|
+
</a-button>
|
|
351
|
+
</a-form-item>
|
|
352
|
+
</a-form>
|
|
353
|
+
</a-card>
|
|
354
|
+
</div>
|
|
355
|
+
</template>
|
|
356
|
+
|
|
357
|
+
<script setup lang="ts">
|
|
358
|
+
import { reactive, ref } from 'vue'
|
|
359
|
+
import type { FormInstance, Rule } from 'ant-design-vue/es/form'
|
|
360
|
+
import { useRouter, useRoute } from 'vue-router'
|
|
361
|
+
import { activationApi } from '/@/api/devmgmt'
|
|
362
|
+
|
|
363
|
+
const route = useRoute()
|
|
364
|
+
const router = useRouter()
|
|
365
|
+
|
|
366
|
+
const uuid = route.query.uuid
|
|
367
|
+
const type = route.query.type
|
|
368
|
+
|
|
369
|
+
const formRef = ref<FormInstance>()
|
|
370
|
+
const formState = reactive({
|
|
371
|
+
name: '',
|
|
372
|
+
label: [{ label: '' }],
|
|
373
|
+
longitude: '',
|
|
374
|
+
latitude: '',
|
|
375
|
+
address: '',
|
|
376
|
+
describe: ''
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
// 表单验证规则
|
|
380
|
+
const rules: Record<string, Rule[]> = {
|
|
381
|
+
name: [
|
|
382
|
+
{ required: true, message: '请输入设备名称', trigger: 'blur' }
|
|
383
|
+
]
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// 新增标签
|
|
387
|
+
const addLabel = () => {
|
|
388
|
+
if (formState.label.length < 20) {
|
|
389
|
+
formState.label.push({ label: '' })
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// 删除标签
|
|
394
|
+
const removeLabel = (item: any) => {
|
|
395
|
+
const index = formState.label.indexOf(item)
|
|
396
|
+
if (index !== -1) {
|
|
397
|
+
formState.label.splice(index, 1)
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// 取消操作
|
|
402
|
+
const handleCancel = () => {
|
|
403
|
+
router.back()
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// 提交表单
|
|
407
|
+
const handleSubmit = async () => {
|
|
408
|
+
if (!formRef.value) return
|
|
409
|
+
|
|
410
|
+
try {
|
|
411
|
+
await formRef.value.validate()
|
|
412
|
+
const label = formState.label.map(item => item.label).filter(Boolean)
|
|
413
|
+
|
|
414
|
+
const params = {
|
|
415
|
+
sid: uuid,
|
|
416
|
+
name: formState.name,
|
|
417
|
+
type: type,
|
|
418
|
+
address: formState.address,
|
|
419
|
+
longitude: parseFloat(formState.longitude),
|
|
420
|
+
latitude: parseFloat(formState.latitude),
|
|
421
|
+
detail: formState.describe,
|
|
422
|
+
label: label
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
activationApi(params).then((res: any) => {
|
|
426
|
+
// 处理成功逻辑
|
|
427
|
+
})
|
|
428
|
+
} catch (error) {
|
|
429
|
+
console.log('验证失败:', error)
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
</script>
|
|
433
|
+
|
|
434
|
+
<style scoped lang="scss">
|
|
435
|
+
.user-form {
|
|
436
|
+
padding: 20px;
|
|
437
|
+
|
|
438
|
+
.width120 {
|
|
439
|
+
width: 120px;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
.latitude-text .width300 {
|
|
443
|
+
width: 300px;
|
|
444
|
+
margin: 0 10px 0 22px;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
</style>
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
---
|
|
451
|
+
|
|
452
|
+
## 二、项目配置规范
|
|
453
|
+
|
|
454
|
+
### 2.1 EditorConfig 配置
|
|
455
|
+
|
|
456
|
+
EditorConfig 帮助团队维护一致的编码风格。
|
|
457
|
+
|
|
458
|
+
在项目根目录创建 `.editorconfig` 文件:
|
|
459
|
+
|
|
460
|
+
```ini
|
|
461
|
+
# http://editorconfig.org
|
|
462
|
+
|
|
463
|
+
root = true
|
|
464
|
+
|
|
465
|
+
[*]
|
|
466
|
+
charset = utf-8
|
|
467
|
+
indent_style = space
|
|
468
|
+
indent_size = 2
|
|
469
|
+
end_of_line = lf
|
|
470
|
+
trim_trailing_whitespace = true
|
|
471
|
+
insert_final_newline = true
|
|
472
|
+
|
|
473
|
+
[*.md]
|
|
474
|
+
max_line_length = off
|
|
475
|
+
trim_trailing_whitespace = false
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
**配置说明:**
|
|
479
|
+
- `charset`: 文件字符集为 UTF-8
|
|
480
|
+
- `indent_style`: 缩进风格(space 或 tab)
|
|
481
|
+
- `indent_size`: 缩进大小为 2 个空格
|
|
482
|
+
- `end_of_line`: 换行符类型(lf、cr 或 crlf)
|
|
483
|
+
- `trim_trailing_whitespace`: 去除行尾空白字符
|
|
484
|
+
- `insert_final_newline`: 文件末尾插入新行
|
|
485
|
+
|
|
486
|
+
**VSCode 插件:** 需安装 `EditorConfig for VS Code`
|
|
487
|
+
|
|
488
|
+
### 2.2 Prettier 配置
|
|
489
|
+
|
|
490
|
+
Prettier 是强大的代码格式化工具,支持多种前端语言。
|
|
491
|
+
|
|
492
|
+
在项目根目录创建 `.prettierrc` 文件:
|
|
493
|
+
|
|
494
|
+
```json
|
|
495
|
+
{
|
|
496
|
+
"useTabs": false,
|
|
497
|
+
"tabWidth": 2,
|
|
498
|
+
"printWidth": 80,
|
|
499
|
+
"singleQuote": true,
|
|
500
|
+
"trailingComma": "none",
|
|
501
|
+
"semi": false
|
|
502
|
+
}
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
**配置说明:**
|
|
506
|
+
|
|
507
|
+
| 属性 | 说明 |
|
|
508
|
+
|------|------|
|
|
509
|
+
| useTabs | false: 使用空格缩进,true: 使用 tab 缩进 |
|
|
510
|
+
| tabWidth | 缩进空格数,设置为 2 |
|
|
511
|
+
| printWidth | 单行最大字符数,推荐 80 或 100 |
|
|
512
|
+
| singleQuote | true: 使用单引号,false: 使用双引号 |
|
|
513
|
+
| trailingComma | 多行输入尾逗号,设置为 none |
|
|
514
|
+
| semi | false: 不加分号,true: 加分号 |
|
|
515
|
+
|
|
516
|
+
**VSCode 插件:** 需安装 `Prettier - Code formatter`
|
|
517
|
+
|
|
518
|
+
**常见问题:**
|
|
519
|
+
|
|
520
|
+
1. **保存代码无法格式化**
|
|
521
|
+
- 原因:未设置默认格式化插件
|
|
522
|
+
- 解决:右键 → 格式化文档 → 选择 Prettier
|
|
523
|
+
|
|
524
|
+
2. **保存无法自动格式化**
|
|
525
|
+
- 原因:未开启保存时格式化
|
|
526
|
+
- 解决:文件 → 设置 → 搜索 "Format On Save" → 勾选
|
|
527
|
+
|
|
528
|
+
**快速格式化所有文件:**
|
|
529
|
+
|
|
530
|
+
在 `package.json` 添加脚本:
|
|
531
|
+
|
|
532
|
+
```json
|
|
533
|
+
{
|
|
534
|
+
"scripts": {
|
|
535
|
+
"prettier": "prettier --write ."
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
运行命令:
|
|
541
|
+
|
|
542
|
+
```bash
|
|
543
|
+
npm run prettier
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
### 2.3 Prettier 忽略配置
|
|
547
|
+
|
|
548
|
+
在项目根目录创建 `.prettierignore` 文件:
|
|
549
|
+
|
|
550
|
+
```
|
|
551
|
+
/dist/*
|
|
552
|
+
.local
|
|
553
|
+
.output.js
|
|
554
|
+
/node_modules/**
|
|
555
|
+
**/*.svg
|
|
556
|
+
**/*.sh
|
|
557
|
+
/public/*
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
### 2.4 ESLint 配置
|
|
561
|
+
|
|
562
|
+
Vue 项目创建时会自动配置 ESLint。
|
|
563
|
+
|
|
564
|
+
**VSCode 插件:** 需安装 `ESLint`
|
|
565
|
+
|
|
566
|
+
**解决 ESLint 和 Prettier 冲突:**
|
|
567
|
+
|
|
568
|
+
安装插件(Vue 创建项目时选择 Prettier 会自动安装):
|
|
569
|
+
|
|
570
|
+
```bash
|
|
571
|
+
npm install eslint-plugin-prettier eslint-config-prettier -D
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
在 `.eslintrc.js` 文件的 `extends` 中添加:
|
|
575
|
+
|
|
576
|
+
```javascript
|
|
577
|
+
module.exports = {
|
|
578
|
+
extends: [
|
|
579
|
+
// ... 其他配置
|
|
580
|
+
'plugin:prettier/recommended'
|
|
581
|
+
]
|
|
582
|
+
}
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
**注意:** `plugin:` 和 `prettier` 之间没有空格
|
|
586
|
+
|
|
587
|
+
---
|
|
588
|
+
|
|
589
|
+
## 三、Git 提交规范
|
|
590
|
+
|
|
591
|
+
### 3.1 Husky 配置
|
|
592
|
+
|
|
593
|
+
Husky 用于在 Git 操作时触发钩子。
|
|
594
|
+
|
|
595
|
+
**安装 Husky:**
|
|
596
|
+
|
|
597
|
+
```bash
|
|
598
|
+
npm install husky -D
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
**配置 prepare 脚本:**
|
|
602
|
+
|
|
603
|
+
```bash
|
|
604
|
+
npm set-script prepare "husky install"
|
|
605
|
+
npm run prepare
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
**添加 pre-commit 钩子:**
|
|
609
|
+
|
|
610
|
+
```bash
|
|
611
|
+
npx husky add .husky/pre-commit "npx eslint ./"
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
如果自动生成失败,手动创建 `.husky/pre-commit` 文件:
|
|
615
|
+
|
|
616
|
+
```bash
|
|
617
|
+
#!/bin/sh
|
|
618
|
+
. "$(dirname "$0")/_/husky.sh"
|
|
619
|
+
|
|
620
|
+
npx eslint ./
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
### 3.2 Commitlint 配置
|
|
624
|
+
|
|
625
|
+
**安装 Commitlint:**
|
|
626
|
+
|
|
627
|
+
```bash
|
|
628
|
+
npm install --save-dev @commitlint/config-conventional @commitlint/cli
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
**创建配置文件:**
|
|
632
|
+
|
|
633
|
+
```bash
|
|
634
|
+
echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
**注意:** 确保文件编码为 UTF-8
|
|
638
|
+
|
|
639
|
+
**添加 commit-msg 钩子:**
|
|
640
|
+
|
|
641
|
+
```bash
|
|
642
|
+
npx husky add .husky/commit-msg "npx --no -- commitlint --edit ${1}"
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
如果自动生成失败,手动创建 `.husky/commit-msg` 文件:
|
|
646
|
+
|
|
647
|
+
```bash
|
|
648
|
+
#!/bin/sh
|
|
649
|
+
. "$(dirname "$0")/_/husky.sh"
|
|
650
|
+
|
|
651
|
+
npx --no-install commitlint --edit $1
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
**禁用 Husky(如需要):**
|
|
655
|
+
|
|
656
|
+
在 `.git/config` 文件中注释掉:
|
|
657
|
+
|
|
658
|
+
```
|
|
659
|
+
# hooksPath = .husky
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
### 3.3 Commit 提交规范
|
|
663
|
+
|
|
664
|
+
**提交类型说明:**
|
|
665
|
+
|
|
666
|
+
| 类型 | 说明 |
|
|
667
|
+
|------|------|
|
|
668
|
+
| feat | 新增功能 |
|
|
669
|
+
| fix | 修复 Bug |
|
|
670
|
+
| docs | 文档更新 |
|
|
671
|
+
| style | 代码格式调整(不影响功能) |
|
|
672
|
+
| refactor | 代码重构 |
|
|
673
|
+
| perf | 性能优化 |
|
|
674
|
+
| test | 测试相关 |
|
|
675
|
+
| chore | 构建过程或辅助工具的变动 |
|
|
676
|
+
| revert | 回滚提交 |
|
|
677
|
+
| build | 影响构建系统或外部依赖 |
|
|
678
|
+
| ci | CI 配置文件和脚本的变动 |
|
|
679
|
+
| types | 类型定义文件更改 |
|
|
680
|
+
| wip | 开发中(work in progress) |
|
|
681
|
+
|
|
682
|
+
**提交格式:**
|
|
683
|
+
|
|
684
|
+
```bash
|
|
685
|
+
git commit -m "type: description"
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
**示例:**
|
|
689
|
+
|
|
690
|
+
```bash
|
|
691
|
+
# 新增功能
|
|
692
|
+
git commit -m "feat: 添加用户登录功能"
|
|
693
|
+
|
|
694
|
+
# 修复 Bug
|
|
695
|
+
git commit -m "fix: 修复用户列表分页问题"
|
|
696
|
+
|
|
697
|
+
# 文档更新
|
|
698
|
+
git commit -m "docs: 更新 API 文档"
|
|
699
|
+
|
|
700
|
+
# 代码重构
|
|
701
|
+
git commit -m "refactor: 重构用户模块代码结构"
|
|
702
|
+
|
|
703
|
+
# 性能优化
|
|
704
|
+
git commit -m "perf: 优化列表渲染性能"
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
**完整格式(可选):**
|
|
708
|
+
|
|
709
|
+
```bash
|
|
710
|
+
git commit -m "feat: 添加用户登录功能
|
|
711
|
+
|
|
712
|
+
- 实现用户名密码登录
|
|
713
|
+
- 添加记住密码功能
|
|
714
|
+
- 集成第三方登录"
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
---
|
|
718
|
+
|
|
719
|
+
## 四、最佳实践建议
|
|
720
|
+
|
|
721
|
+
### 4.1 代码组织
|
|
722
|
+
|
|
723
|
+
- 按功能模块组织代码,而非按文件类型
|
|
724
|
+
- 保持组件单一职责
|
|
725
|
+
- 合理拆分大型组件
|
|
726
|
+
- 复用性高的代码抽取为公共组件或工具函数
|
|
727
|
+
|
|
728
|
+
### 4.2 性能优化
|
|
729
|
+
|
|
730
|
+
- 合理使用 `v-if` 和 `v-show`
|
|
731
|
+
- 列表渲染使用唯一 `key`
|
|
732
|
+
- 避免在模板中使用复杂表达式
|
|
733
|
+
- 使用 `computed` 缓存计算结果
|
|
734
|
+
- 大型列表使用虚拟滚动
|
|
735
|
+
|
|
736
|
+
### 4.3 安全规范
|
|
737
|
+
|
|
738
|
+
- 避免使用 `v-html`,防止 XSS 攻击
|
|
739
|
+
- 敏感信息不要硬编码在前端
|
|
740
|
+
- API 请求添加适当的权限验证
|
|
741
|
+
- 用户输入进行验证和过滤
|
|
742
|
+
|
|
743
|
+
### 4.4 可维护性
|
|
744
|
+
|
|
745
|
+
- 编写清晰的注释
|
|
746
|
+
- 保持代码简洁易读
|
|
747
|
+
- 遵循团队统一的编码规范
|
|
748
|
+
- 定期进行代码审查
|
|
749
|
+
- 及时更新文档
|