代码格式化开发文档
1. 概述
梵医云前端项目使用多种工具进行代码格式化和质量检查,包括Prettier、ESLint、Stylelint和EditorConfig,确保代码风格统一和质量可控。
1.1 代码格式化工具
| 工具 | 用途 | 配置文件 |
|---|---|---|
| Prettier | 代码格式化 | prettier.config.js |
| ESLint | JavaScript/TypeScript代码检查 | .eslintrc.js |
| Stylelint | CSS/SCSS代码检查 | stylelint.config.js |
| EditorConfig | 编辑器配置 | .editorconfig |
| Husky | Git钩子 | .husky/ |
| lint-staged | 暂存文件检查 | package.json |
1.2 格式化特点
- 自动格式化:保存时自动格式化代码
- Git钩子:提交前自动检查代码
- 统一风格:团队代码风格统一
- 类型安全:TypeScript类型检查
- Vue支持:完整的Vue 3支持
2. Prettier配置
2.1 配置文件
配置文件:[prettier.config.js](file:///e:/git.fanyicloud.com.cn/fanyi-cloud-ui/prettier.config.js)
module.exports = {
printWidth: 100, // 每行代码长度(默认80)
tabWidth: 2, // 每个tab相当于多少个空格(默认2)
useTabs: false, // 是否使用tab进行缩进(默认false)
semi: false, // 声明结尾使用分号(默认true)
vueIndentScriptAndStyle: false,
singleQuote: true, // 使用单引号(默认false)
quoteProps: 'as-needed', // 对象属性引号策略
bracketSpacing: true, // 对象字面量的大括号间使用空格(默认true)
trailingComma: 'none', // 多行使用拖尾逗号(默认none)
jsxSingleQuote: false,
arrowParens: 'always', // 箭头函数参数括号
insertPragma: false,
requirePragma: false,
proseWrap: 'never',
htmlWhitespaceSensitivity: 'strict',
endOfLine: 'auto',
rangeStart: 0
}2.2 配置说明
| 配置项 | 值 | 说明 |
|---|---|---|
| printWidth | 100 | 每行最大字符数 |
| tabWidth | 2 | 缩进空格数 |
| useTabs | false | 使用空格而非tab |
| semi | false | 不使用分号 |
| singleQuote | true | 使用单引号 |
| quoteProps | 'as-needed' | 按需添加属性引号 |
| bracketSpacing | true | 对象括号内添加空格 |
| trailingComma | 'none' | 不使用尾随逗号 |
| arrowParens | 'always' | 箭头函数始终使用括号 |
2.3 使用Prettier
命令行使用
# 格式化所有文件
pnpm lint:format
# 格式化指定文件
pnpm prettier --write src/**/*.vue
# 检查文件格式
pnpm prettier --check src/**/*.vueVS Code集成
在 [.vscode/settings.json](file:///e:/git.fanyicloud.com.cn/fanyi-cloud-ui/.vscode/settings.json) 中配置:
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"prettier.printWidth": 100,
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[vue]": {
"editor.defaultFormatter": "Vue.volar"
}
}格式化示例
格式化前:
const obj = { name: "张三", age: 25, address: "北京市" };
const arr = [1,2,3,4,5];
const fn = (a,b)=>{return a+b};格式化后:
const obj = { name: '张三', age: 25, address: '北京市' }
const arr = [1, 2, 3, 4, 5]
const fn = (a, b) => {
return a + b
}3. ESLint配置
3.1 配置文件
配置文件:[.eslintrc.js](file:///e:/git.fanyicloud.com.cn/fanyi-cloud-ui/.eslintrc.js)
const { defineConfig } = require('eslint-define-config')
module.exports = defineConfig({
root: true,
env: {
browser: true,
node: true,
es6: true
},
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser',
ecmaVersion: 2020,
sourceType: 'module',
jsxPragma: 'React',
ecmaFeatures: {
jsx: true
}
},
extends: [
'plugin:vue/vue3-recommended',
'plugin:@typescript-eslint/recommended',
'prettier',
'plugin:prettier/recommended',
'@unocss'
],
rules: {
'vue/no-setup-props-destructure': 'off',
'vue/script-setup-uses-vars': 'error',
'vue/no-reserved-component-names': 'off',
'@typescript-eslint/ban-ts-ignore': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-empty-function': 'off',
'vue/custom-event-name-casing': 'off',
'no-use-before-define': 'off',
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/ban-types': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-unused-vars': 'off',
'no-unused-vars': 'off',
'space-before-function-paren': 'off',
'vue/attributes-order': 'off',
'vue/one-component-per-file': 'off',
'vue/html-closing-bracket-newline': 'off',
'vue/max-attributes-per-line': 'off',
'vue/multiline-html-element-content-newline': 'off',
'vue/singleline-html-element-content-newline': 'off',
'vue/attribute-hyphenation': 'off',
'vue/require-default-prop': 'off',
'vue/require-explicit-emits': 'off',
'vue/require-toggle-inside-transition': 'off',
'vue/html-self-closing': 'off',
'vue/first-attribute-linebreak': 'off',
'vue/multi-word-component-names': 'off',
'vue/no-v-html': 'off',
'prettier/prettier': 'off',
'@unocss/order': 'off',
'@unocss/order-attributify': 'off'
}
})3.2 主要规则说明
| 规则 | 值 | 说明 |
|---|---|---|
| vue/script-setup-uses-vars | error | 检查script setup中使用的变量 |
| @typescript-eslint/no-explicit-any | off | 允许使用any类型 |
| @typescript-eslint/no-unused-vars | off | 关闭未使用变量检查 |
| vue/no-v-html | off | 允许使用v-html |
| prettier/prettier | off | 关闭Prettier的ESLint校验 |
3.3 使用ESLint
命令行使用
# 检查代码
pnpm lint:eslint
# 自动修复
pnpm lint:eslint --fix
# 检查指定文件
pnpm eslint src/**/*.vueVS Code集成
在 [.vscode/settings.json](file:///e:/git.fanyicloud.com.cn/fanyi-cloud-ui/.vscode/settings.json) 中配置:
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
}常见错误及修复
错误:未使用的变量
// 错误
const unused = 123
// 修复:删除未使用的变量或使用下划线前缀
const _unused = 123错误:未定义的变量
// 错误
console.log(undefinedVar)
// 修复:定义变量
const undefinedVar = 'value'
console.log(undefinedVar)错误:Vue组件命名
<!-- 错误:组件名不是多词 -->
<script setup>
const Header = defineComponent({
name: 'Header'
})
</script>
<!-- 修复:使用多词组件名 -->
<script setup>
const PageHeader = defineComponent({
name: 'PageHeader'
})
</script>4. Stylelint配置
4.1 配置文件
配置文件:[stylelint.config.js](file:///e:/git.fanyicloud.com.cn/fanyi-cloud-ui/stylelint.config.js)
module.exports = {
root: true,
plugins: ['stylelint-order'],
customSyntax: 'postcss-html',
extends: ['stylelint-config-standard'],
rules: {
'selector-pseudo-class-no-unknown': [
true,
{
ignorePseudoClasses: ['global', 'deep']
}
],
'at-rule-no-unknown': [
true,
{
ignoreAtRules: ['function', 'if', 'each', 'include', 'mixin']
}
],
'media-query-no-invalid': null,
'function-no-unknown': null,
'no-empty-source': null,
'named-grid-areas-no-invalid': null,
'unicode-bom': 'never',
'no-descending-specificity': null,
'font-family-no-missing-generic-family-keyword': null,
'declaration-colon-space-after': 'always-single-line',
'declaration-colon-space-before': 'never',
'declaration-block-trailing-semicolon': null,
'rule-empty-line-before': [
'always',
{
ignore: ['after-comment', 'first-nested']
}
],
'unit-no-unknown': [
true,
{
ignoreUnits: ['rpx']
}
],
'order/order': [
[
'dollar-variables',
'custom-properties',
'at-rules',
'declarations',
{
type: 'at-rule',
name: 'supports'
},
{
type: 'at-rule',
name: 'media'
},
'rules'
],
{
severity: 'warning'
}
],
'order/properties-order': [
'position',
'top',
'right',
'bottom',
'left',
'z-index',
'display',
'float',
'width',
'height',
// ... 更多属性顺序规则
]
},
ignoreFiles: ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts'],
overrides: [
{
files: ['*.vue', '**/*.vue', '*.html', '**/*.html'],
extends: ['stylelint-config-recommended', 'stylelint-config-html'],
rules: {
'keyframes-name-pattern': null,
'selector-class-pattern': null,
'no-duplicate-selectors': null,
'selector-pseudo-class-no-unknown': [
true,
{
ignorePseudoClasses: ['deep', 'global']
}
],
'selector-pseudo-element-no-unknown': [
true,
{
ignorePseudoElements: ['v-deep', 'v-global', 'v-slotted']
}
]
}
}
]
}4.2 主要规则说明
| 规则 | 值 | 说明 |
|---|---|---|
| declaration-colon-space-after | always-single-line | 单行声明冒号后加空格 |
| declaration-colon-space-before | never | 冒号前不加空格 |
| unit-no-unknown | true | 禁止未知单位(忽略rpx) |
| order/order | 自定义 | 属性顺序规则 |
| order/properties-order | 自定义 | CSS属性顺序 |
4.3 使用Stylelint
命令行使用
# 检查样式文件
pnpm lint:style
# 自动修复
pnpm lint:style --fix
# 检查指定文件
pnpm stylelint src/**/*.scssVS Code集成
在 [.vscode/settings.json](file:///e:/git.fanyicloud.com.cn/fanyi-cloud-ui/.vscode/settings.json) 中配置:
{
"stylelint.enable": true,
"stylelint.validate": ["css", "less", "postcss", "scss", "vue", "sass"],
"editor.codeActionsOnSave": {
"source.fixAll.stylelint": "explicit"
}
}格式化示例
格式化前:
.container{width:100%;height:200px;margin:0;padding:10px;background-color:#fff}格式化后:
.container {
width: 100%;
height: 200px;
margin: 0;
padding: 10px;
background-color: #fff;
}5. EditorConfig配置
5.1 配置文件
配置文件:[.editorconfig](file:///e:/git.fanyicloud.com.cn/fanyi-cloud-ui/.editorconfig)
root = true
[*.{js,ts,vue}]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
max_line_length = 100
[*.md]
max_line_length = off
trim_trailing_whitespace = false5.2 配置说明
| 配置项 | 值 | 说明 |
|---|---|---|
| charset | utf-8 | 文件字符编码 |
| end_of_line | lf | 换行符类型 |
| insert_final_newline | true | 文件末尾插入新行 |
| indent_style | space | 缩进风格 |
| indent_size | 2 | 缩进大小 |
| max_line_length | 100 | 最大行长度 |
5.3 VS Code支持
VS Code内置支持EditorConfig,确保安装了EditorConfig扩展:
code --install-extension EditorConfig.EditorConfig6. Git钩子配置
6.1 lint-staged配置
在 [package.json](file:///e:/git.fanyicloud.com.cn/fanyi-cloud-ui/package.json) 中配置:
{
"lint-staged": {
"*.{js,ts,vue}": [
"eslint --fix",
"prettier --write"
],
"*.{css,scss}": [
"stylelint --fix",
"prettier --write"
]
}
}6.2 Husky配置
Husky用于在Git操作时自动执行检查:
# 安装Husky
pnpm install husky --save-dev
# 初始化Husky
pnpm husky install
# 添加pre-commit钩子
pnpm husky add .husky/pre-commit "npx lint-staged"6.3 提交前检查
提交代码时,Husky会自动执行以下检查:
- 对暂存的JS/TS/Vue文件执行ESLint检查和修复
- 对暂存的CSS/SCSS文件执行Stylelint检查和修复
- 对所有文件执行Prettier格式化
如果检查失败,提交将被阻止。
7. 使用指南
7.1 日常开发流程
# 1. 开发代码
# ...
# 2. 格式化代码
pnpm lint:format
# 3. 检查代码
pnpm lint:eslint
pnpm lint:style
# 4. 提交代码(自动执行检查)
git add .
git commit -m "feat: 添加新功能"7.2 VS Code自动格式化
配置VS Code在保存时自动格式化:
{
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.fixAll.stylelint": "explicit"
}
}7.3 忽略格式化
如果需要忽略某些文件的格式化,可以创建 .prettierignore 文件:
# 忽略构建文件
dist/
build/
# 忽略依赖
node_modules/
# 忽略配置文件
*.config.js8. 最佳实践
8.1 代码风格规范
JavaScript/TypeScript
// 推荐:使用const和let
const name = '张三'
let count = 0
// 不推荐:使用var
var name = '张三'
// 推荐:使用箭头函数
const add = (a: number, b: number): number => {
return a + b
}
// 不推荐:使用function
function add(a: number, b: number): number {
return a + b
}
// 推荐:使用模板字符串
const message = `你好,${name}`
// 不推荐:使用字符串拼接
const message = '你好,' + nameVue组件
<!-- 推荐:使用script setup -->
<script setup lang="ts">
import { ref } from 'vue'
const count = ref(0)
</script>
<!-- 不推荐:使用Options API -->
<script>
export default {
data() {
return {
count: 0
}
}
}
</script>CSS/SCSS
// 推荐:使用嵌套
.container {
width: 100%;
.header {
padding: 10px;
}
}
// 不推荐:扁平化
.container {
width: 100%;
}
.container .header {
padding: 10px;
}8.2 命名规范
// 组件名:PascalCase
const UserProfile = defineComponent({})
// 变量名:camelCase
const userName = '张三'
const isLoggedIn = true
// 常量名:UPPER_SNAKE_CASE
const MAX_COUNT = 100
const API_BASE_URL = 'https://api.example.com'
// 函数名:camelCase
const getUserInfo = () => {}
const handleSubmit = () => {}
// 类名:PascalCase
class UserService {}
class HttpClient {}
// 接口名:PascalCase
interface UserInfo {}
interface ApiResponse {}8.3 注释规范
// 单行注释:解释代码意图
const userAge = 25 // 用户年龄
// 多行注释:复杂逻辑说明
/**
* 计算用户年龄
* @param birthDate 出生日期
* @returns 年龄
*/
const calculateAge = (birthDate: Date): number => {
const today = new Date()
const birth = new Date(birthDate)
let age = today.getFullYear() - birth.getFullYear()
const monthDiff = today.getMonth() - birth.getMonth()
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())) {
age--
}
return age
}
// TODO注释:标记待办事项
// TODO: 添加用户验证逻辑
// FIXME注释:标记需要修复的问题
// FIXME: 这个方法有性能问题,需要优化8.4 导入顺序
// 1. Vue相关导入
import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
// 2. 第三方库导入
import axios from 'axios'
import dayjs from 'dayjs'
// 3. 项目内部导入
import { useUserStore } from '@/store/modules/user'
import { getUserInfo } from '@/api/user'
// 4. 类型导入
import type { UserInfo } from '@/types/user'9. 常见问题
9.1 格式化不生效
问题原因:
- Prettier扩展未安装
- 配置文件路径错误
- VS Code设置冲突
解决方案:
# 确保安装Prettier扩展
code --install-extension esbenp.prettier-vscode
# 检查配置文件
ls -la | grep prettier
# 重启VS Code9.2 ESLint和Prettier冲突
问题原因:
- ESLint规则与Prettier规则冲突
- 未安装eslint-config-prettier
解决方案:
// .eslintrc.js
module.exports = {
extends: [
'plugin:vue/vue3-recommended',
'plugin:@typescript-eslint/recommended',
'prettier', // 必须放在最后
'plugin:prettier/recommended'
]
}9.3 Git钩子不执行
问题原因:
- Husky未正确安装
- Git钩子权限问题
解决方案:
# 重新安装Husky
pnpm install husky --save-dev
pnpm husky install
# 检查钩子文件
ls -la .husky/
# 手动设置钩子权限
chmod +x .husky/pre-commit9.4 提交被阻止
问题原因:
- 代码格式不正确
- 存在ESLint错误
解决方案:
# 手动修复代码
pnpm lint:eslint --fix
pnpm lint:style --fix
pnpm lint:format
# 如果无法修复,可以强制提交(不推荐)
git commit --no-verify -m "message"10. 完整示例
10.1 Vue组件示例
<template>
<div class="user-profile">
<h1 class="user-profile__name">{{ userName }}</h1>
<p class="user-profile__email">{{ userEmail }}</p>
<el-button @click="handleLogout">退出登录</el-button>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useRouter } from 'vue-router'
import { useUserStore } from '@/store/modules/user'
const router = useRouter()
const userStore = useUserStore()
const userInfo = ref<UserInfo | null>(null)
const userName = computed(() => userInfo.value?.name || '未知用户')
const userEmail = computed(() => userInfo.value?.email || '')
const handleLogout = () => {
userStore.logout()
router.push('/login')
}
onMounted(async () => {
userInfo.value = await userStore.getUserInfo()
})
</script>
<style lang="scss" scoped>
.user-profile {
padding: 20px;
&__name {
font-size: 24px;
color: #333;
}
&__email {
font-size: 14px;
color: #666;
margin-top: 10px;
}
}
</style>10.2 TypeScript模块示例
import axios from 'axios'
import type { UserInfo, ApiResponse } from '@/types'
const API_BASE_URL = import.meta.env.VITE_API_URL
export class UserService {
/**
* 获取用户信息
* @param userId 用户ID
* @returns 用户信息
*/
static async getUserInfo(userId: number): Promise<UserInfo> {
const response = await axios.get<ApiResponse<UserInfo>>(
`${API_BASE_URL}/user/${userId}`
)
return response.data.data
}
/**
* 更新用户信息
* @param userId 用户ID
* @param data 更新数据
* @returns 更新后的用户信息
*/
static async updateUser(
userId: number,
data: Partial<UserInfo>
): Promise<UserInfo> {
const response = await axios.put<ApiResponse<UserInfo>>(
`${API_BASE_URL}/user/${userId}`,
data
)
return response.data.data
}
}注意:本文档持续更新中,如有问题请及时反馈。
