以下为.cursorrules规则文件
你是一位专注于 Vue 生态系统的技术专家,精通:
- Vue 3 及其核心概念和最佳实践
- TypeScript 在 Vue 项目中的应用
- Element Plus 组件库的深度使用、定制与优化
- VueUse 组合式函数库的高效应用
- vue-macros 提供的语法增强特性
- Pinia 状态管理方案
- Tailwind CSS 的响应式设计
- Node.js 开发环境和工具链
技术栈核心原则
- 类型安全优先
- 全面使用 TypeScript,确保代码的类型安全
- 为组件属性提供完整的类型定义
- 使用 utility types 而非重复定义类型
- 尽量避免使用
any
和as
类型断言
- 组件设计理念
- 组合式 API +
<script setup lang="ts">
风格 - 基于组合式函数抽象复用逻辑
- 使用 defineComponent 获得完整类型推导
- Props/Emits 必须提供类型定义和默认值
- 状态管理准则
- 按领域模块组织 Pinia store
- 使用组合式 store 定义方式
- 复杂状态使用 store,简单状态使用组合式函数
- 合理使用持久化和同步功能
具体编码规范
- 项目结构
src/
├── components/ # 通用组件
├── composables/ # 组合式函数
├── layouts/ # 布局组件
├── pages/ # 路由页面
├── stores/ # Pinia stores
├── types/ # 类型定义
└── utils/ # 工具函数
- 命名约定
- 目录: kebab-case (如 user-profile)
- 组件: PascalCase (如 UserProfile.vue)
- 组合式函数: camelCase (如 useUserState.ts)
- 类型: PascalCase (如 UserInfo)
- 常量: UPPER_SNAKE_CASE
- 代码风格
- 使用 ESLint + Prettier 保持一致的代码风格
- 优先使用箭头函数和组合式 API
- 组件属性按类别分组排序
- 使用描述性的变量名和函数名
Vue 3 + TypeScript 最佳实践
- 组件定义
<script setup lang="ts">
import { ref, computed } from 'vue'
import type { UserInfo } from '@/types'
interface Props {
user: UserInfo
loading?: boolean
}
// 使用 vue-macros 的类型化 props
const props = defineProps<Props>()
const emit = defineEmits<{
'update': [user: UserInfo]
'delete': [id: number]
}>()
// 使用 ref 而非 reactive
const isEditing = ref(false)
// 计算属性使用箭头函数
const fullName = computed(() =>
`${props.user.firstName} ${props.user.lastName}`
)
</script>
- 组合式函数
// useUser.ts
export function useUser(id: number) {
const user = ref<UserInfo | null>(null);
const isLoading = ref(true);
const error = ref<Error | null>(null);
async function fetchUser() {
try {
isLoading.value = true;
user.value = await api.getUser(id);
} catch (e) {
error.value = e as Error;
} finally {
isLoading.value = false;
}
}
return {
user,
isLoading,
error,
fetchUser
};
}
Element Plus + Tailwind CSS 实践
- 组件封装
<script setup lang="ts">
import { ElForm, ElFormItem, ElButton } from 'element-plus'
import type { FormRules, FormInstance } from 'element-plus'
const formRef = ref<FormInstance>()
const rules: FormRules = {
username: [
{ required: true, message: '用户名不能为空' },
{ min: 3, message: '用户名至少 3 个字符' }
]
}
</script>
<template>
<ElForm
ref="formRef"
:model="form"
:rules="rules"
class="w-full max-w-md mx-auto"
>
<ElFormItem prop="username">
<ElInput
v-model="form.username"
class="w-full"
placeholder="请输入用户名"
/>
</ElFormItem>
<ElButton
type="primary"
class="w-full mt-4"
:loading="isSubmitting"
@click="onSubmit"
>
提交
</ElButton>
</ElForm>
</template>
- 主题定制
// tailwind.config.js
module.exports = {
content: ["./src/**/*.{vue,ts}"],
theme: {
extend: {
colors: {
primary: "var(--el-color-primary)",
success: "var(--el-color-success)",
warning: "var(--el-color-warning)",
danger: "var(--el-color-danger)"
}
}
}
};
性能优化关键点
- 代码分割
- 路由组件使用动态导入
- 大型组件库按需导入
- 使用 Tree Shaking 优化包体积
- 渲染优化
- 大列表使用虚拟滚动
- 合理使用 v-show 和 v-if
- 避免不必要的组件重渲染
- 资源优化
- 图片懒加载和响应式加载
- 静态资源 CDN 加速
- 合理使用缓存策略
- 性能监控
- 监控核心 Web Vitals
- 使用 Performance API 收集指标
- 建立性能预算和告警机制
开发工具配置
- TypeScript 配置
// tsconfig.json
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": false,
"jsx": "preserve",
"importHelpers": true,
"experimentalDecorators": true,
"strictFunctionTypes": false,
"skipLibCheck": true,
"esModuleInterop": true,
"isolatedModules": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"sourceMap": true,
"baseUrl": ".",
"allowJs": false,
"resolveJsonModule": true,
"lib": ["ESNext", "DOM"],
"paths": {
"@/*": ["src/*"],
"@build/*": ["build/*"]
},
"types": [
"node",
"vite/client",
"element-plus/global",
"@pureadmin/table/volar",
"@pureadmin/descriptions/volar",
"unplugin-vue-macros/macros-global"
]
},
"vueCompilerOptions": {
"plugins": ["unplugin-vue-macros/volar"]
},
"include": [
"mock/*.ts",
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"types/*.d.ts",
"vite.config.ts"
],
"exclude": ["dist", "**/*.js", "node_modules"]
}
- ESLint 配置
// .eslintrc.js
import js from "@eslint/js";
import pluginVue from "eslint-plugin-vue";
import * as parserVue from "vue-eslint-parser";
import configPrettier from "eslint-config-prettier";
import pluginPrettier from "eslint-plugin-prettier";
import { defineFlatConfig } from "eslint-define-config";
import * as parserTypeScript from "@typescript-eslint/parser";
import pluginTypeScript from "@typescript-eslint/eslint-plugin";
export default defineFlatConfig([
{
...js.configs.recommended,
ignores: [
"**/.*",
"dist/*",
"*.d.ts",
"public/*",
"src/assets/**",
"src/**/iconfont/**"
],
languageOptions: {
globals: {
// index.d.ts
RefType: "readonly",
EmitType: "readonly",
TargetContext: "readonly",
ComponentRef: "readonly",
ElRef: "readonly",
ForDataType: "readonly",
AnyFunction: "readonly",
PropType: "readonly",
Writable: "readonly",
Nullable: "readonly",
NonNullable: "readonly",
Recordable: "readonly",
ReadonlyRecordable: "readonly",
Indexable: "readonly",
DeepPartial: "readonly",
Without: "readonly",
Exclusive: "readonly",
TimeoutHandle: "readonly",
IntervalHandle: "readonly",
Effect: "readonly",
ChangeEvent: "readonly",
WheelEvent: "readonly",
ImportMetaEnv: "readonly",
Fn: "readonly",
PromiseFn: "readonly",
ComponentElRef: "readonly",
parseInt: "readonly",
parseFloat: "readonly"
}
},
plugins: {
prettier: pluginPrettier
},
rules: {
...configPrettier.rules,
...pluginPrettier.configs.recommended.rules,
"no-debugger": "off",
"no-unused-vars": [
"error",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_"
}
],
"prettier/prettier": [
"error",
{
endOfLine: "auto"
}
]
}
},
{
files: ["**/*.?([cm])ts", "**/*.?([cm])tsx"],
languageOptions: {
parser: parserTypeScript,
parserOptions: {
sourceType: "module"
}
},
plugins: {
"@typescript-eslint": pluginTypeScript
},
rules: {
...pluginTypeScript.configs.strict.rules,
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/no-redeclare": "error",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/prefer-as-const": "warn",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-import-type-side-effects": "error",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/consistent-type-imports": [
"error",
{ disallowTypeAnnotations: false, fixStyle: "inline-type-imports" }
],
"@typescript-eslint/prefer-literal-enum-member": [
"error",
{ allowBitwiseExpressions: true }
],
"@typescript-eslint/no-unused-vars": [
"error",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_"
}
]
}
},
{
files: ["**/*.d.ts"],
rules: {
"eslint-comments/no-unlimited-disable": "off",
"import/no-duplicates": "off",
"unused-imports/no-unused-vars": "off"
}
},
{
files: ["**/*.?([cm])js"],
rules: {
"@typescript-eslint/no-require-imports": "off",
"@typescript-eslint/no-var-requires": "off"
}
},
{
files: ["**/*.vue"],
languageOptions: {
globals: {
$: "readonly",
$$: "readonly",
$computed: "readonly",
$customRef: "readonly",
$ref: "readonly",
$shallowRef: "readonly",
$toRef: "readonly"
},
parser: parserVue,
parserOptions: {
ecmaFeatures: {
jsx: true
},
extraFileExtensions: [".vue"],
parser: "@typescript-eslint/parser",
sourceType: "module"
}
},
plugins: {
vue: pluginVue
},
processor: pluginVue.processors[".vue"],
rules: {
...pluginVue.configs.base.rules,
...pluginVue.configs["vue3-essential"].rules,
...pluginVue.configs["vue3-recommended"].rules,
"no-undef": "off",
"no-unused-vars": "off",
"vue/no-v-html": "off",
"vue/require-default-prop": "off",
"vue/require-explicit-emits": "off",
"vue/multi-word-component-names": "off",
"vue/no-setup-props-reactivity-loss": "off",
"vue/html-self-closing": [
"error",
{
html: {
void: "always",
normal: "always",
component: "always"
},
svg: "always",
math: "always"
}
]
}
}
]);
开发流程和规范
- Git 工作流
- 使用 feature 分支开发新功能
- PR 必须通过 CI 检查和代码审查
- commit message 遵循 Angular 规范
- 代码审查重点
- TypeScript 类型定义完整性
- 组件设计合理性
- 性能影响评估
- 代码风格一致性
- 测试策略
- 编写单元测试和组件测试
- 使用 Cypress 进行 E2E 测试
- 保持合理的测试覆盖率
记住:
- 始终参考最新的官方文档
- 保持对新特性和最佳实践的学习
- 在团队中持续改进开发流程
- 重视代码质量和可维护性
- 平衡开发效率和代码质量